예제 #1
0
    def compose(self, pod_id: int, context: dict, request: RequestedMachine):
        """Compose a virtual machine."""
        with self._get_client(pod_id, context) as client:
            storage_pools = client.storage_pools.all()
            default_storage_pool = context.get(
                "default_storage_pool_id", context.get("default_storage_pool"))

            include_profile = client.profiles.exists(LXD_MAAS_PROFILE)
            definition = get_lxd_machine_definition(
                request, include_profile=include_profile)
            definition["devices"] = {
                **self._get_machine_disks(request.block_devices, storage_pools, default_storage_pool),
                **self._get_machine_nics(request),
            }

            # Create the machine.
            machine = client.virtual_machines.create(definition, wait=True)
            # Pod hints are updated on the region after the machine is composed.
            discovered_machine = self._get_discovered_machine(client,
                                                              machine,
                                                              storage_pools,
                                                              request=request)
            # Update the machine cpu speed.
            discovered_machine.cpu_speed = lxd_cpu_speed(client.resources)
            return discovered_machine, DiscoveredPodHints()
예제 #2
0
    def _discover(self, client: Client, pod_id: int, context: dict):
        self._check_required_extensions(client)

        if not client.trusted:
            # return empty information as the client is not authenticated and
            # gathering other info requires auth.
            return DiscoveredPod()

        self._ensure_project(client)

        environment = client.host_info["environment"]
        # After the region creates the Pod object it will sync LXD commissioning
        # data for all hardware information.
        discovered_pod = DiscoveredPod(
            # client.host_info["environment"]["architectures"] reports all the
            # architectures the host CPU supports, not the architectures LXD
            # supports. On x86_64 LXD reports [x86_64, i686] however LXD does
            # not currently support VMs on i686. The LXD API currently does not
            # have a way to query which architectures are usable for VMs. The
            # safest bet is to just use the kernel_architecture.
            architectures=[
                kernel_to_debian_architecture(
                    environment["kernel_architecture"])
            ],
            name=environment["server_name"],
            version=environment["server_version"],
            capabilities=[
                Capabilities.COMPOSABLE,
                Capabilities.DYNAMIC_LOCAL_STORAGE,
                Capabilities.OVER_COMMIT,
                Capabilities.STORAGE_POOLS,
            ],
        )

        # Discover networks. "unknown" interfaces are considered too to match
        # ethernets in containers.
        networks_state = [
            net.state() for net in client.networks.all()
            if net.type in ("unknown", "physical")
        ]
        discovered_pod.mac_addresses = list(
            {state.hwaddr
             for state in networks_state if state.hwaddr})

        # Discover storage pools.
        storage_pools = client.storage_pools.all()
        if not storage_pools:
            raise LXDPodError(
                "No storage pools exists.  Please create a storage pool in LXD."
            )
        pools = []
        local_storage = 0
        for storage_pool in storage_pools:
            discovered_storage_pool = self._get_discovered_pod_storage_pool(
                storage_pool)
            local_storage += discovered_storage_pool.storage
            pools.append(discovered_storage_pool)
        discovered_pod.storage_pools = pools
        discovered_pod.local_storage = local_storage

        # Discover VMs.
        host_cpu_speed = lxd_cpu_speed(client.resources)
        projects = [project.name for project in client.projects.all()]
        machines = []
        for project in projects:
            with self._get_client(pod_id, context,
                                  project=project) as project_cli:
                for virtual_machine in project_cli.virtual_machines.all():
                    discovered_machine = self._get_discovered_machine(
                        project_cli,
                        virtual_machine,
                        storage_pools=discovered_pod.storage_pools,
                    )
                    discovered_machine.cpu_speed = host_cpu_speed
                    machines.append(discovered_machine)
        discovered_pod.machines = machines

        return discovered_pod
예제 #3
0
 def test_lxd_cpu_speed(self):
     self.assertEqual(2400, lxd_cpu_speed(SAMPLE_LXD_RESOURCES))
