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 (
"fmt"
"strconv"
"strings"
)
type InterfaceGenerator interface {
@ -11,6 +12,8 @@ type InterfaceGenerator interface {
Netdev() string
Link() string
Network() string
Type() string
ModprobeParams() string
}
type networkInterface interface {
@ -68,6 +71,10 @@ func (i *logicalInterface) Children() []networkInterface {
return i.children
}
func (i *logicalInterface) ModprobeParams() string {
return ""
}
func (i *logicalInterface) setConfigDepth(depth int) {
i.configDepth = depth
}
@ -84,9 +91,14 @@ func (p *physicalInterface) Netdev() string {
return ""
}
func (p *physicalInterface) Type() string {
return "physical"
}
type bondInterface struct {
logicalInterface
slaves []string
options map[string]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)
}
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 {
logicalInterface
id int
@ -123,6 +148,10 @@ func (v *vlanInterface) Netdev() string {
return config
}
func (v *vlanInterface) Type() string {
return "vlan"
}
func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {
interfaceMap := createInterfaces(stanzas)
linkAncestors(interfaceMap)
@ -141,15 +170,22 @@ func createInterfaces(stanzas []*stanzaInterface) map[string]networkInterface {
for _, iface := range stanzas {
switch iface.kind {
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{
logicalInterface{
name: iface.name,
config: iface.configMethod,
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 {
interfaceMap[slave] = &physicalInterface{
logicalInterface{

View File

@ -36,6 +36,7 @@ func TestPhysicalInterfaceNetwork(t *testing.T) {
name: "testbond1",
},
nil,
nil,
},
&vlanInterface{
logicalInterface{
@ -67,14 +68,14 @@ VLAN=testvlan2
}
func TestBondInterfaceName(t *testing.T) {
b := bondInterface{logicalInterface{name: "testname"}, nil}
b := bondInterface{logicalInterface{name: "testname"}, nil, nil}
if b.Name() != "testname" {
t.FailNow()
}
}
func TestBondInterfaceNetdev(t *testing.T) {
b := bondInterface{logicalInterface{name: "testname"}, nil}
b := bondInterface{logicalInterface{name: "testname"}, nil, nil}
netdev := `[NetDev]
Kind=bond
Name=testname
@ -102,6 +103,7 @@ func TestBondInterfaceNetwork(t *testing.T) {
name: "testbond1",
},
nil,
nil,
},
&vlanInterface{
logicalInterface{
@ -120,6 +122,7 @@ func TestBondInterfaceNetwork(t *testing.T) {
},
},
nil,
nil,
}
network := `[Match]
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) {
stanzas := []*stanzaInterface{
&stanzaInterface{
@ -242,7 +300,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
auto: false,
configMethod: configMethodManual{},
options: map[string][]string{
"slaves": []string{"eth0"},
"bond-slaves": []string{"eth0"},
},
},
}
@ -255,6 +313,7 @@ func TestBuildInterfacesBlindBond(t *testing.T) {
configDepth: 1,
},
[]string{"eth0"},
map[string]string{},
}
eth0 := &physicalInterface{
logicalInterface{
@ -323,7 +382,9 @@ func TestBuildInterfaces(t *testing.T) {
auto: false,
configMethod: configMethodManual{},
options: map[string][]string{
"slaves": []string{"eth0"},
"bond-slaves": []string{"eth0"},
"bond-mode": []string{"4"},
"bond-miimon": []string{"100"},
},
},
&stanzaInterface{
@ -332,7 +393,7 @@ func TestBuildInterfaces(t *testing.T) {
auto: false,
configMethod: configMethodManual{},
options: map[string][]string{
"slaves": []string{"bond0"},
"bond-slaves": []string{"bond0"},
},
},
&stanzaInterface{
@ -385,6 +446,7 @@ func TestBuildInterfaces(t *testing.T) {
configDepth: 2,
},
[]string{"bond0"},
map[string]string{},
}
bond0 := &bondInterface{
logicalInterface{
@ -394,6 +456,10 @@ func TestBuildInterfaces(t *testing.T) {
configDepth: 1,
},
[]string{"eth0"},
map[string]string{
"mode": "4",
"miimon": "100",
},
}
eth0 := &physicalInterface{
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) {
options["slaves"] = options["bond-slaves"]
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 {
t.FailNow()
}
if bond.options["slaves"] != nil {
if bond.options["bond-slaves"] != nil {
t.FailNow()
}
}
@ -152,9 +152,6 @@ func TestParseBondStanza(t *testing.T) {
if bond.configMethod != conf {
t.FailNow()
}
if !reflect.DeepEqual(bond.options["slaves"], options["bond-slaves"]) {
t.FailNow()
}
}
func TestParsePhysicalStanza(t *testing.T) {

View File

@ -6,6 +6,7 @@ import (
"net"
"os/exec"
"path"
"strings"
"github.com/coreos/coreos-cloudinit/network"
"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
}
if err = probe8012q(); err != nil {
if err = maybeProbe8012q(interfaces); err != nil {
return
}
return
return maybeProbeBonding(interfaces)
}
func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
@ -55,8 +56,24 @@ func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
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 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 {