Esempio n. 1
0
def list_all_usable_osystems(releases=None):
    """Return all operating systems that can be used for nodes."""
    if releases is None:
        releases = list_all_usable_releases()
    osystems = []
    for os_name, releases in releases.items():
        osystem = OperatingSystemRegistry.get_item(os_name)
        if osystem:
            default_commissioning_release = (
                osystem.get_default_commissioning_release()
            )
            default_release = osystem.get_default_release()
            title = osystem.title
        else:
            default_commissioning_release = ""
            default_release = ""
            title = os_name
        osystems.append(
            {
                "name": os_name,
                "title": title,
                "default_commissioning_release": default_commissioning_release,
                "default_release": default_release,
                "releases": releases,
            }
        )
    return sorted(osystems, key=itemgetter("title"))
Esempio n. 2
0
def extract_image_params(path, maas_meta):
    """Represent a list of TFTP path elements as a list of boot-image dicts.

    :param path: Tuple or list that consists of a full [osystem, architecture,
        subarchitecture, release] that identify a kind of boot for which we
        may need an image.
    :param maas_meta: Contents of the maas.meta file.  This may be an
        empty string.

    :return: A list of dicts, each of which may also include additional
        items of meta-data that are not elements in the path, such as
        "subarches".
    """
    if path[0] == "bootloader":
        osystem, release, arch = path
        subarch = "generic"
        label = "*"
    else:
        osystem, arch, subarch, release, label = path
    osystem_obj = OperatingSystemRegistry.get_item(osystem, default=None)
    if osystem_obj is None:
        return []

    purposes = osystem_obj.get_boot_image_purposes(arch, subarch, release,
                                                   label)

    # Expand the path into a list of dicts, one for each boot purpose.
    params = []
    for purpose in purposes:
        image = dict(
            osystem=osystem,
            architecture=arch,
            subarchitecture=subarch,
            release=release,
            label=label,
            purpose=purpose,
        )
        if (purpose == BOOT_IMAGE_PURPOSE.XINSTALL
                or purpose == BOOT_IMAGE_PURPOSE.EPHEMERAL):
            xinstall_path, xinstall_type = osystem_obj.get_xinstall_parameters(
                arch, subarch, release, label)
            image["xinstall_path"] = xinstall_path
            image["xinstall_type"] = xinstall_type
        else:
            image["xinstall_path"] = ""
            image["xinstall_type"] = ""
        params.append(image)

    # Merge in the meta-data.
    for image_dict in params:
        metadata = extract_metadata(maas_meta, image_dict)
        image_dict.update(metadata)

    return params
Esempio n. 3
0
def list_all_usable_releases():
    """Return all releases for all operating systems that can be used."""
    distro_series = {}
    seen_releases = set()
    for br in BootResource.objects.filter(bootloader_type=None):
        # An OS can have multiple boot resource for one release. e.g Ubuntu
        # Bionic has ga-18.04 and ga-18.04-lowlatency. This list should only
        # contain one entry per OS.
        if br.name in seen_releases:
            continue
        seen_releases.add(br.name)

        if "/" in br.name:
            os_name, release = br.name.split("/")
        else:
            os_name = "custom"
            release = br.name

        osystem = OperatingSystemRegistry.get_item(os_name)
        if osystem is not None:
            title = osystem.get_release_title(release)
            if title is None:
                title = release
            can_commission = (
                release in osystem.get_supported_commissioning_releases())
            requires_license_key = osystem.requires_license_key(release)
        else:
            title = br.name
            can_commission = requires_license_key = False

        if br.rtype == BOOT_RESOURCE_TYPE.UPLOADED:
            # User may set the title of an uploaded resource.
            if "title" in br.extra:
                title = br.extra["title"]
            else:
                title = release

        if os_name not in distro_series:
            distro_series[os_name] = []
        distro_series[os_name].append({
            "name":
            release,
            "title":
            title,
            "can_commission":
            can_commission,
            "requires_license_key":
            requires_license_key,
        })
    for osystem, releases in distro_series.items():
        distro_series[osystem] = sorted(releases, key=itemgetter("title"))
    return OrderedDict(sorted(distro_series.items()))
