bonding: Add support for probing the bonding module with parameters

Until support for bonding params is added to networkd, this will be
neccessary in order to use bonding parameters (i.e. miimon, mode).
This also makes it such that the 8012q module will only be loaded if
the network config makes use of VLANs.
This commit is contained in:
Alex Crawford 2014-06-30 13:37:09 -07:00
parent d4e048a1f4
commit c820f2b1cf
5 changed files with 132 additions and 17 deletions

View File

@ -3,6 +3,7 @@ package network
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
type InterfaceGenerator interface { type InterfaceGenerator interface {
@ -11,6 +12,8 @@ type InterfaceGenerator interface {
Netdev() string Netdev() string
Link() string Link() string
Network() string Network() string
Type() string
ModprobeParams() string
} }
type networkInterface interface { type networkInterface interface {
@ -68,6 +71,10 @@ func (i *logicalInterface) Children() []networkInterface {
return i.children return i.children
} }
func (i *logicalInterface) ModprobeParams() string {
return ""
}
func (i *logicalInterface) setConfigDepth(depth int) { func (i *logicalInterface) setConfigDepth(depth int) {
i.configDepth = depth i.configDepth = depth
} }
@ -84,9 +91,14 @@ func (p *physicalInterface) Netdev() string {
return "" return ""
} }
func (p *physicalInterface) Type() string {
return "physical"
}
type bondInterface struct { type bondInterface struct {
logicalInterface logicalInterface
slaves []string slaves []string
options map[string]string
} }
func (b *bondInterface) Name() string { func (b *bondInterface) Name() string {
@ -97,6 +109,19 @@ func (b *bondInterface) Netdev() string {
return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name) return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name)
} }
func (b *bondInterface) Type() string {
return "bond"
}
func (b *bondInterface) ModprobeParams() string {
params := ""
for name, val := range b.options {
params += fmt.Sprintf("%s=%s ", name, val)
}
params = strings.TrimSuffix(params, " ")
return params
}
type vlanInterface struct { type vlanInterface struct {
logicalInterface logicalInterface
id int id int
@ -123,6 +148,10 @@ func (v *vlanInterface) Netdev() string {
return config return config
} }
func (v *vlanInterface) Type() string {
return "vlan"
}
func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
interfaceMap := createInterfaces(stanzas) interfaceMap := createInterfaces(stanzas)
linkAncestors(interfaceMap) linkAncestors(interfaceMap)
@ -141,15 +170,22 @@ func createInterfaces(stanzas []*stanzaInterface) map[string]networkInterface {
for _, iface := range stanzas { for _, iface := range stanzas {
switch iface.kind { switch iface.kind {
case interfaceBond: case interfaceBond:
bondOptions := make(map[string]string)
for _, k := range []string{"mode", "miimon", "lacp-rate"} {
if v, ok := iface.options["bond-"+k]; ok && len(v) > 0 {
bondOptions[k] = v[0]
}
}
interfaceMap[iface.name] = &bondInterface{ interfaceMap[iface.name] = &bondInterface{
logicalInterface{ logicalInterface{
name: iface.name, name: iface.name,
config: iface.configMethod, config: iface.configMethod,
children: []networkInterface{}, children: []networkInterface{},
}, },
iface.options["slaves"], iface.options["bond-slaves"],
bondOptions,
} }
for _, slave := range iface.options["slaves"] { for _, slave := range iface.options["bond-slaves"] {
if _, ok := interfaceMap[slave]; !ok { if _, ok := interfaceMap[slave]; !ok {
interfaceMap[slave] = &physicalInterface{ interfaceMap[slave] = &physicalInterface{
logicalInterface{ logicalInterface{

View File

@ -36,6 +36,7 @@ func TestPhysicalInterfaceNetwork(t *testing.T) {
name: "testbond1", name: "testbond1",
}, },
nil, nil,
nil,
}, },
&vlanInterface{ &vlanInterface{
logicalInterface{ logicalInterface{
@ -67,14 +68,14 @@ VLAN=testvlan2
} }
func TestBondInterfaceName(t *testing.T) { func TestBondInterfaceName(t *testing.T) {
b := bondInterface{logicalInterface{name: "testname"}, nil} b := bondInterface{logicalInterface{name: "testname"}, nil, nil}
if b.Name() != "testname" { if b.Name() != "testname" {
t.FailNow() t.FailNow()
} }
} }
func TestBondInterfaceNetdev(t *testing.T) { func TestBondInterfaceNetdev(t *testing.T) {
b := bondInterface{logicalInterface{name: "testname"}, nil} b := bondInterface{logicalInterface{name: "testname"}, nil, nil}
netdev := `[NetDev] netdev := `[NetDev]
Kind=bond Kind=bond
Name=testname Name=testname
@ -102,6 +103,7 @@ func TestBondInterfaceNetwork(t *testing.T) {
name: "testbond1", name: "testbond1",
}, },
nil, nil,
nil,
}, },
&vlanInterface{ &vlanInterface{
logicalInterface{ logicalInterface{
@ -120,6 +122,7 @@ func TestBondInterfaceNetwork(t *testing.T) {
}, },
}, },
nil, nil,
nil,
} }
network := `[Match] network := `[Match]
Name=testname Name=testname
@ -218,6 +221,61 @@ Gateway=1.2.3.4
} }
} }
func TestType(t *testing.T) {
for _, tt := range []struct {
i InterfaceGenerator
t string
}{
{
i: &physicalInterface{},
t: "physical",
},
{
i: &vlanInterface{},
t: "vlan",
},
{
i: &bondInterface{},
t: "bond",
},
} {
if tp := tt.i.Type(); tp != tt.t {
t.Fatalf("bad type (%q): got %s, want %s", tt.i, tp, tt.t)
}
}
}
func TestModprobeParams(t *testing.T) {
for _, tt := range []struct {
i InterfaceGenerator
p string
}{
{
i: &physicalInterface{},
p: "",
},
{
i: &vlanInterface{},
p: "",
},
{
i: &bondInterface{
logicalInterface{},
nil,
map[string]string{
"a": "1",
"b": "2",
},
},
p: "a=1 b=2",
},
} {
if p := tt.i.ModprobeParams(); p != tt.p {
t.Fatalf("bad params (%q): got %s, want %s", tt.i, p, tt.p)
}
}
}
func TestBuildInterfacesLo(t *testing.T) { func TestBuildInterfacesLo(t *testing.T) {
stanzas := []*stanzaInterface{ stanzas := []*stanzaInterface{
&stanzaInterface{ &stanzaInterface{
@ -242,7 +300,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
auto: false, auto: false,
configMethod: configMethodManual{}, configMethod: configMethodManual{},
options: map[string][]string{ options: map[string][]string{
"slaves": []string{"eth0"}, "bond-slaves": []string{"eth0"},
}, },
}, },
} }
@ -255,6 +313,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
configDepth: 1, configDepth: 1,
}, },
[]string{"eth0"}, []string{"eth0"},
map[string]string{},
} }
eth0 := &physicalInterface{ eth0 := &physicalInterface{
logicalInterface{ logicalInterface{
@ -323,7 +382,9 @@ func TestBuildInterfaces(t *testing.T) {
auto: false, auto: false,
configMethod: configMethodManual{}, configMethod: configMethodManual{},
options: map[string][]string{ options: map[string][]string{
"slaves": []string{"eth0"}, "bond-slaves": []string{"eth0"},
"bond-mode": []string{"4"},
"bond-miimon": []string{"100"},
}, },
}, },
&stanzaInterface{ &stanzaInterface{
@ -332,7 +393,7 @@ func TestBuildInterfaces(t *testing.T) {
auto: false, auto: false,
configMethod: configMethodManual{}, configMethod: configMethodManual{},
options: map[string][]string{ options: map[string][]string{
"slaves": []string{"bond0"}, "bond-slaves": []string{"bond0"},
}, },
}, },
&stanzaInterface{ &stanzaInterface{
@ -385,6 +446,7 @@ func TestBuildInterfaces(t *testing.T) {
configDepth: 2, configDepth: 2,
}, },
[]string{"bond0"}, []string{"bond0"},
map[string]string{},
} }
bond0 := &bondInterface{ bond0 := &bondInterface{
logicalInterface{ logicalInterface{
@ -394,6 +456,10 @@ func TestBuildInterfaces(t *testing.T) {
configDepth: 1, configDepth: 1,
}, },
[]string{"eth0"}, []string{"eth0"},
map[string]string{
"mode": "4",
"miimon": "100",
},
} }
eth0 := &physicalInterface{ eth0 := &physicalInterface{
logicalInterface{ logicalInterface{

View File

@ -293,7 +293,6 @@ func parseHwaddress(options map[string][]string, iface string) (net.HardwareAddr
} }
func parseBondStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) { func parseBondStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) {
options["slaves"] = options["bond-slaves"]
return &stanzaInterface{name: iface, kind: interfaceBond, configMethod: conf, options: options}, nil return &stanzaInterface{name: iface, kind: interfaceBond, configMethod: conf, options: options}, nil
} }

View File

@ -129,7 +129,7 @@ func TestParseBondStanzaNoSlaves(t *testing.T) {
if err != nil { if err != nil {
t.FailNow() t.FailNow()
} }
if bond.options["slaves"] != nil { if bond.options["bond-slaves"] != nil {
t.FailNow() t.FailNow()
} }
} }
@ -152,9 +152,6 @@ func TestParseBondStanza(t *testing.T) {
if bond.configMethod != conf { if bond.configMethod != conf {
t.FailNow() t.FailNow()
} }
if !reflect.DeepEqual(bond.options["slaves"], options["bond-slaves"]) {
t.FailNow()
}
} }
func TestParsePhysicalStanza(t *testing.T) { func TestParsePhysicalStanza(t *testing.T) {

View File

@ -6,6 +6,7 @@ import (
"net" "net"
"os/exec" "os/exec"
"path" "path"
"strings"
"github.com/coreos/coreos-cloudinit/network" "github.com/coreos/coreos-cloudinit/network"
"github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink" "github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink"
@ -26,10 +27,10 @@ func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) {
return return
} }
if err = probe8012q(); err != nil { if err = maybeProbe8012q(interfaces); err != nil {
return return
} }
return return maybeProbeBonding(interfaces)
} }
func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error { func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
@ -55,8 +56,24 @@ func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
return nil return nil
} }
func probe8012q() error { func maybeProbe8012q(interfaces []network.InterfaceGenerator) error {
for _, iface := range interfaces {
if iface.Type() == "vlan" {
return exec.Command("modprobe", "8021q").Run() return exec.Command("modprobe", "8021q").Run()
}
}
return nil
}
func maybeProbeBonding(interfaces []network.InterfaceGenerator) error {
args := []string{"bonding"}
for _, iface := range interfaces {
if iface.Type() == "bond" {
args = append(args, strings.Split(iface.ModprobeParams(), " ")...)
break
}
}
return exec.Command("modprobe", args...).Run()
} }
func restartNetworkd() error { func restartNetworkd() error {