~ubuntu-branches/ubuntu/raring/maas/raring-updates

« back to all changes in this revision

Viewing changes to src/maasserver/tests/test_preseed.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-07-03 17:42:37 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20120703174237-p8l0keuuznfg721k
Tags: 0.1+bzr709+dfsg-0ubuntu1
* New Upstream release
* debian/control:
  - Depends on python-celery, python-tempita, libjs-yui3-{full,min},
    libjs-raphael
* debian/maas.install:
  - Install apiclient, celeryconfig.py, maas-import-pxe-files, preseeds_v2.
  - Update to install various files from chroot, rather tha manually copy
    them from the source.
* debian/maas.links: symlink celeryconfig.py
* debian/maas.maas-celery.upstart: Add job.
* debian/rules:
  - Install celery upstart job.
  - Do not install jslibs as packages are now used.
  - Drop copying of maas_local_settings_sample.py as source now ships
    a maas_local_settings.py
* debian/patches:
  - 04-maas-http-fix.patch: Drop. Merged upstream.
  - 01-fix-database-settings.patch: Refreshed.
  - 99_enums_js.patch: Added until creation of enum.js / build process
    is fixed.
* debian/maas.postinst: Update bzr version to correctly handle upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2012 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Test `maasserver.preseed` and related bits and bobs."""
 
5
 
 
6
from __future__ import (
 
7
    absolute_import,
 
8
    print_function,
 
9
    unicode_literals,
 
10
    )
 
11
 
 
12
__metaclass__ = type
 
13
__all__ = []
 
14
 
 
15
import os
 
16
from pipes import quote
 
17
 
 
18
from django.conf import settings
 
19
from maasserver.enum import (
 
20
    NODE_STATUS,
 
21
    PRESEED_TYPE,
 
22
    )
 
23
from maasserver.models import Config
 
24
from maasserver.preseed import (
 
25
    GENERIC_FILENAME,
 
26
    get_enlist_preseed,
 
27
    get_maas_server_host,
 
28
    get_preseed,
 
29
    get_preseed_context,
 
30
    get_preseed_filenames,
 
31
    get_preseed_template,
 
32
    load_preseed_template,
 
33
    PreseedTemplate,
 
34
    render_preseed,
 
35
    split_subarch,
 
36
    TemplateNotFoundError,
 
37
    )
 
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 (
 
42
    AllMatch,
 
43
    IsInstance,
 
44
    )
 
45
 
 
46
 
 
47
class TestSplitSubArch(TestCase):
 
48
    """Tests for `split_subarch`."""
 
49
 
 
50
    def test_split_subarch_returns_list(self):
 
51
        self.assertEqual(['amd64'], split_subarch('amd64'))
 
52
 
 
53
    def test_split_subarch_splits_sub_architecture(self):
 
54
        self.assertEqual(['amd64', 'test'], split_subarch('amd64/test'))
 
55
 
 
56
 
 
57
class TestGetPreseedFilenames(TestCase):
 
58
    """Tests for `get_preseed_filenames`."""
 
59
 
 
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(
 
66
            [
 
67
                '%s_%s_%s_%s' % (prefix, node.architecture, release, hostname),
 
68
                '%s_%s_%s' % (prefix, node.architecture, release),
 
69
                '%s_%s' % (prefix, node.architecture),
 
70
                '%s' % prefix,
 
71
                'generic',
 
72
            ],
 
73
            list(get_preseed_filenames(node, prefix, release, default=True)))
 
74
 
 
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
 
84
        # sub-architecture.
 
85
        node.architecture = fake_arch
 
86
        self.assertSequenceEqual(
 
87
            [
 
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),
 
92
                '%s' % prefix,
 
93
                'generic',
 
94
            ],
 
95
            list(get_preseed_filenames(node, prefix, release, default=True)))
 
96
 
 
97
    def test_get_preseed_filenames_if_node_is_None(self):
 
98
        release = factory.getRandomString()
 
99
        prefix = factory.getRandomString()
 
100
        self.assertSequenceEqual(
 
101
            [
 
102
                '%s_%s' % (prefix, release),
 
103
                '%s' % prefix,
 
104
            ],
 
105
            list(get_preseed_filenames(None, prefix, release)))
 
106
 
 
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(
 
112
            [
 
113
                '%s_%s_%s' % (node.architecture, release, hostname),
 
114
                '%s_%s' % (node.architecture, release),
 
115
                '%s' % node.architecture,
 
116
            ],
 
117
            list(get_preseed_filenames(node, '', release)))
 
118
 
 
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(
 
128
            'generic',
 
129
            list(get_preseed_filenames(
 
130
                node, prefix, release, default=True))[-1])
 
131
 
 
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(
 
141
            prefix,
 
142
            list(get_preseed_filenames(
 
143
                node, prefix, release, default=False))[-1])
 
144
 
 
145
 
 
146
class TestConfiguration(TestCase):
 
147
    """Test for correct configuration of the preseed component."""
 
148
 
 
149
    def test_setting_defined(self):
 
150
        self.assertThat(
 
151
            settings.PRESEED_TEMPLATE_LOCATIONS,
 
152
            AllMatch(IsInstance(basestring)))
 
153
 
 
154
 
 
155
class TestGetPreseedTemplate(TestCase):
 
156
    """Tests for `get_preseed_template`."""
 
157
 
 
158
    def test_get_preseed_template_returns_None_if_no_template_locations(self):
 
159
        # get_preseed_template() returns None when no template locations are
 
160
        # defined.
 
161
        self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", [])
 
162
        self.assertEqual(
 
163
            (None, None),
 
164
            get_preseed_template(
 
165
                (factory.getRandomString(), factory.getRandomString())))
 
166
 
 
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(()))
 
171
 
 
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)
 
176
        locations = [
 
177
            os.path.dirname(template_path),
 
178
            self.make_dir(),
 
179
            ]
 
180
        self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", locations)
 
181
        self.assertEqual(
 
182
            (template_path, template_content),
 
183
            get_preseed_template([template_filename]))
 
184
 
 
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)
 
189
        locations = [
 
190
            self.make_dir(),
 
191
            os.path.dirname(template_path),
 
192
            ]
 
193
        self.patch(settings, "PRESEED_TEMPLATE_LOCATIONS", locations)
 
194
        self.assertEqual(
 
195
            (template_path, template_content),
 
196
            get_preseed_template([template_filename]))
 
197
 
 
198
 
 
199
class TestLoadPreseedTemplate(TestCase):
 
200
    """Tests for `load_preseed_template`."""
 
201
 
 
202
    def setUp(self):
 
203
        super(TestLoadPreseedTemplate, self).setUp()
 
204
        self.location = self.make_dir()
 
205
        self.patch(
 
206
            settings, "PRESEED_TEMPLATE_LOCATIONS", [self.location])
 
207
 
 
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
 
214
        if content is None:
 
215
            rendered_content = factory.getRandomString()
 
216
            content = b'{{def stuff}}%s{{enddef}}{{stuff}}' % rendered_content
 
217
        with open(path, "wb") as outf:
 
218
            outf.write(content)
 
219
        return rendered_content
 
220
 
 
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)
 
227
 
 
228
    def test_load_preseed_template_raises_if_no_template(self):
 
229
        node = factory.make_node()
 
230
        unknown_template_name = factory.getRandomString()
 
231
        self.assertRaises(
 
232
            TemplateNotFoundError, load_preseed_template, node,
 
233
            unknown_template_name)
 
234
 
 
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())
 
242
 
 
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
 
250
        # picked up.
 
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())
 
255
 
 
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())
 
272
 
 
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())
 
285
 
 
286
    def test_load_preseed_template_parent_lookup_doesnt_include_default(self):
 
287
        # The lookup for parent templates does not include the default
 
288
        # 'generic' file.
 
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)
 
299
        self.assertRaises(
 
300
            TemplateNotFoundError, template.substitute)
 
301
 
 
302
 
 
303
class TestGetMAASServerHost(TestCase):
 
304
    """Tests for `get_maas_server_host`."""
 
305
 
 
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())
 
309
 
 
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())
 
314
 
 
315
 
 
316
class TestPreseedContext(TestCase):
 
317
    """Tests for `get_preseed_context`."""
 
318
 
 
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'],
 
326
            context)
 
327
 
 
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
 
331
        # node context.
 
332
        release = factory.getRandomString()
 
333
        context = get_preseed_context(None, release)
 
334
        self.assertItemsEqual(
 
335
            ['release', 'server_host'],
 
336
            context)
 
337
 
 
338
 
 
339
class TestPreseedTemplate(TestCase):
 
340
    """Tests for class:`PreseedTemplate`."""
 
341
 
 
342
    def test_escape_shell(self):
 
343
        template = PreseedTemplate("{{var|escape.shell}}")
 
344
        var = "$ ! ()"
 
345
        observed = template.substitute(var=var)
 
346
        self.assertEqual(quote(var), observed)
 
347
 
 
348
 
 
349
class TestRenderPreseed(TestCase):
 
350
    """Tests for `render_preseed`.
 
351
 
 
352
    These tests check that the templates render (i.e. that no variable is
 
353
    missing).
 
354
    """
 
355
 
 
356
    # Create a scenario for each possible value of PRESEED_TYPE.
 
357
    scenarios = [
 
358
        (name, {'preseed': value})
 
359
        for name, value in map_enum(PRESEED_TYPE).items()]
 
360
 
 
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
 
365
        # error.
 
366
        self.assertIsInstance(preseed, str)
 
367
 
 
368
 
 
369
class TestPreseedMethods(TestCase):
 
370
    """Tests for `get_enlist_preseed` and `get_preseed`.
 
371
 
 
372
    These tests check that the preseed templates render and 'look right'.
 
373
    """
 
374
 
 
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)
 
379
 
 
380
    def test_get_enlist_preseed_returns_enlist_preseed(self):
 
381
        preseed = get_enlist_preseed()
 
382
        self.assertIn('maas-enlist-udeb', preseed)
 
383
 
 
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)