← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~allenap/maas/decouple-kernel-opts into lp:maas

 

Gavin Panella has proposed merging lp:~allenap/maas/decouple-kernel-opts into lp:maas with lp:~allenap/maas/new-tftp-layout as a prerequisite.

Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~allenap/maas/decouple-kernel-opts/+merge/120870

This decouples the kernel command-line generation from the MAAS server
core. This is stuff that we need to push to the "edge" so that
hardware enablement packs can hook into them. However, much more
pressingly, I want to render multiple command lines in the PXE config
files, depending on arch, so moving this over means I don't have to
come up with ugly scaffolding to make multiple calls back to the
pxeconfig view.

The steps I took:

- Moved EPHEMERAL_ROOT setting over to pserv.yaml.

- Decoupled kernel_opts from maasserver.

  The maasserver specific bits are now in maasserver.kernel, but this
  is *transitional*, as are its tests. A following branch will move
  this logic into the pxeconfig API view.

  Many of the tests in TestComposeKernelCommandLine are copied from
  TestKernelOpts before kernel_opts was decoupled from maasserver.
  Thus they're much the same as the old tests.

- Moved kernel_opts to provisioningserver.

-- 
https://code.launchpad.net/~allenap/maas/decouple-kernel-opts/+merge/120870
Your team MAAS Maintainers is requested to review the proposed merge of lp:~allenap/maas/decouple-kernel-opts into lp:maas.
=== modified file 'etc/pserv.yaml'
--- etc/pserv.yaml	2012-08-16 10:50:16 +0000
+++ etc/pserv.yaml	2012-08-22 20:55:23 +0000
@@ -35,3 +35,9 @@
   # port: 5244
   ## The URL to be contacted to generate PXE configurations.
   # generator: http://localhost:5243/api/1.0/pxeconfig/
+
+## Boot configuration.
+boot:
+  # ephemeral:
+  ## Directory containing ephemeral boot images, etc.
+  #   directory: /var/lib/maas/ephemeral

=== modified file 'src/maas/settings.py'
--- src/maas/settings.py	2012-08-16 08:24:49 +0000
+++ src/maas/settings.py	2012-08-22 20:55:23 +0000
@@ -283,9 +283,6 @@
 # doing.
 COMMISSIONING_SCRIPT = '/etc/maas/commissioning-user-data'
 
-# The location of the ephemeral images/infos.
-EPHEMERAL_ROOT = "/var/lib/maas/ephemeral"
-
 # The duration, in minutes, after which we consider a commissioning node
 # to have failed and mark it as FAILED_TESTS.
 COMMISSIONING_TIMEOUT = 60

=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py	2012-08-20 10:52:29 +0000
+++ src/maasserver/api.py	2012-08-22 20:55:23 +0000
@@ -115,7 +115,7 @@
     get_node_create_form,
     get_node_edit_form,
     )
-from maasserver.kernel_opts import compose_kernel_command_line
+from maasserver.kernel import compose_kernel_command_line
 from maasserver.models import (
     Config,
     DHCPLease,

=== modified file 'src/maasserver/exceptions.py'
--- src/maasserver/exceptions.py	2012-08-07 14:59:24 +0000
+++ src/maasserver/exceptions.py	2012-08-22 20:55:23 +0000
@@ -40,10 +40,6 @@
     """Could not reach RabbitMQ."""
 
 
-class EphemeralImagesDirectoryNotFound(MAASException):
-    """The ephemeral images directory cannot be found."""
-
-
 class MAASAPIException(Exception):
     """Base class for MAAS' API exceptions.
 

=== added file 'src/maasserver/kernel.py'
--- src/maasserver/kernel.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/kernel.py	2012-08-22 20:55:23 +0000
@@ -0,0 +1,63 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Generate kernel command-line for inclusion in PXE configs.
+
+This is a TRANSITIONAL module. This functionality will move to the pxeconfig()
+view in the most likely scenario.
+"""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'compose_kernel_command_line',
+    ]
+
+from maasserver.preseed import (
+    compose_enlistment_preseed_url,
+    compose_preseed_url,
+    )
+from maasserver.server_address import get_maas_facing_server_address
+from provisioningserver.kernel_opts import (
+    compose_kernel_command_line_new,
+    KernelParameters,
+    )
+
+
+def compose_kernel_command_line(node, arch, subarch, purpose):
+    """Generate a line of kernel options for booting `node`.
+
+    Include these options in the PXE config file's APPEND argument.
+
+    The node may be None, in which case it will boot into enlistment.
+    """
+    # XXX JeroenVermeulen 2012-08-06 bug=1013146: Stop hard-coding this.
+    release = 'precise'
+
+    # TODO: This is probably not enough!
+    domain = 'local.lan'
+
+    if node is None:
+        preseed_url = compose_enlistment_preseed_url()
+    else:
+        preseed_url = compose_preseed_url(node)
+
+    if node is None:
+        # Not a known host; still needs enlisting.  Make up a name.
+        hostname = 'maas-enlist'
+    else:
+        hostname = node.hostname
+
+    server_address = get_maas_facing_server_address()
+
+    params = KernelParameters(
+        arch=arch, subarch=subarch, release=release, purpose=purpose,
+        hostname=hostname, domain=domain, preseed_url=preseed_url,
+        log_host=server_address, fs_host=server_address)
+
+    return compose_kernel_command_line_new(params)

