github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/network/containerizer/bridgepolicy_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package containerizer_test
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v6"
    13  
    14  	"github.com/juju/juju/core/constraints"
    15  	"github.com/juju/juju/core/instance"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/network/containerizer"
    18  	"github.com/juju/juju/state"
    19  	statetesting "github.com/juju/juju/state/testing"
    20  	"github.com/juju/juju/testcharms"
    21  )
    22  
    23  // bridgePolicyStateSuite contains white-box tests for how we handle applying
    24  // bridge information to containers, but includes tests that are backed by Mongo.
    25  type bridgePolicyStateSuite struct {
    26  	statetesting.StateSuite
    27  
    28  	machine          containerizer.Machine
    29  	containerMachine containerizer.Container
    30  
    31  	bridgePolicy *containerizer.BridgePolicy
    32  }
    33  
    34  var _ = gc.Suite(&bridgePolicyStateSuite{})
    35  
    36  func addApplication(c *gc.C, st *state.State, series, name string, ch *state.Charm, bindings map[string]string) *state.Application {
    37  	c.Assert(ch, gc.NotNil)
    38  	service, err := st.AddApplication(state.AddApplicationArgs{
    39  		Name:             name,
    40  		Series:           series,
    41  		Charm:            ch,
    42  		EndpointBindings: bindings,
    43  		Storage:          nil,
    44  	})
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	return service
    47  }
    48  
    49  func addCharm(c *gc.C, st *state.State, series string, name string) *state.Charm {
    50  	ch := testcharms.Repo.CharmDir(name)
    51  	ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision())
    52  	url := "local:" + series + "/" + ident
    53  	if series == "" {
    54  		ident = fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision())
    55  		url = "local:" + ident
    56  	}
    57  	curl := charm.MustParseURL(url)
    58  	info := state.CharmInfo{
    59  		Charm:       ch,
    60  		ID:          curl,
    61  		StoragePath: "dummy-path",
    62  		SHA256:      ident + "-sha256",
    63  	}
    64  	sch, err := st.AddCharm(info)
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	return sch
    67  }
    68  
    69  func (s *bridgePolicyStateSuite) SetUpTest(c *gc.C) {
    70  	s.StateSuite.SetUpTest(c)
    71  
    72  	var err error
    73  	m, err := s.State.AddMachine("quantal", state.JobHostUnits)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	s.machine = &containerizer.MachineShim{m}
    76  
    77  	s.bridgePolicy = &containerizer.BridgePolicy{
    78  		NetBondReconfigureDelay:   13,
    79  		ContainerNetworkingMethod: "provider",
    80  	}
    81  }
    82  
    83  func (s *bridgePolicyStateSuite) addContainerMachine(c *gc.C) {
    84  	// Add a container machine with s.machine as its host.
    85  	containerTemplate := state.MachineTemplate{
    86  		Series: "quantal",
    87  		Jobs:   []state.MachineJob{state.JobHostUnits},
    88  	}
    89  	container, err := s.State.AddMachineInsideMachine(containerTemplate, s.machine.Id(), instance.LXD)
    90  	c.Assert(err, jc.ErrorIsNil)
    91  	s.containerMachine = &containerizer.MachineShim{container}
    92  }
    93  
    94  func (s *bridgePolicyStateSuite) assertNoDevicesOnMachine(c *gc.C, machine containerizer.Container) {
    95  	s.assertAllLinkLayerDevicesOnMachineMatchCount(c, machine, 0)
    96  }
    97  
    98  func (s *bridgePolicyStateSuite) assertAllLinkLayerDevicesOnMachineMatchCount(
    99  	c *gc.C, machine containerizer.Container, expectedCount int,
   100  ) {
   101  	results, err := machine.AllLinkLayerDevices()
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	c.Check(results, gc.HasLen, expectedCount)
   104  }
   105  
   106  func (s *bridgePolicyStateSuite) createSpaceAndSubnet(c *gc.C, spaceName, CIDR string) {
   107  	_, err := s.State.AddSpace(spaceName, network.Id(spaceName), nil, true)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	_, err = s.State.AddSubnet(state.SubnetInfo{
   110  		CIDR:      CIDR,
   111  		SpaceName: spaceName,
   112  	})
   113  	c.Assert(err, jc.ErrorIsNil)
   114  }
   115  
   116  // setupTwoSpaces creates a 'default' and a 'dmz' space, each with a single
   117  // registered subnet. 10.0.0.0/24 for 'default', and '10.10.0.0/24' for 'dmz'
   118  func (s *bridgePolicyStateSuite) setupTwoSpaces(c *gc.C) {
   119  	s.createSpaceAndSubnet(c, "default", "10.0.0.0/24")
   120  	s.createSpaceAndSubnet(c, "dmz", "10.10.0.0/24")
   121  }
   122  
   123  func (s *bridgePolicyStateSuite) createNICWithIP(c *gc.C, machine containerizer.Machine, deviceName, cidrAddress string) {
   124  	err := machine.SetLinkLayerDevices(
   125  		state.LinkLayerDeviceArgs{
   126  			Name:       deviceName,
   127  			Type:       state.EthernetDevice,
   128  			ParentName: "",
   129  			IsUp:       true,
   130  		},
   131  	)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	err = machine.SetDevicesAddresses(
   134  		state.LinkLayerDeviceAddress{
   135  			DeviceName:   deviceName,
   136  			CIDRAddress:  cidrAddress,
   137  			ConfigMethod: state.StaticAddress,
   138  		},
   139  	)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  }
   142  
   143  func (s *bridgePolicyStateSuite) createBridgeWithIP(
   144  	c *gc.C, machine containerizer.Machine, bridgeName, cidrAddress string,
   145  ) {
   146  	err := machine.SetLinkLayerDevices(
   147  		state.LinkLayerDeviceArgs{
   148  			Name:       bridgeName,
   149  			Type:       state.BridgeDevice,
   150  			ParentName: "",
   151  			IsUp:       true,
   152  		},
   153  	)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	err = machine.SetDevicesAddresses(
   156  		state.LinkLayerDeviceAddress{
   157  			DeviceName:   bridgeName,
   158  			CIDRAddress:  cidrAddress,
   159  			ConfigMethod: state.StaticAddress,
   160  		},
   161  	)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  }
   164  
   165  // createNICAndBridgeWithIP creates a network interface and a bridge on the
   166  // machine, and assigns the requested CIDRAddress to the bridge.
   167  func (s *bridgePolicyStateSuite) createNICAndBridgeWithIP(
   168  	c *gc.C, machine containerizer.Machine, deviceName, bridgeName, cidrAddress string,
   169  ) {
   170  	s.createBridgeWithIP(c, machine, bridgeName, cidrAddress)
   171  	err := machine.SetLinkLayerDevices(
   172  		state.LinkLayerDeviceArgs{
   173  			Name:       deviceName,
   174  			Type:       state.EthernetDevice,
   175  			ParentName: bridgeName,
   176  			IsUp:       true,
   177  		},
   178  	)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  }
   181  
   182  func (s *bridgePolicyStateSuite) setupMachineInTwoSpaces(c *gc.C) {
   183  	s.setupTwoSpaces(c)
   184  	s.createNICAndBridgeWithIP(c, s.machine, "ens33", "br-ens33", "10.0.0.20/24")
   185  	s.createNICAndBridgeWithIP(c, s.machine, "ens0p10", "br-ens0p10", "10.10.0.20/24")
   186  }
   187  
   188  func (s *bridgePolicyStateSuite) createLoopbackNIC(c *gc.C, machine containerizer.Machine) {
   189  	err := machine.SetLinkLayerDevices(
   190  		state.LinkLayerDeviceArgs{
   191  			Name:       "lo",
   192  			Type:       state.LoopbackDevice,
   193  			ParentName: "",
   194  			IsUp:       true,
   195  		},
   196  	)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	err = machine.SetDevicesAddresses(
   199  		state.LinkLayerDeviceAddress{
   200  			DeviceName:   "lo",
   201  			CIDRAddress:  "127.0.0.1/24",
   202  			ConfigMethod: state.StaticAddress,
   203  		},
   204  	)
   205  	c.Assert(err, jc.ErrorIsNil)
   206  }
   207  
   208  // createAllDefaultDevices creates the loopback, lxcbr0, lxdbr0, and virbr0 devices
   209  func (s *bridgePolicyStateSuite) createAllDefaultDevices(c *gc.C, machine containerizer.Machine) {
   210  	// loopback
   211  	s.createLoopbackNIC(c, machine)
   212  	// container.DefaultLxcBridge
   213  	s.createBridgeWithIP(c, machine, "lxcbr0", "10.0.3.1/24")
   214  	// container.DefaultLxdBridge
   215  	s.createBridgeWithIP(c, machine, "lxdbr0", "10.0.4.1/24")
   216  	// container.DefaultKvmBridge
   217  	s.createBridgeWithIP(c, machine, "virbr0", "192.168.124.1/24")
   218  }
   219  
   220  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesCorrectlyPaired(c *gc.C) {
   221  	// The device names chosen and the order are very explicit. We
   222  	// need to ensure that we have a list that does not sort well
   223  	// alphabetically. This is because SetParentLinkLayerDevices()
   224  	// uses a natural sort ordering and we want to verify the
   225  	// pairing between the container's NIC name and its parent in
   226  	// the host machine during this unit test.
   227  
   228  	devicesArgs := []state.LinkLayerDeviceArgs{
   229  		{
   230  			Name: "br-eth10",
   231  			Type: state.BridgeDevice,
   232  		},
   233  		{
   234  			Name: "br-eth1",
   235  			Type: state.BridgeDevice,
   236  		},
   237  		{
   238  			Name: "br-eth10-100",
   239  			Type: state.BridgeDevice,
   240  		},
   241  		{
   242  			Name: "br-eth2",
   243  			Type: state.BridgeDevice,
   244  		},
   245  		{
   246  			Name: "br-eth0",
   247  			Type: state.BridgeDevice,
   248  		},
   249  		{
   250  			Name: "br-eth4",
   251  			Type: state.BridgeDevice,
   252  		},
   253  		{
   254  			Name: "br-eth3",
   255  			Type: state.BridgeDevice,
   256  		},
   257  	}
   258  	// Put each of those bridges into a different subnet that is part
   259  	// of the same space.
   260  	_, err := s.State.AddSpace("default", "default", nil, true)
   261  	c.Assert(err, jc.ErrorIsNil)
   262  
   263  	devAddresses := make([]state.LinkLayerDeviceAddress, len(devicesArgs))
   264  	for i, devArg := range devicesArgs {
   265  		subnet := i*10 + 1
   266  		subnetCIDR := fmt.Sprintf("10.%d.0.0/24", subnet)
   267  		_, err = s.State.AddSubnet(state.SubnetInfo{
   268  			CIDR:      subnetCIDR,
   269  			SpaceName: "default",
   270  		})
   271  		devAddresses[i] = state.LinkLayerDeviceAddress{
   272  			DeviceName:   devArg.Name,
   273  			CIDRAddress:  fmt.Sprintf("10.%d.0.10/24", subnet),
   274  			ConfigMethod: state.StaticAddress,
   275  		}
   276  	}
   277  
   278  	expectedParents := []string{
   279  		"br-eth0",
   280  		"br-eth1",
   281  		"br-eth2",
   282  		"br-eth3",
   283  		"br-eth4",
   284  		"br-eth10",
   285  		"br-eth10-100",
   286  	}
   287  
   288  	err = s.machine.SetParentLinkLayerDevicesBeforeTheirChildren(devicesArgs[:])
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	err = s.machine.SetDevicesAddresses(devAddresses...)
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	s.addContainerMachine(c)
   293  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   294  	err = s.containerMachine.SetConstraints(constraints.Value{
   295  		Spaces: &[]string{"default"},
   296  	})
   297  	c.Assert(err, jc.ErrorIsNil)
   298  
   299  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  
   302  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Assert(containerDevices, gc.HasLen, len(devicesArgs))
   305  
   306  	for i, containerDevice := range containerDevices {
   307  		c.Check(containerDevice.Name(), gc.Matches, "eth"+strconv.Itoa(i))
   308  		c.Check(containerDevice.Type(), gc.Equals, state.EthernetDevice)
   309  		c.Check(containerDevice.MTU(), gc.Equals, uint(0)) // inherited from the parent device.
   310  		c.Check(containerDevice.MACAddress(), gc.Matches, "00:16:3e(:[0-9a-f]{2}){3}")
   311  		c.Check(containerDevice.IsUp(), jc.IsTrue)
   312  		c.Check(containerDevice.IsAutoStart(), jc.IsTrue)
   313  		c.Check(containerDevice.ParentName(), gc.Equals, fmt.Sprintf("m#0#d#%s", expectedParents[i]))
   314  	}
   315  }
   316  
   317  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesConstraintsBindOnlyOne(c *gc.C) {
   318  	s.setupMachineInTwoSpaces(c)
   319  	s.addContainerMachine(c)
   320  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   321  	err := s.containerMachine.SetConstraints(constraints.Value{
   322  		Spaces: &[]string{"dmz"},
   323  	})
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	spaces, err := s.containerMachine.DesiredSpaces()
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Check(spaces.SortedValues(), gc.DeepEquals, []string{"dmz"})
   328  
   329  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  
   332  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Assert(containerDevices, gc.HasLen, 1)
   335  
   336  	containerDevice := containerDevices[0]
   337  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   338  	c.Check(containerDevice.Type(), gc.Equals, state.EthernetDevice)
   339  	c.Check(containerDevice.MTU(), gc.Equals, uint(0)) // inherited from the parent device.
   340  	c.Check(containerDevice.MACAddress(), gc.Matches, "00:16:3e(:[0-9a-f]{2}){3}")
   341  	c.Check(containerDevice.IsUp(), jc.IsTrue)
   342  	c.Check(containerDevice.IsAutoStart(), jc.IsTrue)
   343  	// br-ens0p10 on the host machine is in space dmz, while br-ens33 is in space default
   344  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens0p10`)
   345  }
   346  
   347  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesUnitBindingBindOnlyOne(c *gc.C) {
   348  	s.setupMachineInTwoSpaces(c)
   349  	s.addContainerMachine(c)
   350  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   351  	app := addApplication(c, s.State, "", "mysql",
   352  		addCharm(c, s.State, "quantal", "mysql"), map[string]string{"server": "default"})
   353  	unit, err := app.AddUnit(state.AddUnitParams{})
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	err = unit.AssignToMachine(s.containerMachine.Raw())
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	spaces, err := s.containerMachine.DesiredSpaces()
   358  	c.Assert(err, jc.ErrorIsNil)
   359  	c.Check(spaces.SortedValues(), gc.DeepEquals, []string{"default"})
   360  
   361  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   362  	c.Assert(err, jc.ErrorIsNil)
   363  
   364  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	c.Assert(containerDevices, gc.HasLen, 1)
   367  
   368  	containerDevice := containerDevices[0]
   369  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   370  	c.Check(containerDevice.Type(), gc.Equals, state.EthernetDevice)
   371  	c.Check(containerDevice.MTU(), gc.Equals, uint(0)) // inherited from the parent device.
   372  	c.Check(containerDevice.MACAddress(), gc.Matches, "00:16:3e(:[0-9a-f]{2}){3}")
   373  	c.Check(containerDevice.IsUp(), jc.IsTrue)
   374  	c.Check(containerDevice.IsAutoStart(), jc.IsTrue)
   375  	// br-ens0p10 on the host machine is in space dmz, while br-ens33 is in space default
   376  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens33`)
   377  }
   378  
   379  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesHostOneSpace(c *gc.C) {
   380  	s.setupTwoSpaces(c)
   381  	// Is put into the 'default' space
   382  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.0.0.20/24")
   383  	// We change the machine to be in 'dmz' instead of 'default', but it is
   384  	// still in a single space. Adding a container to a machine that is in a
   385  	// single space puts that container into the same space.
   386  	err := s.machine.RemoveAllAddresses()
   387  	c.Assert(err, jc.ErrorIsNil)
   388  	err = s.machine.SetDevicesAddresses(
   389  		state.LinkLayerDeviceAddress{
   390  			DeviceName: "br-eth0",
   391  			// In the DMZ subnet
   392  			CIDRAddress:  "10.10.0.20/24",
   393  			ConfigMethod: state.StaticAddress,
   394  		},
   395  	)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	s.addContainerMachine(c)
   398  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   399  
   400  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   401  	c.Assert(err, jc.ErrorIsNil)
   402  
   403  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   404  	c.Assert(err, jc.ErrorIsNil)
   405  	// c.Assert(containerDevices, gc.HasLen, 0)
   406  	// c.Skip("known failure, we don't handle containers no bindings and no constraints")
   407  	// Ideally we would get a single container device that matches to
   408  	// the 'default' space.
   409  	c.Assert(containerDevices, gc.HasLen, 1)
   410  
   411  	containerDevice := containerDevices[0]
   412  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   413  	c.Check(containerDevice.Type(), gc.Equals, state.EthernetDevice)
   414  	c.Check(containerDevice.MTU(), gc.Equals, uint(0)) // inherited from the parent device.
   415  	c.Check(containerDevice.MACAddress(), gc.Matches, "00:16:3e(:[0-9a-f]{2}){3}")
   416  	c.Check(containerDevice.IsUp(), jc.IsTrue)
   417  	c.Check(containerDevice.IsAutoStart(), jc.IsTrue)
   418  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-eth0`)
   419  }
   420  
   421  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesDefaultSpace(c *gc.C) {
   422  	// TODO(jam): 2016-12-28 Eventually we probably want to have a
   423  	// model-config level default-space, but for now, 'default' should not be
   424  	// special.
   425  	// The host machine is in both 'default' and 'dmz', and the container is
   426  	// not requested to be in any particular space. But because we have
   427  	// access to the 'default' space, we go ahead and use that for the
   428  	// container.
   429  	s.setupMachineInTwoSpaces(c)
   430  	s.addContainerMachine(c)
   431  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   432  
   433  	err := s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   434  	c.Assert(err, gc.ErrorMatches, "no obvious space for container.*")
   435  }
   436  
   437  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesNoValidSpace(c *gc.C) {
   438  	// The host machine will be in 2 spaces, but neither one is 'default',
   439  	// thus we are unable to find a valid space to put the container in.
   440  	s.setupTwoSpaces(c)
   441  	// Is put into the 'dmz' space
   442  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.10.0.20/24")
   443  	// Second bridge is in the 'db' space
   444  	s.createSpaceAndSubnet(c, "db", "10.20.0.0/24")
   445  	s.createNICAndBridgeWithIP(c, s.machine, "ens4", "br-ens4", "10.20.0.10/24")
   446  	s.addContainerMachine(c)
   447  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   448  
   449  	err := s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   450  	c.Assert(err, gc.ErrorMatches, `no obvious space for container "0/lxd/0", host machine has spaces: .*`)
   451  
   452  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   453  }
   454  
   455  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesMismatchConstraints(c *gc.C) {
   456  	// Machine is in 'default' but container wants to be in 'dmz'
   457  	s.setupTwoSpaces(c)
   458  	// Is put into the 'default' space
   459  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.0.0.20/24")
   460  	s.addContainerMachine(c)
   461  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   462  	err := s.containerMachine.SetConstraints(constraints.Value{
   463  		Spaces: &[]string{"dmz"},
   464  	})
   465  	c.Assert(err, jc.ErrorIsNil)
   466  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   467  	c.Assert(err.Error(), gc.Equals, `unable to find host bridge for space(s) "dmz" for container "0/lxd/0"`)
   468  
   469  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   470  }
   471  
   472  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesMissingBridge(c *gc.C) {
   473  	// Machine is in 'default' and 'dmz' but doesn't have a bridge for 'dmz'
   474  	s.setupTwoSpaces(c)
   475  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.0.0.20/24")
   476  	s.createNICWithIP(c, s.machine, "ens5", "10.20.0.10/24")
   477  	s.addContainerMachine(c)
   478  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   479  	err := s.containerMachine.SetConstraints(constraints.Value{
   480  		Spaces: &[]string{"dmz"},
   481  	})
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   484  	c.Assert(err.Error(), gc.Equals, `unable to find host bridge for space(s) "dmz" for container "0/lxd/0"`)
   485  
   486  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   487  }
   488  
   489  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesNoDefaultNoConstraints(c *gc.C) {
   490  	// The host machine will be in 2 spaces, but neither one is 'default',
   491  	// thus we are unable to find a valid space to put the container in.
   492  	s.setupTwoSpaces(c)
   493  	// In 'dmz'
   494  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.10.0.20/24")
   495  	// Second bridge is in the 'db' space
   496  	s.createSpaceAndSubnet(c, "db", "10.20.0.0/24")
   497  	s.createNICAndBridgeWithIP(c, s.machine, "ens4", "br-ens4", "10.20.0.10/24")
   498  	s.addContainerMachine(c)
   499  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   500  
   501  	err := s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   502  	c.Assert(err, gc.ErrorMatches, `no obvious space for container "0/lxd/0", host machine has spaces: .*`)
   503  
   504  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   505  	c.Assert(err, jc.ErrorIsNil)
   506  	c.Assert(containerDevices, gc.HasLen, 0)
   507  }
   508  
   509  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesTwoDevicesOneBridged(c *gc.C) {
   510  	// The host machine has 2 devices in one space, but only one is bridged.
   511  	// We'll only use the one that is bridged, and not complain about the other.
   512  	s.setupTwoSpaces(c)
   513  	// In 'default'
   514  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   515  	s.createNICAndBridgeWithIP(c, s.machine, "eth1", "br-eth1", "10.0.0.21/24")
   516  	s.addContainerMachine(c)
   517  	err := s.containerMachine.SetConstraints(constraints.Value{
   518  		Spaces: &[]string{"default"},
   519  	})
   520  	c.Assert(err, jc.ErrorIsNil)
   521  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   522  
   523  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   524  	c.Assert(err, jc.ErrorIsNil)
   525  
   526  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(containerDevices, gc.HasLen, 1)
   529  
   530  	containerDevice := containerDevices[0]
   531  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   532  	c.Check(containerDevice.Type(), gc.Equals, state.EthernetDevice)
   533  	c.Check(containerDevice.MTU(), gc.Equals, uint(0)) // inherited from the parent device.
   534  	c.Check(containerDevice.MACAddress(), gc.Matches, "00:16:3e(:[0-9a-f]{2}){3}")
   535  	c.Check(containerDevice.IsUp(), jc.IsTrue)
   536  	c.Check(containerDevice.IsAutoStart(), jc.IsTrue)
   537  	// br-eth1 is a valid bridge in the 'default' space
   538  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-eth1`)
   539  }
   540  
   541  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesTwoBridgedSameSpace(c *gc.C) {
   542  	// The host machine has 2 devices and both are bridged into the desired space
   543  	// We'll use both
   544  	s.setupTwoSpaces(c)
   545  	// In 'default'
   546  	s.createNICAndBridgeWithIP(c, s.machine, "ens33", "br-ens33", "10.0.0.20/24")
   547  	s.createNICAndBridgeWithIP(c, s.machine, "ens44", "br-ens44", "10.0.0.21/24")
   548  	s.addContainerMachine(c)
   549  	err := s.containerMachine.SetConstraints(constraints.Value{
   550  		Spaces: &[]string{"default"},
   551  	})
   552  	c.Assert(err, jc.ErrorIsNil)
   553  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   554  
   555  	err = s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   556  	c.Assert(err, jc.ErrorIsNil)
   557  
   558  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   559  	c.Assert(err, jc.ErrorIsNil)
   560  	c.Assert(containerDevices, gc.HasLen, 2)
   561  
   562  	containerDevice := containerDevices[0]
   563  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   564  	// br-ens33 and br-ens44 are both bridges in the 'default' space
   565  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens33`)
   566  	containerDevice = containerDevices[1]
   567  	c.Check(containerDevice.Name(), gc.Matches, "eth1")
   568  	// br-ens33 and br-ens44 are both bridges in the 'default' space
   569  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens44`)
   570  }
   571  
   572  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesTwoBridgesNoSpaces(c *gc.C) {
   573  	// The host machine has 2 network devices and 2 bridges, but none of them
   574  	// are in a known space. The container also has no requested space.
   575  	// In that case, we will use all of the unknown bridges for container
   576  	// devices.
   577  	s.setupTwoSpaces(c)
   578  	s.createNICAndBridgeWithIP(c, s.machine, "ens3", "br-ens3", "172.12.1.10/24")
   579  	s.createNICAndBridgeWithIP(c, s.machine, "ens4", "br-ens4", "192.168.3.4/24")
   580  	s.createAllDefaultDevices(c, s.machine)
   581  	s.addContainerMachine(c)
   582  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   583  
   584  	err := s.bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   585  	c.Assert(err, jc.ErrorIsNil)
   586  
   587  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   588  	c.Assert(err, jc.ErrorIsNil)
   589  	c.Assert(containerDevices, gc.HasLen, 2)
   590  
   591  	containerDevice := containerDevices[0]
   592  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   593  	// br-ens33 and br-ens44 are both bridges in the 'default' space
   594  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens3`)
   595  	containerDevice = containerDevices[1]
   596  	c.Check(containerDevice.Name(), gc.Matches, "eth1")
   597  	// br-ens33 and br-ens44 are both bridges in the 'default' space
   598  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#br-ens4`)
   599  }
   600  
   601  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesNoLocal(c *gc.C) {
   602  	// The host machine has 1 network device and only local bridges, but none of them
   603  	// are in a known space. The container also has no requested space.
   604  	s.setupTwoSpaces(c)
   605  	s.createNICWithIP(c, s.machine, "ens3", "172.12.1.10/24")
   606  	s.createAllDefaultDevices(c, s.machine)
   607  	s.addContainerMachine(c)
   608  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   609  
   610  	bridgePolicy := &containerizer.BridgePolicy{
   611  		NetBondReconfigureDelay:   13,
   612  		ContainerNetworkingMethod: "provider",
   613  	}
   614  	err := bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   615  	c.Assert(err.Error(), gc.Equals, `unable to find host bridge for space(s) "" for container "0/lxd/0"`)
   616  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   617  }
   618  
   619  func (s *bridgePolicyStateSuite) TestPopulateContainerLinkLayerDevicesUseLocal(c *gc.C) {
   620  	// The host machine has 1 network device and only local bridges, but none of them
   621  	// are in a known space. The container also has no requested space.
   622  	s.setupTwoSpaces(c)
   623  	s.createNICWithIP(c, s.machine, "ens3", "172.12.1.10/24")
   624  	s.createAllDefaultDevices(c, s.machine)
   625  	s.addContainerMachine(c)
   626  	s.assertNoDevicesOnMachine(c, s.containerMachine)
   627  
   628  	bridgePolicy := &containerizer.BridgePolicy{
   629  		NetBondReconfigureDelay:   13,
   630  		ContainerNetworkingMethod: "local",
   631  	}
   632  	err := bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   633  	c.Assert(err, jc.ErrorIsNil)
   634  
   635  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   636  	c.Assert(err, jc.ErrorIsNil)
   637  	c.Assert(containerDevices, gc.HasLen, 1)
   638  
   639  	containerDevice := containerDevices[0]
   640  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   641  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#lxdbr0`)
   642  }
   643  
   644  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerNoneMissing(c *gc.C) {
   645  	s.setupTwoSpaces(c)
   646  	s.createNICAndBridgeWithIP(c, s.machine, "eth0", "br-eth0", "10.0.0.20/24")
   647  	s.addContainerMachine(c)
   648  	err := s.containerMachine.SetConstraints(constraints.Value{
   649  		Spaces: &[]string{"default"},
   650  	})
   651  	c.Assert(err, jc.ErrorIsNil)
   652  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   653  	c.Assert(err, jc.ErrorIsNil)
   654  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{})
   655  	c.Check(reconfigureDelay, gc.Equals, 0)
   656  }
   657  
   658  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerDefaultUnbridged(c *gc.C) {
   659  	s.setupTwoSpaces(c)
   660  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   661  	s.addContainerMachine(c)
   662  	err := s.containerMachine.SetConstraints(constraints.Value{
   663  		Spaces: &[]string{"default"},
   664  	})
   665  	c.Assert(err, jc.ErrorIsNil)
   666  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   667  	c.Assert(err, jc.ErrorIsNil)
   668  	c.Check(missing, gc.DeepEquals, []network.DeviceToBridge{{
   669  		DeviceName: "eth0",
   670  		BridgeName: "br-eth0",
   671  	}})
   672  	c.Check(reconfigureDelay, gc.Equals, 0)
   673  }
   674  
   675  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerNoHostDevices(c *gc.C) {
   676  	s.setupTwoSpaces(c)
   677  	s.createSpaceAndSubnet(c, "third", "10.20.0.0/24")
   678  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   679  	s.addContainerMachine(c)
   680  	err := s.containerMachine.SetConstraints(constraints.Value{
   681  		Spaces: &[]string{"dmz", "third"},
   682  	})
   683  	c.Assert(err, jc.ErrorIsNil)
   684  	_, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   685  	c.Assert(err.Error(), gc.Equals, `host machine "0" has no available device in space(s) "dmz", "third"`)
   686  	c.Check(reconfigureDelay, gc.Equals, 0)
   687  }
   688  
   689  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerTwoSpacesOneMissing(c *gc.C) {
   690  	s.setupTwoSpaces(c)
   691  	// dmz
   692  	s.createNICAndBridgeWithIP(c, s.machine, "eth1", "br-eth1", "10.10.0.20/24")
   693  	s.addContainerMachine(c)
   694  	err := s.containerMachine.SetConstraints(constraints.Value{
   695  		Spaces: &[]string{"default", "dmz"},
   696  	})
   697  	_, _, err = s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   698  	// both default and dmz are needed, but default is missing
   699  	c.Assert(err.Error(), gc.Equals, `host machine "0" has no available device in space(s) "default"`)
   700  }
   701  
   702  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerNoSpaces(c *gc.C) {
   703  	// There is a "default" and "dmz" space, and our machine has 2 network
   704  	// interfaces, but is not part of any known space. In this circumstance,
   705  	// we should try to bridge all of the unknown space devices, not just one
   706  	// of them. This is are fallback mode when we don't understand the spaces of a machine.
   707  	s.setupTwoSpaces(c)
   708  	s.createNICWithIP(c, s.machine, "ens3", "172.12.0.10/24")
   709  	s.createNICWithIP(c, s.machine, "ens4", "192.168.0.10/24")
   710  	s.createAllDefaultDevices(c, s.machine)
   711  	s.addContainerMachine(c)
   712  	// No defined spaces for the container, no *known* spaces for the host
   713  	// machine. Triggers the fallback code to have us bridge all devices.
   714  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	c.Check(missing, gc.DeepEquals, []network.DeviceToBridge{{
   717  		DeviceName: "ens3",
   718  		BridgeName: "br-ens3",
   719  	}, {
   720  		DeviceName: "ens4",
   721  		BridgeName: "br-ens4",
   722  	}})
   723  	c.Check(reconfigureDelay, gc.Equals, 0)
   724  }
   725  
   726  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerContainerNetworkingMethodLocal(c *gc.C) {
   727  	// There is a "default" and "dmz" space, our machine has 1 network
   728  	// interface, but is not part of a known space. We have ContainerNetworkingMethod set to "local",
   729  	// which means we should fall back to using 'lxdbr0' instead of
   730  	// bridging the host device.
   731  	s.setupTwoSpaces(c)
   732  	s.createNICWithIP(c, s.machine, "ens3", "172.12.0.10/24")
   733  	s.createAllDefaultDevices(c, s.machine)
   734  	s.addContainerMachine(c)
   735  	bridgePolicy := &containerizer.BridgePolicy{
   736  		NetBondReconfigureDelay:   13,
   737  		ContainerNetworkingMethod: "local",
   738  	}
   739  	// No defined spaces for the container, no *known* spaces for the host
   740  	// machine. Triggers the fallback code to have us bridge all devices.
   741  	missing, reconfigureDelay, err := bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   742  	c.Assert(err, jc.ErrorIsNil)
   743  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{})
   744  	c.Check(reconfigureDelay, gc.Equals, 0)
   745  }
   746  
   747  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerContainerNetworkingMethodLocalDefinedHostSpace(c *gc.C) {
   748  	// There is a "default" and "dmz" space, our machine has 1 network
   749  	// interface, but is not part of a known space. We have ContainerNetworkingMethod set to "local",
   750  	// which means we should fall back to using 'lxdbr0' instead of
   751  	// bridging the host device.
   752  	s.setupTwoSpaces(c)
   753  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   754  	s.createAllDefaultDevices(c, s.machine)
   755  	s.addContainerMachine(c)
   756  	bridgePolicy := &containerizer.BridgePolicy{
   757  		NetBondReconfigureDelay:   13,
   758  		ContainerNetworkingMethod: "local",
   759  	}
   760  	// No defined spaces for the container, host has spaces but we have
   761  	// ContainerNetworkingMethodLocal set so we should fall back to lxdbr0
   762  	missing, reconfigureDelay, err := bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   763  	c.Assert(err, jc.ErrorIsNil)
   764  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{})
   765  	c.Check(reconfigureDelay, gc.Equals, 0)
   766  
   767  	err = bridgePolicy.PopulateContainerLinkLayerDevices(s.machine, s.containerMachine)
   768  	c.Assert(err, jc.ErrorIsNil)
   769  
   770  	containerDevices, err := s.containerMachine.AllLinkLayerDevices()
   771  	c.Assert(err, jc.ErrorIsNil)
   772  	c.Assert(containerDevices, gc.HasLen, 1)
   773  
   774  	containerDevice := containerDevices[0]
   775  	c.Check(containerDevice.Name(), gc.Matches, "eth0")
   776  	c.Check(containerDevice.ParentName(), gc.Equals, `m#0#d#lxdbr0`)
   777  }
   778  
   779  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerContainerNetworkingMethodLocalNoAddress(c *gc.C) {
   780  	// We should only use 'lxdbr0' instead of bridging the host device.
   781  	s.setupTwoSpaces(c)
   782  	s.createNICWithIP(c, s.machine, "ens3", "172.12.0.10/24")
   783  	err := s.machine.SetLinkLayerDevices(
   784  		state.LinkLayerDeviceArgs{
   785  			Name:       "lxdbr0",
   786  			Type:       state.BridgeDevice,
   787  			ParentName: "",
   788  			IsUp:       true,
   789  		},
   790  	)
   791  	c.Assert(err, jc.ErrorIsNil)
   792  	s.addContainerMachine(c)
   793  	bridgePolicy := &containerizer.BridgePolicy{
   794  		NetBondReconfigureDelay:   13,
   795  		ContainerNetworkingMethod: "local",
   796  	}
   797  	// No defined spaces for the container, no *known* spaces for the host
   798  	// machine. Triggers the fallback code to have us bridge all devices.
   799  	missing, reconfigureDelay, err := bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   800  	c.Assert(err, jc.ErrorIsNil)
   801  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{})
   802  	c.Check(reconfigureDelay, gc.Equals, 0)
   803  }
   804  
   805  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerUnknownWithConstraint(c *gc.C) {
   806  	// If we have a host machine where we don't understand its spaces, but
   807  	// the container requests a specific space, we won't use the unknown
   808  	// ones.
   809  	s.setupTwoSpaces(c)
   810  	s.createNICWithIP(c, s.machine, "ens3", "172.12.0.10/24")
   811  	s.createNICWithIP(c, s.machine, "ens4", "192.168.0.10/24")
   812  	s.createAllDefaultDevices(c, s.machine)
   813  	s.addContainerMachine(c)
   814  	err := s.containerMachine.SetConstraints(constraints.Value{
   815  		Spaces: &[]string{"default"},
   816  	})
   817  	c.Assert(err, jc.ErrorIsNil)
   818  	_, _, err = s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   819  	c.Assert(err.Error(), gc.Equals,
   820  		`host machine "0" has no available device in space(s) "default"`)
   821  }
   822  
   823  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerUnknownAndDefault(c *gc.C) {
   824  	// The host machine has 2 devices, one is in a known 'default' space, the other is in an unknown space.
   825  	// We will ignore the unknown space and just return the one in 'default',
   826  	// cause that is the only declared space on the machine.
   827  	s.setupTwoSpaces(c)
   828  	// Default
   829  	s.createNICWithIP(c, s.machine, "ens3", "10.0.0.10/24")
   830  	s.createNICWithIP(c, s.machine, "ens4", "192.168.0.10/24")
   831  	s.createAllDefaultDevices(c, s.machine)
   832  	s.addContainerMachine(c)
   833  	// We don't need a container constraint, as the host machine is in a single space.
   834  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   835  	c.Assert(err, jc.ErrorIsNil)
   836  	c.Check(missing, gc.DeepEquals, []network.DeviceToBridge{{
   837  		DeviceName: "ens3",
   838  		BridgeName: "br-ens3",
   839  	}})
   840  	c.Check(reconfigureDelay, gc.Equals, 0)
   841  }
   842  
   843  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerOneOfTwoBridged(c *gc.C) {
   844  	// With two host devices that could be bridged, we will only ask for the
   845  	// first one to be bridged.
   846  	s.setupTwoSpaces(c)
   847  	s.createNICWithIP(c, s.machine, "ens3", "10.0.0.20/24")
   848  	s.createNICWithIP(c, s.machine, "ens4", "10.0.0.21/24")
   849  	s.createNICWithIP(c, s.machine, "ens5", "10.0.0.22/24")
   850  	s.createNICWithIP(c, s.machine, "ens6", "10.0.0.23/24")
   851  	s.createNICWithIP(c, s.machine, "ens7", "10.0.0.24/24")
   852  	s.createNICWithIP(c, s.machine, "ens8", "10.0.0.25/24")
   853  	s.createNICWithIP(c, s.machine, "ens3.1", "10.0.0.26/24")
   854  	s.createNICWithIP(c, s.machine, "ens3:1", "10.0.0.27/24")
   855  	s.createNICWithIP(c, s.machine, "ens2.1", "10.0.0.28/24")
   856  	s.createNICWithIP(c, s.machine, "ens2.2", "10.0.0.29/24")
   857  	s.createNICWithIP(c, s.machine, "ens20", "10.0.0.30/24")
   858  	s.addContainerMachine(c)
   859  	err := s.containerMachine.SetConstraints(constraints.Value{
   860  		Spaces: &[]string{"default"},
   861  	})
   862  	c.Assert(err, jc.ErrorIsNil)
   863  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   864  	c.Assert(err, jc.ErrorIsNil)
   865  	// Only the first device (by sort order) should be selected
   866  	c.Check(missing, gc.DeepEquals, []network.DeviceToBridge{{
   867  		DeviceName: "ens2.1",
   868  		BridgeName: "br-ens2-1",
   869  	}})
   870  	c.Check(reconfigureDelay, gc.Equals, 0)
   871  }
   872  
   873  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerTwoHostDevicesOneBridged(c *gc.C) {
   874  	// With two host devices that could be bridged, we will only ask for the
   875  	// first one to be bridged.
   876  	s.setupTwoSpaces(c)
   877  	s.createNICWithIP(c, s.machine, "ens3", "10.0.0.20/24")
   878  	s.createNICAndBridgeWithIP(c, s.machine, "ens4", "br-ens4", "10.0.0.21/24") // TODO: different subnet?
   879  	s.addContainerMachine(c)
   880  	err := s.containerMachine.SetConstraints(constraints.Value{
   881  		Spaces: &[]string{"default"},
   882  	})
   883  	c.Assert(err, jc.ErrorIsNil)
   884  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   885  	c.Assert(err, jc.ErrorIsNil)
   886  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{})
   887  	c.Check(reconfigureDelay, gc.Equals, 0)
   888  }
   889  
   890  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerNoConstraintsDefaultNotSpecial(c *gc.C) {
   891  	// TODO(jam): 2016-12-28 Eventually we probably want to have a
   892  	// model-config level default-space, but for now, 'default' should not be
   893  	// special.
   894  	s.setupTwoSpaces(c)
   895  	// Default
   896  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   897  	// DMZ
   898  	s.createNICWithIP(c, s.machine, "eth1", "10.10.0.20/24")
   899  	s.addContainerMachine(c)
   900  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   901  	c.Assert(err, gc.ErrorMatches, "no obvious space for container.*")
   902  	c.Assert(missing, gc.IsNil)
   903  	c.Check(reconfigureDelay, gc.Equals, 0)
   904  }
   905  
   906  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerTwoSpacesOneBridged(c *gc.C) {
   907  	s.setupTwoSpaces(c)
   908  	// Default
   909  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   910  	// DMZ
   911  	s.createNICAndBridgeWithIP(c, s.machine, "eth1", "br-eth1", "10.10.0.20/24")
   912  	s.addContainerMachine(c)
   913  	err := s.containerMachine.SetConstraints(constraints.Value{
   914  		Spaces: &[]string{"default", "dmz"},
   915  	})
   916  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   917  	c.Assert(err, jc.ErrorIsNil)
   918  	// both default and dmz are needed, but default needs to be bridged
   919  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{{
   920  		DeviceName: "eth0",
   921  		BridgeName: "br-eth0",
   922  	}})
   923  	c.Check(reconfigureDelay, gc.Equals, 0)
   924  }
   925  
   926  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerMultipleSpacesNoneBridged(c *gc.C) {
   927  	s.setupTwoSpaces(c)
   928  	// Default
   929  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
   930  	// DMZ
   931  	s.createNICWithIP(c, s.machine, "eth1", "10.10.0.20/24")
   932  	// abba
   933  	s.createSpaceAndSubnet(c, "abba", "172.12.10.0/24")
   934  	s.createNICWithIP(c, s.machine, "eth0.1", "172.12.10.3/24")
   935  	s.addContainerMachine(c)
   936  	err := s.containerMachine.SetConstraints(constraints.Value{
   937  		Spaces: &[]string{"default", "dmz", "abba"},
   938  	})
   939  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
   940  	c.Assert(err, jc.ErrorIsNil)
   941  	// both default and dmz are needed, but default needs to be bridged
   942  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{{
   943  		DeviceName: "eth0",
   944  		BridgeName: "br-eth0",
   945  	}, {
   946  		DeviceName: "eth0.1",
   947  		BridgeName: "br-eth0-1",
   948  	}, {
   949  		DeviceName: "eth1",
   950  		BridgeName: "br-eth1",
   951  	}})
   952  	c.Check(reconfigureDelay, gc.Equals, 0)
   953  }
   954  
   955  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerBondedNICs(c *gc.C) {
   956  	s.setupTwoSpaces(c)
   957  	// Default
   958  	// We call it 'zbond' so it sorts late instead of first
   959  	err := s.machine.SetLinkLayerDevices(
   960  		state.LinkLayerDeviceArgs{
   961  			Name:       "zbond0",
   962  			Type:       state.BondDevice,
   963  			ParentName: "",
   964  			IsUp:       true,
   965  		},
   966  	)
   967  	c.Assert(err, jc.ErrorIsNil)
   968  	err = s.machine.SetLinkLayerDevices(
   969  		state.LinkLayerDeviceArgs{
   970  			Name:       "eth0",
   971  			Type:       state.EthernetDevice,
   972  			ParentName: "zbond0",
   973  			IsUp:       true,
   974  		},
   975  		state.LinkLayerDeviceArgs{
   976  			Name:       "eth1",
   977  			Type:       state.EthernetDevice,
   978  			ParentName: "zbond0",
   979  			IsUp:       true,
   980  		},
   981  	)
   982  	err = s.machine.SetDevicesAddresses(
   983  		state.LinkLayerDeviceAddress{
   984  			DeviceName:   "zbond0",
   985  			CIDRAddress:  "10.0.0.10/24",
   986  			ConfigMethod: state.StaticAddress,
   987  		},
   988  		// TODO(jam): 2016-12-20 These devices *shouldn't* have IP addresses
   989  		// when they are in a bond, however eventually we should detect what
   990  		// space a device is in by something other than just IP address, and
   991  		// we want to test that we don't try to bond these devices.
   992  		// So for now we give them IP addresses so they show up in the space
   993  		state.LinkLayerDeviceAddress{
   994  			DeviceName:   "eth0",
   995  			CIDRAddress:  "10.0.0.11/24",
   996  			ConfigMethod: state.StaticAddress,
   997  		},
   998  		state.LinkLayerDeviceAddress{
   999  			DeviceName:   "eth1",
  1000  			CIDRAddress:  "10.0.0.12/24",
  1001  			ConfigMethod: state.StaticAddress,
  1002  		},
  1003  	)
  1004  	c.Assert(err, jc.ErrorIsNil)
  1005  	s.addContainerMachine(c)
  1006  	err = s.containerMachine.SetConstraints(constraints.Value{
  1007  		Spaces: &[]string{"default"},
  1008  	})
  1009  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
  1010  	c.Assert(err, jc.ErrorIsNil)
  1011  	// both default and dmz are needed, but default needs to be bridged
  1012  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{{
  1013  		DeviceName: "zbond0",
  1014  		BridgeName: "br-zbond0",
  1015  	}})
  1016  	// We are creating a bridge on a bond, so we use a non-zero delay
  1017  	c.Check(reconfigureDelay, gc.Equals, 13)
  1018  }
  1019  
  1020  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerVLAN(c *gc.C) {
  1021  	s.setupTwoSpaces(c)
  1022  	// We create an eth0 that has an address, and then an eth0.100 which is
  1023  	// VLAN tagged on top of that ethernet device.
  1024  	// "eth0" is in "default", "eth0.100" is in "dmz"
  1025  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.10/24")
  1026  	err := s.machine.SetLinkLayerDevices(
  1027  		state.LinkLayerDeviceArgs{
  1028  			Name:       "eth0.100",
  1029  			Type:       state.VLAN_8021QDevice,
  1030  			ParentName: "eth0",
  1031  			IsUp:       true,
  1032  		},
  1033  	)
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	// In dmz
  1036  	err = s.machine.SetDevicesAddresses(
  1037  		state.LinkLayerDeviceAddress{
  1038  			DeviceName:   "eth0.100",
  1039  			CIDRAddress:  "10.10.0.11/24",
  1040  			ConfigMethod: state.StaticAddress,
  1041  		},
  1042  	)
  1043  	c.Assert(err, jc.ErrorIsNil)
  1044  
  1045  	// We create a container in both spaces, and we should see that it wants
  1046  	// to bridge both devices.
  1047  	s.addContainerMachine(c)
  1048  	err = s.containerMachine.SetConstraints(constraints.Value{
  1049  		Spaces: &[]string{"default", "dmz"},
  1050  	})
  1051  	c.Assert(err, jc.ErrorIsNil)
  1052  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
  1053  	c.Assert(err, jc.ErrorIsNil)
  1054  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{{
  1055  		DeviceName: "eth0",
  1056  		BridgeName: "br-eth0",
  1057  	}, {
  1058  		DeviceName: "eth0.100",
  1059  		BridgeName: "br-eth0-100",
  1060  	}})
  1061  	c.Check(reconfigureDelay, gc.Equals, 0)
  1062  }
  1063  
  1064  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerVLANOnBond(c *gc.C) {
  1065  	s.setupTwoSpaces(c)
  1066  	// We have eth0 and eth1 that don't have IP addresses, that are in a
  1067  	// bond, which then has a VLAN on top of that bond. The VLAN should still
  1068  	// be a valid target for bridging
  1069  	err := s.machine.SetLinkLayerDevices(
  1070  		state.LinkLayerDeviceArgs{
  1071  			Name:       "bond0",
  1072  			Type:       state.BondDevice,
  1073  			ParentName: "",
  1074  			IsUp:       true,
  1075  		},
  1076  	)
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  	err = s.machine.SetLinkLayerDevices(
  1079  		[]state.LinkLayerDeviceArgs{{
  1080  			Name:       "eth0",
  1081  			Type:       state.EthernetDevice,
  1082  			ParentName: "bond0",
  1083  			IsUp:       true,
  1084  		}, {
  1085  			Name:       "eth1",
  1086  			Type:       state.EthernetDevice,
  1087  			ParentName: "bond0",
  1088  			IsUp:       true,
  1089  		}, {
  1090  			Name:       "bond0.100",
  1091  			Type:       state.VLAN_8021QDevice,
  1092  			ParentName: "bond0",
  1093  			IsUp:       true,
  1094  		}}...,
  1095  	)
  1096  	c.Assert(err, jc.ErrorIsNil)
  1097  	err = s.machine.SetDevicesAddresses(
  1098  		state.LinkLayerDeviceAddress{
  1099  			DeviceName:   "bond0",
  1100  			CIDRAddress:  "10.0.0.20/24", // default
  1101  			ConfigMethod: state.StaticAddress,
  1102  		},
  1103  		state.LinkLayerDeviceAddress{
  1104  			DeviceName:   "bond0.100",
  1105  			CIDRAddress:  "10.10.0.20/24", // dmz
  1106  			ConfigMethod: state.StaticAddress,
  1107  		},
  1108  	)
  1109  	c.Assert(err, jc.ErrorIsNil)
  1110  
  1111  	// We create a container in both spaces, and we should see that it wants
  1112  	// to bridge both devices.
  1113  	s.addContainerMachine(c)
  1114  	err = s.containerMachine.SetConstraints(constraints.Value{
  1115  		Spaces: &[]string{"default", "dmz"},
  1116  	})
  1117  	c.Assert(err, jc.ErrorIsNil)
  1118  	missing, reconfigureDelay, err := s.bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
  1119  	c.Assert(err, jc.ErrorIsNil)
  1120  	c.Check(missing, jc.DeepEquals, []network.DeviceToBridge{{
  1121  		DeviceName: "bond0",
  1122  		BridgeName: "br-bond0",
  1123  	}, {
  1124  		DeviceName: "bond0.100",
  1125  		BridgeName: "br-bond0-100",
  1126  	}})
  1127  	c.Check(reconfigureDelay, gc.Equals, 13)
  1128  }
  1129  
  1130  func (s *bridgePolicyStateSuite) TestFindMissingBridgesForContainerNetworkingMethodFAN(c *gc.C) {
  1131  	s.setupTwoSpaces(c)
  1132  	s.createNICWithIP(c, s.machine, "eth0", "10.0.0.20/24")
  1133  	s.addContainerMachine(c)
  1134  	err := s.containerMachine.SetConstraints(constraints.Value{
  1135  		Spaces: &[]string{"default"},
  1136  	})
  1137  	c.Assert(err, jc.ErrorIsNil)
  1138  	bridgePolicy := &containerizer.BridgePolicy{
  1139  		NetBondReconfigureDelay:   13,
  1140  		ContainerNetworkingMethod: "fan",
  1141  	}
  1142  	_, _, err = bridgePolicy.FindMissingBridgesForContainer(s.machine, s.containerMachine)
  1143  	c.Assert(err, gc.ErrorMatches, `host machine "0" has no available FAN devices in space\(s\) "default"`)
  1144  }
  1145  
  1146  var bridgeNames = map[string]string{
  1147  	"eno0":            "br-eno0",
  1148  	"enovlan.123":     "br-enovlan-123",
  1149  	"twelvechars0":    "br-twelvechars0",
  1150  	"thirteenchars":   "b-thirteenchars",
  1151  	"enfourteenchar":  "b-fourteenchar",
  1152  	"enfifteenchars0": "b-fifteenchars0",
  1153  	"fourteenchars1":  "b-5590a4-chars1",
  1154  	"fifteenchars.12": "b-38b496-ars-12",
  1155  	"zeros0526193032": "b-000000-193032",
  1156  	"enx00e07cc81e1d": "b-x00e07cc81e1d",
  1157  }
  1158  
  1159  func (s *bridgePolicyStateSuite) TestBridgeNameForDevice(c *gc.C) {
  1160  	for deviceName, bridgeName := range bridgeNames {
  1161  		generatedBridgeName := containerizer.BridgeNameForDevice(deviceName)
  1162  		c.Assert(generatedBridgeName, gc.Equals, bridgeName)
  1163  	}
  1164  }
  1165  
  1166  // TODO(jam): 2017-01-31 Make sure KVM guests default to virbr0, and LXD guests use lxdbr0
  1167  // Add tests for UseLocal = True, but we have named spaces
  1168  // Add tests for UseLocal = True, but the host device is bridged