diff --git a/network/interface.go b/network/interface.go index be0b04b..e8e4752 100644 --- a/network/interface.go +++ b/network/interface.go @@ -3,6 +3,7 @@ package network import ( "fmt" "strconv" + "strings" ) type InterfaceGenerator interface { @@ -11,6 +12,8 @@ type InterfaceGenerator interface { Netdev() string Link() string Network() string + Type() string + ModprobeParams() string } type networkInterface interface { @@ -68,6 +71,10 @@ func (i *logicalInterface) Children() []networkInterface { return i.children } +func (i *logicalInterface) ModprobeParams() string { + return "" +} + func (i *logicalInterface) setConfigDepth(depth int) { i.configDepth = depth } @@ -84,9 +91,14 @@ func (p *physicalInterface) Netdev() string { return "" } +func (p *physicalInterface) Type() string { + return "physical" +} + type bondInterface struct { logicalInterface - slaves []string + slaves []string + options map[string]string } func (b *bondInterface) Name() string { @@ -97,6 +109,19 @@ func (b *bondInterface) Netdev() string { return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name) } +func (b *bondInterface) Type() string { + return "bond" +} + +func (b *bondInterface) ModprobeParams() string { + params := "" + for name, val := range b.options { + params += fmt.Sprintf("%s=%s ", name, val) + } + params = strings.TrimSuffix(params, " ") + return params +} + type vlanInterface struct { logicalInterface id int @@ -123,6 +148,10 @@ func (v *vlanInterface) Netdev() string { return config } +func (v *vlanInterface) Type() string { + return "vlan" +} + func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { interfaceMap := createInterfaces(stanzas) linkAncestors(interfaceMap) @@ -141,15 +170,22 @@ func createInterfaces(stanzas []*stanzaInterface) map[string]networkInterface { for _, iface := range stanzas { switch iface.kind { case interfaceBond: + bondOptions := make(map[string]string) + for _, k := range []string{"mode", "miimon", "lacp-rate"} { + if v, ok := iface.options["bond-"+k]; ok && len(v) > 0 { + bondOptions[k] = v[0] + } + } interfaceMap[iface.name] = &bondInterface{ logicalInterface{ name: iface.name, config: iface.configMethod, children: []networkInterface{}, }, - iface.options["slaves"], + iface.options["bond-slaves"], + bondOptions, } - for _, slave := range iface.options["slaves"] { + for _, slave := range iface.options["bond-slaves"] { if _, ok := interfaceMap[slave]; !ok { interfaceMap[slave] = &physicalInterface{ logicalInterface{ diff --git a/network/interface_test.go b/network/interface_test.go index c98bf60..eaf1aff 100644 --- a/network/interface_test.go +++ b/network/interface_test.go @@ -36,6 +36,7 @@ func TestPhysicalInterfaceNetwork(t *testing.T) { name: "testbond1", }, nil, + nil, }, &vlanInterface{ logicalInterface{ @@ -67,14 +68,14 @@ VLAN=testvlan2 } func TestBondInterfaceName(t *testing.T) { - b := bondInterface{logicalInterface{name: "testname"}, nil} + b := bondInterface{logicalInterface{name: "testname"}, nil, nil} if b.Name() != "testname" { t.FailNow() } } func TestBondInterfaceNetdev(t *testing.T) { - b := bondInterface{logicalInterface{name: "testname"}, nil} + b := bondInterface{logicalInterface{name: "testname"}, nil, nil} netdev := `[NetDev] Kind=bond Name=testname @@ -102,6 +103,7 @@ func TestBondInterfaceNetwork(t *testing.T) { name: "testbond1", }, nil, + nil, }, &vlanInterface{ logicalInterface{ @@ -120,6 +122,7 @@ func TestBondInterfaceNetwork(t *testing.T) { }, }, nil, + nil, } network := `[Match] Name=testname @@ -218,6 +221,61 @@ Gateway=1.2.3.4 } } +func TestType(t *testing.T) { + for _, tt := range []struct { + i InterfaceGenerator + t string + }{ + { + i: &physicalInterface{}, + t: "physical", + }, + { + i: &vlanInterface{}, + t: "vlan", + }, + { + i: &bondInterface{}, + t: "bond", + }, + } { + if tp := tt.i.Type(); tp != tt.t { + t.Fatalf("bad type (%q): got %s, want %s", tt.i, tp, tt.t) + } + } +} + +func TestModprobeParams(t *testing.T) { + for _, tt := range []struct { + i InterfaceGenerator + p string + }{ + { + i: &physicalInterface{}, + p: "", + }, + { + i: &vlanInterface{}, + p: "", + }, + { + i: &bondInterface{ + logicalInterface{}, + nil, + map[string]string{ + "a": "1", + "b": "2", + }, + }, + p: "a=1 b=2", + }, + } { + if p := tt.i.ModprobeParams(); p != tt.p { + t.Fatalf("bad params (%q): got %s, want %s", tt.i, p, tt.p) + } + } +} + func TestBuildInterfacesLo(t *testing.T) { stanzas := []*stanzaInterface{ &stanzaInterface{ @@ -242,7 +300,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) { auto: false, configMethod: configMethodManual{}, options: map[string][]string{ - "slaves": []string{"eth0"}, + "bond-slaves": []string{"eth0"}, }, }, } @@ -255,6 +313,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) { configDepth: 1, }, []string{"eth0"}, + map[string]string{}, } eth0 := &physicalInterface{ logicalInterface{ @@ -323,7 +382,9 @@ func TestBuildInterfaces(t *testing.T) { auto: false, configMethod: configMethodManual{}, options: map[string][]string{ - "slaves": []string{"eth0"}, + "bond-slaves": []string{"eth0"}, + "bond-mode": []string{"4"}, + "bond-miimon": []string{"100"}, }, }, &stanzaInterface{ @@ -332,7 +393,7 @@ func TestBuildInterfaces(t *testing.T) { auto: false, configMethod: configMethodManual{}, options: map[string][]string{ - "slaves": []string{"bond0"}, + "bond-slaves": []string{"bond0"}, }, }, &stanzaInterface{ @@ -385,6 +446,7 @@ func TestBuildInterfaces(t *testing.T) { configDepth: 2, }, []string{"bond0"}, + map[string]string{}, } bond0 := &bondInterface{ logicalInterface{ @@ -394,6 +456,10 @@ func TestBuildInterfaces(t *testing.T) { configDepth: 1, }, []string{"eth0"}, + map[string]string{ + "mode": "4", + "miimon": "100", + }, } eth0 := &physicalInterface{ logicalInterface{ diff --git a/network/stanza.go b/network/stanza.go index 005231b..3319ce7 100644 --- a/network/stanza.go +++ b/network/stanza.go @@ -293,7 +293,6 @@ func parseHwaddress(options map[string][]string, iface string) (net.HardwareAddr } func parseBondStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) { - options["slaves"] = options["bond-slaves"] return &stanzaInterface{name: iface, kind: interfaceBond, configMethod: conf, options: options}, nil } diff --git a/network/stanza_test.go b/network/stanza_test.go index 440dcf8..02cc006 100644 --- a/network/stanza_test.go +++ b/network/stanza_test.go @@ -129,7 +129,7 @@ func TestParseBondStanzaNoSlaves(t *testing.T) { if err != nil { t.FailNow() } - if bond.options["slaves"] != nil { + if bond.options["bond-slaves"] != nil { t.FailNow() } } @@ -152,9 +152,6 @@ func TestParseBondStanza(t *testing.T) { if bond.configMethod != conf { t.FailNow() } - if !reflect.DeepEqual(bond.options["slaves"], options["bond-slaves"]) { - t.FailNow() - } } func TestParsePhysicalStanza(t *testing.T) { diff --git a/system/networkd.go b/system/networkd.go index a982869..9562789 100644 --- a/system/networkd.go +++ b/system/networkd.go @@ -6,6 +6,7 @@ import ( "net" "os/exec" "path" + "strings" "github.com/coreos/coreos-cloudinit/network" "github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink" @@ -26,10 +27,10 @@ func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) { return } - if err = probe8012q(); err != nil { + if err = maybeProbe8012q(interfaces); err != nil { return } - return + return maybeProbeBonding(interfaces) } func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error { @@ -55,8 +56,24 @@ func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error { return nil } -func probe8012q() error { - return exec.Command("modprobe", "8021q").Run() +func maybeProbe8012q(interfaces []network.InterfaceGenerator) error { + for _, iface := range interfaces { + if iface.Type() == "vlan" { + return exec.Command("modprobe", "8021q").Run() + } + } + return nil +} + +func maybeProbeBonding(interfaces []network.InterfaceGenerator) error { + args := []string{"bonding"} + for _, iface := range interfaces { + if iface.Type() == "bond" { + args = append(args, strings.Split(iface.ModprobeParams(), " ")...) + break + } + } + return exec.Command("modprobe", args...).Run() } func restartNetworkd() error {