def test_yields_configuration_when_machine_install_rackd_true(self):
        node = factory.make_Node(osystem="ubuntu", netboot=False)
        node.install_rackd = True
        proxy = "http://proxy.example.com/"
        configuration = generate_rack_controller_configuration(
            node, proxy=proxy
        )
        secret = "1234"
        Config.objects.set_config("rpc_shared_secret", secret)
        channel = version.get_maas_version_track_channel()
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller()
        )
        cmd = "/bin/snap/maas init --mode rack"

        self.assertThat(
            dict(configuration),
            KeysEqual(
                {
                    "runcmd": [
                        "snap set system proxy.http=%s proxy.https=%s"
                        % (proxy, proxy),
                        f"snap install maas --channel={channel}",
                        "%s --maas-url %s --secret %s"
                        % (cmd, maas_url, secret),
                    ]
                }
            ),
        )
    def test_yields_configuration_with_ubuntu(self):
        tag = factory.make_Tag(name="wedge100")
        node = factory.make_Node(osystem="ubuntu", netboot=False)
        node.tags.add(tag)
        configuration = generate_rack_controller_configuration(
            node, proxy="http://proxy.example.com/"
        )
        secret = "1234"
        Config.objects.set_config("rpc_shared_secret", secret)
        channel = version.get_maas_version_track_channel()
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller()
        )
        cmd = "/bin/snap/maas init --mode rack"

        self.assertThat(
            dict(configuration),
            KeysEqual(
                {
                    "runcmd": [
                        f"snap install maas --channel={channel}",
                        "%s --maas-url %s --secret %s"
                        % (cmd, maas_url, secret),
                    ]
                }
            ),
        )
Exemple #3
0
def generate_rack_controller_configuration(node, proxy):
    """Generate cloud-init configuration to install the rack controller."""

    # FIXME: For now, we are using a tag ('switch') to deploy the rack
    # controller but once the switch model is complete we need to switch.
    # In the meatime we will leave it as is for testing purposes.
    node_tags = node.tag_names()
    # To determine this is a machine that's accessing the metadata after
    # initial deployment, we use 'node.netboot'. This flag is set to off after
    # curtin has installed the operating system and before the machine reboots
    # for the first time.
    if (node.netboot is False and node.osystem in ['ubuntu', 'ubuntu-core']
            and ('switch' in node_tags or 'wedge40' in node_tags
                 or 'wedge100' in node_tags or node.install_rackd is True)):
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller())
        secret = Config.objects.get_config("rpc_shared_secret")
        source = get_maas_version_track_channel()
        yield "runcmd", [[
            'snap', 'set', 'system',
            'proxy.http=%s' % proxy,
            'proxy.https=%s' % proxy
        ], ['snap', 'install', 'maas', '--devmode',
            '--channel=%s' % source], ['systemctl', 'restart', 'snapd'],
                         ['export', 'PATH=$PATH'],
                         [
                             '/snap/bin/maas', 'init', '--mode', 'rack',
                             '--maas-url',
                             '%s' % maas_url, '--secret',
                             '%s' % secret
                         ]]
Exemple #4
0
def get_preseed_context(
        request, osystem='', release='', rack_controller=None):
    """Return the node-independent context dictionary to be used to render
    preseed templates.

    :param osystem: See `get_preseed_filenames`.
    :param release: See `get_preseed_filenames`.
    :param rack_controller: The rack controller used to generate the preseed.
    :return: The context dictionary.
    :rtype: dict.
    """
    region_ip = get_default_region_ip(request)
    server_host = get_maas_facing_server_host(
        rack_controller=rack_controller, default_region_ip=region_ip)
    server_url = request.build_absolute_uri(reverse('machines_handler'))
    metadata_enlist_url = request.build_absolute_uri(reverse('enlist'))
    configs = Config.objects.get_configs(['remote_syslog', 'maas_syslog_port'])
    syslog = configs['remote_syslog']
    if not syslog:
        syslog_port = configs['maas_syslog_port']
        if not syslog_port:
            syslog_port = RSYSLOG_PORT
        syslog = '%s:%d' % (server_host, syslog_port)
    return {
        'osystem': osystem,
        'release': release,
        'server_host': server_host,
        'server_url': server_url,
        'syslog_host_port': syslog,
        'metadata_enlist_url': metadata_enlist_url,
        }
