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), ] } ), )
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 ]]
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, }
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, }
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'), }
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
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
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, ], ]
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}", ]
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
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), ] }))
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
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, }
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), }
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) ]
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}", ], ), ], )
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())
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))
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
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 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))
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())
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))
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))
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))
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())
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
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))
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())