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