Exemple #5
0
def get_preseed_context(request, osystem="", release="", rack_controller=None):
    """Return the node-independent context dictionary to be used to render
    preseed templates.

    :param osystem: See `get_preseed_filenames`.
    :param release: See `get_preseed_filenames`.
    :param rack_controller: The rack controller used to generate the preseed.
    :return: The context dictionary.
    :rtype: dict.
    """
    region_ip = get_default_region_ip(request)
    server_host = get_maas_facing_server_host(
        rack_controller=rack_controller, default_region_ip=region_ip
    )
    server_url = request.build_absolute_uri(reverse("machines_handler"))
    metadata_enlist_url = request.build_absolute_uri(reverse("enlist"))
    configs = Config.objects.get_configs(["remote_syslog", "maas_syslog_port"])
    syslog = configs["remote_syslog"]
    http_proxy = get_apt_proxy(request, rack_controller)
    if not syslog:
        syslog_port = configs["maas_syslog_port"]
        if not syslog_port:
            syslog_port = RSYSLOG_PORT
        syslog = "%s:%d" % (server_host, syslog_port)
    return {
        "osystem": osystem,
        "release": release,
        "server_host": server_host,
        "server_url": server_url,
        "syslog_host_port": syslog,
        "metadata_enlist_url": metadata_enlist_url,
        "http_proxy": http_proxy,
    }
Exemple #6
0
def get_preseed_context(release='', nodegroup=None):
    """Return the node-independent context dictionary to be used to render
    preseed templates.

    :param release: See `get_preseed_filenames`.
    :param nodegroup: The nodegroup used to generate the preseed.
    :return: The context dictionary.
    :rtype: dict.
    """
    server_host = get_maas_facing_server_host(nodegroup=nodegroup)
    main_archive_hostname, main_archive_directory = get_hostname_and_path(
        Config.objects.get_config('main_archive'))
    ports_archive_hostname, ports_archive_directory = get_hostname_and_path(
        Config.objects.get_config('ports_archive'))
    base_url = nodegroup.maas_url if nodegroup is not None else None
    return {
        'main_archive_hostname': main_archive_hostname,
        'main_archive_directory': main_archive_directory,
        'ports_archive_hostname': ports_archive_hostname,
        'ports_archive_directory': ports_archive_directory,
        'release': release,
        'server_host': server_host,
        'server_url': absolute_reverse('nodes_handler', base_url=base_url),
        'metadata_enlist_url': absolute_reverse('enlist', base_url=base_url),
        'http_proxy': Config.objects.get_config('http_proxy'),
        }
Exemple #7
0
def get_apt_proxy(request, rack_controller=None, node=None):
    """Return the APT proxy for the `rack_controller`."""
    config = Config.objects.get_configs([
        "enable_http_proxy",
        "http_proxy",
        "use_peer_proxy",
        "maas_proxy_port",
        "maas_internal_domain",
        "use_rack_proxy",
    ])
    if config["enable_http_proxy"]:
        http_proxy = config["http_proxy"]
        if http_proxy is not None:
            http_proxy = http_proxy.strip()
        use_peer_proxy = config["use_peer_proxy"]
        if http_proxy and not use_peer_proxy:
            return http_proxy
        else:
            # Ensure the proxy port is the default if not set.
            maas_proxy_port = config["maas_proxy_port"]
            if not maas_proxy_port:
                maas_proxy_port = 8000
            # Use the client requesting the preseed to determine how they
            # should access the APT proxy.
            subnet = None
            remote_ip = get_remote_ip(request)
            if remote_ip is not None:
                subnet = Subnet.objects.get_best_subnet_for_ip(remote_ip)
            use_dns = (subnet is not None and not subnet.dns_servers
                       and subnet.vlan.dhcp_on)
            if config["use_rack_proxy"] and use_dns:
                # Client can use the MAAS proxy on the rack controller with
                # DNS resolution providing better HA.
                return "http://%s.%s:%d/" % (
                    get_resource_name_for_subnet(subnet),
                    config["maas_internal_domain"],
                    maas_proxy_port,
                )
            elif (config["use_rack_proxy"] and node is not None
                  and node.boot_cluster_ip):
                # Client can use the MAAS proxy on the rack controller with
                # IP address, instead of DNS.
                return "http://%s:%d/" % (
                    node.boot_cluster_ip,
                    maas_proxy_port,
                )
            else:
                # Fallback to sending the APT directly to the
                # region controller.
                region_ip = get_default_region_ip(request)
                url = "http://:%d/" % maas_proxy_port
                return compose_URL(
                    url,
                    get_maas_facing_server_host(rack_controller,
                                                default_region_ip=region_ip),
                )
    else:
        return None
