diff --git a/src/go/api/experiment/experiment.go b/src/go/api/experiment/experiment.go index f548474f..9a2230a8 100644 --- a/src/go/api/experiment/experiment.go +++ b/src/go/api/experiment/experiment.go @@ -552,6 +552,26 @@ func Start(ctx context.Context, opts ...StartOption) error { } } + // Creating experiment bridge after launching VMs to ensure the bridge + // already exists in minimega (and OVS) before creating GRE tunnels between + // them. This cannot be done as part of the minimega script template since + // the VM taps (and thus bridges) do not get created until the overall + // minimega namespace is launched. + if exp.Spec.UseGREMesh() || exp.Spec.DefaultBridge() != "phenix" { + if err := mm.CreateBridge(mm.NS(exp.Metadata.Name), mm.Bridge(exp.Spec.DefaultBridge())); err != nil { + if !o.mmErrAsWarn { + mm.ClearNamespace(exp.Spec.ExperimentName()) + return fmt.Errorf("creating experiment bridge: %w", err) + } + + if merr, ok := err.(*multierror.Error); ok { + notes.AddWarnings(ctx, false, merr.Errors...) + } else { + notes.AddWarnings(ctx, false, err) + } + } + } + schedule := make(map[string]string) for _, vm := range mm.GetVMInfo(mm.NS(exp.Spec.ExperimentName())) { diff --git a/src/go/tmpl/templates/minimega_script.tmpl b/src/go/tmpl/templates/minimega_script.tmpl index 34117e76..6044107c 100644 --- a/src/go/tmpl/templates/minimega_script.tmpl +++ b/src/go/tmpl/templates/minimega_script.tmpl @@ -18,10 +18,6 @@ ns del-host all ns add-host localhost {{- end }} -{{- if or (ne .DefaultBridge "phenix") .UseGREMesh }} -ns bridge {{ .DefaultBridge }} gre -{{- end }} - {{- $basedir := .BaseDir }} {{- range .Topology.Nodes }} diff --git a/src/go/util/mm/minimega.go b/src/go/util/mm/minimega.go index d3f83c8d..f83e1b0e 100644 --- a/src/go/util/mm/minimega.go +++ b/src/go/util/mm/minimega.go @@ -507,6 +507,19 @@ func (Minimega) DisconnectVMInterface(opts ...Option) error { return nil } +func (Minimega) CreateBridge(opts ...Option) error { + o := NewOptions(opts...) + + cmd := mmcli.NewNamespacedCommand(o.ns) + cmd.Command = fmt.Sprintf("ns bridge %s gre", o.bridge) + + if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { + return fmt.Errorf("creating bridge %s with GRE mesh between namespace hosts: %w", o.bridge, err) + } + + return nil +} + func (Minimega) CreateTunnel(opts ...Option) error { host, err := GetVMHost(opts...) if err != nil { diff --git a/src/go/util/mm/mm.go b/src/go/util/mm/mm.go index 4fe5e8e0..83ecb091 100644 --- a/src/go/util/mm/mm.go +++ b/src/go/util/mm/mm.go @@ -22,6 +22,8 @@ type MM interface { ConnectVMInterface(...Option) error DisconnectVMInterface(...Option) error + CreateBridge(...Option) error + CreateTunnel(...Option) error GetTunnels(...Option) []map[string]string CloseTunnel(...Option) error diff --git a/src/go/util/mm/option.go b/src/go/util/mm/option.go index 49b0b45f..c95d7087 100644 --- a/src/go/util/mm/option.go +++ b/src/go/util/mm/option.go @@ -10,11 +10,12 @@ import ( type Option func(*options) type options struct { - ns string - vm string - cpu int - mem int - disk string + ns string + vm string + cpu int + mem int + disk string + bridge string injectPart int injects []string @@ -73,6 +74,12 @@ func Disk(d string) Option { } } +func Bridge(b string) Option { + return func(o *options) { + o.bridge = b + } +} + func InjectPartition(p int) Option { return func(o *options) { o.injectPart = p diff --git a/src/go/util/mm/package.go b/src/go/util/mm/package.go index c7583d7d..67c515e5 100644 --- a/src/go/util/mm/package.go +++ b/src/go/util/mm/package.go @@ -60,6 +60,10 @@ func DisconnectVMInterface(opts ...Option) error { return DefaultMM.DisconnectVMInterface(opts...) } +func CreateBridge(opts ...Option) error { + return DefaultMM.CreateBridge(opts...) +} + func CreateTunnel(opts ...Option) error { return DefaultMM.CreateTunnel(opts...) }