2.1.1 static routes for containers #7034

Merged
merged 15 commits into from Feb 27, 2017
@@ -212,6 +212,14 @@ func (st *State) prepareOrGetContainerInterfaceInfo(
machineConf := result.Results[0]
ifaceInfo := make([]network.InterfaceInfo, len(machineConf.Config))
for i, cfg := range machineConf.Config {
+ routes := make([]network.Route, len(cfg.Routes))
+ for j, route := range cfg.Routes {
+ routes[j] = network.Route{
+ DestinationCIDR: route.DestinationCIDR,
+ GatewayIP: route.GatewayIP,
+ Metric: route.Metric,
+ }
+ }
ifaceInfo[i] = network.InterfaceInfo{
DeviceIndex: cfg.DeviceIndex,
MACAddress: cfg.MACAddress,
@@ -233,6 +241,7 @@ func (st *State) prepareOrGetContainerInterfaceInfo(
DNSServers: network.NewAddresses(cfg.DNSServers...),
DNSSearchDomains: cfg.DNSSearchDomains,
GatewayAddress: network.NewAddress(cfg.GatewayAddress),
+ Routes: routes,
}
}
return ifaceInfo, nil
@@ -12,6 +12,7 @@ import (
"time"
"github.com/juju/errors"
+ gitjujutesting "github.com/juju/testing"
jc "github.com/juju/testing/checkers"
"github.com/juju/utils"
"github.com/juju/utils/arch"
@@ -21,6 +22,8 @@ import (
"gopkg.in/juju/names.v2"
"github.com/juju/juju/api"
+ "github.com/juju/juju/api/base"
+ apibasetesting "github.com/juju/juju/api/base/testing"
"github.com/juju/juju/api/provisioner"
apitesting "github.com/juju/juju/api/testing"
"github.com/juju/juju/apiserver/common"
@@ -719,3 +722,133 @@ func (s *provisionerSuite) TestHostChangesForContainer(c *gc.C) {
}})
c.Check(reconfigureDelay, gc.Equals, 0)
}
+
+var _ = gc.Suite(&prepareContainerSuite{})
+
+type prepareContainerSuite struct {
+ coretesting.BaseSuite
+
+ *gitjujutesting.Stub
+}
+
+type prepareFacade struct {
+ *gitjujutesting.Stub
+
+ prepareResult []params.NetworkConfig
+ prepareError error
+}
+
+func (f *prepareFacade) FacadeCall(objType string, version int, id, request string, requestParams, response interface{}) error {
+ // We only support PrepareContainerInterfaceInfo
+ if objType != "Provisioner" {
+ return errors.Errorf("bad facade name: %q", objType)
+ }
+ if version != 0 {
+ return errors.Errorf("bad version: %d", version)
+ }
+ if request != "PrepareContainerInterfaceInfo" {
+ return errors.Errorf("bad method name: %q", request)
+ }
+ entities, ok := requestParams.(params.Entities)
+ if !ok {
+ return errors.Errorf("unknown request type: %t", requestParams)
+ }
+ f.Stub.AddCall("api.PrepareContainerInterfaceInfo", entities.Entities)
+ if len(entities.Entities) != 1 {
+ return errors.Errorf("only support a single entity not: %v", entities.Entities)
+ }
+ result, ok := response.(*params.MachineNetworkConfigResults)
+ if !ok {
+ return errors.Errorf("invalid return type: %t", response)
+ }
+ result.Results = make([]params.MachineNetworkConfigResult, len(entities.Entities))
+ if f.prepareError != nil {
+ result.Results[0].Error = common.ServerError(f.prepareError)
+ } else {
+ result.Results[0].Config = f.prepareResult
+ }
+ return nil
+}
+
+func (s *prepareContainerSuite) SetUpTest(c *gc.C) {
+ s.BaseSuite.SetUpTest(c)
+
+ s.Stub = &gitjujutesting.Stub{}
+}
+
+func (s *prepareContainerSuite) apiForPrepareContainer(config []params.NetworkConfig, err error) base.APICaller {
+ facade := &prepareFacade{
+ Stub: s.Stub,
+ prepareResult: config,
+ prepareError: err,
+ }
+ return apibasetesting.APICallerFunc(facade.FacadeCall)
+}
+
+func (s *prepareContainerSuite) TestPrepareContainerInterfaceInfoNoValues(c *gc.C) {
+ apicaller := s.apiForPrepareContainer(nil, nil)
+ st := provisioner.NewState(apicaller)
+ networkInfo, err := st.PrepareContainerInterfaceInfo(names.NewMachineTag("machine-0/lxd/0"))
+ c.Assert(err, gc.IsNil)
+ c.Check(networkInfo, jc.DeepEquals, []network.InterfaceInfo{})
+}
+
+func (s *prepareContainerSuite) TestPrepareContainerInterfaceInfoSingleNIC(c *gc.C) {
+ apicaller := s.apiForPrepareContainer([]params.NetworkConfig{{
+ DeviceIndex: 1,
+ MACAddress: "de:ad:be:ff:11:22",
+ CIDR: "192.168.0.5/24",
+ MTU: 9000,
+ ProviderId: "prov-id",
+ ProviderSubnetId: "prov-sub-id",
+ ProviderSpaceId: "prov-space-id",
+ ProviderAddressId: "prov-address-id",
+ ProviderVLANId: "prov-vlan-id",
+ VLANTag: 25,
+ InterfaceName: "eth5",
+ ParentInterfaceName: "parent#br-eth5",
+ InterfaceType: "ethernet",
+ Disabled: false,
+ NoAutoStart: false,
+ ConfigType: "static",
+ Address: "192.168.0.6",
+ DNSServers: []string{"8.8.8.8"},
+ DNSSearchDomains: []string{"mydomain"},
+ GatewayAddress: "192.168.0.1",
+ Routes: []params.NetworkRoute{{
+ DestinationCIDR: "10.0.0.0/16",
+ GatewayIP: "192.168.0.1",
+ Metric: 55,
+ }},
+ }}, nil)
+ st := provisioner.NewState(apicaller)
+ networkInfo, err := st.PrepareContainerInterfaceInfo(names.NewMachineTag("machine-0/lxd/0"))
+ c.Assert(err, gc.IsNil)
+ c.Check(networkInfo, jc.DeepEquals, []network.InterfaceInfo{{
+ DeviceIndex: 1,
+ MACAddress: "de:ad:be:ff:11:22",
+ CIDR: "192.168.0.5/24",
+ MTU: 9000,
+ ProviderId: "prov-id",
+ ProviderSubnetId: "prov-sub-id",
+ ProviderSpaceId: "prov-space-id",
+ ProviderAddressId: "prov-address-id",
+ ProviderVLANId: "prov-vlan-id",
+ VLANTag: 25,
+ InterfaceName: "eth5",
+ ParentInterfaceName: "parent#br-eth5",
+ InterfaceType: "ethernet",
+ Disabled: false,
+ NoAutoStart: false,
+ ConfigType: "static",
+ Address: network.NewAddress("192.168.0.6"),
+ DNSServers: network.NewAddresses("8.8.8.8"),
+ DNSSearchDomains: []string{"mydomain"},
+ GatewayAddress: network.NewAddress("192.168.0.1"),
+ Routes: []network.Route{{
+ DestinationCIDR: "10.0.0.0/16",
+ GatewayIP: "192.168.0.1",
+ Metric: 55,
+ }},
+ }})
+}
@@ -157,6 +157,14 @@ func NetworkConfigFromInterfaceInfo(interfaceInfos []network.InterfaceInfo) []pa
for _, nameserver := range v.DNSServers {
dnsServers = append(dnsServers, nameserver.Value)
}
+ routes := make([]params.NetworkRoute, len(v.Routes))
+ for j, route := range v.Routes {
+ routes[j] = params.NetworkRoute{
+ DestinationCIDR: route.DestinationCIDR,
+ GatewayIP: route.GatewayIP,
+ Metric: route.Metric,
+ }
+ }
result[i] = params.NetworkConfig{
DeviceIndex: v.DeviceIndex,
MACAddress: v.MACAddress,
@@ -178,6 +186,7 @@ func NetworkConfigFromInterfaceInfo(interfaceInfos []network.InterfaceInfo) []pa
DNSServers: dnsServers,
DNSSearchDomains: v.DNSSearchDomains,
GatewayAddress: v.GatewayAddress.Value,
+ Routes: routes,
}
}
return result
@@ -41,6 +41,17 @@ type Subnet struct {
Status string `json:"status,omitempty"`
}
+// NetworkRoute describes a special route that should be added for a given
+// network interface.
+type NetworkRoute struct {
+ // DestinationCIDR is the Subnet CIDR of traffic that needs a custom route.
+ DestinationCIDR string `json:"destination-cidr"`
+ // GatewayIP is the target IP to use as the next-hop when sending traffic to DestinationCIDR
+ GatewayIP string `json:"gateway-ip"`
+ // Metric is the cost for this particular route.
+ Metric int `json:"metric"`
+}
+
// NetworkConfig describes the necessary information to configure
// a single network interface on a machine. This mostly duplicates
// network.InterfaceInfo type and it's defined here so it can be kept
@@ -130,6 +141,10 @@ type NetworkConfig struct {
// configure for this network interface. For containers this
// usually (one of) the host address(es).
GatewayAddress string `json:"gateway-address,omitempty"`
+
+ // Routes is a list of routes that should be applied when this interface is
+ // active.
+ Routes []NetworkRoute `json:"routes,omitempty"`
}
// DeviceBridgeInfo lists the host device and the expected bridge to be
@@ -124,6 +124,12 @@ func GenerateNetworkConfig(networkConfig *container.NetworkConfig) (string, erro
gatewayHandled = true // write it only once
}
}
+ for _, route := range prepared.NameToRoutes[name] {
+ output.WriteString(fmt.Sprintf(" post-up ip route add %s via %s metric %d\n",
+ route.DestinationCIDR, route.GatewayIP, route.Metric))
+ output.WriteString(fmt.Sprintf(" pre-down ip route del %s via %s metric %d\n",
+ route.DestinationCIDR, route.GatewayIP, route.Metric))
+ }
}
generatedConfig := output.String()
@@ -144,6 +150,7 @@ type PreparedConfig struct {
DNSServers []string
DNSSearchDomains []string
NameToAddress map[string]string
+ NameToRoutes map[string][]network.Route
GatewayAddress string
}
@@ -156,6 +163,7 @@ func PrepareNetworkConfigFromInterfaces(interfaces []network.InterfaceInfo) *Pre
gatewayAddress := ""
namesInOrder := make([]string, 1, len(interfaces)+1)
nameToAddress := make(map[string]string)
+ nameToRoutes := make(map[string][]network.Route)
// Always include the loopback.
namesInOrder[0] = "lo"
@@ -171,6 +179,7 @@ func PrepareNetworkConfigFromInterfaces(interfaces []network.InterfaceInfo) *Pre
} else if info.ConfigType == network.ConfigDHCP {
nameToAddress[info.InterfaceName] = string(network.ConfigDHCP)
}
+ nameToRoutes[info.InterfaceName] = info.Routes
for _, dns := range info.DNSServers {
dnsServers.Add(dns.Value)
@@ -188,6 +197,7 @@ func PrepareNetworkConfigFromInterfaces(interfaces []network.InterfaceInfo) *Pre
prepared := &PreparedConfig{
InterfaceNames: namesInOrder,
NameToAddress: nameToAddress,
+ NameToRoutes: nameToRoutes,
AutoStarted: autoStarted.SortedValues(),
DNSServers: dnsServers.SortedValues(),
DNSSearchDomains: dnsSearchDomains.SortedValues(),
@@ -354,14 +364,19 @@ func shutdownInitCommands(initSystem, series string) ([]string, error) {
func raiseJujuNetworkInterfacesScript(oldInterfacesFile, newInterfacesFile string) string {
return fmt.Sprintf(`
if [ -f %[2]s ]; then
+ echo "stopping all interfaces"
ifdown -a
sleep 1.5
if ifup -a --interfaces=%[2]s; then
+ echo "ifup with %[2]s succeeded, renaming to %[1]s"
cp %[1]s %[1]s-orig
cp %[2]s %[1]s
else
+ echo "ifup with %[2]s failed, leaving old %[1]s alone"
ifup -a
fi
+else
+ echo "did not find %[2]s, not reconfiguring networking"
fi`[1:],
oldInterfacesFile, newInterfacesFile)
}
@@ -66,6 +66,11 @@ func (s *UserDataSuite) SetUpTest(c *gc.C) {
DNSSearchDomains: []string{"foo", "bar"},
GatewayAddress: network.NewAddress("0.2.2.1"),
MACAddress: "aa:bb:cc:dd:ee:f1",
+ Routes: []network.Route{{
+ DestinationCIDR: "0.5.6.0/24",
+ GatewayIP: "0.2.2.1",
+ Metric: 50,
+ }},
}, {
InterfaceName: "any2",
ConfigType: network.ConfigDHCP,
@@ -92,6 +97,8 @@ iface any0 inet static
iface any1 inet static
address 0.2.2.4/24
+ post-up ip route add 0.5.6.0/24 via 0.2.2.1 metric 50
+ pre-down ip route del 0.5.6.0/24 via 0.2.2.1 metric 50
iface any2 inet dhcp
@@ -117,6 +124,8 @@ bootcmd:
iface any1 inet static
address 0.2.2.4/24
+ post-up ip route add 0.5.6.0/24 via 0.2.2.1 metric 50
+ pre-down ip route del 0.5.6.0/24 via 0.2.2.1 metric 50
iface any2 inet dhcp
@@ -127,14 +136,19 @@ bootcmd:
runcmd:
- |-
if [ -f %[1]s ]; then
+ echo "stopping all interfaces"
ifdown -a
sleep 1.5
if ifup -a --interfaces=%[1]s; then
+ echo "ifup with %[1]s succeeded, renaming to %[2]s"
cp %[2]s %[2]s-orig
cp %[1]s %[2]s
else
+ echo "ifup with %[1]s failed, leaving old %[2]s alone"
ifup -a
fi
+ else
+ echo "did not find %[1]s, not reconfiguring networking"
fi
`[1:]
@@ -160,14 +174,19 @@ bootcmd:
runcmd:
- |-
if [ -f %[1]s ]; then
+ echo "stopping all interfaces"
ifdown -a
sleep 1.5
if ifup -a --interfaces=%[1]s; then
+ echo "ifup with %[1]s succeeded, renaming to %[2]s"
cp %[2]s %[2]s-orig
cp %[1]s %[2]s
else
+ echo "ifup with %[1]s failed, leaving old %[2]s alone"
ifup -a
fi
+ else
+ echo "did not find %[1]s, not reconfiguring networking"
fi
`[1:]
View
@@ -21,17 +21,16 @@ github.com/juju/blobstore git 06056004b3d7b54bbb7984d830c537bad00fec21 2015-07-2
github.com/juju/bundlechanges git 7725027b95e0d54635e0fb11efc2debdcdf19f75 2016-12-15T16:06:52Z
github.com/juju/cmd git 0f05ac592d6925a1b8ca0f77524d3348fafa66cc 2017-02-07T03:13:09Z
github.com/juju/errors git 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3 2015-09-16T12:56:42Z
-github.com/juju/jsonschema git a0ef8b74ebcffeeff9fc374854deb4af388f037e 2016-11-02T18:19:19Z
github.com/juju/gnuflag git 4e76c56581859c14d9d87e1ddbe29e1c0f10195f 2016-08-09T16:52:14Z
github.com/juju/go4 git 40d72ab9641a2a8c36a9c46a51e28367115c8e59 2016-02-22T16:32:58Z
github.com/juju/gojsonpointer git afe8b77aa08f272b49e01b82de78510c11f61500 2015-02-04T19:46:29Z
github.com/juju/gojsonreference git f0d24ac5ee330baa21721cdff56d45e4ee42628e 2015-02-04T19:46:33Z
github.com/juju/gojsonschema git e1ad140384f254c82f89450d9a7c8dd38a632838 2015-03-12T17:00:16Z
-github.com/juju/gomaasapi git f0300c96aa5e0deb3cc599ca8a8949097b955e60 2016-11-02T12:07:14Z
-github.com/vmware/govmomi git c0c7ce63df7edd78e713257b924c89d9a2dac119 2016-06-30T15:37:42Z
+github.com/juju/gomaasapi git cfbc096bd45f276c17a391efc4db710b60ae3ad7 2017-02-27T07:51:07Z
github.com/juju/httpprof git 14bf14c307672fd2456bdbf35d19cf0ccd3cf565 2014-12-17T16:00:36Z
github.com/juju/httprequest git 266fd1e9debf09c037a63f074d099a2da4559ece 2016-10-06T15:09:09Z
github.com/juju/idmclient git 3dda079a75cccb85083d4c3877e638f5d6ab79c2 2016-05-26T05:00:34Z
+github.com/juju/jsonschema git a0ef8b74ebcffeeff9fc374854deb4af388f037e 2016-11-02T18:19:19Z
github.com/juju/loggo git 3b7ece48644d35850f4ced4c2cbc2cf8413f58e0 2016-08-18T02:57:24Z
github.com/juju/mempool git 24974d6c264fe5a29716e7d56ea24c4bd904b7cc 2016-02-05T10:49:27Z
github.com/juju/mutex git 59c26ee163447c5c57f63ff71610d433862013de 2016-06-17T01:09:07Z
@@ -70,9 +69,12 @@ github.com/prometheus/client_model git fa8ad6fec33561be4280a8f0514318c79d7f6cb6
github.com/prometheus/common git dd586c1c5abb0be59e60f942c22af711a2008cb4 2016-05-03T22:05:32Z
github.com/prometheus/procfs git abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 2016-04-11T19:08:41Z
github.com/rogpeppe/fastuuid git 6724a57986aff9bff1a1770e9347036def7c89f6 2015-01-06T09:32:20Z
+github.com/vmware/govmomi git c0c7ce63df7edd78e713257b924c89d9a2dac119 2016-06-30T15:37:42Z
golang.org/x/crypto git 8e06e8ddd9629eb88639aba897641bff8031f1d3 2016-09-22T17:06:29Z
golang.org/x/net git ea47fc708ee3e20177f3ca3716217c4ab75942cb 2015-08-29T23:03:18Z
golang.org/x/oauth2 git 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a 2015-03-25T02:00:22Z
+golang.org/x/sys git 7a6e5648d140666db5d920909e082ca00a87ba2c 2017-02-01T05:12:45Z
+golang.org/x/text git 2910a502d2bf9e43193af9d68ca516529614eed3 2016-07-26T16:48:57Z
google.golang.org/api git 0d3983fb069cb6651353fc44c5cb604e263f2a93 2014-12-10T23:51:26Z
google.golang.org/cloud git f20d6dcccb44ed49de45ae3703312cb46e627db1 2015-03-19T22:36:35Z
gopkg.in/amz.v3 git 18899065239e006cc73b0e66800c98c2ce4eee50 2016-10-06T07:29:34Z
@@ -95,5 +97,3 @@ gopkg.in/natefinch/npipe.v2 git c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6 2016-06
gopkg.in/retry.v1 git c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db 2016-10-25T18:14:30Z
gopkg.in/tomb.v1 git dd632973f1e7218eb1089048e0798ec9ae7dceb8 2014-10-24T13:56:13Z
gopkg.in/yaml.v2 git a3f3340b5840cee44f372bddb5880fcbc419b46a 2017-02-08T14:18:51Z
-golang.org/x/sys git 7a6e5648d140666db5d920909e082ca00a87ba2c 2017-02-01T05:12:45Z
-golang.org/x/text git 2910a502d2bf9e43193af9d68ca516529614eed3 2016-07-26T16:48:57Z
Oops, something went wrong.