From d6a0d0908cf954e0e2a93e61b2a6c07e67c1a931 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Thu, 12 Jun 2014 18:13:19 -0500 Subject: [PATCH] fix(network): Generate prefixes to ensure proper lexicographical ordering In order for networkd to properly configure the network interfaces, the configs must be prefixed to ensure that they load in the correct order (parent interfaces have a lower prefix than their children). --- network/interface.go | 77 ++++++++++++++++++++++++++++++------ network/interface_test.go | 83 +++++++++++++++++++++++++-------------- system/networkd.go | 6 +-- 3 files changed, 121 insertions(+), 45 deletions(-) diff --git a/network/interface.go b/network/interface.go index 070c4ea..be0b04b 100644 --- a/network/interface.go +++ b/network/interface.go @@ -7,15 +7,23 @@ import ( type InterfaceGenerator interface { Name() string + Filename() string Netdev() string Link() string Network() string } +type networkInterface interface { + InterfaceGenerator + Children() []networkInterface + setConfigDepth(int) +} + type logicalInterface struct { - name string - config configMethod - children []InterfaceGenerator + name string + config configMethod + children []networkInterface + configDepth int } func (i *logicalInterface) Network() string { @@ -52,6 +60,18 @@ func (i *logicalInterface) Link() string { return "" } +func (i *logicalInterface) Filename() string { + return fmt.Sprintf("%02x-%s", i.configDepth, i.name) +} + +func (i *logicalInterface) Children() []networkInterface { + return i.children +} + +func (i *logicalInterface) setConfigDepth(depth int) { + i.configDepth = depth +} + type physicalInterface struct { logicalInterface } @@ -104,8 +124,20 @@ func (v *vlanInterface) Netdev() string { } func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { - interfaceMap := make(map[string]InterfaceGenerator) + interfaceMap := createInterfaces(stanzas) + linkAncestors(interfaceMap) + markConfigDepths(interfaceMap) + interfaces := make([]InterfaceGenerator, 0, len(interfaceMap)) + for _, iface := range interfaceMap { + interfaces = append(interfaces, iface) + } + + return interfaces +} + +func createInterfaces(stanzas []*stanzaInterface) map[string]networkInterface { + interfaceMap := make(map[string]networkInterface) for _, iface := range stanzas { switch iface.kind { case interfaceBond: @@ -113,7 +145,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { logicalInterface{ name: iface.name, config: iface.configMethod, - children: []InterfaceGenerator{}, + children: []networkInterface{}, }, iface.options["slaves"], } @@ -123,7 +155,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { logicalInterface{ name: slave, config: configMethodManual{}, - children: []InterfaceGenerator{}, + children: []networkInterface{}, }, } } @@ -137,7 +169,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { logicalInterface{ name: iface.name, config: iface.configMethod, - children: []InterfaceGenerator{}, + children: []networkInterface{}, }, } @@ -151,7 +183,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { logicalInterface{ name: rawDevice, config: configMethodManual{}, - children: []InterfaceGenerator{}, + children: []networkInterface{}, }, } } @@ -160,14 +192,17 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { logicalInterface{ name: iface.name, config: iface.configMethod, - children: []InterfaceGenerator{}, + children: []networkInterface{}, }, id, rawDevice, } } } + return interfaceMap +} +func linkAncestors(interfaceMap map[string]networkInterface) { for _, iface := range interfaceMap { switch i := iface.(type) { case *vlanInterface: @@ -192,11 +227,27 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { } } } +} - interfaces := make([]InterfaceGenerator, 0, len(interfaceMap)) - for _, iface := range interfaceMap { - interfaces = append(interfaces, iface) +func markConfigDepths(interfaceMap map[string]networkInterface) { + rootInterfaceMap := make(map[string]networkInterface) + for k, v := range interfaceMap { + rootInterfaceMap[k] = v } - return interfaces + for _, iface := range interfaceMap { + for _, child := range iface.Children() { + delete(rootInterfaceMap, child.Name()) + } + } + for _, iface := range rootInterfaceMap { + setDepth(iface, 0) + } +} + +func setDepth(iface networkInterface, depth int) { + iface.setConfigDepth(depth) + for _, child := range iface.Children() { + setDepth(child, depth+1) + } } diff --git a/network/interface_test.go b/network/interface_test.go index 25b4c3b..c98bf60 100644 --- a/network/interface_test.go +++ b/network/interface_test.go @@ -30,7 +30,7 @@ func TestPhysicalInterfaceLink(t *testing.T) { func TestPhysicalInterfaceNetwork(t *testing.T) { p := physicalInterface{logicalInterface{ name: "testname", - children: []InterfaceGenerator{ + children: []networkInterface{ &bondInterface{ logicalInterface{ name: "testbond1", @@ -96,7 +96,7 @@ func TestBondInterfaceNetwork(t *testing.T) { logicalInterface{ name: "testname", config: configMethodDHCP{}, - children: []InterfaceGenerator{ + children: []networkInterface{ &bondInterface{ logicalInterface{ name: "testbond1", @@ -249,17 +249,19 @@ func TestBuildInterfacesBlindBond(t *testing.T) { interfaces := buildInterfaces(stanzas) bond0 := &bondInterface{ logicalInterface{ - name: "bond0", - config: configMethodManual{}, - children: []InterfaceGenerator{}, + name: "bond0", + config: configMethodManual{}, + children: []networkInterface{}, + configDepth: 1, }, []string{"eth0"}, } eth0 := &physicalInterface{ logicalInterface{ - name: "eth0", - config: configMethodManual{}, - children: []InterfaceGenerator{bond0}, + name: "eth0", + config: configMethodManual{}, + children: []networkInterface{bond0}, + configDepth: 0, }, } expect := []InterfaceGenerator{bond0, eth0} @@ -284,18 +286,20 @@ func TestBuildInterfacesBlindVLAN(t *testing.T) { interfaces := buildInterfaces(stanzas) vlan0 := &vlanInterface{ logicalInterface{ - name: "vlan0", - config: configMethodManual{}, - children: []InterfaceGenerator{}, + name: "vlan0", + config: configMethodManual{}, + children: []networkInterface{}, + configDepth: 1, }, 0, "eth0", } eth0 := &physicalInterface{ logicalInterface{ - name: "eth0", - config: configMethodManual{}, - children: []InterfaceGenerator{vlan0}, + name: "eth0", + config: configMethodManual{}, + children: []networkInterface{vlan0}, + configDepth: 0, }, } expect := []InterfaceGenerator{eth0, vlan0} @@ -355,43 +359,48 @@ func TestBuildInterfaces(t *testing.T) { interfaces := buildInterfaces(stanzas) vlan1 := &vlanInterface{ logicalInterface{ - name: "vlan1", - config: configMethodManual{}, - children: []InterfaceGenerator{}, + name: "vlan1", + config: configMethodManual{}, + children: []networkInterface{}, + configDepth: 2, }, 1, "bond0", } vlan0 := &vlanInterface{ logicalInterface{ - name: "vlan0", - config: configMethodManual{}, - children: []InterfaceGenerator{}, + name: "vlan0", + config: configMethodManual{}, + children: []networkInterface{}, + configDepth: 1, }, 0, "eth0", } bond1 := &bondInterface{ logicalInterface{ - name: "bond1", - config: configMethodManual{}, - children: []InterfaceGenerator{}, + name: "bond1", + config: configMethodManual{}, + children: []networkInterface{}, + configDepth: 2, }, []string{"bond0"}, } bond0 := &bondInterface{ logicalInterface{ - name: "bond0", - config: configMethodManual{}, - children: []InterfaceGenerator{bond1, vlan1}, + name: "bond0", + config: configMethodManual{}, + children: []networkInterface{bond1, vlan1}, + configDepth: 1, }, []string{"eth0"}, } eth0 := &physicalInterface{ logicalInterface{ - name: "eth0", - config: configMethodManual{}, - children: []InterfaceGenerator{bond0, vlan0}, + name: "eth0", + config: configMethodManual{}, + children: []networkInterface{bond0, vlan0}, + configDepth: 0, }, } expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1} @@ -399,3 +408,19 @@ func TestBuildInterfaces(t *testing.T) { t.FailNow() } } + +func TestFilename(t *testing.T) { + for _, tt := range []struct { + i logicalInterface + f string + }{ + {logicalInterface{name: "iface", configDepth: 0}, "00-iface"}, + {logicalInterface{name: "iface", configDepth: 9}, "09-iface"}, + {logicalInterface{name: "iface", configDepth: 10}, "0a-iface"}, + {logicalInterface{name: "iface", configDepth: 53}, "35-iface"}, + } { + if tt.i.Filename() != tt.f { + t.Fatalf("bad filename (%q): got %q, want %q", tt.i, tt.i.Filename(), tt.f) + } + } +} diff --git a/system/networkd.go b/system/networkd.go index f49a15d..a982869 100644 --- a/system/networkd.go +++ b/system/networkd.go @@ -66,15 +66,15 @@ func restartNetworkd() error { func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error { for _, iface := range interfaces { - filename := path.Join(runtimeNetworkPath, fmt.Sprintf("%s.netdev", iface.Name())) + filename := path.Join(runtimeNetworkPath, fmt.Sprintf("%s.netdev", iface.Filename())) if err := writeConfig(filename, iface.Netdev()); err != nil { return err } - filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.link", iface.Name())) + filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.link", iface.Filename())) if err := writeConfig(filename, iface.Link()); err != nil { return err } - filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.network", iface.Name())) + filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.network", iface.Filename())) if err := writeConfig(filename, iface.Network()); err != nil { return err }