mirror of
https://github.com/rancher/os.git
synced 2025-06-25 22:41:36 +00:00
144 lines
2.9 KiB
Go
144 lines
2.9 KiB
Go
package netconf
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rancher/os/log"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
const (
|
|
base = "/sys/class/net/"
|
|
bondingMasters = "/sys/class/net/bonding_masters"
|
|
)
|
|
|
|
type Bonding struct {
|
|
name string
|
|
}
|
|
|
|
func (b *Bonding) init() error {
|
|
_, err := os.Stat(bondingMasters)
|
|
if os.IsNotExist(err) {
|
|
log.Info("Loading bonding kernel module")
|
|
cmd := exec.Command("modprobe", "bonding")
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdout = os.Stdin
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
for i := 0; i < 30; i++ {
|
|
if _, err = os.Stat(bondingMasters); err == nil {
|
|
break
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
}
|
|
_, err = os.Stat(bondingMasters)
|
|
return err
|
|
}
|
|
|
|
func contains(file, word string) (bool, error) {
|
|
words, err := ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for _, s := range strings.Split(strings.TrimSpace(string(words)), " ") {
|
|
if s == strings.TrimSpace(word) {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func (b *Bonding) linkDown() error {
|
|
link, err := netlink.LinkByName(b.name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return netlink.LinkSetDown(link)
|
|
}
|
|
|
|
func (b *Bonding) ListSlaves() ([]string, error) {
|
|
file := base + b.name + "/bonding/slaves"
|
|
words, err := ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := []string{}
|
|
for _, s := range strings.Split(strings.TrimSpace(string(words)), " ") {
|
|
if s != "" {
|
|
result = append(result, s)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (b *Bonding) RemoveSlave(slave string) error {
|
|
if ok, err := contains(base+b.name+"/bonding/slaves", slave); err != nil {
|
|
return err
|
|
} else if !ok {
|
|
return nil
|
|
}
|
|
|
|
p := base + b.name + "/bonding/slaves"
|
|
log.Infof("Removing slave %s from master %s", slave, b.name)
|
|
return ioutil.WriteFile(p, []byte("-"+slave), 0644)
|
|
}
|
|
|
|
func (b *Bonding) AddSlave(slave string) error {
|
|
if ok, err := contains(base+b.name+"/bonding/slaves", slave); err != nil {
|
|
return err
|
|
} else if ok {
|
|
return nil
|
|
}
|
|
|
|
p := base + b.name + "/bonding/slaves"
|
|
log.Infof("Adding slave %s to master %s", slave, b.name)
|
|
return ioutil.WriteFile(p, []byte("+"+slave), 0644)
|
|
}
|
|
|
|
func (b *Bonding) Opt(key, value string) error {
|
|
if key == "mode" {
|
|
// Don't care about errors here
|
|
b.linkDown()
|
|
slaves, _ := b.ListSlaves()
|
|
for _, slave := range slaves {
|
|
b.RemoveSlave(slave)
|
|
}
|
|
}
|
|
|
|
p := base + b.name + "/bonding/" + key
|
|
if err := ioutil.WriteFile(p, []byte(value), 0644); err != nil {
|
|
log.Errorf("Failed to set %s=%s on %s: %v", key, value, b.name, err)
|
|
return err
|
|
}
|
|
|
|
log.Infof("Set %s=%s on %s", key, value, b.name)
|
|
|
|
return nil
|
|
}
|
|
|
|
func Bond(name string) (*Bonding, error) {
|
|
b := &Bonding{name: name}
|
|
if err := b.init(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if ok, err := contains(bondingMasters, name); err != nil {
|
|
return nil, err
|
|
} else if ok {
|
|
return b, nil
|
|
}
|
|
|
|
log.Infof("Creating bond %s", name)
|
|
return b, ioutil.WriteFile(bondingMasters, []byte("+"+name), 0644)
|
|
}
|