예제 #4
0
    def compose(self, pod_id: str, context: dict, request: RequestedMachine):
        """Compose a virtual machine."""
        client = yield self.get_client(pod_id, context)
        # Check to see if there is a maas profile.  If not, use the default.
        try:
            profile = yield deferToThread(client.profiles.get, "maas")
        except NotFound:
            # Fall back to default
            try:
                profile = yield deferToThread(client.profiles.get, "default")
            except NotFound:
                raise LXDPodError(
                    f"Pod {pod_id}: MAAS needs LXD to have either a 'maas' "
                    "profile or a 'default' profile, defined.")
        resources = yield deferToThread(lambda: client.resources)

        definition = get_lxd_machine_definition(request, profile.name)

        # Add disk to the definition.
        # XXX: LXD VMs currently only support one virtual block device.
        # Loop will need to be modified once LXD has multiple virtual
        # block device support.
        devices = {}
        storage_pools = yield deferToThread(client.storage_pools.all)
        default_storage_pool = context.get("default_storage_pool_id",
                                           context.get("default_storage_pool"))
        for idx, disk in enumerate(request.block_devices):
            usable_pool = self.get_usable_storage_pool(disk, storage_pools,
                                                       default_storage_pool)
            devices["root"] = {
                "path": "/",
                "type": "disk",
                "pool": usable_pool,
                "size": str(disk.size),
                "boot.priority": "0",
            }

        # Create and attach interfaces to the machine.
        # The reason we are doing this after the machine is created
        # is because pylxd doesn't have a way to override the devices
        # that are defined in the profile.  Since the profile is provided
        # by the user, we have no idea how many interfaces are defined.
        #
        # Currently, only the bridged type is supported with virtual machines.
        # https://lxd.readthedocs.io/en/latest/instances/#device-types
        nic_devices = {}
        profile_devices = profile.devices
        device_names = []
        boot = True
        for interface in request.interfaces:
            if interface.ifname is None:
                # No interface constraints sent so use the best
                # nic device from the profile's devices.
                device_name, device = self.get_best_nic_device_from_profile(
                    profile_devices)
                nic_devices[device_name] = device
                if "boot.priority" not in device and boot:
                    nic_devices[device_name]["boot.priority"] = "1"
                    boot = False
                device_names.append(device_name)
            else:
                nic_devices[interface.ifname] = get_lxd_nic_device(interface)

                # Set to boot from the first nic
                if boot:
                    nic_devices[interface.ifname]["boot.priority"] = "1"
                    boot = False
                device_names.append(interface.ifname)

        # Iterate over all of the profile's devices with type=nic
        # and set to type=none if not nic_device.  This overrides
        # the device settings on the profile used by the machine.
        for dk, dv in profile_devices.items():
            if dk not in device_names and dv["type"] == "nic":
                nic_devices[dk] = {"type": "none"}

        # Merge the devices and attach the devices to the defintion.
        for k, v in nic_devices.items():
            devices[k] = v
        definition["devices"] = devices

        # Create the machine.
        machine = yield deferToThread(client.virtual_machines.create,
                                      definition,
                                      wait=True)
        # Pod hints are updated on the region after the machine
        # is composed.
        discovered_machine = yield ensureDeferred(
            self.get_discovered_machine(client,
                                        machine,
                                        storage_pools,
                                        request=request))
        # Update the machine cpu speed.
        discovered_machine.cpu_speed = lxd_cpu_speed(resources)
        return discovered_machine, DiscoveredPodHints()
