def test_preseed_context_cluster_host(self): # The cluster_host context variable is derived from the nodegroup. release = factory.getRandomString() nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) context = get_preseed_context(release, nodegroup) self.assertIsNotNone(context["cluster_host"]) self.assertEqual(nodegroup.get_managed_interface().ip, context["cluster_host"])
def test_get_preseed_context_contains_keys(self): release = factory.getRandomString() nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) context = get_preseed_context(release, nodegroup) self.assertItemsEqual([ 'release', 'metadata_enlist_url', 'server_host', 'server_url', 'cluster_host', 'main_archive_hostname', 'main_archive_directory', 'ports_archive_hostname', 'ports_archive_directory', 'http_proxy' ], context)
def test_get_preseed_context_contains_keys(self): release = factory.getRandomString() nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) context = get_preseed_context(release, nodegroup) self.assertItemsEqual( ['release', 'metadata_enlist_url', 'server_host', 'server_url', 'main_archive_hostname', 'main_archive_directory', 'ports_archive_hostname', 'ports_archive_directory', 'http_proxy', ], context)
def test_preseed_context_cluster_host_if_unmanaged(self): # If the nodegroup has no managed interface recorded, the cluster_host # context variable is still present and derived from the nodegroup. release = factory.getRandomString() nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) for interface in nodegroup.nodegroupinterface_set.all(): interface.management = NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED interface.save() context = get_preseed_context(release, nodegroup) self.assertIsNotNone(context["cluster_host"]) self.assertEqual(nodegroup.get_any_interface().ip, context["cluster_host"])
def generate_user_data(nodegroup=None): """Produce the main commissioning script. The main template file contains references to so-called ``snippets'' which are read in here, and substituted. In addition, the regular preseed context variables are available (such as 'http_proxy'). The final result is a MIME multipart message that consists of a 'cloud-config' part and an 'x-shellscript' part. This allows maximum flexibility with cloud-init as we read in a template 'user_data_config.template' to set cloud-init configs before the script is run. :rtype: `bytes` """ commissioning_dir = get_userdata_template_dir() userdata_template_file = os.path.join(commissioning_dir, 'user_data.template') config_template_file = os.path.join(commissioning_dir, 'user_data_config.template') userdata_template = tempita.Template.from_filename(userdata_template_file, encoding=ENCODING) config_template = tempita.Template.from_filename(config_template_file, encoding=ENCODING) # The preseed context is a dict containing various configs that the # templates can use. preseed_context = get_preseed_context(nodegroup=nodegroup) # Render the snippets in the main template. snippets = get_snippet_context(encoding=ENCODING) snippets.update(preseed_context) userdata = userdata_template.substitute(snippets).encode(ENCODING) # Render the config. config = config_template.substitute(preseed_context) # Create a MIME multipart message from the config and the userdata. config_part = MIMEText(config, 'cloud-config', ENCODING) config_part.add_header('Content-Disposition', 'attachment; filename="config"') data_part = MIMEText(userdata, 'x-shellscript', ENCODING) data_part.add_header('Content-Disposition', 'attachment; filename="user_data.sh"') combined = MIMEMultipart() combined.attach(config_part) combined.attach(data_part) return combined.as_string()
def generate_user_data(node, userdata_template_file, extra_context=None): """Produce a user_data script for use by an ephemeral environment. The main template file contains references to so-called ``snippets'' which are read in here, and substituted. In addition, the regular preseed context variables are available (such as 'http_proxy'). The final result is a MIME multipart message that consists of a 'cloud-config' part and an 'x-shellscript' part. This allows maximum flexibility with cloud-init as we read in a template 'user_data_config.template' to set cloud-init configs before the script is run. :rtype: `bytes` """ # Avoid circular dependencies. from maasserver.preseed import get_preseed_context userdata_template = tempita.Template.from_filename( userdata_template_file, encoding=ENCODING) # The preseed context is a dict containing various configs that the # templates can use. preseed_context = get_preseed_context( rack_controller=node.get_boot_rack_controller()) preseed_context['node'] = node # Render the snippets in the main template. snippets = get_snippet_context(encoding=ENCODING) snippets.update(preseed_context) if extra_context is not None: snippets.update(extra_context) userdata = userdata_template.substitute(snippets).encode(ENCODING) data_part = MIMEText(userdata, 'x-shellscript', ENCODING) data_part.add_header( 'Content-Disposition', 'attachment; filename="user_data.sh"') combined = MIMEMultipart() combined.attach(data_part) return combined.as_bytes()
def test_get_preseed_context_archive_refs(self): # urlparse lowercases the hostnames. That should not have any # impact but for testing, create lower-case hostnames. main_archive = make_url('main_archive') ports_archive = make_url('ports_archive') Config.objects.set_config('main_archive', main_archive) Config.objects.set_config('ports_archive', ports_archive) nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) context = get_preseed_context(factory.make_node(), nodegroup) parsed_main_archive = urlparse(main_archive) parsed_ports_archive = urlparse(ports_archive) self.assertEqual(( parsed_main_archive.hostname, parsed_main_archive.path, parsed_ports_archive.hostname, parsed_ports_archive.path, ), ( context['main_archive_hostname'], context['main_archive_directory'], context['ports_archive_hostname'], context['ports_archive_directory'], ))
def test_get_preseed_context_archive_refs(self): # urlparse lowercases the hostnames. That should not have any # impact but for testing, create lower-case hostnames. main_archive = make_url('main_archive') ports_archive = make_url('ports_archive') Config.objects.set_config('main_archive', main_archive) Config.objects.set_config('ports_archive', ports_archive) nodegroup = factory.make_node_group(maas_url=factory.getRandomString()) context = get_preseed_context(factory.make_node(), nodegroup) parsed_main_archive = urlparse(main_archive) parsed_ports_archive = urlparse(ports_archive) self.assertEqual( ( parsed_main_archive.hostname, parsed_main_archive.path, parsed_ports_archive.hostname, parsed_ports_archive.path, ), ( context['main_archive_hostname'], context['main_archive_directory'], context['ports_archive_hostname'], context['ports_archive_directory'], ))
def test_preseed_context_null_cluster_host_if_does_not_exist(self): # If there's no nodegroup, the cluster_host context variable is # present, but None. release = factory.getRandomString() context = get_preseed_context(release) self.assertIsNone(context["cluster_host"])