Esempio n. 4
0
    def get_boot_image(self, params, client, remote_ip):
        """Get the boot image for the params on this rack controller.

        Calls `MarkNodeFailed` for the machine if its a known machine.
        """
        is_ephemeral = False
        try:
            osystem_obj = OperatingSystemRegistry.get_item(params['osystem'],
                                                           default=None)
            purposes = osystem_obj \
                .get_boot_image_purposes(params["arch"], params["subarch"],
                                         params.get("release", ""),
                                         params.get("label", ""))
            if "ephemeral" in purposes:
                is_ephemeral = True
        except:
            pass

        system_id = params.pop("system_id", None)
        if params["purpose"] == "local" and not is_ephemeral:
            # Local purpose doesn't use a boot image so jsut set the label
            # to "local", but this value will no be used.
            params["label"] = "local"
            return params
        else:
            if params["purpose"] == "local" and is_ephemeral:
                params["purpose"] = "ephemeral"
            boot_image = get_boot_image(params)
            if boot_image is None:
                # No matching boot image.
                description = "Missing boot image %s/%s/%s/%s." % (
                    params['osystem'], params["arch"], params["subarch"],
                    params["release"])
                # Call MarkNodeFailed if this was a known machine.
                if system_id is not None:
                    d = client(MarkNodeFailed,
                               system_id=system_id,
                               error_description=description)
                    d.addErrback(
                        log.err,
                        "Failed to mark machine failed: %s" % description)
                else:
                    maaslog.error(
                        "Enlistment failed to boot %s; missing required boot "
                        "image %s/%s/%s/%s." %
                        (remote_ip, params['osystem'], params["arch"],
                         params["subarch"], params["release"]))
                params["label"] = "no-such-image"
            else:
                params["label"] = boot_image["label"]
            return params
Esempio n. 5
0
    def clean(self):
        """Validate the model.

        Checks that the name is in a valid format, for its type.
        """
        if self.rtype == BOOT_RESOURCE_TYPE.UPLOADED:
            if "/" in self.name:
                os_name = self.name.split("/")[0]
                osystem = OperatingSystemRegistry.get_item(os_name)
                if osystem is None:
                    raise ValidationError(
                        "%s boot resource cannot contain a '/' in it's name "
                        "unless it starts with a supported operating system." %
                        (self.display_rtype))
        elif self.rtype in RTYPE_REQUIRING_OS_SERIES_NAME:
            if "/" not in self.name:
                raise ValidationError(
                    "%s boot resource must contain a '/' in it's name." %
                    (self.display_rtype))
Esempio n. 6
0
    def get_boot_image(self, params, client, remote_ip):
        """Get the boot image for the params on this rack controller.

        Calls `MarkNodeFailed` for the machine if its a known machine.
        """
        is_ephemeral = False
        try:
            osystem_obj = OperatingSystemRegistry.get_item(
                params["osystem"], default=None
            )
            purposes = osystem_obj.get_boot_image_purposes(
                params["arch"],
                params["subarch"],
                params.get("release", ""),
                params.get("label", ""),
            )
            if "ephemeral" in purposes:
                is_ephemeral = True
        except Exception:
            pass

        # Check to see if the we are PXE booting a device.
        if params["purpose"] == "local-device":
            mac = network.find_mac_via_arp(remote_ip)
            log.info(
                "Device %s with MAC address %s is PXE booting; "
                "instructing the device to boot locally."
                % (params["hostname"], mac)
            )
            # Set purpose back to local now that we have the message logged.
            params["purpose"] = "local"

        system_id = params.pop("system_id", None)
        if params["purpose"] == "local" and not is_ephemeral:
            # Local purpose doesn't use a boot image so just set the label
            # to "local".
            params["label"] = "local"
            return params
        else:
            if params["purpose"] == "local" and is_ephemeral:
                params["purpose"] = "ephemeral"
            boot_image = get_boot_image(params)
            if boot_image is None:
                # No matching boot image.
                description = "Missing boot image %s/%s/%s/%s." % (
                    params["osystem"],
                    params["arch"],
                    params["subarch"],
                    params["release"],
                )
                # Call MarkNodeFailed if this was a known machine.
                if system_id is not None:
                    d = client(
                        MarkNodeFailed,
                        system_id=system_id,
                        error_description=description,
                    )
                    d.addErrback(
                        log.err,
                        "Failed to mark machine failed: %s" % description,
                    )
                else:
                    maaslog.error(
                        "Enlistment failed to boot %s; missing required boot "
                        "image %s/%s/%s/%s."
                        % (
                            remote_ip,
                            params["osystem"],
                            params["arch"],
                            params["subarch"],
                            params["release"],
                        )
                    )
                params["label"] = "no-such-image"
            else:
                params["label"] = boot_image["label"]
            return params
