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