1
# Copyright 2014 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for `provisioningserver.boot.uefi`."""
6
from __future__ import (
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 (
26
from provisioningserver.tests.test_kernel_opts import make_kernel_parameters
27
from testtools.matchers import (
35
def compose_config_path(mac=None, arch=None, subarch=None):
36
"""Compose the TFTP path for a UEFI configuration file.
38
The path returned is relative to the TFTP root, as it would be
39
identified by clients on the network.
41
:param mac: A MAC address, in IEEE 802 colon-separated form,
42
corresponding to the machine for which this configuration is
44
:param arch: Architecture for the booting machine, for UEFI this is
46
:param subarch: Sub-architecture type, this is normally always generic.
47
:return: Path for the corresponding PXE config file as exposed over
51
return "grub/grub.cfg-{mac}".format(mac=mac)
55
return "grub/grub.cfg-{arch}-{subarch}".format(
56
arch=arch, subarch=subarch)
57
return "grub/grub.cfg"
60
class TestRenderUEFIConfig(MAASTestCase):
61
"""Tests for `provisioningserver.boot.uefi.UEFIBootMethod`."""
63
def test_render(self):
64
# Given the right configuration options, the UEFI configuration is
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)
82
r'.*^\s+linux %s/di-kernel .+?$' % re.escape(image_dir),
83
re.MULTILINE | re.DOTALL),
85
r'.*^\s+initrd %s/di-initrd$' % re.escape(image_dir),
86
re.MULTILINE | re.DOTALL)))
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()
92
"kernel_params": make_kernel_parameters(purpose="install"),
94
# Capture the output before sprinking in some random options.
95
output_before = method.render_config(**options)
96
# Sprinkle some magic in.
98
(factory.make_name("name"), factory.make_name("value"))
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)
105
def test_render_config_with_local_purpose(self):
106
# If purpose is "local", the config.localboot.template should be
108
method = UEFIBootMethod()
110
"kernel_params": make_kernel_parameters(purpose="local"),
112
output = method.render_config(**options)
113
self.assertIn("configfile /efi/ubuntu/grub.cfg", output)
116
class TestUEFIBootMethodRegex(MAASTestCase):
117
"""Tests `provisioningserver.boot.uefi.UEFIBootMethod.re_config_file`."""
120
def get_example_path_and_components():
121
"""Return a plausible UEFI path and its components.
123
The path is intended to match `re_config_file`, and
124
the components are the expected groups from a match.
126
components = {"mac": factory.getRandomMACAddress(":"),
129
config_path = compose_config_path(components["mac"])
130
return config_path, components
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())
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())
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())
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},
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},
180
def test_re_config_file_does_not_match_default_grub_config_file(self):
181
self.assertIsNone(re_config_file.match('grub/grub.cfg'))
183
def test_re_config_file_with_default(self):
184
match = re_config_file.match('grub/grub.cfg-default')
185
self.assertIsNotNone(match)
187
{'mac': None, 'arch': None, 'subarch': None},
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)
195
{'mac': None, 'arch': arch, 'subarch': None},
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)
205
{'mac': None, 'arch': arch, 'subarch': subarch},