Merge pull request #174 from crawford/teeth
networkd: Fix issues with bonding and VLANs
This commit is contained in:
commit
a974e85103
@ -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) {
|
||||||
|
@ -4,8 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
@ -17,6 +20,13 @@ const (
|
|||||||
|
|
||||||
func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) {
|
func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if e := restartNetworkd(); e != nil {
|
||||||
|
err = e
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(crawford): Get rid of this once networkd fixes the race
|
||||||
|
// https://bugs.freedesktop.org/show_bug.cgi?id=76077
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
if e := restartNetworkd(); e != nil {
|
if e := restartNetworkd(); e != nil {
|
||||||
err = e
|
err = e
|
||||||
}
|
}
|
||||||
@ -26,10 +36,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 +65,24 @@ func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func probe8012q() error {
|
func maybeProbe8012q(interfaces []network.InterfaceGenerator) error {
|
||||||
return exec.Command("modprobe", "8021q").Run()
|
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 {
|
func restartNetworkd() error {
|
||||||
@ -86,6 +112,8 @@ func writeConfig(filename string, config string) error {
|
|||||||
if config == "" {
|
if config == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := os.MkdirAll(path.Dir(filename), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return ioutil.WriteFile(filename, []byte(config), 0444)
|
return ioutil.WriteFile(filename, []byte(config), 0444)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user