def test_get_reader_scenarios(self): method = PXEBootMethod() get_ephemeral_name = self.patch(kernel_opts, "get_ephemeral_name") get_ephemeral_name.return_value = factory.make_name("ephemeral") osystem = factory.make_name('osystem') arch = factory.make_name('arch') subarch = factory.make_name('subarch') options = { "backend": None, "kernel_params": make_kernel_parameters(testcase=self, osystem=osystem, subarch=subarch, arch=arch, purpose=self.purpose), } fs_host = 'http://%s:5248/images' % (convert_host_to_uri_str( options['kernel_params'].fs_host)) output = method.get_reader(**options).read(10000).decode("utf-8") config = parse_pxe_config(output) # The default section is defined. default_section_label = config.header["DEFAULT"] self.assertThat(config, Contains(default_section_label)) default_section = dict(config[default_section_label]) contains_arch_path = StartsWith("%s/%s/%s/%s" % (fs_host, osystem, arch, subarch)) self.assertThat(default_section["KERNEL"], contains_arch_path) self.assertThat(default_section["INITRD"], contains_arch_path) self.assertEqual("2", default_section["IPAPPEND"])
def test_get_reader(self): # Given the right configuration options, the UEFI configuration is # correctly rendered. method = UEFIAMD64BootMethod() params = make_kernel_parameters(arch="amd64", purpose="xinstall") fs_host = '(http,%s:5248)/images' % (convert_host_to_uri_str( params.fs_host)) output = method.get_reader(backend=None, kernel_params=params) # The output is a BytesReader. self.assertThat(output, IsInstance(BytesReader)) output = output.read(10000).decode("utf-8") # The template has rendered without error. UEFI configurations # typically start with a DEFAULT line. self.assertThat(output, StartsWith("set default=\"0\"")) # The UEFI parameters are all set according to the options. image_dir = compose_image_path(osystem=params.osystem, arch=params.arch, subarch=params.subarch, release=params.release, label=params.label) self.assertThat( output, MatchesAll( MatchesRegex( r".*\s+lin.*cc:\\{\'datasource_list\':" r" \[\'MAAS\'\]\\}end_cc.*", re.MULTILINE | re.DOTALL), MatchesRegex( r'.*^\s+linuxefi %s/%s/%s .+?$' % (re.escape(fs_host), re.escape(image_dir), params.kernel), re.MULTILINE | re.DOTALL), MatchesRegex( r'.*^\s+initrdefi %s/%s/%s$' % (re.escape(fs_host), re.escape(image_dir), params.initrd), re.MULTILINE | re.DOTALL)))
def test_get_reader_install(self): # Given the right configuration options, the PXE configuration is # correctly rendered. method = PXEBootMethod() params = make_kernel_parameters(self, purpose="xinstall") fs_host = 'http://%s:5248/images' % (convert_host_to_uri_str( params.fs_host)) output = method.get_reader(backend=None, kernel_params=params) # The output is a BytesReader. self.assertThat(output, IsInstance(BytesReader)) output = output.read(10000).decode("utf-8") # The template has rendered without error. PXELINUX configurations # typically start with a DEFAULT line. self.assertThat(output, StartsWith("DEFAULT ")) # The PXE parameters are all set according to the options. image_dir = compose_image_path(osystem=params.osystem, arch=params.arch, subarch=params.subarch, release=params.release, label=params.label) self.assertThat( output, MatchesAll( MatchesRegex( r'.*^\s+KERNEL %s/%s/%s$' % (re.escape(fs_host), re.escape(image_dir), params.kernel), re.MULTILINE | re.DOTALL), MatchesRegex( r'.*^\s+INITRD %s/%s/%s$' % (re.escape(fs_host), re.escape(image_dir), params.initrd), re.MULTILINE | re.DOTALL), MatchesRegex(r'.*^\s+APPEND .+?$', re.MULTILINE | re.DOTALL)))
def test_get_reader_ephemeral(self): # Given the right configuration options, the iPXE configuration is # correctly rendered. method = IPXEBootMethod() xtra = ("custom_xtra_cfg=http://{{ kernel_params.fs_host }}/" "my_extra_config?mac={{ kernel_params.mac }}") params = make_kernel_parameters( self, arch="amd64", subarch="generic", purpose="ephemeral", extra_opts=xtra, ) fs_host = "http://%s:5248/images" % (convert_host_to_uri_str( params.fs_host)) output = method.get_reader(backend=None, kernel_params=params) # The output is a BytesReader. self.assertThat(output, IsInstance(BytesReader)) output = output.read(10000).decode("utf-8") # The template has rendered without error. iPXE configurations # start with #ipxe. self.assertThat(output, StartsWith("#!ipxe")) # The iPXE parameters are all set according to the options. image_dir = compose_image_path( osystem=params.osystem, arch=params.arch, subarch=params.subarch, release=params.release, label=params.label, ) self.assertThat( output, MatchesAll( MatchesRegex( r".*^\s*kernel %s/%s/%s$" % ( re.escape(fs_host), re.escape(image_dir), params.kernel, ), re.MULTILINE | re.DOTALL, ), MatchesRegex( r".*^\s*initrd %s/%s/%s\s+" % ( re.escape(fs_host), re.escape(image_dir), params.initrd, ), re.MULTILINE | re.DOTALL, ), MatchesRegex( r".*\s+custom_xtra_cfg=http://%s/my_extra_config.*?\s+" % (params.fs_host), re.MULTILINE | re.DOTALL, ), MatchesRegex(r".*\s+maas_url=.+?$", re.MULTILINE | re.DOTALL), ), )
def test_get_reader_scenarios(self): # The commissioning config uses an extra PXELINUX module to auto # select between i386 and amd64. method = PXEBootMethod() get_ephemeral_name = self.patch(kernel_opts, "get_ephemeral_name") get_ephemeral_name.return_value = factory.make_name("ephemeral") osystem = factory.make_name("osystem") options = { "backend": None, "kernel_params": make_kernel_parameters( testcase=self, osystem=osystem, subarch="generic", purpose="enlist", ), } fs_host = "http://%s:5248/images" % ( convert_host_to_uri_str(options["kernel_params"].fs_host) ) output = method.get_reader(**options).read(10000).decode("utf-8") config = parse_pxe_config(output) # The default section is defined. default_section_label = config.header["DEFAULT"] self.assertThat(config, Contains(default_section_label)) default_section = config[default_section_label] # The default section uses the ifcpu64 module, branching to the "i386" # or "amd64" labels accordingly. self.assertEqual("ifcpu64.c32", default_section["KERNEL"]) self.assertEqual( ["amd64", "--", "i386"], default_section["APPEND"].split() ) # Both "i386" and "amd64" sections exist. self.assertThat(config, ContainsAll(("i386", "amd64"))) # Each section defines KERNEL, INITRD, and APPEND settings. The # KERNEL and INITRD ones contain paths referring to their # architectures. for section_label in ("i386", "amd64"): section = config[section_label] self.assertThat( section, ContainsAll(("KERNEL", "INITRD", "APPEND")) ) contains_arch_path = StartsWith( "%s/%s/%s/" % (fs_host, osystem, section_label) ) self.assertThat(section["KERNEL"], contains_arch_path) self.assertThat(section["INITRD"], contains_arch_path) self.assertIn("APPEND", section)
def test_get_reader_install(self): # Given the right configuration options, the PXE configuration is # correctly rendered. method = IPXEBootMethod() params = make_kernel_parameters(self, purpose="xinstall") fs_host = "http://%s:5248/images" % (convert_host_to_uri_str( params.fs_host)) output = method.get_reader(backend=None, kernel_params=params) # The output is a BytesReader. self.assertThat(output, IsInstance(BytesReader)) output = output.read(10000).decode("utf-8") # The template has rendered without error. iPXE configurations # start with #ipxe. self.assertThat(output, StartsWith("#!ipxe")) # The iPXE parameters are all set according to the options. image_dir = compose_image_path( osystem=params.osystem, arch=params.arch, subarch=params.subarch, release=params.release, label=params.label, ) self.assertThat( output, MatchesAll( MatchesRegex( r".*^\s*kernel %s/%s/%s$" % ( re.escape(fs_host), re.escape(image_dir), params.kernel, ), re.MULTILINE | re.DOTALL, ), MatchesRegex( r".*^\s*initrd %s/%s/%s$" % ( re.escape(fs_host), re.escape(image_dir), params.initrd, ), re.MULTILINE | re.DOTALL, ), MatchesRegex(r".*^\s*imgargs .+?$", re.MULTILINE | re.DOTALL), ), )
def get_reader(self, backend, kernel_params, protocol, **extra): """Render a configuration file as a unicode string. :param backend: requesting backend :param kernel_params: An instance of `KernelParameters`. :param protocol: The protocol the transfer is happening over. :param extra: Allow for other arguments. This is a safety valve; parameters generated in another component (for example, see `TFTPBackend.get_boot_method_reader`) won't cause this to break. """ def kernel_command(params): """Return the kernel command, adjusted for UEFI to work. See the similar function in BootMethod, and the callsite below. The issue here is that grub throws a fit when the braces on cc:{...}end_cc are hit, for whatever reason. Escape _JUST_ those. """ return re.sub( r"cc:{(?P<inner>[^}]*)}end_cc", r"cc:\{\g<inner>\}end_cc", compose_kernel_command_line(params), ) template = self.get_template(kernel_params.purpose, kernel_params.arch, kernel_params.subarch) namespace = self.compose_template_namespace(kernel_params) # TFTP is much slower than HTTP. If GRUB was transfered over TFTP use # GRUBs internal HTTP implementation to download the kernel and initrd. # If HTTP or HTTPS was used don't specify host to continue to use the # UEFI firmware's internal HTTP implementation. if protocol == "tftp": namespace["fs_efihost"] = "(http,%s:5248)/images/" % ( convert_host_to_uri_str(kernel_params.fs_host)) else: namespace["fs_efihost"] = "/images/" # Bug#1651452 - kernel command needs some extra escapes, but ONLY for # UEFI. And so we fix it here, instead of in the common code. See # also src/provisioningserver/kernel_opts.py. namespace["kernel_command"] = kernel_command return BytesReader( template.substitute(namespace).strip().encode("utf-8"))
def fs_efihost(params): return "(http,%s:5248)/images/" % ( convert_host_to_uri_str(params.fs_host) )
def fs_host(params): return "http://%s:5248/images/" % ( convert_host_to_uri_str(params.fs_host) )
def fs_host(params): return convert_host_to_uri_str(params.fs_host)