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"))
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
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()))
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
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))
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
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)