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