Exemple #8
0
def get_apt_proxy(rack_controller=None):
    """Return the APT proxy for the `rack_controller`."""
    if Config.objects.get_config("enable_http_proxy"):
        http_proxy = Config.objects.get_config("http_proxy")
        if http_proxy is not None:
            http_proxy = http_proxy.strip()
        use_peer_proxy = Config.objects.get_config("use_peer_proxy")
        if http_proxy and not use_peer_proxy:
            return http_proxy
        else:
            return compose_URL(
                "http://:8000/", get_maas_facing_server_host(rack_controller))
    else:
        return None
Exemple #9
0
def generate_rack_controller_configuration(node, proxy):
    """Generate cloud-init configuration to install the rack controller."""

    # FIXME: For now, we are using a tag ('switch') to deploy the rack
    # controller but once the switch model is complete we need to switch.
    # In the meatime we will leave it as is for testing purposes.
    node_tags = node.tag_names()
    # To determine this is a machine that's accessing the metadata after
    # initial deployment, we use 'node.netboot'. This flag is set to off after
    # curtin has installed the operating system and before the machine reboots
    # for the first time.
    if (
        node.netboot is False
        and node.osystem in ["ubuntu", "ubuntu-core"]
        and (
            "switch" in node_tags
            or "wedge40" in node_tags
            or "wedge100" in node_tags
            or node.install_rackd is True
        )
    ):
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller()
        )
        secret = Config.objects.get_config("rpc_shared_secret")
        source = get_maas_version_track_channel()
        yield "runcmd", [
            [
                "snap",
                "set",
                "system",
                f"proxy.http={proxy}",
                f"proxy.https={proxy}",
            ],
            ["snap", "install", "maas", f"--channel={source}"],
            ["systemctl", "restart", "snapd"],
            ["export", "PATH=$PATH"],
            [
                "/snap/bin/maas",
                "init",
                "--mode",
                "rack",
                "--maas-url",
                maas_url,
                "--secret",
                secret,
            ],
        ]
Exemple #10
0
def generate_rack_controller_configuration(node):
    """Generate cloud-init configuration to install the rack controller."""
    # To determine this is a machine that's accessing the metadata after
    # initial deployment, we use 'node.netboot'. This flag is set to off after
    # curtin has installed the operating system and before the machine reboots
    # for the first time.
    if (not node.netboot and node.install_rackd
            and node.osystem in ("ubuntu", "ubuntu-core")):
        hostname = get_maas_facing_server_host(node.get_boot_rack_controller())
        maas_url = f"http://{hostname}:5240/MAAS"
        secret = Config.objects.get_config("rpc_shared_secret")
        channel = str(get_target_version().snap_channel)
        yield "runcmd", [
            f"snap install maas --channel={channel}",
            f"/snap/bin/maas init rack --maas-url {maas_url} --secret {secret}",
        ]
