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).
This commit is contained in:
Alex Crawford 2014-06-12 18:13:19 -05:00
parent 033c8d352f
commit d6a0d0908c
3 changed files with 121 additions and 45 deletions

View File

@ -7,15 +7,23 @@ import (
type InterfaceGenerator interface { type InterfaceGenerator interface {
Name() string Name() string
Filename() string
Netdev() string Netdev() string
Link() string Link() string
Network() string Network() string
} }
type networkInterface interface {
InterfaceGenerator
Children() []networkInterface
setConfigDepth(int)
}
type logicalInterface struct { type logicalInterface struct {
name string name string
config configMethod config configMethod
children []InterfaceGenerator children []networkInterface
configDepth int
} }
func (i *logicalInterface) Network() string { func (i *logicalInterface) Network() string {
@ -52,6 +60,18 @@ func (i *logicalInterface) Link() string {
return "" 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 { type physicalInterface struct {
logicalInterface logicalInterface
} }
@ -104,8 +124,20 @@ func (v *vlanInterface) Netdev() string {
} }
func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { 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 { for _, iface := range stanzas {
switch iface.kind { switch iface.kind {
case interfaceBond: case interfaceBond:
@ -113,7 +145,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
logicalInterface{ logicalInterface{
name: iface.name, name: iface.name,
config: iface.configMethod, config: iface.configMethod,
children: []InterfaceGenerator{}, children: []networkInterface{},
}, },
iface.options["slaves"], iface.options["slaves"],
} }
@ -123,7 +155,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
logicalInterface{ logicalInterface{
name: slave, name: slave,
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
}, },
} }
} }
@ -137,7 +169,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
logicalInterface{ logicalInterface{
name: iface.name, name: iface.name,
config: iface.configMethod, config: iface.configMethod,
children: []InterfaceGenerator{}, children: []networkInterface{},
}, },
} }
@ -151,7 +183,7 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
logicalInterface{ logicalInterface{
name: rawDevice, name: rawDevice,
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
}, },
} }
} }
@ -160,14 +192,17 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
logicalInterface{ logicalInterface{
name: iface.name, name: iface.name,
config: iface.configMethod, config: iface.configMethod,
children: []InterfaceGenerator{}, children: []networkInterface{},
}, },
id, id,
rawDevice, rawDevice,
} }
} }
} }
return interfaceMap
}
func linkAncestors(interfaceMap map[string]networkInterface) {
for _, iface := range interfaceMap { for _, iface := range interfaceMap {
switch i := iface.(type) { switch i := iface.(type) {
case *vlanInterface: case *vlanInterface:
@ -192,11 +227,27 @@ func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
} }
} }
} }
}
interfaces := make([]InterfaceGenerator, 0, len(interfaceMap)) func markConfigDepths(interfaceMap map[string]networkInterface) {
for _, iface := range interfaceMap { rootInterfaceMap := make(map[string]networkInterface)
interfaces = append(interfaces, iface) 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)
}
} }

View File

@ -30,7 +30,7 @@ func TestPhysicalInterfaceLink(t *testing.T) {
func TestPhysicalInterfaceNetwork(t *testing.T) { func TestPhysicalInterfaceNetwork(t *testing.T) {
p := physicalInterface{logicalInterface{ p := physicalInterface{logicalInterface{
name: "testname", name: "testname",
children: []InterfaceGenerator{ children: []networkInterface{
&bondInterface{ &bondInterface{
logicalInterface{ logicalInterface{
name: "testbond1", name: "testbond1",
@ -96,7 +96,7 @@ func TestBondInterfaceNetwork(t *testing.T) {
logicalInterface{ logicalInterface{
name: "testname", name: "testname",
config: configMethodDHCP{}, config: configMethodDHCP{},
children: []InterfaceGenerator{ children: []networkInterface{
&bondInterface{ &bondInterface{
logicalInterface{ logicalInterface{
name: "testbond1", name: "testbond1",
@ -251,7 +251,8 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
logicalInterface{ logicalInterface{
name: "bond0", name: "bond0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
configDepth: 1,
}, },
[]string{"eth0"}, []string{"eth0"},
} }
@ -259,7 +260,8 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
logicalInterface{ logicalInterface{
name: "eth0", name: "eth0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{bond0}, children: []networkInterface{bond0},
configDepth: 0,
}, },
} }
expect := []InterfaceGenerator{bond0, eth0} expect := []InterfaceGenerator{bond0, eth0}
@ -286,7 +288,8 @@ func TestBuildInterfacesBlindVLAN(t *testing.T) {
logicalInterface{ logicalInterface{
name: "vlan0", name: "vlan0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
configDepth: 1,
}, },
0, 0,
"eth0", "eth0",
@ -295,7 +298,8 @@ func TestBuildInterfacesBlindVLAN(t *testing.T) {
logicalInterface{ logicalInterface{
name: "eth0", name: "eth0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{vlan0}, children: []networkInterface{vlan0},
configDepth: 0,
}, },
} }
expect := []InterfaceGenerator{eth0, vlan0} expect := []InterfaceGenerator{eth0, vlan0}
@ -357,7 +361,8 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "vlan1", name: "vlan1",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
configDepth: 2,
}, },
1, 1,
"bond0", "bond0",
@ -366,7 +371,8 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "vlan0", name: "vlan0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
configDepth: 1,
}, },
0, 0,
"eth0", "eth0",
@ -375,7 +381,8 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "bond1", name: "bond1",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{}, children: []networkInterface{},
configDepth: 2,
}, },
[]string{"bond0"}, []string{"bond0"},
} }
@ -383,7 +390,8 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "bond0", name: "bond0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{bond1, vlan1}, children: []networkInterface{bond1, vlan1},
configDepth: 1,
}, },
[]string{"eth0"}, []string{"eth0"},
} }
@ -391,7 +399,8 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "eth0", name: "eth0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{bond0, vlan0}, children: []networkInterface{bond0, vlan0},
configDepth: 0,
}, },
} }
expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1} expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1}
@ -399,3 +408,19 @@ func TestBuildInterfaces(t *testing.T) {
t.FailNow() 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)
}
}
}

View File

@ -66,15 +66,15 @@ func restartNetworkd() error {
func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error { func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error {
for _, iface := range interfaces { 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 { if err := writeConfig(filename, iface.Netdev()); err != nil {
return err 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 { if err := writeConfig(filename, iface.Link()); err != nil {
return err 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 { if err := writeConfig(filename, iface.Network()); err != nil {
return err return err
} }