=== modified file 'src/maasserver/preseed.py'
--- src/maasserver/preseed.py	2012-08-16 13:38:51 +0000
+++ src/maasserver/preseed.py	2012-08-22 20:55:23 +0000
@@ -11,6 +11,8 @@
 
 __metaclass__ = type
 __all__ = [
+    'compose_enlistment_preseed_url',
+    'compose_preseed_url',
     'get_enlist_preseed',
     'get_preseed',
     ]
@@ -247,3 +249,21 @@
     template = load_preseed_template(node, prefix, release)
     context = get_preseed_context(node, release)
     return template.substitute(**context)
+
+
+def compose_enlistment_preseed_url():
+    """Compose enlistment preseed URL."""
+    # Always uses the latest version of the metadata API.
+    version = 'latest'
+    return absolute_reverse(
+        'metadata-enlist-preseed', args=[version],
+        query={'op': 'get_enlist_preseed'})
+
+
+def compose_preseed_url(node):
+    """Compose a metadata URL for `node`'s preseed data."""
+    # Always uses the latest version of the metadata API.
+    version = 'latest'
+    return absolute_reverse(
+        'metadata-node-by-id', args=[version, node.system_id],
+        query={'op': 'get_preseed'})

=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py	2012-08-22 02:07:30 +0000
+++ src/maasserver/tests/test_api.py	2012-08-22 20:55:23 +0000
@@ -33,10 +33,7 @@
 from django.core.urlresolvers import reverse
 from django.http import QueryDict
 from fixtures import Fixture
-from maasserver import (
-    api,
-    kernel_opts,
-    )
+from maasserver import api
 from maasserver.api import (
     extract_constraints,
     extract_oauth_key,
@@ -52,10 +49,7 @@
     )
 from maasserver.exceptions import Unauthorized
 from maasserver.fields import mac_error_msg
-from maasserver.kernel_opts import (
-    compose_enlistment_preseed_url,
-    compose_preseed_url,
-    )
+from maasserver.kernel import compose_enlistment_preseed_url
 from maasserver.models import (
     Config,
     DHCPLease,
@@ -67,6 +61,7 @@
     create_auth_token,
     get_auth_tokens,
     )
+from maasserver.preseed import compose_preseed_url
 from maasserver.testing import (
     reload_object,
     reload_objects,
@@ -89,7 +84,10 @@
     NodeUserData,
     )
 from metadataserver.nodeinituser import get_node_init_user