Exemple #11
0
def get_apt_proxy(request, rack_controller=None):
    """Return the APT proxy for the `rack_controller`."""
    config = Config.objects.get_configs([
        'enable_http_proxy', 'http_proxy', 'use_peer_proxy', 'maas_proxy_port',
        'maas_internal_domain', 'use_rack_proxy'
    ])
    if config["enable_http_proxy"]:
        http_proxy = config["http_proxy"]
        if http_proxy is not None:
            http_proxy = http_proxy.strip()
        use_peer_proxy = config["use_peer_proxy"]
        if http_proxy and not use_peer_proxy:
            return http_proxy
        else:
            # Ensure the proxy port is the default if not set.
            maas_proxy_port = config["maas_proxy_port"]
            if not maas_proxy_port:
                maas_proxy_port = 8000
            # Use the client requesting the preseed to determine how they
            # should access the APT proxy.
            subnet = None
            remote_ip = get_remote_ip(request)
            if remote_ip is not None:
                subnet = Subnet.objects.get_best_subnet_for_ip(remote_ip)
            if (config['use_rack_proxy'] and subnet is not None
                    and not subnet.dns_servers):
                # Client can use the MAAS proxy on the rack controller.
                return "http://%s.%s:%d/" % (get_resource_name_for_subnet(
                    subnet), config["maas_internal_domain"], maas_proxy_port)
            else:
                # Client cannot use the MAAS proxy on the rack controller
                # because rack proxy is disabled, the subnet the IP belongs to
                # is unknown or the subnet is using DNS servers that are not
                # MAAS. Fallback to using the old way pre MAAS 2.5.
                region_ip = get_default_region_ip(request)
                url = "http://:%d/" % maas_proxy_port
                return compose_URL(
                    url,
                    get_maas_facing_server_host(rack_controller,
                                                default_region_ip=region_ip))
    else:
        return None
Exemple #12
0
    def test_yields_configuration_when_machine_install_rackd_true(self):
        node = factory.make_Node(osystem='ubuntu', netboot=False)
        node.install_rackd = True
        configuration = generate_rack_controller_configuration(node)

        secret = '1234'
        Config.objects.set_config("rpc_shared_secret", secret)
        channel = version.get_maas_version_track_channel()
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller())
        cmd = "/bin/snap/maas init --mode rack"

        self.assertThat(
            dict(configuration),
            KeysEqual({
                "runcmd": [
                    "snap install maas --devmode --channel=%s" % channel,
                    "%s --maas-url %s --secret %s" % (cmd, maas_url, secret),
                ]
            }))
Exemple #13
0
def get_apt_proxy(request, rack_controller=None):
    """Return the APT proxy for the `rack_controller`."""
    config = Config.objects.get_configs([
        'enable_http_proxy', 'http_proxy', 'use_peer_proxy', 'maas_proxy_port'
    ])
    if config["enable_http_proxy"]:
        http_proxy = config["http_proxy"]
        if http_proxy is not None:
            http_proxy = http_proxy.strip()
        use_peer_proxy = config["use_peer_proxy"]
        if http_proxy and not use_peer_proxy:
            return http_proxy
        else:
            region_ip = get_default_region_ip(request)
            url = "http://:%d/" % config["maas_proxy_port"]
            return compose_URL(
                url,
                get_maas_facing_server_host(rack_controller,
                                            default_region_ip=region_ip))
    else:
        return None
Exemple #14
0
def get_preseed_context(release='', nodegroup=None):
    """Return the node-independent context dictionary to be used to render
    preseed templates.

    :param release: See `get_preseed_filenames`.
    :param nodegroup: The nodegroup used to generate the preseed.
    :return: The context dictionary.
    :rtype: dict.
    """
    server_host = get_maas_facing_server_host(nodegroup=nodegroup)
    main_archive_hostname, main_archive_directory = get_hostname_and_path(
        Config.objects.get_config('main_archive'))
    ports_archive_hostname, ports_archive_directory = get_hostname_and_path(
        Config.objects.get_config('ports_archive'))
    if nodegroup is None:
        base_url = None
        cluster_host = None
    else:
        base_url = nodegroup.maas_url
        cluster_if = nodegroup.get_managed_interface()
        any_cluster_if = nodegroup.get_any_interface()
        cluster_host = None
        if cluster_if is None:
            if any_cluster_if is not None:
                cluster_host = any_cluster_if.ip
        else:
            cluster_host = cluster_if.ip
    return {
        'main_archive_hostname': main_archive_hostname,
        'main_archive_directory': main_archive_directory,
        'ports_archive_hostname': ports_archive_hostname,
        'ports_archive_directory': ports_archive_directory,
        'release': release,
        'server_host': server_host,
        'server_url': absolute_reverse('nodes_handler', base_url=base_url),
        'metadata_enlist_url': absolute_reverse('enlist', base_url=base_url),
        'http_proxy': Config.objects.get_config('http_proxy'),
        'cluster_host': cluster_host,
    }
