Ejemplo n.º 1
0
 def test_validate_hwe_kern_fails_with_old_release_and_newer_hwe_kern(self):
     exception_raised = False
     try:
         validate_hwe_kernel(
             None, 'hwe-v', 'amd64/generic', 'ubuntu', 'trusty')
     except ValidationError as e:
         self.assertEqual(
             'trusty has no kernels available which meet' +
             ' min_hwe_kernel(hwe-v).', e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 2
0
 def test_validate_hwe_kernel_fails_with_nongeneric_arch_and_kernel(self):
     exception_raised = False
     try:
         validate_hwe_kernel(
             'hwe-v', None, 'armfh/hardbank', 'ubuntu', 'trusty')
     except ValidationError as e:
         self.assertEqual(
             'Subarchitecture(hardbank) must be generic when setting ' +
             'hwe_kernel.', e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 3
0
 def test_validate_hwe_kernel_fails_with_old_kernel_and_newer_release(self):
     exception_raised = False
     self.patch(BootResource.objects,
                "get_usable_hwe_kernels").return_value = ("hwe-t", "hwe-v")
     try:
         validate_hwe_kernel("hwe-t", None, "amd64/generic", "ubuntu",
                             "vivid")
     except ValidationError as e:
         self.assertEqual("hwe-t is too old to use on ubuntu/vivid.",
                          e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 4
0
 def test_validate_hwe_kernel_fails_with_old_kernel_and_newer_release(self):
     exception_raised = False
     self.patch(BootResource.objects,
                'get_usable_hwe_kernels').return_value = ('hwe-t', 'hwe-v')
     try:
         validate_hwe_kernel('hwe-t', None, 'amd64/generic', 'ubuntu',
                             'vivid')
     except ValidationError as e:
         self.assertEqual('hwe-t is too old to use on ubuntu/vivid.',
                          e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 5
0
 def test_validate_hwe_kernel_fails_with_no_avalible_kernels(self):
     exception_raised = False
     self.patch(BootResource.objects,
                'get_usable_hwe_kernels').return_value = ('hwe-t', 'hwe-v')
     try:
         validate_hwe_kernel('hwe-t', 'hwe-v', 'amd64/generic', 'ubuntu',
                             'precise')
     except ValidationError as e:
         self.assertEqual(
             'hwe_kernel(hwe-t) is older than min_hwe_kernel(hwe-v).',
             e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 6
0
 def test_validate_hwe_kernel_fails_with_nongeneric_arch_and_kernel(self):
     exception_raised = False
     try:
         validate_hwe_kernel("hwe-v", None, "armfh/hardbank", "ubuntu",
                             "trusty")
     except ValidationError as e:
         self.assertEqual(
             "Subarchitecture(hardbank) must be generic when setting " +
             "hwe_kernel.",
             e.message,
         )
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 7
0
 def test_validate_hwe_kernel_fails_with_missing_hwe_kernel(self):
     exception_raised = False
     self.patch(BootResource.objects,
                'get_usable_hwe_kernels').return_value = ('hwe-t', 'hwe-u')
     try:
         validate_hwe_kernel('hwe-v', None, 'amd64/generic', 'ubuntu',
                             'trusty')
     except ValidationError as e:
         self.assertEqual(
             'hwe-v is not available for ubuntu/trusty on amd64/generic.',
             e.message)
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 8
0
 def test_validate_hwe_kern_fails_with_old_release_and_newer_hwe_kern(self):
     exception_raised = False
     try:
         validate_hwe_kernel(None, "hwe-v", "amd64/generic", "ubuntu",
                             "trusty")
     except ValidationError as e:
         self.assertEqual(
             "trusty has no kernels available which meet" +
             " min_hwe_kernel(hwe-v).",
             e.message,
         )
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 9
0
 def test_validate_hwe_kernel_fails_with_no_avalible_kernels(self):
     exception_raised = False
     self.patch(BootResource.objects,
                "get_usable_hwe_kernels").return_value = ("hwe-t", "hwe-v")
     try:
         validate_hwe_kernel("hwe-t", "hwe-v", "amd64/generic", "ubuntu",
                             "precise")
     except ValidationError as e:
         self.assertEqual(
             "hwe_kernel(hwe-t) is older than min_hwe_kernel(hwe-v).",
             e.message,
         )
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 10
0
 def test_validate_hwe_kernel_fails_with_missing_hwe_kernel(self):
     exception_raised = False
     self.patch(BootResource.objects,
                "get_usable_hwe_kernels").return_value = ("hwe-t", "hwe-u")
     try:
         validate_hwe_kernel("hwe-v", None, "amd64/generic", "ubuntu",
                             "trusty")
     except ValidationError as e:
         self.assertEqual(
             "hwe-v is not available for ubuntu/trusty on amd64/generic.",
             e.message,
         )
         exception_raised = True
     self.assertEqual(True, exception_raised)
Ejemplo n.º 11
0
    def execute(self, osystem=None, distro_series=None, hwe_kernel=None):
        """See `NodeAction.execute`."""
        if self.node.owner is None:
            with locks.node_acquire:
                self.node.acquire(self.user, token=None)

        if osystem and distro_series:
            try:
                self.node.osystem, self.node.distro_series = (
                    validate_osystem_and_distro_series(osystem, distro_series))
                self.node.save()
            except ValidationError as e:
                raise NodeActionError(e)

        try:
            self.node.hwe_kernel = validate_hwe_kernel(
                hwe_kernel, self.node.min_hwe_kernel, self.node.architecture,
                self.node.osystem, self.node.distro_series)
            self.node.save()
        except ValidationError as e:
            raise NodeActionError(e)

        try:
            get_curtin_config(self.node)
        except Exception as e:
            raise NodeActionError("Failed to retrieve curtin config: %s" % e)

        try:
            self.node.start(self.user)
        except StaticIPAddressExhaustion:
            raise NodeActionError(
                "%s: Failed to start, static IP addresses are exhausted." %
                self.node.hostname)
        except RPC_EXCEPTIONS + (ExternalProcessError, ) as exception:
            raise NodeActionError(exception)
Ejemplo n.º 12
0
 def test_validate_hwe_kernel_set_kernel(self):
     self.patch(
         BootResource.objects,
         'get_usable_hwe_kernels').return_value = ('hwe-t', 'hwe-v')
     hwe_kernel = validate_hwe_kernel(
         'hwe-v', None, 'amd64/generic', 'ubuntu', 'trusty')
     self.assertEqual(hwe_kernel, 'hwe-v')
Ejemplo n.º 13
0
 def test_validate_hwe_kernel_accepts_ga_kernel(self):
     self.patch(
         BootResource.objects,
         'get_usable_hwe_kernels').return_value = ('ga-16.04',)
     hwe_kernel = validate_hwe_kernel(
         'ga-16.04', None, 'amd64/generic', 'ubuntu', 'xenial')
     self.assertEqual(hwe_kernel, 'ga-16.04')
Ejemplo n.º 14
0
 def test_validate_hwe_kernel_accepts_ga_kernel(self):
     self.patch(
         BootResource.objects, "get_usable_hwe_kernels"
     ).return_value = ("ga-16.04",)
     hwe_kernel = validate_hwe_kernel(
         "ga-16.04", None, "amd64/generic", "ubuntu", "xenial"
     )
     self.assertEqual(hwe_kernel, "ga-16.04")
Ejemplo n.º 15
0
 def test_validate_hwe_kernel_set_kernel(self):
     self.patch(
         BootResource.objects, "get_usable_hwe_kernels"
     ).return_value = ("hwe-t", "hwe-v")
     hwe_kernel = validate_hwe_kernel(
         "hwe-v", None, "amd64/generic", "ubuntu", "trusty"
     )
     self.assertEqual(hwe_kernel, "hwe-v")
Ejemplo n.º 16
0
 def test_validate_hwe_kern_always_sets_kern_with_commissionable_os(self):
     self.patch(BootResource.objects,
                'get_usable_hwe_kernels').return_value = ('hwe-t', 'hwe-v')
     mock_get_config = self.patch(Config.objects, "get_config")
     mock_get_config.return_value = 'trusty'
     kernel = validate_hwe_kernel(None, 'hwe-v',
                                  '%s/generic' % factory.make_name('arch'),
                                  factory.make_name("osystem"),
                                  factory.make_name("distro"))
     self.assertThat(mock_get_config, MockAnyCall('commissioning_osystem'))
     self.assertThat(mock_get_config,
                     MockAnyCall('commissioning_distro_series'))
     self.assertEqual('hwe-v', kernel)
Ejemplo n.º 17
0
def get_boot_filenames(
    arch,
    subarch,
    osystem,
    series,
    commissioning_osystem=undefined,
    commissioning_distro_series=undefined,
):
    """Return the filenames of the kernel, initrd, and boot_dtb for the boot
    resource."""
    if subarch == "generic":
        # MAAS doesn't store in the BootResource table what subarch is the
        # generic subarch so lookup what the generic subarch maps to.
        try:
            boot_resource_subarch = validate_hwe_kernel(
                subarch,
                None,
                "%s/%s" % (arch, subarch),
                osystem,
                series,
                commissioning_osystem=commissioning_osystem,
                commissioning_distro_series=commissioning_distro_series,
            )
        except ValidationError:
            # It's possible that no kernel's exist at all for this arch,
            # subarch, osystem, series combination. In that case just fallback
            # to 'generic'.
            boot_resource_subarch = "generic"
    else:
        boot_resource_subarch = subarch

    try:
        # Get the filename for the kernel, initrd, and boot_dtb the rack should
        # use when booting.
        boot_resource = BootResource.objects.get(
            architecture="%s/%s" % (arch, boot_resource_subarch),
            name="%s/%s" % (osystem, series),
        )
        boot_resource_set = boot_resource.get_latest_complete_set()
        boot_resource_files = {
            bfile.filetype: bfile.filename
            for bfile in boot_resource_set.files.all()
        }
    except ObjectDoesNotExist:
        # If a filename can not be found return None to allow the rack to
        # figure out what todo.
        return None, None, None
    kernel = boot_resource_files.get(BOOT_RESOURCE_FILE_TYPE.BOOT_KERNEL)
    initrd = boot_resource_files.get(BOOT_RESOURCE_FILE_TYPE.BOOT_INITRD)
    boot_dtb = boot_resource_files.get(BOOT_RESOURCE_FILE_TYPE.BOOT_DTB)
    return kernel, initrd, boot_dtb
Ejemplo n.º 18
0
    def test_validate_hwe_kern_sets_hwe_kern_to_min_hwe_kern_for_edge(self):
        # Regression test for LP:1654412
        mock_get_usable_hwe_kernels = self.patch(BootResource.objects,
                                                 'get_usable_hwe_kernels')
        mock_get_usable_hwe_kernels.return_value = ('hwe-16.04',
                                                    'hwe-16.04-edge')
        arch = factory.make_name('arch')

        kernel = validate_hwe_kernel(None, 'hwe-16.04-edge',
                                     '%s/generic' % arch, 'ubuntu', 'xenial')

        self.assertEquals('hwe-16.04-edge', kernel)
        self.assertThat(mock_get_usable_hwe_kernels,
                        MockCalledOnceWith('ubuntu/xenial', arch, 'generic'))
Ejemplo n.º 19
0
 def test_validate_hwe_kern_always_sets_kern_with_commissionable_os(self):
     self.patch(BootResource.objects,
                "get_usable_hwe_kernels").return_value = ("hwe-t", "hwe-v")
     mock_get_config = self.patch(Config.objects, "get_config")
     mock_get_config.return_value = "trusty"
     kernel = validate_hwe_kernel(
         None,
         "hwe-v",
         "%s/generic" % factory.make_name("arch"),
         factory.make_name("osystem"),
         factory.make_name("distro"),
     )
     self.assertThat(mock_get_config, MockAnyCall("commissioning_osystem"))
     self.assertThat(mock_get_config,
                     MockAnyCall("commissioning_distro_series"))
     self.assertEqual("hwe-v", kernel)
Ejemplo n.º 20
0
    def execute(self, osystem=None, distro_series=None, hwe_kernel=None):
        """See `NodeAction.execute`."""
        if self.node.owner is None:
            with locks.node_acquire:
                try:
                    self.node.acquire(self.user, token=None)
                except ValidationError as e:
                    raise NodeActionError(e)

        if osystem and distro_series:
            try:
                self.node.osystem, self.node.distro_series = (
                    validate_osystem_and_distro_series(osystem, distro_series))
                self.node.save()
            except ValidationError as e:
                raise NodeActionError(e)

        try:
            self.node.hwe_kernel = validate_hwe_kernel(
                hwe_kernel, self.node.min_hwe_kernel, self.node.architecture,
                self.node.osystem, self.node.distro_series)
            self.node.save()
        except ValidationError as e:
            raise NodeActionError(e)

        request = self.request
        if request is None:
            # Being called from the websocket, just to ensure that the curtin
            # configuration is valid. The request object does not need to be
            # an actual request. 'SERVER_NAME' and 'SERVER_PORT' are required
            # so `build_absolure_uri` can create an actual absolute URI.
            request = HttpRequest()
            request.META['SERVER_NAME'] = 'localhost'
            request.META['SERVER_PORT'] = 5248
        try:
            get_curtin_config(request, self.node)
        except Exception as e:
            raise NodeActionError("Failed to retrieve curtin config: %s" % e)

        try:
            self.node.start(self.user)
        except StaticIPAddressExhaustion:
            raise NodeActionError(
                "%s: Failed to start, static IP addresses are exhausted." %
                self.node.hostname)
        except RPC_EXCEPTIONS + (ExternalProcessError, ) as exception:
            raise NodeActionError(exception)
Ejemplo n.º 21
0
Archivo: boot.py Proyecto: Kryndex/maas
def get_boot_filenames(arch, subarch, osystem, series):
    """Return the filenames of the kernel, initrd, and boot_dtb for the boot
    resource."""
    if subarch == 'generic':
        # MAAS doesn't store in the BootResource table what subarch is the
        # generic subarch so lookup what the generic subarch maps to.
        try:
            boot_resource_subarch = validate_hwe_kernel(
                subarch, None, "%s/%s" % (arch, subarch), osystem, series)
        except ValidationError:
            # It's possible that no kernel's exist at all for this arch,
            # subarch, osystem, series combination. In that case just fallback
            # to 'generic'.
            boot_resource_subarch = 'generic'
    else:
        boot_resource_subarch = subarch

    try:
        # Get the filename for the kernel, initrd, and boot_dtb the rack should
        # use when booting.
        boot_resource = BootResource.objects.get(
            architecture="%s/%s" % (arch, boot_resource_subarch),
            name="%s/%s" % (osystem, series)
        )
        boot_resource_set = boot_resource.get_latest_complete_set()
        kernel = boot_resource_set.files.get(
            filetype=BOOT_RESOURCE_FILE_TYPE.BOOT_KERNEL).filename
    except ObjectDoesNotExist:
        # If a filename can not be found return None to allow the rack to
        # figure out what todo.
        return None, None, None
    try:
        initrd = boot_resource_set.files.get(
            filetype=BOOT_RESOURCE_FILE_TYPE.BOOT_INITRD).filename
    except ObjectDoesNotExist:
        # An initrd is not needed to boot if the kernel contains all driver
        # support.
        initrd = None
    try:
        boot_dtb = boot_resource_set.files.get(
            filetype=BOOT_RESOURCE_FILE_TYPE.BOOT_DTB).filename
    except ObjectDoesNotExist:
        # Not all archs use boot_dtb so allow just this to fail
        boot_dtb = None

    return kernel, initrd, boot_dtb
Ejemplo n.º 22
0
    def test_validate_hwe_kern_sets_hwe_kern_to_min_hwe_kern_for_edge(self):
        # Regression test for LP:1654412
        mock_get_usable_hwe_kernels = self.patch(BootResource.objects,
                                                 "get_usable_hwe_kernels")
        mock_get_usable_hwe_kernels.return_value = (
            "hwe-16.04",
            "hwe-16.04-edge",
        )
        arch = factory.make_name("arch")

        kernel = validate_hwe_kernel(None, "hwe-16.04-edge",
                                     "%s/generic" % arch, "ubuntu", "xenial")

        self.assertEquals("hwe-16.04-edge", kernel)
        self.assertThat(
            mock_get_usable_hwe_kernels,
            MockCalledOnceWith("ubuntu/xenial", arch, "generic"),
        )
Ejemplo n.º 23
0
    def _execute(self,
                 osystem=None,
                 distro_series=None,
                 hwe_kernel=None,
                 install_kvm=False):
        """See `NodeAction.execute`."""
        if install_kvm:
            if not self.user.is_superuser:
                raise NodeActionError(
                    "You must be a MAAS administrator to deploy a machine "
                    "as a MAAS-managed KVM Pod.")
        if self.node.owner is None:
            with locks.node_acquire:
                try:
                    bridge_all = True if install_kvm else False
                    self.node.acquire(self.user,
                                      token=None,
                                      bridge_all=bridge_all)
                except ValidationError as e:
                    raise NodeActionError(e)
        if install_kvm:
            try:
                # KVM Pod installation should default to ubuntu/bionic, since
                # that was the release it was tested on.
                if osystem is None:
                    osystem = 'ubuntu'
                if distro_series is None:
                    distro_series = 'bionic'
                self.node.osystem, self.node.distro_series = (
                    validate_osystem_and_distro_series(osystem, distro_series))
                self.node.install_kvm = True
                self.node.save()
            except ValidationError as e:
                raise NodeActionError(e)
        elif osystem and distro_series:
            try:
                self.node.osystem, self.node.distro_series = (
                    validate_osystem_and_distro_series(osystem, distro_series))
                self.node.save()
            except ValidationError as e:
                raise NodeActionError(e)
        try:
            self.node.hwe_kernel = validate_hwe_kernel(
                hwe_kernel, self.node.min_hwe_kernel, self.node.architecture,
                self.node.osystem, self.node.distro_series)
            self.node.save()
        except ValidationError as e:
            raise NodeActionError(e)

        request = self.request
        if request is None:
            # `compile_node_actions` is the path by which the node
            # actions are instantiated.  There are other places within the
            # code that call compile_node_actions without a request object.
            # In this event, and for future uses of these node actions without
            # a request being passed in, we need to create one here.
            # 'SERVER_NAME' and 'SERVER_PORT' are required so
            # `build_absolure_uri` can create an actual absolute URI so that
            # the curtin configuration is valid.
            request = HttpRequest()
            request.META['SERVER_NAME'] = 'localhost'
            request.META['SERVER_PORT'] = 5248
        try:
            get_curtin_config(request, self.node)
        except Exception as e:
            raise NodeActionError("Failed to retrieve curtin config: %s" % e)

        try:
            self.node.start(self.user)
        except StaticIPAddressExhaustion:
            raise NodeActionError(
                "%s: Failed to start, static IP addresses are exhausted." %
                self.node.hostname)
        except RPC_EXCEPTIONS + (ExternalProcessError, ) as exception:
            raise NodeActionError(exception)
Ejemplo n.º 24
0
Archivo: boot.py Proyecto: Kryndex/maas
def get_config(
        system_id, local_ip, remote_ip, arch=None, subarch=None, mac=None,
        bios_boot_method=None):
    """Get the booting configration for the a machine.

    Returns a structure suitable for returning in the response
    for :py:class:`~provisioningserver.rpc.region.GetBootConfig`.

    Raises BootConfigNoResponse when booting machine should fail to next file.
    """
    rack_controller = RackController.objects.get(system_id=system_id)
    region_ip = None
    if remote_ip is not None:
        region_ip = get_source_address(remote_ip)
    machine = get_node_from_mac_string(mac)

    # Fail with no response early so no extra work is performed.
    if machine is None and arch is None and mac is not None:
        # Request was pxelinux.cfg/01-<mac> for a machine MAAS does not know
        # about. So attempt fall back to pxelinux.cfg/default-<arch>-<subarch>
        # for arch detection.
        raise BootConfigNoResponse()

    if machine is not None:
        # Update the last interface, last access cluster IP address, and
        # the last used BIOS boot method. Only saving the fields that have
        # changed on the machine.
        update_fields = []
        if (machine.boot_interface is None or
                machine.boot_interface.mac_address != mac):
            machine.boot_interface = PhysicalInterface.objects.get(
                mac_address=mac)
            update_fields.append("boot_interface")
        if (machine.boot_cluster_ip is None or
                machine.boot_cluster_ip != local_ip):
            machine.boot_cluster_ip = local_ip
            update_fields.append("boot_cluster_ip")
        if machine.bios_boot_method != bios_boot_method:
            machine.bios_boot_method = bios_boot_method
            update_fields.append("bios_boot_method")
        if len(update_fields) > 0:
            machine.save(update_fields=update_fields)

        # Update the VLAN of the boot interface to be the same VLAN for the
        # interface on the rack controller that the machine communicated with,
        # unless the VLAN is being relayed.
        rack_interface = rack_controller.interface_set.filter(
            ip_addresses__ip=local_ip).first()
        if (rack_interface is not None and
                machine.boot_interface.vlan != rack_interface.vlan):
            # Rack controller and machine is not on the same VLAN, with DHCP
            # relay this is possible. Lets ensure that the VLAN on the
            # interface is setup to relay through the identified VLAN.
            if not VLAN.objects.filter(
                    id=machine.boot_interface.vlan_id,
                    relay_vlan=rack_interface.vlan).exists():
                # DHCP relay is not being performed for that VLAN. Set the VLAN
                # to the VLAN of the rack controller.
                machine.boot_interface.vlan = rack_interface.vlan
                machine.boot_interface.save()

        arch, subarch = machine.split_arch()
        preseed_url = compose_preseed_url(
            machine, rack_controller, default_region_ip=region_ip)
        hostname = machine.hostname
        domain = machine.domain.name
        purpose = machine.get_boot_purpose()

        # Log the request into the event log for that machine.
        if (machine.status == NODE_STATUS.ENTERING_RESCUE_MODE and
                purpose == 'commissioning'):
            event_log_pxe_request(machine, 'rescue')
        else:
            event_log_pxe_request(machine, purpose)

        # Get the correct operating system and series based on the purpose
        # of the booting machine.
        if purpose == "commissioning":
            osystem = Config.objects.get_config('commissioning_osystem')
            series = Config.objects.get_config('commissioning_distro_series')
        else:
            osystem = machine.get_osystem()
            series = machine.get_distro_series()
            if purpose == "xinstall" and osystem != "ubuntu":
                # Use only the commissioning osystem and series, for operating
                # systems other than Ubuntu. As Ubuntu supports HWE kernels,
                # and needs to use that kernel to perform the installation.
                osystem = Config.objects.get_config('commissioning_osystem')
                series = Config.objects.get_config(
                    'commissioning_distro_series')

        # Pre MAAS-1.9 the subarchitecture defined any kernel the machine
        # needed to be able to boot. This could be a hardware enablement
        # kernel(e.g hwe-t) or something like highbank. With MAAS-1.9 any
        # hardware enablement kernel must be specifed in the hwe_kernel field,
        # any other kernel, such as highbank, is still specifed as a
        # subarchitecture. Since Ubuntu does not support architecture specific
        # hardware enablement kernels(i.e a highbank hwe-t kernel on precise)
        # we give precedence to any kernel defined in the subarchitecture field
        if subarch == "generic" and machine.hwe_kernel:
            subarch = machine.hwe_kernel
        elif(subarch == "generic" and
             purpose == "commissioning" and
             machine.min_hwe_kernel):
            try:
                subarch = validate_hwe_kernel(
                    None, machine.min_hwe_kernel, machine.architecture,
                    osystem, series)
            except ValidationError:
                subarch = "no-such-kernel"

        # We don't care if the kernel opts is from the global setting or a tag,
        # just get the options
        _, effective_kernel_opts = machine.get_effective_kernel_options()

        # Add any extra options from a third party driver.
        use_driver = Config.objects.get_config('enable_third_party_drivers')
        if use_driver:
            driver = get_third_party_driver(machine)
            driver_kernel_opts = driver.get('kernel_opts', '')

            combined_opts = ('%s %s' % (
                '' if effective_kernel_opts is None else effective_kernel_opts,
                driver_kernel_opts)).strip()
            if len(combined_opts):
                extra_kernel_opts = combined_opts
            else:
                extra_kernel_opts = None
        else:
            extra_kernel_opts = effective_kernel_opts

        kparams = BootResource.objects.get_kparams_for_node(machine)
        extra_kernel_opts = merge_kparams_with_extra(kparams,
                                                     extra_kernel_opts)
    else:
        purpose = "commissioning"  # enlistment
        preseed_url = compose_enlistment_preseed_url(
            rack_controller, default_region_ip=region_ip)
        hostname = 'maas-enlist'
        domain = 'local'
        osystem = Config.objects.get_config('commissioning_osystem')
        series = Config.objects.get_config('commissioning_distro_series')
        min_hwe_kernel = Config.objects.get_config('default_min_hwe_kernel')

        # When no architecture is defined for the enlisting machine select
        # the best boot resource for the operating system and series. If
        # none exists fallback to the default architecture. LP #1181334
        if arch is None:
            resource = (
                BootResource.objects.get_default_commissioning_resource(
                    osystem, series))
            if resource is None:
                arch = DEFAULT_ARCH
            else:
                arch, _ = resource.split_arch()
        # The subarch defines what kernel is booted. With MAAS 2.1 this changed
        # from hwe-<letter> to hwe-<version> or ga-<version>. Validation
        # converts between the two formats to make sure a bootable subarch is
        # selected.
        if subarch is None:
            min_hwe_kernel = validate_hwe_kernel(
                None, min_hwe_kernel, '%s/generic' % arch, osystem, series)
        else:
            min_hwe_kernel = validate_hwe_kernel(
                None, min_hwe_kernel, '%s/%s' % (arch, subarch), osystem,
                series)
        # If no hwe_kernel was found set the subarch to the default, 'generic.'
        if min_hwe_kernel is None:
            subarch = 'generic'
        else:
            subarch = min_hwe_kernel

        # Global kernel options for enlistment.
        extra_kernel_opts = Config.objects.get_config("kernel_opts")

    # Set the final boot purpose.
    if machine is None and arch == DEFAULT_ARCH:
        # If the machine is enlisting and the arch is the default arch (i386),
        # use the dedicated enlistment template which performs architecture
        # detection.
        boot_purpose = "enlist"
    elif purpose == 'poweroff':
        # In order to power the machine off, we need to get it booted in the
        # commissioning environment and issue a `poweroff` command.
        boot_purpose = 'commissioning'
    else:
        boot_purpose = purpose

    # Get the service address to the region for that given rack controller.
    server_host = get_maas_facing_server_host(
        rack_controller=rack_controller, default_region_ip=region_ip)

    kernel, initrd, boot_dtb = get_boot_filenames(
        arch, subarch, osystem, series)

    # Return the params to the rack controller. Include the system_id only
    # if the machine was known.
    params = {
        "arch": arch,
        "subarch": subarch,
        "osystem": osystem,
        "release": series,
        "kernel": kernel,
        "initrd": initrd,
        "boot_dtb": boot_dtb,
        "purpose": boot_purpose,
        "hostname": hostname,
        "domain": domain,
        "preseed_url": preseed_url,
        "fs_host": local_ip,
        "log_host": server_host,
        "extra_opts": '' if extra_kernel_opts is None else extra_kernel_opts,
        # As of MAAS 2.4 only HTTP boot is supported. This ensures MAAS 2.3
        # rack controllers use HTTP boot as well.
        "http_boot": True,
    }
    if machine is not None:
        params["system_id"] = machine.system_id
    return params
Ejemplo n.º 25
0
    def _execute(
        self,
        osystem=None,
        distro_series=None,
        hwe_kernel=None,
        install_kvm=False,
        user_data=None,
    ):
        """See `NodeAction.execute`."""
        if install_kvm:
            if not self.user.is_superuser:
                raise NodeActionError(
                    "You must be a MAAS administrator to deploy a machine "
                    "as a MAAS-managed KVM Pod.")
        if self.node.owner is None:
            with locks.node_acquire:
                try:
                    self.node.acquire(self.user)
                except ValidationError as e:
                    raise NodeActionError(e)
        if osystem and distro_series:
            try:
                (
                    self.node.osystem,
                    self.node.distro_series,
                ) = validate_osystem_and_distro_series(osystem, distro_series)
                self.node.save()
            except ValidationError as e:
                raise NodeActionError(e)
        else:
            configs = Config.objects.get_configs(
                ["default_osystem", "default_distro_series"])
            self.node.osystem = configs["default_osystem"]
            self.node.distro_series = configs["default_distro_series"]
            self.node.save()
        try:
            self.node.hwe_kernel = validate_hwe_kernel(
                hwe_kernel,
                self.node.min_hwe_kernel,
                self.node.architecture,
                self.node.osystem,
                self.node.distro_series,
            )
            self.node.save()
        except ValidationError as e:
            raise NodeActionError(e)
        user_data = user_data.encode() if user_data else None
        request = self.request
        if request is None:
            # `compile_node_actions` is the path by which the node
            # actions are instantiated.  There are other places within the
            # code that call compile_node_actions without a request object.
            # In this event, and for future uses of these node actions without
            # a request being passed in, we need to create one here.
            # 'SERVER_NAME' and 'SERVER_PORT' are required so
            # `build_absolure_uri` can create an actual absolute URI so that
            # the curtin configuration is valid.
            request = HttpRequest()
            request.META["SERVER_NAME"] = "localhost"
            request.META["SERVER_PORT"] = 5248
        try:
            get_curtin_config(request, self.node)
        except Exception as e:
            raise NodeActionError("Failed to retrieve curtin config: %s" % e)

        try:
            self.node.start(self.user,
                            user_data=user_data,
                            install_kvm=install_kvm)
        except StaticIPAddressExhaustion:
            raise NodeActionError(
                "%s: Failed to start, static IP addresses are exhausted." %
                self.node.hostname)
        except IPAddressCheckFailed:
            raise NodeActionError(
                f"{self.node.hostname}: Failed to start, IP addresses check failed."
            )
        except RPC_EXCEPTIONS + (ExternalProcessError, ) as exception:
            raise NodeActionError(exception)
Ejemplo n.º 26
0
def get_config(
        system_id, local_ip, remote_ip, arch=None, subarch=None, mac=None,
        bios_boot_method=None):
    """Get the booting configration for the a machine.

    Returns a structure suitable for returning in the response
    for :py:class:`~provisioningserver.rpc.region.GetBootConfig`.

    Raises BootConfigNoResponse when booting machine should fail to next file.
    """
    rack_controller = RackController.objects.get(system_id=system_id)
    region_ip = None
    if remote_ip is not None:
        region_ip = get_source_address(remote_ip)
    machine = get_node_from_mac_string(mac)

    # Get the service address to the region for that given rack controller.
    server_host = get_maas_facing_server_host(
        rack_controller=rack_controller, default_region_ip=region_ip)

    # Fail with no response early so no extra work is performed.
    if machine is None and arch is None and mac is not None:
        # Request was pxelinux.cfg/01-<mac> for a machine MAAS does not know
        # about. So attempt fall back to pxelinux.cfg/default-<arch>-<subarch>
        # for arch detection.
        raise BootConfigNoResponse()

    configs = Config.objects.get_configs([
        'commissioning_osystem',
        'commissioning_distro_series',
        'enable_third_party_drivers',
        'default_min_hwe_kernel',
        'default_osystem',
        'default_distro_series',
        'kernel_opts',
        'use_rack_proxy',
        'maas_internal_domain',
    ])
    if machine is not None:
        # Update the last interface, last access cluster IP address, and
        # the last used BIOS boot method.
        if (machine.boot_interface is None or
                machine.boot_interface.mac_address != mac):
            machine.boot_interface = PhysicalInterface.objects.get(
                mac_address=mac)
        if (machine.boot_cluster_ip is None or
                machine.boot_cluster_ip != local_ip):
            machine.boot_cluster_ip = local_ip
        if machine.bios_boot_method != bios_boot_method:
            machine.bios_boot_method = bios_boot_method

        # Reset the machine's status_expires whenever the boot_config is called
        # on a known machine. This allows a machine to take up to the maximum
        # timeout status to POST.
        machine.reset_status_expires()

        # Does nothing if the machine hasn't changed.
        machine.save()

        # Update the VLAN of the boot interface to be the same VLAN for the
        # interface on the rack controller that the machine communicated with,
        # unless the VLAN is being relayed.
        rack_interface = rack_controller.interface_set.filter(
            ip_addresses__ip=local_ip).select_related('vlan').first()
        if (rack_interface is not None and
                machine.boot_interface.vlan_id != rack_interface.vlan_id):
            # Rack controller and machine is not on the same VLAN, with DHCP
            # relay this is possible. Lets ensure that the VLAN on the
            # interface is setup to relay through the identified VLAN.
            if not VLAN.objects.filter(
                    id=machine.boot_interface.vlan_id,
                    relay_vlan=rack_interface.vlan_id).exists():
                # DHCP relay is not being performed for that VLAN. Set the VLAN
                # to the VLAN of the rack controller.
                machine.boot_interface.vlan = rack_interface.vlan
                machine.boot_interface.save()

        arch, subarch = machine.split_arch()
        if configs['use_rack_proxy']:
            preseed_url = compose_preseed_url(
                machine, base_url=get_base_url_for_local_ip(
                    local_ip, configs['maas_internal_domain']))
        else:
            preseed_url = compose_preseed_url(
                machine, base_url=rack_controller.url,
                default_region_ip=region_ip)
        hostname = machine.hostname
        domain = machine.domain.name
        purpose = machine.get_boot_purpose()

        # Early out if the machine is booting local.
        if purpose == 'local':
            return {
                "system_id": machine.system_id,
                "arch": arch,
                "subarch": subarch,
                "osystem": machine.osystem,
                "release": machine.distro_series,
                "kernel": '',
                "initrd": '',
                "boot_dtb": '',
                "purpose": purpose,
                "hostname": hostname,
                "domain": domain,
                "preseed_url": preseed_url,
                "fs_host": local_ip,
                "log_host": server_host,
                "extra_opts": '',
                "http_boot": True,
            }

        # Log the request into the event log for that machine.
        if (machine.status in [
                NODE_STATUS.ENTERING_RESCUE_MODE,
                NODE_STATUS.RESCUE_MODE] and purpose == 'commissioning'):
            event_log_pxe_request(machine, 'rescue')
        else:
            event_log_pxe_request(machine, purpose)

        osystem, series, subarch = get_boot_config_for_machine(
            machine, configs, purpose)

        # We don't care if the kernel opts is from the global setting or a tag,
        # just get the options
        _, effective_kernel_opts = machine.get_effective_kernel_options(
            default_kernel_opts=configs['kernel_opts'])

        # Add any extra options from a third party driver.
        use_driver = configs['enable_third_party_drivers']
        if use_driver:
            driver = get_third_party_driver(machine)
            driver_kernel_opts = driver.get('kernel_opts', '')

            combined_opts = ('%s %s' % (
                '' if effective_kernel_opts is None else effective_kernel_opts,
                driver_kernel_opts)).strip()
            if len(combined_opts):
                extra_kernel_opts = combined_opts
            else:
                extra_kernel_opts = None
        else:
            extra_kernel_opts = effective_kernel_opts

        kparams = BootResource.objects.get_kparams_for_node(
            machine, default_osystem=configs['default_osystem'],
            default_distro_series=configs['default_distro_series'])
        extra_kernel_opts = merge_kparams_with_extra(kparams,
                                                     extra_kernel_opts)
    else:
        purpose = "commissioning"  # enlistment
        if configs['use_rack_proxy']:
            preseed_url = compose_enlistment_preseed_url(
                base_url=get_base_url_for_local_ip(
                    local_ip, configs['maas_internal_domain']))
        else:
            preseed_url = compose_enlistment_preseed_url(
                rack_controller=rack_controller, default_region_ip=region_ip)
        hostname = 'maas-enlist'
        domain = 'local'
        osystem = configs['commissioning_osystem']
        series = configs['commissioning_distro_series']
        min_hwe_kernel = configs['default_min_hwe_kernel']

        # When no architecture is defined for the enlisting machine select
        # the best boot resource for the operating system and series. If
        # none exists fallback to the default architecture. LP #1181334
        if arch is None:
            resource = (
                BootResource.objects.get_default_commissioning_resource(
                    osystem, series))
            if resource is None:
                arch = DEFAULT_ARCH
            else:
                arch, _ = resource.split_arch()
        # The subarch defines what kernel is booted. With MAAS 2.1 this changed
        # from hwe-<letter> to hwe-<version> or ga-<version>. Validation
        # converts between the two formats to make sure a bootable subarch is
        # selected.
        if subarch is None:
            min_hwe_kernel = validate_hwe_kernel(
                None, min_hwe_kernel, '%s/generic' % arch, osystem, series)
        else:
            min_hwe_kernel = validate_hwe_kernel(
                None, min_hwe_kernel, '%s/%s' % (arch, subarch), osystem,
                series)
        # If no hwe_kernel was found set the subarch to the default, 'generic.'
        if min_hwe_kernel is None:
            subarch = 'generic'
        else:
            subarch = min_hwe_kernel

        # Global kernel options for enlistment.
        extra_kernel_opts = configs["kernel_opts"]

    # Set the final boot purpose.
    if machine is None and arch == DEFAULT_ARCH:
        # If the machine is enlisting and the arch is the default arch (i386),
        # use the dedicated enlistment template which performs architecture
        # detection.
        boot_purpose = "enlist"
    elif purpose == 'poweroff':
        # In order to power the machine off, we need to get it booted in the
        # commissioning environment and issue a `poweroff` command.
        boot_purpose = 'commissioning'
    else:
        boot_purpose = purpose

    kernel, initrd, boot_dtb = get_boot_filenames(
        arch, subarch, osystem, series,
        commissioning_osystem=configs['commissioning_osystem'],
        commissioning_distro_series=configs['commissioning_distro_series'])

    # Return the params to the rack controller. Include the system_id only
    # if the machine was known.
    params = {
        "arch": arch,
        "subarch": subarch,
        "osystem": osystem,
        "release": series,
        "kernel": kernel,
        "initrd": initrd,
        "boot_dtb": boot_dtb,
        "purpose": boot_purpose,
        "hostname": hostname,
        "domain": domain,
        "preseed_url": preseed_url,
        "fs_host": local_ip,
        "log_host": server_host,
        "extra_opts": '' if extra_kernel_opts is None else extra_kernel_opts,
        # As of MAAS 2.4 only HTTP boot is supported. This ensures MAAS 2.3
        # rack controllers use HTTP boot as well.
        "http_boot": True,
    }
    if machine is not None:
        params["system_id"] = machine.system_id
    return params
Ejemplo n.º 27
0
def get_boot_config_for_machine(machine, configs, purpose):
    """Get the correct operating system and series based on the purpose
    of the booting machine.
    """
    _, subarch = machine.split_arch()
    precise = False
    if purpose == "commissioning":
        # LP: #1768321 - Fix a regression introduced by, and really fix
        # the issue that LP: #1730525 was meant to fix. This ensures that
        # when DISK_ERASING, or when ENTERING_RESCUE_MODE on a deployed
        # machine it uses the OS from the deployed system for the
        # ephemeral environment.
        if machine.osystem == "ubuntu" and (
                machine.status == NODE_STATUS.DISK_ERASING or (
                machine.status in [
                    NODE_STATUS.ENTERING_RESCUE_MODE,
                    NODE_STATUS.RESCUE_MODE,
                ] and machine.previous_status == NODE_STATUS.DEPLOYED)):
            osystem = machine.get_osystem(default=configs['default_osystem'])
            series = machine.get_distro_series(
                default=configs['default_distro_series'])
        else:
            osystem = configs['commissioning_osystem']
            series = configs['commissioning_distro_series']
    else:
        osystem = machine.get_osystem(default=configs['default_osystem'])
        series = machine.get_distro_series(
            default=configs['default_distro_series'])
        # XXX: roaksoax LP: #1739761 - Since the switch to squashfs (and
        # drop of iscsi), precise is no longer deployable. To address a
        # squashfs image is made available allowing it to be deployed in
        # the commissioning ephemeral environment.
        precise = True if series == "precise" else False
        if purpose == "xinstall" and (osystem != "ubuntu" or precise):
            # Use only the commissioning osystem and series, for operating
            # systems other than Ubuntu. As Ubuntu supports HWE kernels,
            # and needs to use that kernel to perform the installation.
            osystem = configs['commissioning_osystem']
            series = configs['commissioning_distro_series']

    # Pre MAAS-1.9 the subarchitecture defined any kernel the machine
    # needed to be able to boot. This could be a hardware enablement
    # kernel(e.g hwe-t) or something like highbank. With MAAS-1.9 any
    # hardware enablement kernel must be specifed in the hwe_kernel field,
    # any other kernel, such as highbank, is still specifed as a
    # subarchitecture. Since Ubuntu does not support architecture specific
    # hardware enablement kernels(i.e a highbank hwe-t kernel on precise)
    # we give precedence to any kernel defined in the subarchitecture field

    # If the machine is deployed, hardcode the use of the Minimum HWE Kernel
    # This is to ensure that machines can always do testing regardless of
    # what they were deployed with, using the defaults from the settings
    testing_from_deployed = (
        machine.previous_status == NODE_STATUS.DEPLOYED and
        machine.status == NODE_STATUS.TESTING and purpose == "commissioning")
    if testing_from_deployed:
        subarch = (subarch if not configs['default_min_hwe_kernel']
                   else configs['default_min_hwe_kernel'])
    # XXX: roaksoax LP: #1739761 - Do not override the subarch (used for
    # the deployment ephemeral env) when deploying precise, provided that
    # it uses the commissioning distro_series and hwe kernels are not
    # needed.
    elif subarch == "generic" and machine.hwe_kernel and not precise:
        subarch = machine.hwe_kernel
    elif(subarch == "generic" and
         purpose == "commissioning" and
         machine.min_hwe_kernel):
        try:
            subarch = validate_hwe_kernel(
                None, machine.min_hwe_kernel, machine.architecture,
                osystem, series)
        except ValidationError:
            subarch = "no-such-kernel"
    return osystem, series, subarch
Ejemplo n.º 28
0
def get_config(
    system_id,
    local_ip,
    remote_ip,
    arch=None,
    subarch=None,
    mac=None,
    hardware_uuid=None,
    bios_boot_method=None,
):
    """Get the booting configration for a machine.

    Returns a structure suitable for returning in the response
    for :py:class:`~provisioningserver.rpc.region.GetBootConfig`.

    Raises BootConfigNoResponse when booting machine should fail to next file.
    """
    rack_controller = RackController.objects.get(system_id=system_id)
    region_ip = None
    if remote_ip is not None:
        region_ip = get_source_address(remote_ip)
    machine = get_node_from_mac_or_hardware_uuid(mac, hardware_uuid)

    # Fail with no response early so no extra work is performed.
    if machine is None and arch is None and (mac or hardware_uuid):
        # PXELinux requests boot configuration in the following order:
        # 1. pxelinux.cfg/<hardware uuid>
        # 2. pxelinux.cfg/01-<mac>
        # 3. pxelinux.cfg/default-<arch>-<subarch>
        # If mac and/or hardware_uuid was given but no Node was found fail the
        # request so PXELinux will move onto the next request.
        raise BootConfigNoResponse()

    # Get all required configuration objects in a single query.
    configs = Config.objects.get_configs([
        "commissioning_osystem",
        "commissioning_distro_series",
        "enable_third_party_drivers",
        "default_min_hwe_kernel",
        "default_osystem",
        "default_distro_series",
        "kernel_opts",
        "use_rack_proxy",
        "maas_internal_domain",
        "remote_syslog",
        "maas_syslog_port",
    ])

    # Compute the syslog server.
    log_host, log_port = (
        local_ip,
        (configs["maas_syslog_port"]
         if configs["maas_syslog_port"] else RSYSLOG_PORT),
    )
    if configs["remote_syslog"]:
        log_host, log_port = splithost(configs["remote_syslog"])
        if log_port is None:
            log_port = 514  # Fallback to default UDP syslog port.

    if machine is not None:
        # Update the last interface, last access cluster IP address, and
        # the last used BIOS boot method.
        if machine.boot_cluster_ip != local_ip:
            machine.boot_cluster_ip = local_ip

        if machine.bios_boot_method != bios_boot_method:
            machine.bios_boot_method = bios_boot_method

        try:
            machine.boot_interface = machine.interface_set.get(
                type=INTERFACE_TYPE.PHYSICAL, mac_address=mac)
        except ObjectDoesNotExist:
            # MAC is unknown or wasn't sent. Determine the boot_interface using
            # the boot_cluster_ip.
            subnet = Subnet.objects.get_best_subnet_for_ip(local_ip)
            boot_vlan = getattr(machine.boot_interface, "vlan", None)
            if subnet and subnet.vlan != boot_vlan:
                # This might choose the wrong interface, but we don't
                # have enough information to decide which interface is
                # the boot one.
                machine.boot_interface = machine.interface_set.filter(
                    vlan=subnet.vlan).first()
        else:
            # Update the VLAN of the boot interface to be the same VLAN for the
            # interface on the rack controller that the machine communicated
            # with, unless the VLAN is being relayed.
            rack_interface = (rack_controller.interface_set.filter(
                ip_addresses__ip=local_ip).select_related("vlan").first())
            if (rack_interface is not None and
                    machine.boot_interface.vlan_id != rack_interface.vlan_id):
                # Rack controller and machine is not on the same VLAN, with
                # DHCP relay this is possible. Lets ensure that the VLAN on the
                # interface is setup to relay through the identified VLAN.
                if not VLAN.objects.filter(
                        id=machine.boot_interface.vlan_id,
                        relay_vlan=rack_interface.vlan_id,
                ).exists():
                    # DHCP relay is not being performed for that VLAN. Set the
                    # VLAN to the VLAN of the rack controller.
                    machine.boot_interface.vlan = rack_interface.vlan
                    machine.boot_interface.save()

        # Reset the machine's status_expires whenever the boot_config is called
        # on a known machine. This allows a machine to take up to the maximum
        # timeout status to POST.
        machine.reset_status_expires()

        # Does nothing if the machine hasn't changed.
        machine.save()

        arch, subarch = machine.split_arch()
        if configs["use_rack_proxy"]:
            preseed_url = compose_preseed_url(
                machine,
                base_url=get_base_url_for_local_ip(
                    local_ip, configs["maas_internal_domain"]),
            )
        else:
            preseed_url = compose_preseed_url(
                machine,
                base_url=rack_controller.url,
                default_region_ip=region_ip,
            )
        hostname = machine.hostname
        domain = machine.domain.name
        purpose = machine.get_boot_purpose()

        # Ephemeral deployments will have 'local' boot
        # purpose on power cycles.  Set purpose back to
        # 'xinstall' so that the system can be re-deployed.
        if purpose == "local" and machine.ephemeral_deployment:
            purpose = "xinstall"

        # Early out if the machine is booting local.
        if purpose == "local":
            if machine.is_device:
                # Log that we are setting to local boot for a device.
                maaslog.warning(
                    "Device %s with MAC address %s is PXE booting; "
                    "instructing the device to boot locally." %
                    (machine.hostname, mac))
                # Set the purpose to 'local-device' so we can log a message
                # on the rack.
                purpose = "local-device"

            return {
                "system_id": machine.system_id,
                "arch": arch,
                "subarch": subarch,
                "osystem": machine.osystem,
                "release": machine.distro_series,
                "kernel": "",
                "initrd": "",
                "boot_dtb": "",
                "purpose": purpose,
                "hostname": hostname,
                "domain": domain,
                "preseed_url": preseed_url,
                "fs_host": local_ip,
                "log_host": log_host,
                "log_port": log_port,
                "extra_opts": "",
                "http_boot": True,
            }

        # Log the request into the event log for that machine.
        if (machine.status
                in [NODE_STATUS.ENTERING_RESCUE_MODE, NODE_STATUS.RESCUE_MODE]
                and purpose == "commissioning"):
            event_log_pxe_request(machine, "rescue")
        else:
            event_log_pxe_request(machine, purpose)

        osystem, series, subarch = get_boot_config_for_machine(
            machine, configs, purpose)

        # We don't care if the kernel opts is from the global setting or a tag,
        # just get the options
        _, effective_kernel_opts = machine.get_effective_kernel_options(
            default_kernel_opts=configs["kernel_opts"])

        # Add any extra options from a third party driver.
        use_driver = configs["enable_third_party_drivers"]
        if use_driver:
            driver = get_third_party_driver(machine)
            driver_kernel_opts = driver.get("kernel_opts", "")

            combined_opts = ("%s %s" % (
                "" if effective_kernel_opts is None else effective_kernel_opts,
                driver_kernel_opts,
            )).strip()
            if len(combined_opts):
                extra_kernel_opts = combined_opts
            else:
                extra_kernel_opts = None
        else:
            extra_kernel_opts = effective_kernel_opts

        kparams = BootResource.objects.get_kparams_for_node(
            machine,
            default_osystem=configs["default_osystem"],
            default_distro_series=configs["default_distro_series"],
        )
        extra_kernel_opts = merge_kparams_with_extra(kparams,
                                                     extra_kernel_opts)
    else:
        purpose = "commissioning"  # enlistment
        if configs["use_rack_proxy"]:
            preseed_url = compose_enlistment_preseed_url(
                base_url=get_base_url_for_local_ip(
                    local_ip, configs["maas_internal_domain"]))
        else:
            preseed_url = compose_enlistment_preseed_url(
                rack_controller=rack_controller, default_region_ip=region_ip)
        hostname = "maas-enlist"
        domain = "local"
        osystem = configs["commissioning_osystem"]
        series = configs["commissioning_distro_series"]
        min_hwe_kernel = configs["default_min_hwe_kernel"]

        # When no architecture is defined for the enlisting machine select
        # the best boot resource for the operating system and series. If
        # none exists fallback to the default architecture. LP #1181334
        if arch is None:
            resource = BootResource.objects.get_default_commissioning_resource(
                osystem, series)
            if resource is None:
                arch = DEFAULT_ARCH
            else:
                arch, _ = resource.split_arch()
        # The subarch defines what kernel is booted. With MAAS 2.1 this changed
        # from hwe-<letter> to hwe-<version> or ga-<version>. Validation
        # converts between the two formats to make sure a bootable subarch is
        # selected.
        if subarch is None:
            min_hwe_kernel = validate_hwe_kernel(None, min_hwe_kernel,
                                                 "%s/generic" % arch, osystem,
                                                 series)
        else:
            min_hwe_kernel = validate_hwe_kernel(
                None,
                min_hwe_kernel,
                "%s/%s" % (arch, subarch),
                osystem,
                series,
            )
        # If no hwe_kernel was found set the subarch to the default, 'generic.'
        if min_hwe_kernel is None:
            subarch = "generic"
        else:
            subarch = min_hwe_kernel

        # Global kernel options for enlistment.
        extra_kernel_opts = configs["kernel_opts"]

    boot_purpose = get_final_boot_purpose(machine, arch, purpose)
    kernel, initrd, boot_dtb = get_boot_filenames(
        arch,
        subarch,
        osystem,
        series,
        commissioning_osystem=configs["commissioning_osystem"],
        commissioning_distro_series=configs["commissioning_distro_series"],
    )

    # Return the params to the rack controller. Include the system_id only
    # if the machine was known.
    params = {
        "arch": arch,
        "subarch": subarch,
        "osystem": osystem,
        "release": series,
        "kernel": kernel,
        "initrd": initrd,
        "boot_dtb": boot_dtb,
        "purpose": boot_purpose,
        "hostname": hostname,
        "domain": domain,
        "preseed_url": preseed_url,
        "fs_host": local_ip,
        "log_host": log_host,
        "log_port": log_port,
        "extra_opts": "" if extra_kernel_opts is None else extra_kernel_opts,
        # As of MAAS 2.4 only HTTP boot is supported. This ensures MAAS 2.3
        # rack controllers use HTTP boot as well.
        "http_boot": True,
    }
    if machine is not None:
        params["system_id"] = machine.system_id
    return params
Ejemplo n.º 29
0
def get_boot_config_for_machine(machine, configs):
    """Get the correct operating system and series based on the purpose
    of the booting machine.
    """
    purpose = machine.get_boot_purpose()
    _, subarch = machine.split_arch()
    precise = False
    if purpose == "commissioning":
        osystem = configs['commissioning_osystem']
        series = configs['commissioning_distro_series']
    else:
        osystem = machine.get_osystem(default=configs['default_osystem'])
        series = machine.get_distro_series(
            default=configs['default_distro_series'])
        # XXX: roaksoax LP: #1739761 - Since the switch to squashfs (and
        # drop of iscsi), precise is no longer deployable. To address a
        # squashfs image is made available allowing it to be deployed in
        # the commissioning ephemeral environment.
        precise = True if series == "precise" else False
        if purpose == "xinstall" and (osystem != "ubuntu" or precise):
            # Use only the commissioning osystem and series, for operating
            # systems other than Ubuntu. As Ubuntu supports HWE kernels,
            # and needs to use that kernel to perform the installation.
            osystem = configs['commissioning_osystem']
            series = configs['commissioning_distro_series']

    # Pre MAAS-1.9 the subarchitecture defined any kernel the machine
    # needed to be able to boot. This could be a hardware enablement
    # kernel(e.g hwe-t) or something like highbank. With MAAS-1.9 any
    # hardware enablement kernel must be specifed in the hwe_kernel field,
    # any other kernel, such as highbank, is still specifed as a
    # subarchitecture. Since Ubuntu does not support architecture specific
    # hardware enablement kernels(i.e a highbank hwe-t kernel on precise)
    # we give precedence to any kernel defined in the subarchitecture field

    # XXX: roaksoax LP: #1739761 - Do not override the subarch (used for
    # the deployment ephemeral env) when deploying precise, provided that
    # it uses the commissioning distro_series and hwe kernels are not
    # needed.
    kernel_validated = False
    if subarch == "generic" and machine.hwe_kernel and not precise:
        subarch = machine.hwe_kernel
    elif (subarch == "generic" and purpose == "commissioning"
          and machine.min_hwe_kernel):
        try:
            subarch = validate_hwe_kernel(None, machine.min_hwe_kernel,
                                          machine.architecture, osystem,
                                          series)
        except ValidationError:
            subarch = "no-such-kernel"
        kernel_validated = True

    # LP:1730525 - If entering the ephemeral environment from a deployed
    # OS subarch will be machine.hwe_kernel. If a newer version of Ubuntu
    # was deployed then the commissioning OS hwe_kernel will not be
    # available. Use the hwe kernel instead of the ga.
    if purpose == "commissioning" and not kernel_validated:
        try:
            subarch = validate_hwe_kernel(subarch, machine.min_hwe_kernel,
                                          machine.architecture, osystem,
                                          series)
        except ValidationError:
            try:
                # Get the default kernel.
                subarch = validate_hwe_kernel(None, machine.min_hwe_kernel,
                                              machine.architecture, osystem,
                                              series)
            except ValidationError:
                subarch = "no-such-kernel"
            else:
                # Switch to hwe-
                subarch = subarch.replace('ga-', 'hwe-')

    return osystem, series, subarch