-from provisioningserver import tasks
+from provisioningserver import (
+    kernel_opts,
+    tasks,
+    )
 from provisioningserver.auth import get_recorded_nodegroup_name
 from provisioningserver.enum import (
     POWER_TYPE,

=== added file 'src/maasserver/tests/test_kernel.py'
--- src/maasserver/tests/test_kernel.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/tests/test_kernel.py	2012-08-22 20:55:23 +0000
@@ -0,0 +1,176 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test composition of kernel command lines."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+import os
+
+from django.conf import settings
+from maasserver.api import get_boot_purpose
+from maasserver.kernel import compose_kernel_command_line
+from maasserver.preseed import (
+    compose_enlistment_preseed_url,
+    compose_preseed_url,
+    )
+from maasserver.server_address import get_maas_facing_server_address
+from maasserver.testing.factory import factory
+from maasserver.testing.testcase import TestCase
+from maastesting.matchers import ContainsAll
+from provisioningserver.kernel_opts import (
+    EphemeralImagesDirectoryNotFound,
+    ISCSI_TARGET_NAME_PREFIX,
+    )
+from provisioningserver.pxe.tftppath import compose_image_path
+from provisioningserver.testing.config import ConfigFixture
+
+
+class TestComposeKernelCommandLine(TestCase):
+
+    def test_compose_kernel_command_line_accepts_None_for_unknown_node(self):
+        self.assertIn(
+            'suite=precise',
+            compose_kernel_command_line(
+                None, factory.make_name('arch'), factory.make_name('subarch'),
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_preseed_url(self):
+        node = factory.make_node()
+        self.assertIn(
+            "auto url=%s" % compose_preseed_url(node),
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_enlistment_preseed_url(self):
+        self.assertIn(
+            "auto url=%s" % compose_enlistment_preseed_url(),
+            compose_kernel_command_line(
+                None, factory.make_name("arch"), 'generic',
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_initrd(self):
+        node = factory.make_node()
+        initrd_path = compose_image_path(
+            node.architecture, 'generic', 'precise',
+            purpose=get_boot_purpose(node))
+        self.assertIn(
+            "initrd=%s" % initrd_path,
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=get_boot_purpose(node)))
+
+    def test_compose_kernel_command_line_includes_suite(self):
+        # At the moment, the OS release we use is hard-coded to "precise."
+        node = factory.make_node()
+        suite = "precise"
+        self.assertIn(
+            "suite=%s" % suite,
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_hostname_and_domain(self):
+        node = factory.make_node()
+        # Cobbler seems to hard-code domain to "local.lan"; we may want
+        # to change it, and update this test.
+        domain = "local.lan"
+        self.assertThat(
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=factory.make_name('purpose')),
+            ContainsAll([
+                "hostname=%s" % node.hostname,
+                "domain=%s" % domain,
+                ]))
+
+    def test_compose_kernel_command_line_makes_up_hostname_for_new_node(self):
+        dummy_hostname = 'maas-enlist'
+        self.assertIn(
+            "hostname=%s" % dummy_hostname,
+            compose_kernel_command_line(
+                None, factory.make_name('arch'),
+                factory.make_name('subarch'),
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_locale(self):
+        node = factory.make_node()
+        locale = "en_US"
+        self.assertIn(
+            "locale=%s" % locale,
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=factory.make_name('purpose')))
+
+    def test_compose_kernel_command_line_includes_log_settings(self):
+        node = factory.make_node()
+        log_host = factory.getRandomIPAddress()
+        self.patch(settings, 'DEFAULT_MAAS_URL', 'http://%s/' % log_host)
+        # Port 514 (UDP) is syslog.
+        log_port = "514"
+        text_priority = "critical"
+        self.assertThat(
+            compose_kernel_command_line(
+                node, node.architecture, 'generic',
+                purpose=factory.make_name('purpose')),
+            ContainsAll([
+                "log_host=%s" % log_host,
+                "log_port=%s" % log_port,
+                "text priority=%s" % text_priority,
+                ]))
+
+    def create_ephemeral_info(self, name, arch, release):
+        """Create a pseudo-real ephemeral info file."""
+        epheneral_info = """
+            release=%s
+            stream=ephemeral
+            label=release
+            serial=20120424
+            arch=%s
+            name=%s
+            """ % (release, arch, name)
+        ephemeral_root = self.make_dir()
+        config = {"boot": {"ephemeral": {"directory": ephemeral_root}}}
+        self.useFixture(ConfigFixture(config))
+        ephemeral_dir = os.path.join(
+            ephemeral_root, release, 'ephemeral', arch, release)
+        os.makedirs(ephemeral_dir)
+        factory.make_file(
+            ephemeral_dir, name='info', contents=epheneral_info)
+
+    def test_compose_kernel_command_line_inc_purpose_opts_comm_node(self):
+        # The result of compose_kernel_command_line includes the purpose
+        # options for a "commissioning" node.
+        ephemeral_name = factory.make_name("ephemeral")
+        arch = factory.make_name('arch')
+        self.create_ephemeral_info(ephemeral_name, arch, "precise")
+        node = factory.make_node()
+        self.assertThat(
+            compose_kernel_command_line(
+                node, arch,
+                factory.make_name('subarch'),
+                purpose="commissioning"),
+            ContainsAll([
+                "iscsi_target_name=%s:%s" % (
+                    ISCSI_TARGET_NAME_PREFIX, ephemeral_name),
+                "iscsi_target_port=3260",
+                "iscsi_target_ip=%s" % get_maas_facing_server_address(),
+                ]))
+
+    def test_compose_kernel_command_line_reports_error_about_missing_dir(self):
+        missing_dir = factory.make_name('missing-dir')
+        config = {"boot": {"ephemeral": {"directory": missing_dir}}}
+        self.useFixture(ConfigFixture(config))
+        node = factory.make_node()
+        self.assertRaises(
+            EphemeralImagesDirectoryNotFound,
+            compose_kernel_command_line, node, factory.make_name('arch'),
+            factory.make_name('subarch'), purpose="commissioning")

=== modified file 'src/maasserver/tests/test_preseed.py'
--- src/maasserver/tests/test_preseed.py	2012-08-14 09:24:03 +0000
+++ src/maasserver/tests/test_preseed.py	2012-08-22 20:55:23 +0000
@@ -12,6 +12,7 @@
 __metaclass__ = type
 __all__ = []
 
+import httplib
 import os
 from pipes import quote
 
@@ -21,6 +22,8 @@
     PRESEED_TYPE,
     )
 from maasserver.preseed import (
+    compose_enlistment_preseed_url,
+    compose_preseed_url,
     GENERIC_FILENAME,
     get_enlist_preseed,
     get_preseed,
@@ -39,6 +42,7 @@
 from testtools.matchers import (
     AllMatch,
     IsInstance,
+    StartsWith,
     )
 
 
@@ -371,3 +375,31 @@
         node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
         preseed = get_preseed(node)
         self.assertIn('cloud-init', preseed)
+
+
+class TestPreseedURLs(TestCase):
+    """Tests for functions that return preseed URLs."""
+
+    def test_compose_enlistment_preseed_url_links_to_enlistment_preseed(self):
+        response = self.client.get(compose_enlistment_preseed_url())
+        self.assertEqual(
+            (httplib.OK, get_enlist_preseed()),
+            (response.status_code, response.content))
+
+    def test_compose_enlistment_preseed_url_returns_absolute_link(self):
+        url = 'http://%s' % factory.make_name('host')
+        self.patch(settings, 'DEFAULT_MAAS_URL', url)
+        self.assertThat(
+            compose_enlistment_preseed_url(), StartsWith(url))
+
+    def test_compose_preseed_url_links_to_preseed_for_node(self):
+        node = factory.make_node()
+        response = self.client.get(compose_preseed_url(node))
+        self.assertEqual(
+            (httplib.OK, get_preseed(node)),
+            (response.status_code, response.content))
+
+    def test_compose_preseed_url_returns_absolute_link(self):
+        self.assertThat(
+            compose_preseed_url(factory.make_node()),
+            StartsWith('http://'))

=== modified file 'src/provisioningserver/config.py'
--- src/provisioningserver/config.py	2012-08-16 10:50:16 +0000
+++ src/provisioningserver/config.py	2012-08-22 20:55:23 +0000
@@ -68,6 +68,22 @@
         )
 
 
+class ConfigBootEphemeral(Schema):
+    """Configuration validator for ephemeral boot configuration."""
+
+    if_key_missing = None
+
+    directory = String(if_missing="/var/lib/maas/ephemeral")
+
+
+class ConfigBoot(Schema):
+    """Configuration validator for boot configuration."""
+
+    if_key_missing = None
+
+    ephemeral = ConfigBootEphemeral
+
+
 class ConfigMeta(DeclarativeMeta):
     """Metaclass for the root configuration schema."""
 
@@ -105,6 +121,7 @@
     oops = ConfigOops
     broker = ConfigBroker
     tftp = ConfigTFTP
+    boot = ConfigBoot
 
     @classmethod
     def parse(cls, stream):

=== renamed file 'src/maasserver/kernel_opts.py' => 'src/provisioningserver/kernel_opts.py'
--- src/maasserver/kernel_opts.py	2012-08-08 11:15:07 +0000
+++ src/provisioningserver/kernel_opts.py	2012-08-22 20:55:23 +0000
@@ -11,51 +11,38 @@
 
 __metaclass__ = type
 __all__ = [
-    'compose_kernel_command_line',
+    'compose_kernel_command_line_new',
+    'KernelParameters',
     ]
 
+from collections import namedtuple
 import os
 
-from django.conf import settings
-from maasserver.exceptions import EphemeralImagesDirectoryNotFound
-from maasserver.server_address import get_maas_facing_server_address
-from maasserver.utils import absolute_reverse
+from provisioningserver.config import Config
 from provisioningserver.pxe.tftppath import compose_image_path
 from provisioningserver.utils import parse_key_value_file
 
 
+class EphemeralImagesDirectoryNotFound(Exception):
+    """The ephemeral images directory cannot be found."""
+
+
+KernelParameters = namedtuple(
+    b"KernelParameters", (
+        "arch", "subarch", "release", "purpose", "hostname",
+        "domain", "preseed_url", "log_host", "fs_host"))
+
+
 def compose_initrd_opt(arch, subarch, release, purpose):
     path = "%s/initrd.gz" % compose_image_path(arch, subarch, release, purpose)
     return "initrd=%s" % path
 
 
-def compose_enlistment_preseed_url():
-    """Compose enlistment preseed URL."""
-    # Always uses the latest version of the metadata API.
-    version = 'latest'
-    return absolute_reverse(
-        'metadata-enlist-preseed', args=[version],
-        query={'op': 'get_enlist_preseed'})
-
-
-def compose_preseed_url(node):
-    """Compose a metadata URL for `node`'s preseed data."""
-    # Always uses the latest version of the metadata API.
-    version = 'latest'
-    return absolute_reverse(
-        'metadata-node-by-id', args=[version, node.system_id],
-        query={'op': 'get_preseed'})
-
-
-def compose_preseed_opt(node):
-    """Compose a kernel option for preseed URL for given `node`.
-
-    :param mac_address: A `Node`, or `None`.
+def compose_preseed_opt(preseed_url):
+    """Compose a kernel option for preseed URL.
+
+    :param preseed_url: The URL from which a preseed can be fetched.
     """
-    if node is None:
-        preseed_url = compose_enlistment_preseed_url()
-    else:
-        preseed_url = compose_preseed_url(node)
     return "auto url=%s" % preseed_url
 
 
@@ -63,18 +50,11 @@
     return "suite=%s" % release
 
 
-def compose_hostname_opt(node):
-    if node is None:
-        # Not a known host; still needs enlisting.  Make up a name.
-        hostname = 'maas-enlist'
-    else:
-        hostname = node.hostname
+def compose_hostname_opt(hostname):
     return "hostname=%s" % hostname
 
 
-def compose_domain_opt(node):
-    # TODO: This is probably not enough!
-    domain = 'local.lan'
+def compose_domain_opt(domain):
     return "domain=%s" % domain
 
 
@@ -83,9 +63,9 @@
     return "locale=%s" % locale
 
 
-def compose_logging_opts():
+def compose_logging_opts(log_host):
     return [
-        'log_host=%s' % get_maas_facing_server_address(),
+        'log_host=%s' % log_host,
         'log_port=%d' % 514,
         'text priority=%s' % 'critical',
         ]
@@ -115,7 +95,10 @@
     ephemeral directory e.g:
     /var/lib/maas/ephemeral/precise/ephemeral/i386/20120424/info
     """
-    root = os.path.join(settings.EPHEMERAL_ROOT, release, 'ephemeral', arch)
+    config = Config.load_from_cache()
+    root = os.path.join(
+        config["boot"]["ephemeral"]["directory"],
+        release, 'ephemeral', arch)
     try:
         filename = os.path.join(get_last_directory(root), 'info')
     except OSError:
@@ -127,15 +110,16 @@
     return name
 
 
-def compose_purpose_opts(release, arch, purpose):
+def compose_purpose_opts(params):
     """Return the list of the purpose-specific kernel options."""
-    if purpose == "commissioning":
+    if params.purpose == "commissioning":
         return [
             "iscsi_target_name=%s:%s" % (
-                ISCSI_TARGET_NAME_PREFIX, get_ephemeral_name(release, arch)),
+                ISCSI_TARGET_NAME_PREFIX,
+                get_ephemeral_name(params.release, params.arch)),
             "ip=dhcp",
             "ro root=LABEL=cloudimg-rootfs",
-            "iscsi_target_ip=%s" % get_maas_facing_server_address(),
+            "iscsi_target_ip=%s" % params.fs_host,
             "iscsi_target_port=3260",
             ]
     else:
@@ -144,24 +128,21 @@
             ]
 
 
-def compose_kernel_command_line(node, arch, subarch, purpose):
+def compose_kernel_command_line_new(params):
     """Generate a line of kernel options for booting `node`.
 
-    Include these options in the PXE config file's APPEND argument.
-
-    The node may be None, in which case it will boot into enlistment.
+    :type params: `KernelParameters`.
     """
-    # XXX JeroenVermeulen 2012-08-06 bug=1013146: Stop hard-coding this.
-    release = 'precise'
-
     options = [
-        compose_initrd_opt(arch, subarch, release, purpose),
-        compose_preseed_opt(node),
-        compose_suite_opt(release),
-        compose_hostname_opt(node),
-        compose_domain_opt(node),
+        compose_initrd_opt(
+            params.arch, params.subarch,
+            params.release, params.purpose),
+        compose_preseed_opt(params.preseed_url),
+        compose_suite_opt(params.release),
+        compose_hostname_opt(params.hostname),
+        compose_domain_opt(params.domain),
         compose_locale_opt(),
         ]
-    options += compose_purpose_opts(release, arch, purpose)
-    options += compose_logging_opts()
+    options += compose_purpose_opts(params)
+    options += compose_logging_opts(params.log_host)
     return ' '.join(options)

=== modified file 'src/provisioningserver/tests/test_config.py'
--- src/provisioningserver/tests/test_config.py	2012-08-16 10:50:16 +0000
+++ src/provisioningserver/tests/test_config.py	2012-08-22 20:55:23 +0000
@@ -102,6 +102,11 @@
 
     def test_defaults(self):
         expected = {
+            'boot': {
+                'ephemeral': {
+                    'directory': '/var/lib/maas/ephemeral',
+                    },
+                },
             'broker': {
                 'host': 'localhost',
                 'port': 5673,

=== renamed file 'src/maasserver/tests/test_kernel_opts.py' => 'src/provisioningserver/tests/test_kernel_opts.py'
--- src/maasserver/tests/test_kernel_opts.py	2012-08-07 14:51:47 +0000
+++ src/provisioningserver/tests/test_kernel_opts.py	2012-08-22 20:55:23 +0000
@@ -12,30 +12,21 @@
 __metaclass__ = type
 __all__ = []
 
-import httplib
 import os
 
-from django.conf import settings
-from maasserver.api import get_boot_purpose
-from maasserver.exceptions import EphemeralImagesDirectoryNotFound
-from maasserver.kernel_opts import (
-    compose_enlistment_preseed_url,
-    compose_kernel_command_line,
+from maastesting.factory import factory
+from maastesting.matchers import ContainsAll
+from maastesting.testcase import TestCase
+from provisioningserver.kernel_opts import (
+    compose_kernel_command_line_new,
     compose_preseed_opt,
-    compose_preseed_url,
+    EphemeralImagesDirectoryNotFound,
     get_last_directory,
     ISCSI_TARGET_NAME_PREFIX,
-    )
-from maasserver.preseed import (
-    get_enlist_preseed,
-    get_preseed,
-    )
-from maasserver.server_address import get_maas_facing_server_address
-from maasserver.testing.factory import factory
-from maasserver.testing.testcase import TestCase
-from maastesting.matchers import ContainsAll
+    KernelParameters,
+    )
 from provisioningserver.pxe.tftppath import compose_image_path
-from testtools.matchers import StartsWith
+from provisioningserver.testing.config import ConfigFixture
 
 
 class TestUtilitiesKernelOpts(TestCase):
@@ -51,89 +42,62 @@
         self.assertEqual(dir1, get_last_directory(root))
 
 
+def generate_kernel_parameters():
+    return KernelParameters(**{
+            field: factory.make_name(field)
+            for field in KernelParameters._fields
+            })
+
+
 class TestKernelOpts(TestCase):
 
-    def test_compose_kernel_command_line_accepts_None_for_unknown_node(self):
-        self.assertIn(
-            'suite=precise',
-            compose_kernel_command_line(
-                None, factory.make_name('arch'), factory.make_name('subarch'),
-                purpose=factory.make_name('purpose')))
-
     def test_compose_kernel_command_line_includes_preseed_url(self):
-        node = factory.make_node()
+        params = generate_kernel_parameters()
         self.assertIn(
-            "auto url=%s" % compose_preseed_url(node),
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=factory.make_name('purpose')))
+            "auto url=%s" % params.preseed_url,
+            compose_kernel_command_line_new(params))
 
     def test_compose_kernel_command_line_includes_initrd(self):
-        node = factory.make_node()
+        params = generate_kernel_parameters()
         initrd_path = compose_image_path(
-            node.architecture, 'generic', 'precise',
-            purpose=get_boot_purpose(node))
+            params.arch, params.subarch, params.release,
+            purpose=params.purpose)
         self.assertIn(
             "initrd=%s" % initrd_path,
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=get_boot_purpose(node)))
+            compose_kernel_command_line_new(params))
 
     def test_compose_kernel_command_line_includes_suite(self):
         # At the moment, the OS release we use is hard-coded to "precise."
