1
0
mirror of https://github.com/rancher/os.git synced 2025-07-06 19:38:37 +00:00

Merge pull request #756 from ibuildthecloud/networking

Networking
This commit is contained in:
Darren Shepherd 2016-02-09 14:34:30 -07:00
commit da7abd871e
17 changed files with 525 additions and 192 deletions

View File

@ -1,3 +1,3 @@
IMAGE_NAME=rancher/os
VERSION=v0.4.3-dev
DFS_IMAGE=rancher/docker:1.10.0
DFS_IMAGE=rancher/docker:v1.10.0-1

View File

@ -3,6 +3,8 @@ package cloudinit
import (
"fmt"
"net/http"
"os"
"path"
"strings"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
@ -40,7 +42,7 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
bondCfg := netconf.InterfaceConfig{
Addresses: []string{},
BondOpts: map[string]string{
"lacp-rate": "1",
"lacp_rate": "1",
"xmit_hash_policy": "layer3+4",
"downdelay": "200",
"updelay": "200",
@ -83,6 +85,10 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
},
}
if err := os.MkdirAll(path.Dir(rancherConfig.CloudConfigNetworkFile), 0700); err != nil {
logrus.Errorf("Failed to create directory for file %s: %v", rancherConfig.CloudConfigNetworkFile, err)
}
if err := rancherConfig.WriteToFile(cc, rancherConfig.CloudConfigNetworkFile); err != nil {
logrus.Errorf("Failed to save config file %s: %v", rancherConfig.CloudConfigNetworkFile, err)
}

View File

@ -64,11 +64,6 @@ rancher:
network:
dns:
nameservers: [8.8.8.8, 8.8.4.4]
interfaces:
eth*:
dhcp: true
lo:
address: 127.0.0.1/8
repositories:
core:
url: https://raw.githubusercontent.com/rancher/os-services/v0.4.3-dev

View File

@ -7,6 +7,8 @@ rancher:
kernel-headers: true
network:
interfaces:
eth*:
dhcp: true
eth1:
address: 10.10.2.17/24
gateway: 10.10.2.2

View File

@ -0,0 +1,25 @@
#cloud-config
rancher:
network:
interfaces:
eth0:
address: 10.0.2.15/24
gateway: 10.0.2.2
br0:
vlans: 100
eth1:
vlans: 100,200:foobar
eth1.100:
bridge: br0
br0.100:
address: 123.123.123.123/32
eth6:
bond: bond0
eth7:
bond: bond0
bond0:
bond_opts:
mode: 1
address: 123.123.123.124/32
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC85w9stZyiLQp/DkVO6fqwiShYcj1ClKdtCqgHtf+PLpJkFReSFu8y21y+ev09gsSMRRrjF7yt0pUHV6zncQhVeqsZtgc5WbELY2DOYUGmRn/CCvPbXovoBrQjSorqlBmpuPwsStYLr92Xn+VVsMNSUIegHY22DphGbDKG85vrKB8HxUxGIDxFBds/uE8FhSy+xsoyT/jUZDK6pgq2HnGl6D81ViIlKecpOpWlW3B+fea99ADNyZNVvDzbHE5pcI3VRw8u59WmpWOUgT6qacNVACl8GqpBvQk8sw7O/X9DSZHCKafeD9G5k+GYbAUz92fKWrx/lOXfUXPS3+c8dRIF

View File

@ -0,0 +1,36 @@
import pytest
import rostest.util as u
from rostest.util import SSH
cloud_config_path = './tests/integration/assets/test_09/cloud-config.yml'
@pytest.fixture(scope="module")
def qemu(request):
q = u.run_qemu(request, run_args=['--cloud-config', cloud_config_path,
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio',
'-net', 'nic,vlan=0,model=virtio'])
u.flush_out(q.stdout)
return q
def test_network_conf(qemu):
SSH(qemu).check_call('bash', '-c', '''cat > test-merge << "SCRIPT"
set -x -e
ip link show dev br0
ip link show dev br0.100 | grep br0.100@br0
ip link show dev eth1.100 | grep 'master br0'
ip link show dev eth6 | grep 'master bond0'
ip link show dev eth7 | grep 'master bond0'
[ "$(</sys/class/net/bond0/bonding/mode)" = "active-backup 1" ]
SCRIPT
sudo bash test-merge
'''.strip())

View File

@ -66,10 +66,10 @@ import:
version: 1349b37bd56f4f5ce2690b5b2c0f53f88a261c67
- package: github.com/rancher/docker-from-scratch
version: 1.10.0
version: v1.10.0-1
- package: github.com/rancher/netconf
version: 8080e4909627bbdf51d7f427211a7c0517136373
version: d7d620ef4ea62a9d04b51c7b3d9dc83fe7ffaa1b
- package: github.com/ryanuber/go-glob
version: 0067a9abd927e50aed5190662702f81231413ae0

View File

@ -51,7 +51,7 @@ This image is really designed to run with overlay. Aufs is known to work but ot
## Seriously, Why?
This code and the supporting files were extracted out of RancherOS into a separate library and are still used by RancherOS. RancherOS runs Docker as a PID 1 but before we can exec Docker we need to setup a minimal environment for Docker in which to run. Since RancherOS is executed by the kernel there is absolutely nothing setup in the system. At Rancher we wrote a small amount of code to setup all the required mounts and directories to launch Docker.
This code and the supporting files were extracted out of RancherOS into a separate library and are still used by RancherOS. RancherOS runs Docker as the first process but before we can exec Docker we need to setup a minimal environment for Docker in which to run. Since RancherOS is executed by the kernel there is absolutely nothing setup in the system. At Rancher we wrote a small amount of code to setup all the required mounts and directories to launch Docker.
We moved this code out into a separate project for two reasons. First was simply that we wanted to clean up and modularize the RancherOS code base. Second is that we wanted to demonstrate clearly what exactly Docker requires from the Linux user space. For the most part Docker requires the standard mounts (`/proc`, `/sys`, `/run`, `/var/run`, etc) and the cgroup mounts in `/sys/fs/cgroup` plus the following programs/files:
@ -90,6 +90,10 @@ If you want to run with a custom bridge name you must pass both `--bip` and `-b`
# Troubleshooting
## Zombies
If you are running containers with `--pid=host` you can get zombies. When you launch docker-from-scratch just add `-e DOCKER_LAUNCH_REAP=true` as a parameter to the `docker run` command to fix this.
## Weird module loading errors
For various reasons Docker or iptables may try to load a kernel module. You can either manually load all the needed modules from the host or you can bind mount in the kernel modules by adding `-v /lib/modules/$(uname -r)/lib/modules/$(uname -r)` to your `docker run` command

23
vendor/github.com/rancher/docker-from-scratch/one.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
// +build linux
package dockerlaunch
import (
"os"
"os/signal"
"syscall"
)
func PidOne() error {
c := make(chan os.Signal, 2048)
signal.Notify(c, syscall.SIGCHLD)
for range c {
for {
if pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); err != nil || pid <= 0 {
break
}
}
}
return nil
}