Exemple #15
0
def get_preseed_context(osystem='',
                        release='',
                        rack_controller=None,
                        default_region_ip=None):
    """Return the node-independent context dictionary to be used to render
    preseed templates.

    :param osystem: See `get_preseed_filenames`.
    :param release: See `get_preseed_filenames`.
    :param rack_controller: The rack controller used to generate the preseed.
    :return: The context dictionary.
    :rtype: dict.
    """
    server_host = get_maas_facing_server_host(
        rack_controller=rack_controller, default_region_ip=default_region_ip)
    if rack_controller is None:
        base_url = None
    else:
        base_url = rack_controller.url

    return {
        'osystem':
        osystem,
        'release':
        release,
        'server_host':
        server_host,
        'server_url':
        absolute_reverse('machines_handler',
                         default_region_ip=default_region_ip,
                         base_url=base_url),
        'syslog_host_port':
        '%s:%d' % (server_host, RSYSLOG_PORT),
        'metadata_enlist_url':
        absolute_reverse('enlist',
                         default_region_ip=default_region_ip,
                         base_url=base_url),
    }
Exemple #16
0
def generate_rack_controller_configuration(node):
    """Generate cloud-init configuration to install the rack controller."""

    # FIXME: For now, we are using a tag ('switch') to deploy the rack
    # controller but once the switch model is complete we need to switch.
    # In the meatime we will leave it as is for testing purposes.
    node_tags = node.tag_names()
    # To determine this is a machine that's accessing the metadata after
    # initial deployment, we use 'node.netboot'. This flag is set to off after
    # curtin has installed the operating system and before the machine reboots
    # for the first time.
    if (node.netboot is False and node.osystem in ['ubuntu', 'ubuntu-core']
            and ('switch' in node_tags or 'wedge40' in node_tags
                 or 'wedge100' in node_tags)):
        maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
            node.get_boot_rack_controller())
        secret = Config.objects.get_config("rpc_shared_secret")
        source = get_maas_version_track_channel()
        yield "runcmd", [
            "snap install maas --devmode --channel=%s" % source,
            "/snap/bin/maas init --mode rack --maas-url %s --secret %s" %
            (maas_url, secret)
        ]
Exemple #17
0
 def test_yields_configuration_when_machine_install_rackd_true(self):
     controller = factory.make_RackController()
     ControllerInfo.objects.set_version(controller, "3.0.0-123-g.abc")
     node = factory.make_Node(
         osystem="ubuntu", netboot=False, install_rackd=True
     )
     configuration = generate_rack_controller_configuration(node)
     secret = "1234"
     Config.objects.set_config("rpc_shared_secret", secret)
     maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host(
         node.get_boot_rack_controller()
     )
     self.assertEqual(
         list(configuration),
         [
             (
                 "runcmd",
                 [
                     "snap install maas --channel=3.0/stable",
                     f"/snap/bin/maas init rack --maas-url {maas_url} --secret {secret}",
                 ],
             ),
         ],
     )
Exemple #18
0
 def test_get_maas_facing_server_host_returns_host_name(self):
     hostname = make_hostname()
     self.set_maas_url(hostname)
     self.assertEqual(hostname,
                      server_address.get_maas_facing_server_host())
Exemple #19
0
 def test__integrates_with_get_maas_facing_server_host(self):
     ip = factory.make_ipv4_address()
     maas_url = 'http://%s' % ip
     rack = factory.make_RackController(url=maas_url)
     self.assertEqual(str(ip),
                      server_address.get_maas_facing_server_host(rack))
Exemple #20
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)

    # 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
Exemple #21
0
 def test_get_maas_facing_server_host_returns_host_name(self):
     hostname = self.make_hostname()
     self.set_DEFAULT_MAAS_URL(hostname)
     self.assertEqual(hostname,
                      server_address.get_maas_facing_server_host())