예제 #5
0
    async def discover(self, pod_id, context):
        """Discover all Pod host resources."""
        # Connect to the Pod and make sure it is valid.
        client = await self.get_client(pod_id, context)
        if not client.has_api_extension("virtual-machines"):
            raise LXDPodError(
                "Please upgrade your LXD host to 3.19+ for virtual machine support."
            )
        resources = await deferToThread(lambda: client.resources)

        mac_addresses = []
        for card in resources["network"]["cards"]:
            for port in card["ports"]:
                mac_addresses.append(port["address"])

        # After the region creates the Pod object it will sync LXD commissioning
        # data for all hardware information.
        discovered_pod = DiscoveredPod(
            # client.host_info["environment"]["architectures"] reports all the
            # architectures the host CPU supports, not the architectures LXD
            # supports. On x86_64 LXD reports [x86_64, i686] however LXD does
            # not currently support VMs on i686. The LXD API currently does not
            # have a way to query which architectures are usable for VMs. The
            # safest bet is to just use the kernel_architecture.
            architectures=[
                kernel_to_debian_architecture(
                    client.host_info["environment"]["kernel_architecture"])
            ],
            name=client.host_info["environment"]["server_name"],
            mac_addresses=mac_addresses,
            capabilities=[
                Capabilities.COMPOSABLE,
                Capabilities.DYNAMIC_LOCAL_STORAGE,
                Capabilities.OVER_COMMIT,
                Capabilities.STORAGE_POOLS,
            ],
        )

        # Check that we have at least one storage pool.
        # If not, user should be warned that they need to create one.
        storage_pools = await deferToThread(client.storage_pools.all)
        if not storage_pools:
            raise LXDPodError(
                "No storage pools exists.  Please create a storage pool in LXD."
            )

        # Discover Storage Pools.
        pools = []
        storage_pools = await deferToThread(client.storage_pools.all)
        local_storage = 0
        for storage_pool in storage_pools:
            discovered_storage_pool = self.get_discovered_pod_storage_pool(
                storage_pool)
            local_storage += discovered_storage_pool.storage
            pools.append(discovered_storage_pool)
        discovered_pod.storage_pools = pools
        discovered_pod.local_storage = local_storage

        # Discover VMs.
        machines = []
        virtual_machines = await deferToThread(client.virtual_machines.all)
        for virtual_machine in virtual_machines:
            discovered_machine = await self.get_discovered_machine(
                client,
                virtual_machine,
                storage_pools=discovered_pod.storage_pools,
            )
            discovered_machine.cpu_speed = lxd_cpu_speed(resources)
            machines.append(discovered_machine)
        discovered_pod.machines = machines

        # Return the DiscoveredPod.
        return discovered_pod
예제 #6
0
    def discover(self, pod_id: int, context: dict):
        """Discover all Pod host resources."""
        # Connect to the Pod and make sure it is valid.
        client = self._get_client(pod_id, context)
        if not client.has_api_extension("virtual-machines"):
            raise LXDPodError(
                "Please upgrade your LXD host to 3.19+ for virtual machine support."
            )

        self._ensure_project(client)

        # get MACs for host interfaces. "unknown" interfaces are considered too
        # to match ethernets in containers
        networks_state = [
            net.state() for net in client.networks.all()
            if net.type in ("unknown", "physical")
        ]
        mac_addresses = list(
            {state.hwaddr
             for state in networks_state if state.hwaddr})

        environment = client.host_info["environment"]
        # After the region creates the Pod object it will sync LXD commissioning
        # data for all hardware information.
        discovered_pod = DiscoveredPod(
            # client.host_info["environment"]["architectures"] reports all the
            # architectures the host CPU supports, not the architectures LXD
            # supports. On x86_64 LXD reports [x86_64, i686] however LXD does
            # not currently support VMs on i686. The LXD API currently does not
            # have a way to query which architectures are usable for VMs. The
            # safest bet is to just use the kernel_architecture.
            architectures=[
                kernel_to_debian_architecture(
                    environment["kernel_architecture"])
            ],
            name=environment["server_name"],
            version=environment["server_version"],
            mac_addresses=mac_addresses,
            capabilities=[
                Capabilities.COMPOSABLE,
                Capabilities.DYNAMIC_LOCAL_STORAGE,
                Capabilities.OVER_COMMIT,
                Capabilities.STORAGE_POOLS,
            ],
        )

        # Check that we have at least one storage pool.
        # If not, user should be warned that they need to create one.
        storage_pools = client.storage_pools.all()
        if not storage_pools:
            raise LXDPodError(
                "No storage pools exists.  Please create a storage pool in LXD."
            )

        # Discover Storage Pools.
        pools = []
        local_storage = 0
        for storage_pool in storage_pools:
            discovered_storage_pool = self._get_discovered_pod_storage_pool(
                storage_pool)
            local_storage += discovered_storage_pool.storage
            pools.append(discovered_storage_pool)
        discovered_pod.storage_pools = pools
        discovered_pod.local_storage = local_storage

        host_cpu_speed = lxd_cpu_speed(client.resources)

        # Discover VMs.
        projects = [project.name for project in client.projects.all()]
        machines = []
        for project in projects:
            project_cli = self._get_client(pod_id, context, project=project)
            for virtual_machine in project_cli.virtual_machines.all():
                discovered_machine = self._get_discovered_machine(
                    project_cli,
                    virtual_machine,
                    storage_pools=discovered_pod.storage_pools,
                )
                discovered_machine.cpu_speed = host_cpu_speed
                machines.append(discovered_machine)
        discovered_pod.machines = machines

        # Return the DiscoveredPod.
        return discovered_pod