View File

@ -44,6 +44,7 @@ var (
type Config struct {
Fork bool
PidOne bool
CommandName string
DnsConfig netconf.DnsConfig
BridgeName string
@ -190,6 +191,12 @@ func execDocker(config *Config, docker, cmd string, args []string) (*exec.Cmd, e
}
cmd.Env = env
err := cmd.Start()
if err != nil {
return cmd, err
}
if config.PidOne {
PidOne()
}
return cmd, err
} else {
err := syscall.Exec(expand(docker), append([]string{cmd}, args...), env)
@ -273,7 +280,7 @@ func setupNetworking(config *Config) error {
return nil
}
hostname, err := os.Hostname();
hostname, err := os.Hostname()
if err != nil {
return err
}
@ -299,7 +306,7 @@ ff02::2 ip6-allrouters
config.BridgeName: {
Address: config.BridgeAddress,
MTU: config.BridgeMtu,
Bridge: true,
Bridge: "true",
},
},
}); err != nil {
@ -578,6 +585,11 @@ func Main() {
var config Config
args = ParseConfig(&config, args...)
if os.Getenv("DOCKER_LAUNCH_REAP") == "true" {
config.Fork = true
config.PidOne = true
}
log.Debugf("Launch config %#v", config)
_, err := LaunchDocker(&config, os.Args[1], args...)

View File

@ -19,7 +19,7 @@ import:
version: 4f4d2c8983a18e2c9c63a3f339bc9a998c4557bc
- package: github.com/rancher/netconf
version: 02925e7cf5a0f0bb0aa5360ee260ef7378e5eff8
version: bf95fd720be9de4f7aa3a4a529b70f2865dd0fc7
- package: github.com/ryanuber/go-glob
version: 0067a9abd927e50aed5190662702f81231413ae0
@ -32,3 +32,6 @@ import:
- package: github.com/vishvananda/netlink
version: edcd99c0881a4de0fdb3818af6b24f4ee6948464
- package: github.com/flynn/go-shlex
version: 3f9db97f856818214da2e1057f8ad84803971cff

1
vendor/github.com/rancher/netconf/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
/netconf

View File

@ -17,25 +17,20 @@ const (
)
type Bonding struct {
err error
name string
}
func (b *Bonding) Error() error {
return b.err
}
func (b *Bonding) init() {
func (b *Bonding) init() error {
_, err := os.Stat(bondingMasters)
if os.IsNotExist(err) {
logrus.Info("Loading bonding kernel module")
cmd := exec.Command("modprobe", "bonding")
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdin
b.err = cmd.Run()
if b.err != nil {
err = cmd.Run()
if err != nil {
for i := 0; i < 30; i++ {
if _, err := os.Stat(bondingMasters); err == nil {
if _, err = os.Stat(bondingMasters); err == nil {
break
}
time.Sleep(100 * time.Millisecond)
@ -43,9 +38,7 @@ func (b *Bonding) init() {
}
}
_, err = os.Stat(bondingMasters)
if err != nil {
b.err = err
}
return err
}
func contains(file, word string) (bool, error) {
@ -63,67 +56,88 @@ func contains(file, word string) (bool, error) {
return false, nil
}
func (b *Bonding) AddSlave(slave string) {
if b.err != nil {
return
}
if ok, err := contains(base+b.name+"/bonding/slaves", slave); err != nil {
b.err = err
return
} else if ok {
return
}
link, err := netlink.LinkByName(slave)
func (b *Bonding) linkDown() error {
link, err := netlink.LinkByName(b.name)
if err != nil {
b.err = err
return
return err
}
b.err = netlink.LinkSetDown(link)
if b.err != nil {
return
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"
logrus.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"
logrus.Infof("Adding slave %s to master %s", slave, b.name)
b.err = ioutil.WriteFile(p, []byte("+"+slave), 0644)
return ioutil.WriteFile(p, []byte("+"+slave), 0644)
}
func (b *Bonding) Opt(key, value string) {
if b.err != nil {
return
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
b.err = ioutil.WriteFile(p, []byte(value), 0644)
if b.err != nil {
logrus.Errorf("Failed to set %s=%s on %s", key, value, b.name)
if err := ioutil.WriteFile(p, []byte(value), 0644); err != nil {
logrus.Errorf("Failed to set %s=%s on %s: %v", key, value, b.name, err)
return err
} else {
logrus.Infof("Set %s=%s on %s", key, value, b.name)
}
return nil
}
func (b *Bonding) Clear() {
b.err = nil
}
func Bond(name string) *Bonding {
func Bond(name string) (*Bonding, error) {
b := &Bonding{name: name}
b.init()
if b.err != nil {
return b
if err := b.init(); err != nil {
return nil, err
}
if ok, err := contains(bondingMasters, name); err != nil {
b.err = err
return b
return nil, err
} else if ok {
return b
return b, nil
}
logrus.Infof("Creating bond %s", name)
b.err = ioutil.WriteFile(bondingMasters, []byte("+"+name), 0644)
return b
return b, ioutil.WriteFile(bondingMasters, []byte("+"+name), 0644)
}

48
vendor/github.com/rancher/netconf/bridge.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package netconf
import (
"fmt"
"github.com/vishvananda/netlink"
)
type Bridge struct {
name string
}
func NewBridge(name string) (*Bridge, error) {
b := &Bridge{name: name}
return b, b.init()
}
func (b *Bridge) init() error {
link, err := netlink.LinkByName(b.name)
if err == nil {
if _, ok := link.(*netlink.Bridge); !ok {
return fmt.Errorf("%s is not a bridge device", b.name)
}
return nil
}
bridge := netlink.Bridge{}
bridge.LinkAttrs.Name = b.name
return netlink.LinkAdd(&bridge)
}
func (b *Bridge) AddLink(link netlink.Link) error {
existing, err := netlink.LinkByName(b.name)
if err != nil {
return err
}
if bridge, ok := existing.(*netlink.Bridge); ok {
if link.Attrs().MasterIndex != bridge.Index {
return netlink.LinkSetMaster(link, bridge)
}
} else {
return fmt.Errorf("%s is not a bridge", b.name)
}
return nil
}

View File

@ -3,11 +3,11 @@ package netconf
import (
"bytes"
"errors"
"io/ioutil"
"net"
"os"
"os/exec"
"strings"
"sync"
"syscall"
log "github.com/Sirupsen/logrus"
@ -19,82 +19,83 @@ import (
const (
CONF = "/var/lib/rancher/conf"
NET_SCRIPT = "/var/lib/rancher/conf/network.sh"
MODE = "mode"
)
func createInterfaces(netCfg *NetworkConfig) error {
for name, iface := range netCfg.Interfaces {
if iface.Bridge {
bridge := netlink.Bridge{}
bridge.LinkAttrs.Name = name
var (
defaultDhcpArgs = []string{"dhcpcd", "-MA4", "-e", "force_hostname=true"}
)
if err := netlink.LinkAdd(&bridge); err != nil {
func createInterfaces(netCfg *NetworkConfig) {
configured := map[string]bool{}
for name, iface := range netCfg.Interfaces {
if iface.Bridge == "true" {
if _, err := NewBridge(name); err != nil {
log.Errorf("Failed to create bridge %s: %v", name, err)
}
} else if iface.Bridge != "" {
if _, err := NewBridge(iface.Bridge); err != nil {
log.Errorf("Failed to create bridge %s: %v", iface.Bridge, err)
}
} else if iface.Bond != "" {
bondIface, ok := netCfg.Interfaces[iface.Bond]
if !ok {
log.Errorf("Failed to find bond configuration for [%s]", iface.Bond)
bond, err := Bond(iface.Bond)
if err != nil {
log.Errorf("Failed to create bond %s: %v", iface.Bond, err)
continue
}
bond := Bond(iface.Bond)
if bond.Error() != nil {
log.Errorf("Failed to create bond [%s]: %v", iface.Bond, bond.Error())
continue
if !configured[iface.Bond] {
if bondIface, ok := netCfg.Interfaces[iface.Bond]; ok {
// Other settings depends on mode, so set it first
if v, ok := bondIface.BondOpts[MODE]; ok {
bond.Opt(MODE, v)
}
for k, v := range bondIface.BondOpts {
if k != MODE {
bond.Opt(k, v)
bond.Clear()
}
}
configured[iface.Bond] = true
}
}
}
return nil
}
func runScript(netCfg *NetworkConfig) error {
if netCfg.Script == "" {
return nil
}
if _, err := os.Stat(CONF); os.IsNotExist(err) {
if err := os.MkdirAll(CONF, 0700); err != nil {
return err
}
}
if err := ioutil.WriteFile(NET_SCRIPT, []byte(netCfg.Script), 0700); err != nil {
return err
}
cmd := exec.Command(NET_SCRIPT)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func ApplyNetworkConfigs(netCfg *NetworkConfig) error {
log.Debugf("Config: %#v", netCfg)
if err := runScript(netCfg); err != nil {
log.Errorf("Failed to run script: %v", err)
}
if err := createInterfaces(netCfg); err != nil {
return err
}
func createSlaveInterfaces(netCfg *NetworkConfig) {
links, err := netlink.LinkList()
if err != nil {
return err
log.Errorf("Failed to list links: %v", err)
return
}
dhcpLinks := []string{}
//apply network config
for _, link := range links {
match, ok := findMatch(link, netCfg)
if !ok {
continue
}
vlanDefs, err := ParseVlanDefinitions(match.Vlans)
if err != nil {
log.Errorf("Failed to create vlans on device %s: %v", link.Attrs().Name, err)
continue
}
for _, vlanDef := range vlanDefs {
if _, err = NewVlan(link, vlanDef.Name, vlanDef.Id); err != nil {
log.Errorf("Failed to create vlans on device %s, id %d: %v", link.Attrs().Name, vlanDef.Id, err)
}
}
}
}
func findMatch(link netlink.Link, netCfg *NetworkConfig) (InterfaceConfig, bool) {
linkName := link.Attrs().Name
var match InterfaceConfig
exactMatch := false
found := false
for key, netConf := range netCfg.Interfaces {
if netConf.Match == "" {
@ -105,43 +106,113 @@ func ApplyNetworkConfigs(netCfg *NetworkConfig) error {
continue
}
if len(netConf.Match) > 4 && strings.ToLower(netConf.Match[:3]) == "mac" {
if strings.HasPrefix(netConf.Match, "mac") {
haAddr, err := net.ParseMAC(netConf.Match[4:])
if err != nil {
return err
log.Errorf("Failed to parse mac %s: %v", netConf.Match[4:], err)
continue
}
// Don't match mac address of the bond because it is the same as the slave
if bytes.Compare(haAddr, link.Attrs().HardwareAddr) == 0 && link.Attrs().Name != netConf.Bond {
// MAC address match is used over all other matches
match = netConf
break
return netConf, true
}
}
// "" means match has not been found
if match.Match == "" && matches(linkName, netConf.Match) {
if !exactMatch && glob.Glob(netConf.Match, linkName) {
match = netConf
found = true
}
if netConf.Match == linkName {
// Found exact match, use it over wildcard match
match = netConf
exactMatch = true
}
}
if match.Match != "" {
return match, exactMatch || found
}
func populateDefault(netCfg *NetworkConfig) {
if netCfg.Interfaces == nil {
netCfg.Interfaces = map[string]InterfaceConfig{}
}
if len(netCfg.Interfaces) == 0 {
netCfg.Interfaces["eth*"] = InterfaceConfig{
DHCP: true,
}
}
if _, ok := netCfg.Interfaces["lo"]; !ok {
netCfg.Interfaces["lo"] = InterfaceConfig{
Address: "127.0.0.1/8",
}
}
}
func ApplyNetworkConfigs(netCfg *NetworkConfig) error {
populateDefault(netCfg)
log.Debugf("Config: %#v", netCfg)
runCmds(netCfg.PreCmds, "")
createInterfaces(netCfg)
createSlaveInterfaces(netCfg)
links, err := netlink.LinkList()
if err != nil {
return err
}
dhcpLinks := map[string]string{}
//apply network config
for _, link := range links {
linkName := link.Attrs().Name
if match, ok := findMatch(link, netCfg); ok {
if match.DHCP {
dhcpLinks = append(dhcpLinks, link.Attrs().Name)
} else if err = applyNetConf(link, match); err != nil {
dhcpLinks[link.Attrs().Name] = match.DHCPArgs
} else if err = applyInterfaceConfig(link, match); err != nil {
log.Errorf("Failed to apply settings to %s : %v", linkName, err)
}
}
}
if len(dhcpLinks) > 0 {
log.Infof("Running DHCP on %v", dhcpLinks)
dhcpcdArgs := append([]string{"-MA4", "-e", "force_hostname=true"}, dhcpLinks...)
cmd := exec.Command("dhcpcd", dhcpcdArgs...)
//run dhcp
wg := sync.WaitGroup{}
for iface, args := range dhcpLinks {
wg.Add(1)
go func(iface, args string) {
runDhcp(iface, args)
wg.Done()
}(iface, args)
}
wg.Wait()
runCmds(netCfg.PostCmds, "")
return err
}
func runDhcp(iface string, argstr string) {
log.Infof("Running DHCP on %s", iface)
args := []string{}
if argstr != "" {
var err error
args, err = shlex.Split(argstr)
if err != nil {
log.Errorf("Failed to parse [%s]: %v", argstr, err)
}
}
if len(args) == 0 {
args = defaultDhcpArgs
}
args = append(args, iface)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
@ -149,13 +220,6 @@ func ApplyNetworkConfigs(netCfg *NetworkConfig) error {
}
}
if err != nil {
return err
}
return nil
}
func linkUp(link netlink.Link, netConf InterfaceConfig) error {
if err := netlink.LinkSetUp(link); err != nil {
log.Errorf("failed to setup link: %v", err)
@ -207,15 +271,27 @@ func setGateway(gateway string) error {
return nil
}
func applyNetConf(link netlink.Link, netConf InterfaceConfig) error {
func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error {
if netConf.Bond != "" {
b := Bond(netConf.Bond)
b.AddSlave(link.Attrs().Name)
if b.Error() != nil {
return b.Error()
b, err := Bond(netConf.Bond)
if err != nil {
return err
}
if err := b.AddSlave(link.Attrs().Name); err != nil {
return err
}
return nil
}
return linkUp(link, netConf)
if netConf.Bridge != "" && netConf.Bridge != "true" {
b, err := NewBridge(netConf.Bridge)
if err != nil {
return err
}
if err := b.AddLink(link); err != nil {
return err
}
return nil
}
if netConf.IPV4LL {
@ -223,16 +299,18 @@ func applyNetConf(link netlink.Link, netConf InterfaceConfig) error {
log.Errorf("IPV4LL set failed: %v", err)
return err
}
} else if netConf.Address == "" && len(netConf.Addresses) == 0 {
return nil
} else {
addresses := []string{}
if netConf.Address != "" {
err := applyAddress(netConf.Address, link, netConf)
if err != nil {
log.Errorf("Failed to apply address %s to %s: %v", netConf.Address, link.Attrs().Name, err)
addresses = append(addresses, netConf.Address)
}
if len(netConf.Addresses) > 0 {
addresses = append(addresses, netConf.Addresses...)
}
for _, address := range netConf.Addresses {
for _, address := range addresses {
err := applyAddress(address, link, netConf)
if err != nil {
log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err)
@ -247,6 +325,8 @@ func applyNetConf(link netlink.Link, netConf InterfaceConfig) error {
}
}
runCmds(netConf.PreUp, link.Attrs().Name)
if err := linkUp(link, netConf); err != nil {
return err
}
@ -259,30 +339,31 @@ func applyNetConf(link netlink.Link, netConf InterfaceConfig) error {
log.Errorf("Fail to set gateway %s", netConf.Gateway)
}
for _, postUp := range netConf.PostUp {
postUp = strings.TrimSpace(postUp)
if postUp == "" {
continue
}
args, err := shlex.Split(strings.Replace(postUp, "$iface", link.Attrs().Name, -1))
if err != nil {
log.Errorf("Failed to parse command [%s]: %v", postUp, err)
continue
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Errorf("Failed to run command [%s]: %v", postUp, err)
continue
}
}
runCmds(netConf.PostUp, link.Attrs().Name)
return nil
}
func matches(link, conf string) bool {
return glob.Glob(conf, link)
func runCmds(cmds []string, iface string) {
for _, cmd := range cmds {
cmd = strings.TrimSpace(cmd)
if cmd == "" {
continue
}
args, err := shlex.Split(strings.Replace(cmd, "$iface", iface, -1))
if err != nil {
log.Errorf("Failed to parse command [%s]: %v", cmd, err)
continue
}
log.Infof("Running command %s %v", args[0], args[1:])
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Errorf("Failed to run command [%s]: %v", cmd, err)
continue
}
}
}

View File

@ -1,24 +1,28 @@
package netconf
type NetworkConfig struct {
Script string `yaml:"script,omitempty"`
PreCmds []string `yaml:"pre_cmds,omitempty"`
Dns DnsConfig `yaml:"dns,omitempty"`
Interfaces map[string]InterfaceConfig `yaml:"interfaces,omitempty"`
PostCmds []string `yaml:"post_cmds,omitempty"`
}
type InterfaceConfig struct {
Match string `yaml:"match,omitempty"`
DHCP bool `yaml:"dhcp,omitempty"`
DHCPArgs string `yaml:"dhcp_args,omitempty"`
Address string `yaml:"address,omitempty"`
Addresses []string `yaml:"addresses,omitempty"`
IPV4LL bool `yaml:"ipv4ll,omitempty"`
Gateway string `yaml:"gateway,omitempty"`
GatewayIpv6 string `yaml:"gateway_ipv6,omitempty"`
MTU int `yaml:"mtu,omitempty"`
Bridge bool `yaml:"bridge,omitempty"`
Bridge string `yaml:"bridge,omitempty"`
Bond string `yaml:"bond,omitempty"`
BondOpts map[string]string `yaml:"bond_opts,omitempty"`
PostUp []string `yaml:"post_up,omitempty"`
PreUp []string `yaml:"pre_up,omitempty"`
Vlans string `yaml:"vlans,omitempty"`
}
type DnsConfig struct {

79
vendor/github.com/rancher/netconf/vlan.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
package netconf
import (
"fmt"
"strconv"
"strings"
"github.com/vishvananda/netlink"
)
type VlanDefinition struct {
Id int
Name string
}
type Vlan struct {
name string
link netlink.Link
id int
}
func NewVlan(link netlink.Link, name string, id int) (*Vlan, error) {
if name == "" {
name = fmt.Sprintf("%s.%d", link.Attrs().Name, id)
}
v := &Vlan{
name: name,
link: link,
id: id,
}
return v, v.init()
}
func (v *Vlan) init() error {
link, err := netlink.LinkByName(v.name)
if err == nil {
if _, ok := link.(*netlink.Vlan); !ok {
return fmt.Errorf("%s is not a VLAN device", v.name)
}
return nil
}
vlan := netlink.Vlan{}
vlan.ParentIndex = v.link.Attrs().Index
vlan.Name = v.name
vlan.VlanId = v.id
return netlink.LinkAdd(&vlan)
}
func ParseVlanDefinitions(vlans string) ([]VlanDefinition, error) {
vlans = strings.TrimSpace(vlans)
if vlans == "" {
return nil, nil
}
result := []VlanDefinition{}
for _, vlan := range strings.Split(vlans, ",") {
idName := strings.SplitN(strings.TrimSpace(vlan), ":", 2)
id, err := strconv.Atoi(idName[0])
if err != nil {
return nil, fmt.Errorf("Invalid format in %s: %v", vlans, err)
}
def := VlanDefinition{
Id: id,
}
if len(idName) > 1 {
def.Name = idName[1]
}
result = append(result, def)
}
return result, nil
}