Esempio n. 7
0
def validate_hwe_kernel(
    hwe_kernel,
    min_hwe_kernel,
    architecture,
    osystem,
    distro_series,
    commissioning_osystem=undefined,
    commissioning_distro_series=undefined,
):
    """Validates that hwe_kernel works on the selected os/release/arch.

    Checks that the current hwe_kernel is avalible for the selected
    os/release/architecture combination, and that the selected hwe_kernel is >=
    min_hwe_kernel. If no hwe_kernel is selected one will be chosen.
    """
    def validate_kernel_str(kstr):
        return kstr.startswith("hwe-") or kstr.startswith("ga-")

    if not all((osystem, architecture, distro_series)):
        return hwe_kernel

    # If we are dealing with an ephemeral image, just return the hwe_kernel
    # as-is, i.e. just stick with generic.
    osystem_obj = OperatingSystemRegistry.get_item(osystem, default=None)
    if osystem_obj is not None:
        arch, subarch = architecture.split("/")
        purposes = osystem_obj.get_boot_image_purposes(arch, subarch,
                                                       distro_series, "*")
        if "ephemeral" in purposes:
            return hwe_kernel

    # If we're not deploying Ubuntu we are just setting the kernel to be used
    # during deployment
    if osystem != "ubuntu":
        osystem = commissioning_osystem
        if osystem is undefined:
            osystem = Config.objects.get_config("commissioning_osystem")
        distro_series = commissioning_distro_series
        if distro_series is undefined:
            distro_series = Config.objects.get_config(
                "commissioning_distro_series")

    arch, subarch = architecture.split("/")

    if subarch != "generic" and (
        (hwe_kernel and validate_kernel_str(hwe_kernel)) or
        (min_hwe_kernel and validate_kernel_str(min_hwe_kernel))):
        raise ValidationError(
            "Subarchitecture(%s) must be generic when setting hwe_kernel." %
            subarch)

    os_release = osystem + "/" + distro_series

    if hwe_kernel and validate_kernel_str(hwe_kernel):
        usable_kernels = BootResource.objects.get_usable_hwe_kernels(
            os_release, arch)
        if hwe_kernel not in usable_kernels:
            raise ValidationError("%s is not available for %s on %s." %
                                  (hwe_kernel, os_release, architecture))
        if not release_a_newer_than_b(hwe_kernel, distro_series):
            raise ValidationError("%s is too old to use on %s." %
                                  (hwe_kernel, os_release))
        if (min_hwe_kernel and validate_kernel_str(min_hwe_kernel)) and (
                not release_a_newer_than_b(hwe_kernel, min_hwe_kernel)):
            raise ValidationError(
                "hwe_kernel(%s) is older than min_hwe_kernel(%s)." %
                (hwe_kernel, min_hwe_kernel))
        return hwe_kernel
    elif min_hwe_kernel and validate_kernel_str(min_hwe_kernel):
        # Determine what kflavor is being used by check against a list of
        # known kflavors.
        valid_kflavors = {
            br.kflavor
            for br in BootResource.objects.exclude(kflavor=None)
        }
        kflavor = "generic"
        for kernel_part in min_hwe_kernel.split("-"):
            if kernel_part in valid_kflavors:
                kflavor = kernel_part
                break
        usable_kernels = BootResource.objects.get_usable_hwe_kernels(
            os_release, arch, kflavor)
        for i in usable_kernels:
            if release_a_newer_than_b(
                    i, min_hwe_kernel) and release_a_newer_than_b(
                        i, distro_series):
                return i
        raise ValidationError(
            "%s has no kernels available which meet min_hwe_kernel(%s)." %
            (distro_series, min_hwe_kernel))
    for kernel in BootResource.objects.get_usable_hwe_kernels(
            os_release, arch, "generic"):
        if release_a_newer_than_b(kernel, distro_series):
            return kernel
    raise ValidationError("%s has no kernels available." % distro_series)