~lutostag/ubuntu/trusty/maas/1.5.2+packagefix

« back to all changes in this revision

Viewing changes to src/provisioningserver/boot/tests/test_uefi.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2014-03-28 10:43:53 UTC
  • mto: This revision was merged to the branch mainline in revision 57.
  • Revision ID: package-import@ubuntu.com-20140328104353-ekpolg0pm5xnvq2s
Tags: upstream-1.5+bzr2204
ImportĀ upstreamĀ versionĀ 1.5+bzr2204

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Tests for `provisioningserver.boot.uefi`."""
 
5
 
 
6
from __future__ import (
 
7
    absolute_import,
 
8
    print_function,
 
9
    unicode_literals,
 
10
    )
 
11
 
 
12
str = None
 
13
 
 
14
__metaclass__ = type
 
15
__all__ = []
 
16
 
 
17
import re
 
18
 
 
19
from maastesting.factory import factory
 
20
from maastesting.testcase import MAASTestCase
 
21
from provisioningserver.boot.tftppath import compose_image_path
 
22
from provisioningserver.boot.uefi import (
 
23
    re_config_file,
 
24
    UEFIBootMethod,
 
25
    )
 
26
from provisioningserver.tests.test_kernel_opts import make_kernel_parameters
 
27
from testtools.matchers import (
 
28
    IsInstance,
 
29
    MatchesAll,
 
30
    MatchesRegex,
 
31
    StartsWith,
 
32
    )
 
33
 
 
34
 
 
35
def compose_config_path(mac=None, arch=None, subarch=None):
 
36
    """Compose the TFTP path for a UEFI configuration file.
 
37
 
 
38
    The path returned is relative to the TFTP root, as it would be
 
39
    identified by clients on the network.
 
40
 
 
41
    :param mac: A MAC address, in IEEE 802 colon-separated form,
 
42
        corresponding to the machine for which this configuration is
 
43
        relevant.
 
44
    :param arch: Architecture for the booting machine, for UEFI this is
 
45
        always amd64.
 
46
    :param subarch: Sub-architecture type, this is normally always generic.
 
47
    :return: Path for the corresponding PXE config file as exposed over
 
48
        TFTP.
 
49
    """
 
50
    if mac is not None:
 
51
        return "grub/grub.cfg-{mac}".format(mac=mac)
 
52
    if arch is not None:
 
53
        if subarch is None:
 
54
            subarch = "generic"
 
55
        return "grub/grub.cfg-{arch}-{subarch}".format(
 
56
            arch=arch, subarch=subarch)
 
57
    return "grub/grub.cfg"
 
58
 
 
59
 
 
60
class TestRenderUEFIConfig(MAASTestCase):
 
61
    """Tests for `provisioningserver.boot.uefi.UEFIBootMethod`."""
 
62
 
 
63
    def test_render(self):
 
64
        # Given the right configuration options, the UEFI configuration is
 
65
        # correctly rendered.
 
66
        method = UEFIBootMethod()
 
67
        params = make_kernel_parameters(purpose="install")
 
68
        output = method.render_config(kernel_params=params)
 
69
        # The output is always a Unicode string.
 
70
        self.assertThat(output, IsInstance(unicode))
 
71
        # The template has rendered without error. UEFI configurations
 
72
        # typically start with a DEFAULT line.
 
73
        self.assertThat(output, StartsWith("set default=\"0\""))
 
74
        # The UEFI parameters are all set according to the options.
 
75
        image_dir = compose_image_path(
 
76
            arch=params.arch, subarch=params.subarch,
 
77
            release=params.release, label=params.label)
 
78
 
 
79
        self.assertThat(
 
80
            output, MatchesAll(
 
81
                MatchesRegex(
 
82
                    r'.*^\s+linux  %s/di-kernel .+?$' % re.escape(image_dir),
 
83
                    re.MULTILINE | re.DOTALL),
 
84
                MatchesRegex(
 
85
                    r'.*^\s+initrd %s/di-initrd$' % re.escape(image_dir),
 
86
                    re.MULTILINE | re.DOTALL)))
 
87
 
 
88
    def test_render_with_extra_arguments_does_not_affect_output(self):
 
89
        # render_config() allows any keyword arguments as a safety valve.
 
90
        method = UEFIBootMethod()
 
91
        options = {
 
92
            "kernel_params": make_kernel_parameters(purpose="install"),
 
93
        }
 
94
        # Capture the output before sprinking in some random options.
 
95
        output_before = method.render_config(**options)
 
96
        # Sprinkle some magic in.
 
97
        options.update(
 
98
            (factory.make_name("name"), factory.make_name("value"))
 
99
            for _ in range(10))
 
100
        # Capture the output after sprinking in some random options.
 
101
        output_after = method.render_config(**options)
 
102
        # The generated template is the same.
 
103
        self.assertEqual(output_before, output_after)
 
104
 
 
105
    def test_render_config_with_local_purpose(self):
 
106
        # If purpose is "local", the config.localboot.template should be
 
107
        # used.
 
108
        method = UEFIBootMethod()
 
109
        options = {
 
110
            "kernel_params": make_kernel_parameters(purpose="local"),
 
111
            }
 
112
        output = method.render_config(**options)
 
113
        self.assertIn("configfile /efi/ubuntu/grub.cfg", output)
 
114
 
 
115
 
 
116
class TestUEFIBootMethodRegex(MAASTestCase):
 
117
    """Tests `provisioningserver.boot.uefi.UEFIBootMethod.re_config_file`."""
 
118
 
 
119
    @staticmethod
 
120
    def get_example_path_and_components():
 
121
        """Return a plausible UEFI path and its components.
 
