示例#1
0
 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)
示例#2
0
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))
示例#3
0
    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
示例#4
0
 def _get_discovered_block_device(name, device, requested_device=None):
     tags = requested_device.tags if requested_device else []
     # When LXD creates a QEMU disk the serial is always lxd_{device
     # name}. The device name 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_{name}"
     source = device.get("source")
     if source:
         pool = client.storage_pools.get(device["pool"])
         volume = pool.volumes.get("custom", source)
         size = volume.config.get("size")
     else:
         size = device.get("size")
     # Default disk size is 10GB in LXD
     size = convert_lxd_byte_suffixes(size or "10GB")
     return DiscoveredMachineBlockDevice(
         model="QEMU HARDDISK",
         serial=serial,
         id_path=f"/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_{serial}",
         size=size,
         tags=tags,
         storage_pool=device.get("pool"),
     )
示例#5
0
 def test_block_device_size(self):
     size = random.randint(512, 512 * 1024)
     device = DiscoveredMachineBlockDevice(
         model=None, serial=None, size=size)
     self.assertEquals(None, device.model)
     self.assertEquals(None, device.serial)
     self.assertEquals(size, device.size)
     self.assertEquals(None, device.id_path)
示例#6
0
 def test_block_device_size_id_path(self):
     size = random.randint(512, 512 * 1024)
     id_path = factory.make_name("id_path")
     device = DiscoveredMachineBlockDevice(
         model=None, serial=None,
         size=size, id_path=id_path)
     self.assertEquals(None, device.model)
     self.assertEquals(None, device.serial)
     self.assertEquals(size, device.size)
     self.assertEquals(id_path, device.id_path)
示例#7
0
 def test_block_device_model_serial_size_block_size(self):
     model = factory.make_name("model")
     serial = factory.make_name("serial")
     size = random.randint(512, 512 * 1024)
     block_size = random.randint(512, 4096)
     device = DiscoveredMachineBlockDevice(
         model=model, serial=serial, size=size, block_size=block_size)
     self.assertEquals(model, device.model)
     self.assertEquals(serial, device.serial)
     self.assertEquals(size, device.size)
     self.assertEquals(block_size, device.block_size)
示例#8
0
 def test_block_device_model_serial_size_block_size_tags(self):
     model = factory.make_name("model")
     serial = factory.make_name("serial")
     size = random.randint(512, 512 * 1024)
     block_size = random.randint(512, 4096)
     tags = [factory.make_name("tag") for _ in range(3)]
     device = DiscoveredMachineBlockDevice(model=model,
                                           serial=serial,
                                           size=size,
                                           block_size=block_size,
                                           tags=tags)
     self.assertEquals(model, device.model)
     self.assertEquals(serial, device.serial)
     self.assertEquals(size, device.size)
     self.assertEquals(block_size, device.block_size)
     self.assertEquals(tags, device.tags)
示例#9
0
 def test_block_device_iscsi(self):
     size = random.randint(512, 512 * 1024)
     block_size = random.randint(512, 4096)
     tags = [
         factory.make_name("tag")
         for _ in range(3)
     ]
     iscsi_target = '%s:6:3260:0:iqn.maas.io:%s' % (
         factory.make_ipv4_address(), factory.make_name('target'))
     device = DiscoveredMachineBlockDevice(
         model=None, serial=None, size=size, block_size=block_size,
         tags=tags, type=BlockDeviceType.ISCSI, iscsi_target=iscsi_target)
     self.assertIsNone(device.model)
     self.assertIsNone(device.serial)
     self.assertEquals(size, device.size)
     self.assertEquals(block_size, device.block_size)
     self.assertEquals(tags, device.tags)
     self.assertEquals(BlockDeviceType.ISCSI, device.type)
     self.assertEquals(iscsi_target, device.iscsi_target)
示例#10
0
 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
             ]),
         }),
     )
示例#11
0
 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)
示例#12
0
    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, [])
示例#13
0
    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",
        )