Пример #1
0
 def get_usable_pool(self, disk, default_pool=None):
     """Return the pool that has enough space for `disk.size`."""
     pools = self.get_pod_storage_pools(with_available=True)
     filtered_pools = [pool for pool in pools if pool.name in disk.tags]
     if filtered_pools:
         for pool in filtered_pools:
             if disk.size <= pool.available:
                 return pool.name
         raise PodInvalidResources(
             "Not enough storage space on storage pools: %s" %
             (', '.join([pool.name for pool in filtered_pools])))
     if default_pool:
         filtered_pools = [
             pool for pool in pools if pool.id == default_pool
         ]
         if not filtered_pools:
             filtered_pools = [
                 pool for pool in pools if pool.name == default_pool
             ]
         if filtered_pools:
             default_pool = filtered_pools[0]
             if disk.size <= default_pool.available:
                 return default_pool.name
             raise PodInvalidResources(
                 "Not enough space in default storage pool: %s" %
                 (default_pool.name))
         raise VirshError("Default storage pool '%s' doesn't exist." %
                          default_pool)
     for pool in pools:
         if disk.size <= pool.available:
             return pool.name
     raise PodInvalidResources(
         "Not enough storage space on any storage pools: %s" %
         (', '.join([pool.name for pool in pools])))
Пример #2
0
    def _get_usable_storage_pool(self,
                                 disk,
                                 storage_pools,
                                 default_storage_pool=None):
        """Return the storage pool and type that has enough space for `disk.size`."""
        # Filter off of tags.
        filtered_storage_pools = [
            storage_pool for storage_pool in storage_pools
            if storage_pool.name in disk.tags
        ]
        if filtered_storage_pools:
            for storage_pool in filtered_storage_pools:
                resources = storage_pool.resources.get()
                available = resources.space["total"] - resources.space["used"]
                if disk.size <= available:
                    return storage_pool
            raise PodInvalidResources(
                "Not enough storage space on storage pools: %s" % (", ".join([
                    storage_pool.name
                    for storage_pool in filtered_storage_pools
                ])))
        # Filter off of default storage pool name.
        if default_storage_pool:
            filtered_storage_pools = [
                storage_pool for storage_pool in storage_pools
                if storage_pool.name == default_storage_pool
            ]
            if filtered_storage_pools:
                default_storage_pool = filtered_storage_pools[0]
                resources = default_storage_pool.resources.get()
                available = resources.space["total"] - resources.space["used"]
                if disk.size <= available:
                    return default_storage_pool
                raise PodInvalidResources(
                    f"Not enough space in default storage pool: {default_storage_pool.name}"
                )
            raise LXDPodError(
                f"Default storage pool '{default_storage_pool}' doesn't exist."
            )

        # No filtering, just find a storage pool with enough space.
        for storage_pool in storage_pools:
            resources = storage_pool.resources.get()
            available = resources.space["total"] - resources.space["used"]
            if disk.size <= available:
                return storage_pool
        raise PodInvalidResources(
            "Not enough storage space on any storage pools: %s" %
            (", ".join([storage_pool.name for storage_pool in storage_pools])))
Пример #3
0
    def create_domain(self, request, default_pool=None):
        """Create a domain based on the `request` with hostname.

        For now this just uses `get_best_network` to connect the interfaces
        of the domain to the network.
        """
        # Create all the block devices first. If cannot complete successfully
        # then fail early. The driver currently doesn't do any tag matching
        # for block devices, and is not really required for Virsh.
        created_disks = []
        for idx, disk in enumerate(request.block_devices):
            try:
                disk_info = self.create_local_volume(disk, default_pool)
            except Exception:
                self.cleanup_disks(created_disks)
                raise
            else:
                if disk_info is None:
                    raise PodInvalidResources(
                        "not enough space for disk %d." % idx)
                else:
                    created_disks.append(disk_info)

        # Construct the domain XML.
        domain_params = self.get_domain_capabilities()
        domain_params['name'] = request.hostname
        domain_params['uuid'] = str(uuid.uuid4())
        domain_params['arch'] = ARCH_FIX_REVERSE[request.architecture]
        domain_params['cores'] = str(request.cores)
        domain_params['memory'] = str(request.memory)
        domain_xml = DOM_TEMPLATE.format(**domain_params)

        # Define the domain in virsh.
        with NamedTemporaryFile() as f:
            f.write(domain_xml.encode('utf-8'))
            f.write(b'\n')
            f.flush()
            self.run(['define', f.name])

        # Attach the created disks in order.
        for idx, (pool, volume) in enumerate(created_disks):
            block_name = self.get_block_name_from_idx(idx)
            self.attach_local_volume(
                request.hostname, pool, volume, block_name)

        # Attach new interfaces to the best possible network.
        best_network = self.get_best_network()
        for _ in request.interfaces:
            self.attach_interface(request.hostname, best_network)

        # Set machine to autostart.
        self.set_machine_autostart(request.hostname)

        # Setup the domain to PXE boot.
        self.configure_pxe_boot(request.hostname)

        # Return the result as a discovered machine.
        return self.get_discovered_machine(request.hostname, request=request)