Exemple #22
0
def get_rsyslog_host_port(node):
    """Return the rsyslog host and port to use."""
    # TODO: In the future, we can make this configurable
    return "%s:%d" % (get_maas_facing_server_host(
        node.get_boot_rack_controller()), RSYSLOG_PORT)
 def test_get_maas_facing_server_address_returns_nodegroup_maas_url(self):
     ip = factory.getRandomIPInNetwork(IPNetwork('127.0.0.0/8'))
     maas_url = 'http://%s' % ip
     nodegroup = factory.make_node_group(maas_url=maas_url)
     self.assertEqual(
         ip, server_address.get_maas_facing_server_host(nodegroup))
Exemple #24
0
 def test_get_maas_facing_server_host_strips_out_port(self):
     hostname = make_hostname()
     self.set_maas_url(hostname, with_port=True)
     self.assertEqual(hostname,
                      server_address.get_maas_facing_server_host())
Exemple #25
0
 def test_get_maas_facing_server_host_returns_nodegroup_maas_url(self):
     hostname = factory.make_hostname()
     maas_url = 'http://%s' % hostname
     nodegroup = factory.make_node_group(maas_url=maas_url)
     self.assertEqual(hostname,
                      server_address.get_maas_facing_server_host(nodegroup))
Exemple #26
0
 def test_get_maas_facing_server_address_returns_nodegroup_maas_url(self):
     ip = factory.getRandomIPInNetwork(IPNetwork('127.0.0.0/8'))
     maas_url = 'http://%s' % ip
     nodegroup = factory.make_node_group(maas_url=maas_url)
     self.assertEqual(ip,
                      server_address.get_maas_facing_server_host(nodegroup))
Exemple #27
0
def get_rsyslog_host_port(request, node):
    """Return the rsyslog host and port to use."""
    region_ip = get_default_region_ip(request)
    host = get_maas_facing_server_host(node.get_boot_rack_controller(),
                                       default_region_ip=region_ip)
    return "%s:%d" % (host, RSYSLOG_PORT)
 def test_get_maas_facing_server_host_returns_host_name(self):
     hostname = self.make_hostname()
     self.set_DEFAULT_MAAS_URL(hostname)
     self.assertEqual(
         hostname, server_address.get_maas_facing_server_host())
 def test_get_maas_facing_server_host_returns_ip_if_ip_configured(self):
     ip = factory.getRandomIPAddress()
     self.set_DEFAULT_MAAS_URL(ip)
     self.assertEqual(ip, server_address.get_maas_facing_server_host())
 def test_get_maas_facing_server_host_returns_nodegroup_maas_url(self):
     hostname = factory.make_hostname()
     maas_url = 'http://%s' % hostname
     nodegroup = factory.make_node_group(maas_url=maas_url)
     self.assertEqual(
         hostname, server_address.get_maas_facing_server_host(nodegroup))
Exemple #31
0
 def test_get_maas_facing_server_host_returns_ip_if_ip_configured(self):
     ip = factory.make_ipv4_address()
     self.set_maas_url(ip)
     self.assertEqual(ip, server_address.get_maas_facing_server_host())
Exemple #32
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
Exemple #33
0
 def test_get_maas_facing_server_host_returns_rack_maas_url(self):
     hostname = factory.make_hostname()
     maas_url = 'http://%s' % hostname
     rack = factory.make_RackController(url=maas_url)
     self.assertEqual(hostname,
                      server_address.get_maas_facing_server_host(rack))
Exemple #34
0
 def test_get_maas_facing_server_host_returns_ip_if_ip_configured(self):
     ip = factory.getRandomIPAddress()
     self.set_DEFAULT_MAAS_URL(ip)
     self.assertEqual(ip, server_address.get_maas_facing_server_host())
Exemple #35
0
 def test_get_maas_facing_server_host_parses_IPv6_address_in_URL(self):
     ip = factory.make_ipv6_address()
     self.set_maas_url('[%s]' % ip)
     self.assertEqual(str(ip), server_address.get_maas_facing_server_host())
 def test_get_maas_facing_server_host_strips_out_port(self):
     hostname = self.make_hostname()
     self.set_DEFAULT_MAAS_URL(hostname, with_port=True)
     self.assertEqual(
         hostname, server_address.get_maas_facing_server_host())