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 {
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

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) {
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}