def test_machine(self): hostname = factory.make_name("hostname") cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) power_state = factory.make_name("unknown") interfaces = [ DiscoveredMachineInterface(mac_address=factory.make_mac_address()) for _ in range(3) ] block_devices = [ DiscoveredMachineBlockDevice( model=factory.make_name("model"), serial=factory.make_name("serial"), size=random.randint(512, 1024), ) for _ in range(3) ] tags = [factory.make_name("tag") for _ in range(3)] machine = DiscoveredMachine( hostname=hostname, architecture="amd64/generic", cores=cores, cpu_speed=cpu_speed, memory=memory, power_state=power_state, interfaces=interfaces, block_devices=block_devices, tags=tags, ) self.assertEquals(cores, machine.cores) self.assertEquals(cpu_speed, machine.cpu_speed) self.assertEquals(memory, machine.memory) self.assertEquals(interfaces, machine.interfaces) self.assertEquals(block_devices, machine.block_devices) self.assertEquals(tags, machine.tags)
class TestDiscoveredMachine(MAASTestCase): example = DiscoveredMachine( hostname=factory.make_name("hostname"), architecture="amd64/generic", cores=random.randint(1, 8), cpu_speed=random.randint(1000, 2000), memory=random.randint(1024, 8192), power_state=factory.make_name("unknown"), power_parameters={"power_id": factory.make_name("power_id")}, interfaces=[ DiscoveredMachineInterface(mac_address=factory.make_mac_address()) for _ in range(3) ], block_devices=[ DiscoveredMachineBlockDevice( model=factory.make_name("model"), serial=factory.make_name("serial"), size=random.randint(512, 1024), id_path=factory.make_name("/dev/vda"), ) for _ in range(3) ], tags=[factory.make_name("tag") for _ in range(3)], ) def test_round_trip(self): argument = arguments.AmpDiscoveredMachine() encoded = argument.toString(self.example) self.assertThat(encoded, IsInstance(bytes)) decoded = argument.fromString(encoded) self.assertThat(decoded, Equals(self.example))
def test_interface_mac_vid(self): mac = factory.make_mac_address() vid = random.randint(1, 300) nic = DiscoveredMachineInterface(mac_address=mac, vid=vid) self.assertEquals(mac, nic.mac_address) self.assertEquals(vid, nic.vid) self.assertEquals([], nic.tags)
def get_discovered_machine(self, machine, request=None): """Gets the discovered machine.""" # Discovered machine. discovered_machine = DiscoveredMachine(architecture="", cores=0, cpu_speed=0, memory=0, interfaces=[], block_devices=[], tags=['virtual']) discovered_machine.hostname = machine discovered_machine.architecture = self.get_machine_arch(machine) discovered_machine.cores = self.get_machine_cpu_count(machine) discovered_machine.memory = self.get_machine_memory(machine) state = self.get_machine_state(machine) discovered_machine.power_state = VM_STATE_TO_POWER_STATE[state] discovered_machine.power_parameters = { 'power_id': machine, } # Discover block devices. block_devices = [] devices = self.list_machine_block_devices(machine) for idx, device in enumerate(devices): # Block device. # When request is provided map the tags from the request block # devices to the discovered block devices. This ensures that # composed machine has the requested tags on the block device. tags = [] if request is not None: tags = request.block_devices[idx].tags size = self.get_machine_local_storage(machine, device) if size is None: # Bug lp:1690144 - When a domain has a block device where its # storage path is no longer available. The domain cannot be # started when the storage path is missing, so we don't add it # to MAAS. maaslog.error( "Unable to discover machine '%s' in virsh pod: storage " "device '%s' is missing its storage backing." % (machine, device)) return None block_devices.append( DiscoveredMachineBlockDevice(model=None, serial=None, size=size, id_path="/dev/%s" % device, tags=tags)) discovered_machine.block_devices = block_devices # Discover interfaces. interfaces = [] mac_addresses = self.list_machine_mac_addresses(machine) boot = True for mac in mac_addresses: interfaces.append( DiscoveredMachineInterface(mac_address=mac, boot=boot)) boot = False discovered_machine.interfaces = interfaces return discovered_machine
def test_interface_mac_vid_tags(self): mac = factory.make_mac_address() vid = random.randint(1, 300) tags = [factory.make_name("tag") for _ in range(3)] nic = DiscoveredMachineInterface(mac_address=mac, vid=vid, tags=tags) self.assertEquals(mac, nic.mac_address) self.assertEquals(vid, nic.vid) self.assertEquals(tags, nic.tags)
def _get_discovered_interface(name, device, boot): if "network" in device: # Try finding the nictype from the networks. # XXX: This should work for "bridge" networks, # but will most likely produce weird results for the # other types. network = client.networks.get(device["network"]) attach_type = network.type attach_name = network.name else: attach_name = device["parent"] nictype = device["nictype"] attach_type = (InterfaceAttachType.BRIDGE if nictype == "bridged" else nictype) mac = device.get("hwaddr") if mac is None: mac = iface_to_mac.get(name) return DiscoveredMachineInterface( mac_address=mac, vid=int(device.get("vlan", 0)), boot=boot, attach_type=attach_type, attach_name=attach_name, )
def test_interface_mac(self): mac = factory.make_mac_address() nic = DiscoveredMachineInterface(mac_address=mac) self.assertEquals(mac, nic.mac_address) self.assertEquals(-1, nic.vid) self.assertEquals([], nic.tags)
def test_pod_asdict(self): hostname = factory.make_name("hostname") cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) local_storage = random.randint(4096, 8192) local_disks = random.randint(1, 8) iscsi_storage = random.randint(4096, 8192) hints = DiscoveredPodHints( cores=random.randint(1, 8), cpu_speed=random.randint(1000, 2000), memory=random.randint(4096, 8192), local_storage=random.randint(4096, 8192), local_disks=random.randint(1, 8), iscsi_storage=random.randint(4096, 8192), ) machines = [] tags = [factory.make_name("tag") for _ in range(3)] storage_pools = [ DiscoveredPodStoragePool( id=factory.make_name("id"), name=factory.make_name("name"), storage=random.randint(4096, 8192), type="dir", path="/var/%s" % factory.make_name("dir"), ) for _ in range(3) ] for _ in range(3): cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) power_state = factory.make_name("unknown") power_parameters = {"power_id": factory.make_name("power_id")} interfaces = [ DiscoveredMachineInterface( mac_address=factory.make_mac_address()) for _ in range(3) ] block_devices = [ DiscoveredMachineBlockDevice( model=factory.make_name("model"), serial=factory.make_name("serial"), size=random.randint(512, 1024), id_path=factory.make_name("/dev/vda"), ) for _ in range(3) ] for _ in range(3): block_devices.append( DiscoveredMachineBlockDevice( model=None, serial=None, size=random.randint(512, 1024), type=BlockDeviceType.ISCSI, iscsi_target=self.make_iscsi_target(), storage_pool=factory.make_name("pool"), )) tags = [factory.make_name("tag") for _ in range(3)] machines.append( DiscoveredMachine( hostname=hostname, architecture="amd64/generic", cores=cores, cpu_speed=cpu_speed, memory=memory, power_state=power_state, power_parameters=power_parameters, interfaces=interfaces, block_devices=block_devices, tags=tags, )) pod = DiscoveredPod( architectures=["amd64/generic"], cores=cores, cpu_speed=cpu_speed, memory=memory, local_storage=local_storage, local_disks=local_disks, iscsi_storage=iscsi_storage, hints=hints, machines=machines, tags=tags, storage_pools=storage_pools, ) self.assertThat( pod.asdict(), MatchesDict({ "architectures": Equals(["amd64/generic"]), "name": Equals(None), "cores": Equals(cores), "cpu_speed": Equals(cpu_speed), "memory": Equals(memory), "local_storage": Equals(local_storage), "local_disks": Equals(local_disks), "iscsi_storage": Equals(iscsi_storage), "mac_addresses": Equals([]), "capabilities": Equals(pod.capabilities), "hints": MatchesDict({ "cores": Equals(hints.cores), "cpu_speed": Equals(hints.cpu_speed), "memory": Equals(hints.memory), "local_storage": Equals(hints.local_storage), "local_disks": Equals(hints.local_disks), "iscsi_storage": Equals(hints.iscsi_storage), }), "machines": MatchesListwise([ MatchesDict({ "hostname": Equals(machine.hostname), "architecture": Equals("amd64/generic"), "cores": Equals(machine.cores), "cpu_speed": Equals(machine.cpu_speed), "memory": Equals(machine.memory), "power_state": Equals(machine.power_state), "power_parameters": Equals(machine.power_parameters), "interfaces": MatchesListwise([ MatchesDict({ "mac_address": Equals(interface.mac_address), "vid": Equals(interface.vid), "tags": Equals(interface.tags), "boot": Equals(False), "attach_type": Is(None), "attach_name": Is(None), }) for interface in machine.interfaces ]), "block_devices": MatchesListwise([ MatchesDict({ "model": Equals(block_device.model), "serial": Equals(block_device.serial), "size": Equals(block_device.size), "block_size": Equals(block_device.block_size), "tags": Equals(block_device.tags), "id_path": Equals(block_device.id_path), "type": Equals(block_device.type), "iscsi_target": Equals(block_device.iscsi_target), "storage_pool": Equals(block_device.storage_pool), }) for block_device in machine.block_devices ]), "tags": Equals(machine.tags), }) for machine in machines ]), "tags": Equals(tags), "storage_pools": MatchesListwise([ MatchesDict({ "id": Equals(pool.id), "name": Equals(pool.name), "storage": Equals(pool.storage), "type": Equals(pool.type), "path": Equals(pool.path), }) for pool in storage_pools ]), }), )
def test_pod(self): hostname = factory.make_name("hostname") cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) local_storage = random.randint(4096, 8192) iscsi_storage = random.randint(4096, 8192) hints = DiscoveredPodHints( cores=random.randint(1, 8), cpu_speed=random.randint(1000, 2000), memory=random.randint(4096, 8192), local_storage=random.randint(4096, 8192), iscsi_storage=random.randint(4096, 8192), ) machines = [] for _ in range(3): cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) power_state = factory.make_name("unknown") power_parameters = {"power_id": factory.make_name("power_id")} interfaces = [ DiscoveredMachineInterface( mac_address=factory.make_mac_address()) for _ in range(3) ] block_devices = [ DiscoveredMachineBlockDevice( model=factory.make_name("model"), serial=factory.make_name("serial"), size=random.randint(512, 1024), ) for _ in range(3) ] for _ in range(3): block_devices.append( DiscoveredMachineBlockDevice( model=None, serial=None, size=random.randint(512, 1024), type=BlockDeviceType.ISCSI, iscsi_target=self.make_iscsi_target(), )) tags = [factory.make_name("tag") for _ in range(3)] machines.append( DiscoveredMachine( hostname=hostname, architecture="amd64/generic", cores=cores, cpu_speed=cpu_speed, memory=memory, power_state=power_state, power_parameters=power_parameters, interfaces=interfaces, block_devices=block_devices, tags=tags, )) pod = DiscoveredPod( architectures=["amd64/generic"], cores=cores, cpu_speed=cpu_speed, memory=memory, local_storage=local_storage, iscsi_storage=iscsi_storage, hints=hints, machines=machines, ) self.assertEquals(cores, pod.cores) self.assertEquals(cpu_speed, pod.cpu_speed) self.assertEquals(memory, pod.memory) self.assertEquals(local_storage, pod.local_storage) self.assertEquals(iscsi_storage, pod.iscsi_storage) self.assertEquals(machines, pod.machines)
def test_get_discovered_machine(self): driver = lxd_module.LXDPodDriver() Client = self.patch(lxd_module, "Client") client = Client.return_value mock_machine = Mock() mock_machine.name = factory.make_name("machine") mock_machine.architecture = "x86_64" expanded_config = { "limits.cpu": "2", "limits.memory": "1024MiB", "volatile.eth0.hwaddr": "00:16:3e:78:be:04", "volatile.eth1.hwaddr": "00:16:3e:f9:fc:cb", "volatile.eth2.hwaddr": "00:16:3e:f9:fc:cc", } expanded_devices = { "eth0": { "name": "eth0", "network": "lxdbr0", "type": "nic", }, "eth1": { "name": "eth1", "nictype": "bridged", "parent": "br1", "type": "nic", }, "eth2": { "name": "eth2", "nictype": "macvlan", "parent": "eno2", "type": "nic", }, # SR-IOV devices created by MAAS have an explicit MAC set on # the device, so that it knows what the MAC will be. "eth3": { "name": "eth3", "hwaddr": "00:16:3e:f9:fc:dd", "nictype": "sriov", "parent": "eno3", "type": "nic", }, "eth4": { "name": "eth4", "hwaddr": "00:16:3e:f9:fc:ee", "nictype": "sriov", "parent": "eno3", "vlan": "33", "type": "nic", }, # An interface not created by MAAS, thus lacking an explicit # MAC. "eth5": { "name": "eth5", "nictype": "sriov", "parent": "eno3", "vlan": "44", "type": "nic", }, "root": { "path": "/", "pool": "default", "type": "disk", "size": "20GB", }, } mock_machine.expanded_config = expanded_config mock_machine.expanded_devices = expanded_devices mock_machine.status_code = 102 mock_storage_pool = Mock() mock_storage_pool.name = "default" mock_storage_pool_resources = Mock() mock_storage_pool_resources.space = { "used": 207111192576, "total": 306027577344, } mock_storage_pool.resources.get.return_value = ( mock_storage_pool_resources) mock_machine.storage_pools.get.return_value = mock_storage_pool mock_network = Mock() mock_network.type = "bridge" mock_network.name = "lxdbr0" client.networks.get.return_value = mock_network discovered_machine = yield ensureDeferred( driver.get_discovered_machine(client, mock_machine, [mock_storage_pool])) self.assertEqual(mock_machine.name, discovered_machine.hostname) self.assertEqual("uefi", discovered_machine.bios_boot_method) self.assertEqual( kernel_to_debian_architecture(mock_machine.architecture), discovered_machine.architecture, ) self.assertEqual( lxd_module.LXD_VM_POWER_STATE[mock_machine.status_code], discovered_machine.power_state, ) self.assertEqual(2, discovered_machine.cores) self.assertEqual(1024, discovered_machine.memory) self.assertEqual( mock_machine.name, discovered_machine.power_parameters["instance_name"], ) self.assertEqual( discovered_machine.block_devices[0], DiscoveredMachineBlockDevice( model="QEMU HARDDISK", serial="lxd_root", id_path="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_lxd_root", size=20 * 1000**3, block_size=512, tags=[], type="physical", storage_pool=expanded_devices["root"]["pool"], iscsi_target=None, ), ) self.assertEqual( discovered_machine.interfaces[0], DiscoveredMachineInterface( mac_address=expanded_config["volatile.eth0.hwaddr"], vid=0, tags=[], boot=True, attach_type=InterfaceAttachType.BRIDGE, attach_name="lxdbr0", ), ) self.assertEqual( discovered_machine.interfaces[1], DiscoveredMachineInterface( mac_address=expanded_config["volatile.eth1.hwaddr"], vid=0, tags=[], boot=False, attach_type=InterfaceAttachType.BRIDGE, attach_name="br1", ), ) self.assertEqual( discovered_machine.interfaces[2], DiscoveredMachineInterface( mac_address=expanded_config["volatile.eth2.hwaddr"], vid=0, tags=[], boot=False, attach_type=InterfaceAttachType.MACVLAN, attach_name="eno2", ), ) self.assertEqual( discovered_machine.interfaces[3], DiscoveredMachineInterface( mac_address=expanded_devices["eth3"]["hwaddr"], vid=0, tags=[], boot=False, attach_type=InterfaceAttachType.SRIOV, attach_name="eno3", ), ) self.assertEqual( discovered_machine.interfaces[4], DiscoveredMachineInterface( mac_address=expanded_devices["eth4"]["hwaddr"], vid=33, tags=[], boot=False, attach_type=InterfaceAttachType.SRIOV, attach_name="eno3", ), ) self.assertEqual( discovered_machine.interfaces[5], DiscoveredMachineInterface( mac_address=None, vid=44, tags=[], boot=False, attach_type=InterfaceAttachType.SRIOV, attach_name="eno3", ), ) self.assertItemsEqual([], discovered_machine.tags) self.assertFalse(discovered_machine.hugepages_backed) self.assertEqual(discovered_machine.pinned_cores, [])
async def get_discovered_machine(self, client, machine, storage_pools, request=None): """Get the discovered machine.""" # Check the power state first. state = machine.status_code try: power_state = LXD_VM_POWER_STATE[state] except KeyError: maaslog.error( f"{machine.name}: Unknown power status code: {state}") power_state = "unknown" expanded_config = machine.expanded_config expanded_devices = machine.expanded_devices # Discover block devices. block_devices = [] for idx, device in enumerate(expanded_devices): # Block device. # When request is provided map the tags from the request block # devices to the discovered block devices. This ensures that # composed machine has the requested tags on the block device. tags = [] if (request is not None and expanded_devices[device]["type"] == "disk"): tags = request.block_devices[0].tags device_info = expanded_devices[device] if device_info["type"] == "disk": # When LXD creates a QEMU disk the serial is always # lxd_{device name}. The device_name is defined by # the LXD profile or when adding a device. This is # commonly "root" for the first disk. The model and # serial must be correctly defined here otherwise # MAAS will delete the disk created during composition # which results in losing the storage pool link. Without # the storage pool link MAAS can't determine how much # of the storage pool has been used. serial = f"lxd_{device}" # Default disk size is 10GB. size = convert_lxd_byte_suffixes( device_info.get("size", "10GB")) storage_pool = device_info.get("pool") block_devices.append( DiscoveredMachineBlockDevice( model="QEMU HARDDISK", serial=serial, id_path= f"/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_{serial}", size=size, tags=tags, storage_pool=storage_pool, )) # Discover interfaces. interfaces = [] boot = True config_mac_address = {} for configuration in expanded_config: if configuration.endswith("hwaddr"): mac = expanded_config[configuration] name = configuration.split(".")[1] config_mac_address[name] = mac for name, device in expanded_devices.items(): if device["type"] != "nic": continue device = expanded_devices[name] if "network" in device: # Try finding the nictype from the networks. # XXX: This should work for "bridge" networks, # but will most likely produce weird results for the # other types. network = await deferToThread(client.networks.get, device["network"]) attach_type = network.type attach_name = network.name else: attach_name = device["parent"] nictype = device["nictype"] attach_type = (InterfaceAttachType.BRIDGE if nictype == "bridged" else nictype) mac = device.get("hwaddr") if mac is None: mac = config_mac_address.get(name) interfaces.append( DiscoveredMachineInterface( mac_address=mac, vid=int(device.get("vlan", get_vid_from_ifname(name))), boot=boot, attach_type=attach_type, attach_name=attach_name, )) boot = False # LXD uses different suffixes to store memory so make # sure we convert to MiB, which is what MAAS uses. memory = expanded_config.get("limits.memory") if memory is not None: memory = convert_lxd_byte_suffixes(memory, divisor=1024**2) else: memory = 1024 hugepages_backed = _get_bool( expanded_config.get("limits.memory.hugepages")) cores, pinned_cores = _parse_cpu_cores( expanded_config.get("limits.cpu")) return DiscoveredMachine( hostname=machine.name, architecture=kernel_to_debian_architecture(machine.architecture), # 1 core and 1GiB of memory (we need it in MiB) is default for # LXD if not specified. cores=cores, memory=memory, cpu_speed=0, interfaces=interfaces, block_devices=block_devices, power_state=power_state, power_parameters={"instance_name": machine.name}, tags=[], hugepages_backed=hugepages_backed, pinned_cores=pinned_cores, # LXD VMs use only UEFI. bios_boot_method="uefi", )