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.
This commit is contained in:
Alex Crawford 2014-06-11 22:07:33 -07:00
parent cf1ffad533
commit 159f4a2c7c
2 changed files with 153 additions and 78 deletions

View File

@ -100,93 +100,98 @@ func (v *vlanInterface) Link() string {
} }
func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
bondStanzas := make(map[string]*stanzaInterface) interfaceMap := make(map[string]InterfaceGenerator)
physicalStanzas := make(map[string]*stanzaInterface)
vlanStanzas := make(map[string]*stanzaInterface)
for _, iface := range stanzas { for _, iface := range stanzas {
switch iface.kind { switch iface.kind {
case interfaceBond: case interfaceBond:
bondStanzas[iface.name] = iface interfaceMap[iface.name] = &bondInterface{
case interfacePhysical: logicalInterface{
physicalStanzas[iface.name] = iface name: iface.name,
case interfaceVLAN: config: iface.configMethod,
vlanStanzas[iface.name] = iface 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{},
},
}
} }
} }
physicals := make(map[string]*physicalInterface) case interfacePhysical:
for _, p := range physicalStanzas { if _, ok := iface.configMethod.(configMethodLoopback); ok {
if _, ok := p.configMethod.(configMethodLoopback); ok {
continue continue
} }
physicals[p.name] = &physicalInterface{ interfaceMap[iface.name] = &physicalInterface{
logicalInterface{ logicalInterface{
name: p.name, name: iface.name,
config: p.configMethod, config: iface.configMethod,
children: []InterfaceGenerator{}, children: []InterfaceGenerator{},
}, },
} }
}
bonds := make(map[string]*bondInterface) case interfaceVLAN:
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 var rawDevice string
id, _ := strconv.Atoi(v.options["id"][0]) id, _ := strconv.Atoi(iface.options["id"][0])
if device := v.options["raw_device"]; len(device) == 1 { if device := iface.options["raw_device"]; len(device) == 1 {
rawDevice = device[0] rawDevice = device[0]
} if _, ok := interfaceMap[rawDevice]; !ok {
vlans[v.name] = &vlanInterface{ interfaceMap[rawDevice] = &physicalInterface{
logicalInterface{ logicalInterface{
name: v.name, name: rawDevice,
config: v.configMethod, config: configMethodManual{},
children: []InterfaceGenerator{},
},
}
}
}
interfaceMap[iface.name] = &vlanInterface{
logicalInterface{
name: iface.name,
config: iface.configMethod,
children: []InterfaceGenerator{}, children: []InterfaceGenerator{},
}, },
id, id,
rawDevice, 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 _, iface := range interfaceMap {
for _, slave := range bond.slaves { switch i := iface.(type) {
if physical, ok := physicals[slave]; ok { case *vlanInterface:
physical.children = append(physical.children, bond) 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)
}
} }
if pBond, ok := bonds[slave]; ok {
pBond.children = append(pBond.children, bond)
} }
} }
} }
interfaces := make([]InterfaceGenerator, 0, len(physicals)+len(bonds)+len(vlans)) interfaces := make([]InterfaceGenerator, 0, len(interfaceMap))
for _, physical := range physicals { for _, iface := range interfaceMap {
interfaces = append(interfaces, physical) interfaces = append(interfaces, iface)
}
for _, bond := range bonds {
interfaces = append(interfaces, bond)
}
for _, vlan := range vlans {
interfaces = append(interfaces, vlan)
} }
return interfaces return interfaces

View File

@ -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) { func TestBuildInterfaces(t *testing.T) {
stanzas := []*stanzaInterface{ stanzas := []*stanzaInterface{
&stanzaInterface{ &stanzaInterface{
@ -303,7 +373,7 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "bond0", name: "bond0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{vlan1, bond1}, children: []InterfaceGenerator{bond1, vlan1},
}, },
[]string{"eth0"}, []string{"eth0"},
} }
@ -311,7 +381,7 @@ func TestBuildInterfaces(t *testing.T) {
logicalInterface{ logicalInterface{
name: "eth0", name: "eth0",
config: configMethodManual{}, config: configMethodManual{},
children: []InterfaceGenerator{vlan0, bond0}, children: []InterfaceGenerator{bond0, vlan0},
}, },
} }
expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1} expect := []InterfaceGenerator{eth0, bond0, bond1, vlan0, vlan1}