Пример #4
0
 def get_pod_local_storage(self):
     """Gets the total local storage for the pod."""
     pools = self.get_pod_pool_size_map("Capacity")
     if len(pools) == 0:
         maaslog.error("Failed to get pod local storage")
         raise PodInvalidResources(
             "Pod does not have a storage pool defined. "
             "Please add a storage pool.")
     return sum(pools.values())
Пример #5
0
 def get_pod_arch(self):
     """Gets architecture of the pod."""
     output = self.run(['nodeinfo']).strip()
     arch = self.get_key_value(output, "CPU model")
     if arch is None:
         maaslog.error("Failed to get pod architecture")
         raise PodInvalidResources("Pod architecture is not supported: %s" %
                                   arch)
     return ARCH_FIX.get(arch, arch)
Пример #6
0
    def get_best_network(self):
        """Return the best possible network."""
        networks = self.get_network_list()
        if 'maas' in networks:
            return 'maas'
        elif 'default' in networks:
            return 'default'
        elif not networks:
            raise PodInvalidResources(
                "Pod does not have a network defined. "
                "Please add a 'default' or 'maas' network.")

        return networks[0]
Пример #7
0
 def convert(result):
     """Convert the result to send over RPC."""
     if result is None:
         # None is allowed when a machine could not be composed with the
         # driver. This means it could not match the request. Returning None
         # allows the region to try another pod if available to compose
         # that machine.
         raise PodInvalidResources()
     else:
         if (isinstance(result, tuple) and len(result) == 2
                 and isinstance(result[0], DiscoveredMachine)
                 and isinstance(result[1], DiscoveredPodHints)):
             return {"machine": result[0], "hints": result[1]}
         else:
             raise PodActionFail("bad pod driver '%s'; 'compose' returned "
                                 "invalid result." % pod_type)
Пример #8
0
    def create_domain(self, request, default_pool=None):
        """Create a domain based on the `request` with hostname.

        For now this just uses `get_best_network` to connect the interfaces
        of the domain to the network.
        """
        # Create all the block devices first. If cannot complete successfully
        # then fail early. The driver currently doesn't do any tag matching
        # for block devices, and is not really required for Virsh.
        created_disks = []
        for idx, disk in enumerate(request.block_devices):
            try:
                disk_info = self.create_local_volume(disk, default_pool)
            except Exception:
                self.cleanup_disks(created_disks)
                raise
            else:
                if disk_info is None:
                    raise PodInvalidResources("not enough space for disk %d." %
                                              idx)
                else:
                    created_disks.append(disk_info)

        # Construct the domain XML.
        domain_params = self.get_domain_capabilities()
        domain_params['name'] = request.hostname
        domain_params['uuid'] = str(uuid4())
        domain_params['arch'] = ARCH_FIX_REVERSE[request.architecture]
        domain_params['cores'] = str(request.cores)
        domain_params['memory'] = str(request.memory)

        # Set the template.
        if domain_params['arch'] == 'aarch64':
            # LP: #1775728 - Changes in the template are required due to
            # libvirt validation issues on the XML template. However, this
            # causes lint issues. We work around these issue by using
            # template variables instead.
            domain_params['loader'] = '/usr/share/AAVMF/AAVMF_CODE.fd'
            domain_params['nvram_path'] = '/var/lib/libvirt/qemu/nvram'
            domain_xml = DOM_TEMPLATE_ARM64.format(**domain_params)
        elif domain_params['arch'] in ('ppc64', 'ppc64le'):
            domain_xml = DOM_TEMPLATE_PPC64.format(**domain_params)
        elif domain_params['arch'] == 's390x':
            domain_xml = DOM_TEMPLATE_S390X.format(**domain_params)
        else:
            domain_xml = DOM_TEMPLATE_AMD64.format(**domain_params)

        # Define the domain in virsh.
        with NamedTemporaryFile() as f:
            f.write(domain_xml.encode('utf-8'))
            f.write(b'\n')
            f.flush()
            self.run(['define', f.name])

        # Attach the created disks in order.
        for idx, (pool, volume) in enumerate(created_disks):
            block_name = self.get_block_name_from_idx(idx)
            self.attach_local_volume(request.hostname, pool, volume,
                                     block_name)

        # Attach new interfaces to the best possible network.
        best_network = self.get_best_network()
        for interface in request.interfaces:
            self.attach_interface(interface, request.hostname, best_network)

        # Set machine to autostart.
        self.set_machine_autostart(request.hostname)

        # Setup the domain to PXE boot.
        self.configure_pxe_boot(request.hostname)

        # Return the result as a discovered machine.
        return self.get_discovered_machine(request.hostname, request=request)