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:
parent
d4e048a1f4
commit
c820f2b1cf
@ -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{
|
||||||
|
@ -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{
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user