-        node = factory.make_node()
-        suite = "precise"
+        params = generate_kernel_parameters()
         self.assertIn(
-            "suite=%s" % suite,
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=factory.make_name('purpose')))
+            "suite=%s" % params.release,
+            compose_kernel_command_line_new(params))
 
     def test_compose_kernel_command_line_includes_hostname_and_domain(self):
-        node = factory.make_node()
-        # Cobbler seems to hard-code domain to "local.lan"; we may want
-        # to change it, and update this test.
-        domain = "local.lan"
+        params = generate_kernel_parameters()
         self.assertThat(
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=factory.make_name('purpose')),
+            compose_kernel_command_line_new(params),
             ContainsAll([
-                "hostname=%s" % node.hostname,
-                "domain=%s" % domain,
+                "hostname=%s" % params.hostname,
+                "domain=%s" % params.domain,
                 ]))
 
-    def test_compose_kernel_command_line_makes_up_hostname_for_new_node(self):
-        dummy_hostname = 'maas-enlist'
-        self.assertIn(
-            "hostname=%s" % dummy_hostname,
-            compose_kernel_command_line(
-                None, factory.make_name('arch'),
-                factory.make_name('subarch'),
-                purpose=factory.make_name('purpose')))
-
     def test_compose_kernel_command_line_includes_locale(self):