122
 
 
123
        The path is intended to match `re_config_file`, and
 
124
        the components are the expected groups from a match.
 
125
        """
 
126
        components = {"mac": factory.getRandomMACAddress(":"),
 
127
                      "arch": None,
 
128
                      "subarch": None}
 
129
        config_path = compose_config_path(components["mac"])
 
130
        return config_path, components
 
131
 
 
132
    def test_re_config_file_is_compatible_with_cfg_path_generator(self):
 
133
        # The regular expression for extracting components of the file path is
 
134
        # compatible with the PXE config path generator.
 
135
        for iteration in range(10):
 
136
            config_path, args = self.get_example_path_and_components()
 
137
            match = re_config_file.match(config_path)
 
138
            self.assertIsNotNone(match, config_path)
 
139
            self.assertEqual(args, match.groupdict())
 
140
 
 
141
    def test_re_config_file_with_leading_slash(self):
 
142
        # The regular expression for extracting components of the file path
 
143
        # doesn't care if there's a leading forward slash; the TFTP server is
 
144
        # easy on this point, so it makes sense to be also.
 
145
        config_path, args = self.get_example_path_and_components()
 
146
        # Ensure there's a leading slash.
 
147
        config_path = "/" + config_path.lstrip("/")
 
148
        match = re_config_file.match(config_path)
 
149
        self.assertIsNotNone(match, config_path)
 
150
        self.assertEqual(args, match.groupdict())
 
151
 
 
152
    def test_re_config_file_without_leading_slash(self):
 
153
        # The regular expression for extracting components of the file path
 
154
        # doesn't care if there's no leading forward slash; the TFTP server is
 
155
        # easy on this point, so it makes sense to be also.
 
156
        config_path, args = self.get_example_path_and_components()
 
157
        # Ensure there's no leading slash.
 
158
        config_path = config_path.lstrip("/")
 
159
        match = re_config_file.match(config_path)
 
160
        self.assertIsNotNone(match, config_path)
 
161
        self.assertEqual(args, match.groupdict())
 
162
 
 
163
    def test_re_config_file_matches_classic_grub_cfg(self):
 
164
        # The default config path is simply "grub.cfg-{mac}" (without
 
165
        # leading slash).  The regex matches this.
 
166
        mac = 'aa:bb:cc:dd:ee:ff'
 
167
        match = re_config_file.match('grub/grub.cfg-%s' % mac)
 
168
        self.assertIsNotNone(match)
 
169
        self.assertEqual({'mac': mac, 'arch': None, 'subarch': None},
 
170
                         match.groupdict())
 
171
 
 
172
    def test_re_config_file_matches_grub_cfg_with_leading_slash(self):
 
173
        mac = 'aa:bb:cc:dd:ee:ff'
 
174
        match = re_config_file.match(
 
175
            '/grub/grub.cfg-%s' % mac)
 
176
        self.assertIsNotNone(match)
 
177
        self.assertEqual({'mac': mac, 'arch': None, 'subarch': None},
 
178
                         match.groupdict())
 
179
 
 
180
    def test_re_config_file_does_not_match_default_grub_config_file(self):
 
181
        self.assertIsNone(re_config_file.match('grub/grub.cfg'))
 
182
 
 
183
    def test_re_config_file_with_default(self):
 
184
        match = re_config_file.match('grub/grub.cfg-default')
 
185
        self.assertIsNotNone(match)
 
186
        self.assertEqual(
 
187
            {'mac': None, 'arch': None, 'subarch': None},
 
188
            match.groupdict())
 
189
 
 
190
    def test_re_config_file_with_default_arch(self):
 
191
        arch = factory.make_name('arch', sep='')
 
192
        match = re_config_file.match('grub/grub.cfg-default-%s' % arch)
 
193
        self.assertIsNotNone(match)
 
194
        self.assertEqual(
 
195
            {'mac': None, 'arch': arch, 'subarch': None},
 
196
            match.groupdict())
 
197
 
 
198
    def test_re_config_file_with_default_arch_and_subarch(self):
 
199
        arch = factory.make_name('arch', sep='')
 
200
        subarch = factory.make_name('subarch', sep='')
 
201
        match = re_config_file.match(
 
202
            'grub/grub.cfg-default-%s-%s' % (arch, subarch))
 
203
        self.assertIsNotNone(match)
 
204
        self.assertEqual(
 
205
            {'mac': None, 'arch': arch, 'subarch': subarch},
 
206
            match.groupdict())