From 159f4a2c7c650706bc6f2c0a49acea8fbd3ab7fe Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Wed, 11 Jun 2014 22:07:33 -0700 Subject: [PATCH] feat(network): Add support for blind interfaces It is valid for an interface to reference another, otherwise undeclared, interface (i.e. a bond enslaves eth0 without eth0 having its own iface stanza). In order to associate the two interfaces, the undeclared interface needs to be implicitly created so that it can be referenced by the other. This adds the capability to forward-declare interfaces in addition to cleaning up the process a little bit. --- network/interface.go | 157 ++++++++++++++++++++------------------ network/interface_test.go | 74 +++++++++++++++++- 2 files changed, 153 insertions(+), 78 deletions(-) diff --git a/network/interface.go b/network/interface.go index a139a67..81d3803 100644 --- a/network/interface.go +++ b/network/interface.go @@ -100,93 +100,98 @@ func (v *vlanInterface) Link() string { } func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { - bondStanzas := make(map[string]*stanzaInterface) - physicalStanzas := make(map[string]*stanzaInterface) - vlanStanzas := make(map[string]*stanzaInterface) + interfaceMap := make(map[string]InterfaceGenerator) + for _, iface := range stanzas { switch iface.kind { case interfaceBond: - bondStanzas[iface.name] = iface + interfaceMap[iface.name] = &bondInterface{ + logicalInterface{ + name: iface.name, + config: iface.configMethod, + children: []InterfaceGenerator{}, + }, + iface.options["slaves"], + } + for _, slave := range iface.options["slaves"] { + if _, ok := interfaceMap[slave]; !ok { + interfaceMap[slave] = &physicalInterface{ + logicalInterface{ + name: slave, + config: configMethodManual{}, + children: []InterfaceGenerator{}, + }, + } + } + } + case interfacePhysical: - physicalStanzas[iface.name] = iface + if _, ok := iface.configMethod.(configMethodLoopback); ok { + continue + } + interfaceMap[iface.name] = &physicalInterface{ + logicalInterface{ + name: iface.name, + config: iface.configMethod, + children: []InterfaceGenerator{}, + }, + } + case interfaceVLAN: - vlanStanzas[iface.name] = iface - } - } - - physicals := make(map[string]*physicalInterface) - for _, p := range physicalStanzas { - if _, ok := p.configMethod.(configMethodLoopback); ok { - continue - } - physicals[p.name] = &physicalInterface{ - logicalInterface{ - name: p.name, - config: p.configMethod, - children: []InterfaceGenerator{}, - }, - } - } - - bonds := make(map[string]*bondInterface) - for _, b := range bondStanzas { - bonds[b.name] = &bondInterface{ - logicalInterface{ - name: b.name, - config: b.configMethod, - children: []InterfaceGenerator{}, - }, - b.options["slaves"], - } - } - - vlans := make(map[string]*vlanInterface) - for _, v := range vlanStanzas { - var rawDevice string - id, _ := strconv.Atoi(v.options["id"][0]) - if device := v.options["raw_device"]; len(device) == 1 { - rawDevice = device[0] - } - vlans[v.name] = &vlanInterface{ - logicalInterface{ - name: v.name, - config: v.configMethod, - children: []InterfaceGenerator{}, - }, - id, - rawDevice, - } - } - - for _, vlan := range vlans { - if physical, ok := physicals[vlan.rawDevice]; ok { - physical.children = append(physical.children, vlan) - } - if bond, ok := bonds[vlan.rawDevice]; ok { - bond.children = append(bond.children, vlan) - } - } - - for _, bond := range bonds { - for _, slave := range bond.slaves { - if physical, ok := physicals[slave]; ok { - physical.children = append(physical.children, bond) + var rawDevice string + id, _ := strconv.Atoi(iface.options["id"][0]) + if device := iface.options["raw_device"]; len(device) == 1 { + rawDevice = device[0] + if _, ok := interfaceMap[rawDevice]; !ok { + interfaceMap[rawDevice] = &physicalInterface{ + logicalInterface{ + name: rawDevice, + config: configMethodManual{}, + children: []InterfaceGenerator{}, + }, + } + } } - if pBond, ok := bonds[slave]; ok { - pBond.children = append(pBond.children, bond) + interfaceMap[iface.name] = &vlanInterface{ + logicalInterface{ + name: iface.name, + config: iface.configMethod, + children: []InterfaceGenerator{}, + }, + id, + rawDevice, } } } - interfaces := make([]InterfaceGenerator, 0, len(physicals)+len(bonds)+len(vlans)) - for _, physical := range physicals { - interfaces = append(interfaces, physical) + for _, iface := range interfaceMap { + switch i := iface.(type) { + case *vlanInterface: + if parent, ok := interfaceMap[i.rawDevice]; ok { + switch p := parent.(type) { + case *physicalInterface: + p.children = append(p.children, iface) + case *bondInterface: + p.children = append(p.children, iface) + } + } + case *bondInterface: + for _, slave := range i.slaves { + if parent, ok := interfaceMap[slave]; ok { + switch p := parent.(type) { + case *physicalInterface: + p.children = append(p.children, iface) + case *bondInterface: + p.children = append(p.children, iface) + } + } + } + } } - for _, bond := range bonds { - interfaces = append(interfaces, bond) - } - for _, vlan := range vlans { - interfaces = append(interfaces, vlan) + + interfaces := make([]InterfaceGenerator, 0, len(interfaceMap)) + for _, iface := range interfaceMap { + interfaces = append(interfaces, iface) } return interfaces diff --git a/network/interface_test.go b/network/interface_test.go index 7ebe80e..b986656 100644 --- a/network/interface_test.go +++ b/network/interface_test.go @@ -224,6 +224,76 @@ func TestBuildInterfacesLo(t *testing.T) { } } +func TestBuildInterfacesBlindBond(t *testing.T) { + stanzas := []*stanzaInterface{ + { + name: "bond0", + kind: interfaceBond, + auto: false, + configMethod: configMethodManual{}, + options: map[string][]string{ + "slaves": []string{"eth0"}, + }, + }, + } + interfaces := buildInterfaces(stanzas) + bond0 := &bondInterface{ + logicalInterface{ + name: "bond0", + config: configMethodManual{}, + children: []InterfaceGenerator{}, + }, + []string{"eth0"}, + } + eth0 := &physicalInterface{ + logicalInterface{ + name: "eth0", + config: configMethodManual{}, + children: []InterfaceGenerator{bond0}, + }, + } + expect := []InterfaceGenerator{bond0, eth0} + if !reflect.DeepEqual(interfaces, expect) { + t.FailNow() + } +} + +func TestBuildInterfacesBlindVLAN(t *testing.T) { + stanzas := []*stanzaInterface{ + { + name: "vlan0", + kind: interfaceVLAN, + auto: false, + configMethod: configMethodManual{}, + options: map[string][]string{ + "id": []string{"0"}, + "raw_device": []string{"eth0"}, + }, + }, + } + interfaces := buildInterfaces(stanzas) + vlan0 := &vlanInterface{ + logicalInterface{ + name: "vlan0", + config: configMethodManual{}, + children: []InterfaceGenerator{}, + }, + 0, + "eth0", + } + eth0 := &physicalInterface{ + logicalInterface{ + name: "eth0", + config: configMethodManual{}, + children: []InterfaceGenerator{vlan0}, + }, + } + expect := []InterfaceGenerator{eth0, vlan0} + if !reflect.DeepEqual(interfaces, expect) { + t.FailNow() + } +} + func TestBuildInterfaces(t *testing.T) { stanzas := []*stanzaInterface{ &stanzaInterface{ @@ -303,7 +373,7 @@ func TestBuildInterfaces(t *testing.T) { logicalInterface{ name: "bond0", config: configMethodManual{}, - children: []InterfaceGenerator{vlan1, bond1}, + children: []InterfaceGenerator{bond1, vlan1}, }, []string{"eth0"}, } @@ -311,7 +381,7 @@ func TestBuildInterfaces(t *testing.T) { logicalInterface{ name: "eth0", config: configMethodManual{}, - children: []InterfaceGenerator{vlan0, bond0}, + children: []InterfaceGenerator{bond0, vlan0}, }, } expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1}