-        node = factory.make_node()
+        params = generate_kernel_parameters()
         locale = "en_US"
         self.assertIn(
             "locale=%s" % locale,
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=factory.make_name('purpose')))
+            compose_kernel_command_line_new(params))
 
     def test_compose_kernel_command_line_includes_log_settings(self):
-        node = factory.make_node()
-        log_host = factory.getRandomIPAddress()
-        self.patch(settings, 'DEFAULT_MAAS_URL', 'http://%s/' % log_host)
+        params = generate_kernel_parameters()
         # Port 514 (UDP) is syslog.
         log_port = "514"
         text_priority = "critical"
         self.assertThat(
-            compose_kernel_command_line(
-                node, node.architecture, 'generic',
-                purpose=factory.make_name('purpose')),
+            compose_kernel_command_line_new(params),
             ContainsAll([
-                "log_host=%s" % log_host,
+                "log_host=%s" % params.log_host,
                 "log_port=%s" % log_port,
                 "text priority=%s" % text_priority,
                 ]))
@@ -141,29 +105,26 @@
     def test_compose_kernel_command_line_inc_purpose_opts(self):
         # The result of compose_kernel_command_line includes the purpose
         # options for a non "commissioning" node.
+        params = generate_kernel_parameters()
         self.assertIn(
             "netcfg/choose_interface=auto",
-            compose_kernel_command_line(
-                None, factory.make_name('arch'),
-                factory.make_name('subarch'),
-                purpose=factory.make_name('purpose')))
+            compose_kernel_command_line_new(params))
 
