1
# Copyright 2012 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Test `maasserver.preseed` and related bits and bobs."""
6
from __future__ import (
16
from pipes import quote
18
from django.conf import settings
19
from maasserver.enum import (
23
from maasserver.models import Config
24
from maasserver.preseed import (
30
get_preseed_filenames,
32
load_preseed_template,
36
TemplateNotFoundError,
38
from maasserver.testing.factory import factory
39
from maasserver.testing.testcase import TestCase
40
from maasserver.utils import map_enum
41
from testtools.matchers import (
47
class TestSplitSubArch(TestCase):
48
"""Tests for `split_subarch`."""
50
def test_split_subarch_returns_list(self):
51
self.assertEqual(['amd64'], split_subarch('amd64'))
53
def test_split_subarch_splits_sub_architecture(self):
54
self.assertEqual(['amd64', 'test'], split_subarch('amd64/test'))
57
class TestGetPreseedFilenames(TestCase):
58
"""Tests for `get_preseed_filenames`."""
60
def test_get_preseed_filenames_returns_filenames(self):
61
hostname = factory.getRandomString()
62
prefix = factory.getRandomString()
63
release = factory.getRandomString()
64
node = factory.make_node(hostname=hostname)
65
self.assertSequenceEqual(
67
'%s_%s_%s_%s' % (prefix, node.architecture, release, hostname),
68
'%s_%s_%s' % (prefix, node.architecture, release),
69
'%s_%s' % (prefix, node.architecture),
73
list(get_preseed_filenames(node, prefix, release, default=True)))
75
def test_get_preseed_filenames_returns_filenames_with_subarch(self):
76
arch = factory.getRandomString()
77
subarch = factory.getRandomString()
78
fake_arch = '%s/%s' % (arch, subarch)
79
hostname = factory.getRandomString()
80
prefix = factory.getRandomString()
81
release = factory.getRandomString()
82
node = factory.make_node(hostname=hostname)
83
# Set an architecture of the form '%s/%s' i.e. with a
85
node.architecture = fake_arch
86
self.assertSequenceEqual(
88
'%s_%s_%s_%s_%s' % (prefix, arch, subarch, release, hostname),
89
'%s_%s_%s_%s' % (prefix, arch, subarch, release),
90
'%s_%s_%s' % (prefix, arch, subarch),
91
'%s_%s' % (prefix, arch),
95
list(get_preseed_filenames(node, prefix, release, default=True)))
97
def test_get_preseed_filenames_if_node_is_None(self):
98
release = factory.getRandomString()
99
prefix = factory.getRandomString()
100
self.assertSequenceEqual(
102
'%s_%s' % (prefix, release),
105
list(get_preseed_filenames(None, prefix, release)))
107
def test_get_preseed_filenames_supports_empty_prefix(self):
108
hostname = factory.getRandomString()
109
release = factory.getRandomString()
110
node = factory.make_node(hostname=hostname)
111
self.assertSequenceEqual(
113
'%s_%s_%s' % (node.architecture, release, hostname),
114
'%s_%s' % (node.architecture, release),
115
'%s' % node.architecture,
117
list(get_preseed_filenames(node, '', release)))
119
def test_get_preseed_filenames_returns_list_without_default(self):
120
# If default=False is passed to get_preseed_filenames, the
121
# returned list won't include the default template name as a
122
# last resort template.
123
hostname = factory.getRandomString()
124
prefix = factory.getRandomString()
125
release = factory.getRandomString()
126
node = factory.make_node(hostname=hostname)
127
self.assertSequenceEqual(
129
list(get_preseed_filenames(
130
node, prefix, release, default=True))[-1])
132
def test_get_preseed_filenames_returns_list_with_default(self):
133
# If default=True is passed to get_preseed_filenames, the
134
# returned list will include the default template name as a
135
# last resort template.
136
hostname = factory.getRandomString()
137
prefix = factory.getRandomString()
138
release = factory.getRandomString()
139
node = factory.make_node(hostname=hostname)
140
self.assertSequenceEqual(
142
list(get_preseed_filenames(
143
node, prefix, release, default=False))[-1])
146
class TestConfiguration(TestCase):
147
"""Test for correct configuration of the preseed component."""
149
def test_setting_defined(self):
151
settings.PRESEED_TEMPLATE_LOCATIONS,
152
AllMatch(IsInstance(basestring)))
155
class TestGetPreseedTemplate(TestCase):
156
"""Tests for `get_preseed_template`."""
158
def test_get_preseed_template_returns_None_if_no_template_locations(self):
159
# get_preseed_template() returns None when no template locations are
161
self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", [])
164
get_preseed_template(
165
(factory.getRandomString(), factory.getRandomString())))
167
def test_get_preseed_template_returns_None_when_no_filenames(self):
168
# get_preseed_template() returns None when no filenames are passed in.
169
self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", [self.make_dir()])
170
self.assertEqual((None, None), get_preseed_template(()))
172
def test_get_preseed_template_find_template_in_first_location(self):
173
template_content = factory.getRandomString()
174
template_path = self.make_file(contents=template_content)
175
template_filename = os.path.basename(template_path)
177
os.path.dirname(template_path),
180
self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", locations)
182
(template_path, template_content),
183
get_preseed_template([template_filename]))
185
def test_get_preseed_template_find_template_in_last_location(self):
186
template_content = factory.getRandomString()
187
template_path = self.make_file(contents=template_content)
188
template_filename = os.path.basename(template_path)
191
os.path.dirname(template_path),
193
self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", locations)
195
(template_path, template_content),
196
get_preseed_template([template_filename]))
199
class TestLoadPreseedTemplate(TestCase):
200
"""Tests for `load_preseed_template`."""
203
super(TestLoadPreseedTemplate, self).setUp()
204
self.location = self.make_dir()
206
settings, "PRESEED_TEMPLATE_LOCATIONS", [self.location])
208
def create_template(self, location, name, content=None):
209
# Create a tempita template in the given `self.location` with the
210
# given `name`. If content is not provided, a random content
211
# will be put inside the template.
212
path = os.path.join(self.location, name)
213
rendered_content = None
215
rendered_content = factory.getRandomString()
216
content = b'{{def stuff}}%s{{enddef}}{{stuff}}' % rendered_content
217
with open(path, "wb") as outf:
219
return rendered_content
221
def test_load_preseed_template_returns_PreseedTemplate(self):
222
name = factory.getRandomString()
223
self.create_template(self.location, name)
224
node = factory.make_node()
225
template = load_preseed_template(node, name)
226
self.assertIsInstance(template, PreseedTemplate)
228
def test_load_preseed_template_raises_if_no_template(self):
229
node = factory.make_node()
230
unknown_template_name = factory.getRandomString()
232
TemplateNotFoundError, load_preseed_template, node,
233
unknown_template_name)
235
def test_load_preseed_template_generic_lookup(self):
236
# The template lookup method ends up picking up a template named
237
# 'generic' if no more specific template exist.
238
content = self.create_template(self.location, GENERIC_FILENAME)
239
node = factory.make_node(hostname=factory.getRandomString())
240
template = load_preseed_template(node, factory.getRandomString())
241
self.assertEqual(content, template.substitute())
243
def test_load_preseed_template_prefix_lookup(self):
244
# 2nd last in the hierarchy is a template named 'prefix'.
245
prefix = factory.getRandomString()
246
# Create the generic template. This one will be ignored due to the
247
# presence of a more specific template.
248
self.create_template(self.location, GENERIC_FILENAME)
249
# Create the 'prefix' template. This is the one which will be
251
content = self.create_template(self.location, prefix)
252
node = factory.make_node(hostname=factory.getRandomString())
253
template = load_preseed_template(node, prefix)
254
self.assertEqual(content, template.substitute())
256
def test_load_preseed_template_node_specific_lookup(self):
257
# At the top of the lookup hierarchy is a template specific to this
258
# node. It will be used first if it's present.
259
prefix = factory.getRandomString()
260
release = factory.getRandomString()
261
# Create the generic and 'prefix' templates. They will be ignored
262
# due to the presence of a more specific template.
263
self.create_template(self.location, GENERIC_FILENAME)
264
self.create_template(self.location, prefix)
265
node = factory.make_node(hostname=factory.getRandomString())
266
node_template_name = "%s_%s_%s_%s" % (
267
prefix, node.architecture, release, node.hostname)
268
# Create the node-specific template.
269
content = self.create_template(self.location, node_template_name)
270
template = load_preseed_template(node, prefix, release)
271
self.assertEqual(content, template.substitute())
273
def test_load_preseed_template_with_inherits(self):
274
# A preseed file can "inherit" from another file.
275
prefix = factory.getRandomString()
276
# Create preseed template.
277
master_template_name = factory.getRandomString()
278
preseed_content = '{{inherit "%s"}}' % master_template_name
279
self.create_template(self.location, prefix, preseed_content)
280
master_content = self.create_template(
281
self.location, master_template_name)
282
node = factory.make_node()
283
template = load_preseed_template(node, prefix)
284
self.assertEqual(master_content, template.substitute())
286
def test_load_preseed_template_parent_lookup_doesnt_include_default(self):
287
# The lookup for parent templates does not include the default
289
prefix = factory.getRandomString()
290
# Create 'generic' template. It won't be used because the
291
# lookup for parent templates does not use the 'generic' template.
292
self.create_template(self.location, GENERIC_FILENAME)
293
unknown_master_template_name = factory.getRandomString()
294
# Create preseed template.
295
preseed_content = '{{inherit "%s"}}' % unknown_master_template_name
296
self.create_template(self.location, prefix, preseed_content)
297
node = factory.make_node()
298
template = load_preseed_template(node, prefix)
300
TemplateNotFoundError, template.substitute)
303
class TestGetMAASServerHost(TestCase):
304
"""Tests for `get_maas_server_host`."""
306
def test_get_maas_server_host_returns_host(self):
307
Config.objects.set_config('maas_url', 'http://example.com/path')
308
self.assertEqual('example.com', get_maas_server_host())
310
def test_get_maas_server_host_strips_out_port(self):
311
Config.objects.set_config(
312
'maas_url', 'http://example.com:%d' % factory.getRandomPort())
313
self.assertEqual('example.com', get_maas_server_host())
316
class TestPreseedContext(TestCase):
317
"""Tests for `get_preseed_context`."""
319
def test_get_preseed_context_contains_keys(self):
320
node = factory.make_node()
321
release = factory.getRandomString()
322
context = get_preseed_context(node, release)
323
self.assertItemsEqual(
324
['node', 'release', 'server_host', 'preseed_data',
325
'node_disable_pxe_url', 'node_disable_pxe_data'],
328
def test_get_preseed_context_if_node_None(self):
329
# If the provided Node is None (when're in the context of an
330
# enlistment preseed) the returned context does not include the
332
release = factory.getRandomString()
333
context = get_preseed_context(None, release)
334
self.assertItemsEqual(
335
['release', 'server_host'],
339
class TestPreseedTemplate(TestCase):
340
"""Tests for class:`PreseedTemplate`."""
342
def test_escape_shell(self):
343
template = PreseedTemplate("{{var|escape.shell}}")
345
observed = template.substitute(var=var)
346
self.assertEqual(quote(var), observed)
349
class TestRenderPreseed(TestCase):
350
"""Tests for `render_preseed`.
352
These tests check that the templates render (i.e. that no variable is
356
# Create a scenario for each possible value of PRESEED_TYPE.
358
(name, {'preseed': value})
359
for name, value in map_enum(PRESEED_TYPE).items()]
361
def test_render_preseed(self):
362
node = factory.make_node()
363
preseed = render_preseed(node, self.preseed, "precise")
364
# The test really is that the preseed is rendered without an
366
self.assertIsInstance(preseed, str)
369
class TestPreseedMethods(TestCase):
370
"""Tests for `get_enlist_preseed` and `get_preseed`.
372
These tests check that the preseed templates render and 'look right'.
375
def test_get_preseed_returns_default_preseed(self):
376
node = factory.make_node()
377
preseed = get_preseed(node)
378
self.assertIn('preseed/late_command', preseed)
380
def test_get_enlist_preseed_returns_enlist_preseed(self):
381
preseed = get_enlist_preseed()
382
self.assertIn('maas-enlist-udeb', preseed)
384
def test_get_preseed_returns_commissioning_preseed(self):
385
node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
386
preseed = get_preseed(node)
387
self.assertIn('cloud-init', preseed)