-    def create_ephemeral_info(self, name, arch):
+    def create_ephemeral_info(self, name, arch, release):
         """Create a pseudo-real ephemeral info file."""
-        release = 'precise'
         epheneral_info = """
-            release=precise
+            release=%s
             stream=ephemeral
             label=release
             serial=20120424
-            arch=i386
+            arch=%s
             name=%s
-            """ % name
+            """ % (release, arch, name)
         ephemeral_root = self.make_dir()
-        self.patch(settings, 'EPHEMERAL_ROOT', ephemeral_root)
+        config = {"boot": {"ephemeral": {"directory": ephemeral_root}}}
+        self.useFixture(ConfigFixture(config))
         ephemeral_dir = os.path.join(
-            ephemeral_root, release, 'ephemeral', arch,
-            factory.make_name('release'))
+            ephemeral_root, release, 'ephemeral', arch, release)
         os.makedirs(ephemeral_dir)
         factory.make_file(
             ephemeral_dir, name='info', contents=epheneral_info)
@@ -172,61 +133,31 @@
         # The result of compose_kernel_command_line includes the purpose
         # options for a "commissioning" node.
         ephemeral_name = factory.make_name("ephemeral")
-        arch = factory.make_name('arch')
-        self.create_ephemeral_info(ephemeral_name, arch)
-        node = factory.make_node()
+        params = generate_kernel_parameters()
+        params = params._replace(purpose="commissioning")
+        self.create_ephemeral_info(
+            ephemeral_name, params.arch, params.release)
         self.assertThat(
-            compose_kernel_command_line(
-                node, arch,
-                factory.make_name('subarch'),
-                purpose="commissioning"),
+            compose_kernel_command_line_new(params),
             ContainsAll([
                 "iscsi_target_name=%s:%s" % (
                     ISCSI_TARGET_NAME_PREFIX, ephemeral_name),
                 "iscsi_target_port=3260",
-                "iscsi_target_ip=%s" % get_maas_facing_server_address(),
+                "iscsi_target_ip=%s" % params.fs_host,
                 ]))
 
     def test_compose_kernel_command_line_reports_error_about_missing_dir(self):
-        self.patch(
-            settings, 'EPHEMERAL_ROOT', factory.make_name('missing-dir'))
-        node = factory.make_node()
+        params = generate_kernel_parameters()
+        params = params._replace(purpose="commissioning")
+        missing_dir = factory.make_name('missing-dir')
+        config = {"boot": {"ephemeral": {"directory": missing_dir}}}
+        self.useFixture(ConfigFixture(config))
         self.assertRaises(
             EphemeralImagesDirectoryNotFound,
-            compose_kernel_command_line, node, factory.make_name('arch'),
-            factory.make_name('subarch'), purpose="commissioning")
-
-    def test_compose_enlistment_preseed_url_links_to_enlistment_preseed(self):
-        response = self.client.get(compose_enlistment_preseed_url())
-        self.assertEqual(
-            (httplib.OK, get_enlist_preseed()),
-            (response.status_code, response.content))
-
-    def test_compose_enlistment_preseed_url_returns_absolute_link(self):
-        url = 'http://%s' % factory.make_name('host')
-        self.patch(settings, 'DEFAULT_MAAS_URL', url)
-        self.assertThat(
-                compose_enlistment_preseed_url(), StartsWith(url))
-
-    def test_compose_preseed_url_links_to_preseed_for_node(self):
-        node = factory.make_node()
-        response = self.client.get(compose_preseed_url(node))
-        self.assertEqual(
-            (httplib.OK, get_preseed(node)),
-            (response.status_code, response.content))
-
-    def test_compose_preseed_url_returns_absolute_link(self):
-        self.assertThat(
-            compose_preseed_url(factory.make_node()),
-            StartsWith('http://'))
-
-    def test_compose_preseed_kernel_opt_returns_option_for_known_node(self):
-        node = factory.make_node()
-        self.assertEqual(
-            "auto url=%s" % compose_preseed_url(node),
-            compose_preseed_opt(node))
-
-    def test_compose_preseed_kernel_opt_returns_option_for_unknown_node(self):
-        self.assertEqual(
-            "auto url=%s" % compose_enlistment_preseed_url(),
-            compose_preseed_opt(None))
+            compose_kernel_command_line_new, params)
+
+    def test_compose_preseed_kernel_opt_returns_kernel_option(self):
+        dummy_preseed_url = factory.make_name("url")
+        self.assertEqual(
+            "auto url=%s" % dummy_preseed_url,
+            compose_preseed_opt(dummy_preseed_url))