~ubuntu-branches/ubuntu/utopic/maas/utopic-security

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez, Jeroen Vermeulen, Andres Rodriguez, Jason Hobbs, Raphaël Badin, Louis Bouchard, Gavin Panella
  • Date: 2014-08-21 19:36:30 UTC
  • mfrom: (1.3.1)
  • Revision ID: package-import@ubuntu.com-20140821193630-kertpu5hd8yyss8h
Tags: 1.7.0~beta7+bzr3266-0ubuntu1
* New Upstream Snapshot, Beta 7 bzr3266

[ Jeroen Vermeulen ]
* debian/extras/99-maas-sudoers
  debian/maas-dhcp.postinst
  debian/rules
  - Add second DHCP server instance for IPv6.
* debian/maas-region-controller-min.install
  debian/maas-region-controller-min.lintian-overrides
  - Install deployment user-data: maas_configure_interfaces.py script.
* debian/maas-cluster-controller.links
  debian/maas-cluster-controller.install
  debian/maas-cluster-controller.postinst
  - Reflect Celery removal changes made in trunk r3067.
  - Don't install celeryconfig_cluster.py any longer. 
  - Don't install maas_local_celeryconfig_cluster.py any longer.
  - Don't symlink maas_local_celeryconfig_cluster.py from /etc to /usr.
  - Don't insert UUID into maas_local_celeryconfig_cluster.py.

[ Andres Rodriguez ]
* debian/maas-region-controller-min.postrm: Cleanup lefover files.
* debian/maas-dhcp.postrm: Clean leftover configs.
* Provide new maas-proxy package that replaces the usage of
  squid-deb-proxy:
  - debian/control: New maas-proxy package that replaces the usage
    of squid-deb-proxy; Drop depends on squid-deb-proxy.
  - Add upstrart job.
  - Ensure squid3 is stopped as maas-proxy uses a caching proxy.
* Remove Celery references to cluster controller:
  - Rename upstart job from maas-pserv to maas-cluster; rename
    maas-cluster-celery to maas-cluster-register. Ensure services
    are stopped on upgrade.
  - debian/maintscript: Cleanup config files.
  - Remove all references to the MAAS celery daemon and config
    files as we don't use it like that anymore
* Move some entries in debian/maintscript to
  debian/maas-cluster-controller.maintscript
* Remove usage of txlongpoll and rabbitmq-server. Handle upgrades
  to ensure these are removed correctly.

[ Jason Hobbs ]
* debian/maas-region-controller-min.install: Install
  maas-generate-winrm-cert script.

[ Raphaël Badin ]
* debian/extras/maas-region-admin: Bypass django-admin as it prints
  spurious messages to stdout (LP: #1365130).

[Louis Bouchard]
* debian/maas-cluster-controller.postinst:
  - Exclude /var/log/maas/rsyslog when changing ownership
    (LP: #1346703)

[Gavin Panella]
* debian/maas-cluster-controller.maas-clusterd.upstart:
  - Don't start-up the cluster controller unless a shared-secret has
    been installed.
* debian/maas-cluster-controller.maas-cluster-register.upstart: Drop.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2012, 2013 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2012-2014 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
"""Tests for `maasserver.populate_tags`."""
14
14
__metaclass__ = type
15
15
__all__ = []
16
16
 
 
17
from itertools import izip
 
18
 
 
19
from fixtures import FakeLogger
17
20
from maasserver import populate_tags as populate_tags_module
 
21
from maasserver.enum import NODEGROUP_STATUS
18
22
from maasserver.models import Tag
19
23
from maasserver.populate_tags import (
 
24
    _do_populate_tags,
 
25
    _get_clients_for_populating_tags,
20
26
    populate_tags,
21
27
    populate_tags_for_single_node,
22
 
    tag_nsmap,
 
28
    )
 
29
from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
 
30
from maasserver.testing.eventloop import (
 
31
    RegionEventLoopFixture,
 
32
    RunningEventLoopFixture,
23
33
    )
24
34
from maasserver.testing.factory import factory
25
35
from maasserver.testing.testcase import MAASServerTestCase
 
36
from maastesting.matchers import MockCalledOnceWith
26
37
from metadataserver.models import commissioningscript
27
 
import mock
 
38
from mock import (
 
39
    ANY,
 
40
    create_autospec,
 
41
    sentinel,
 
42
    )
 
43
from provisioningserver.rpc.cluster import EvaluateTag
 
44
from provisioningserver.rpc.common import Client
 
45
from provisioningserver.rpc.testing import (
 
46
    always_fail_with,
 
47
    always_succeed_with,
 
48
    )
 
49
from provisioningserver.utils.twisted import asynchronous
 
50
from testtools.deferredruntest import extract_result
 
51
from testtools.monkey import MonkeyPatcher
 
52
from twisted.internet import defer
 
53
 
 
54
 
 
55
def make_accepted_NodeGroup():
 
56
    return factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED)
 
57
 
 
58
 
 
59
def make_Tag_without_populating():
 
60
    # Create a tag but prevent evaluation when saving.
 
61
    dont_populate = MonkeyPatcher((Tag, "populate_nodes", lambda self: None))
 
62
    return dont_populate.run_with_patches(factory.make_Tag)
 
63
 
 
64
 
 
65
class TestGetClientsForPopulatingTags(MAASServerTestCase):
 
66
 
 
67
    def test__returns_no_clients_when_there_are_no_clusters(self):
 
68
        tag_name = factory.make_name("tag")
 
69
        clients = _get_clients_for_populating_tags([], tag_name)
 
70
        self.assertEqual([], clients)
 
71
 
 
72
    def patch_getClientFor(self):
 
73
        return self.patch_autospec(populate_tags_module, "getClientFor")
 
74
 
 
75
    def test__returns_no_clients_when_there_is_an_error(self):
 
76
        nodegroup_with_connection = make_accepted_NodeGroup()
 
77
        nodegroup_without_connection = make_accepted_NodeGroup()
 
78
 
 
79
        def getClientFor(uuid, timeout):
 
80
            if uuid == nodegroup_with_connection.uuid:
 
81
                return defer.succeed(sentinel.client)
 
82
            else:
 
83
                return defer.fail(ZeroDivisionError())
 
84
        self.patch_getClientFor().side_effect = getClientFor
 
85
 
 
86
        tag_name = factory.make_name("tag")
 
87
        clusters = [
 
88
            (nodegroup_with_connection.uuid,
 
89
             nodegroup_with_connection.cluster_name),
 
90
            (nodegroup_without_connection.uuid,
 
91
             nodegroup_without_connection.cluster_name),
 
92
        ]
 
93
        clients = _get_clients_for_populating_tags(clusters, tag_name)
 
94
        self.assertEqual([sentinel.client], clients)
 
95
 
 
96
    def test__logs_errors_obtaining_clients(self):
 
97
        getClientFor = self.patch_getClientFor()
 
98
        getClientFor.side_effect = always_fail_with(
 
99
            ZeroDivisionError("an error message one would surmise"))
 
100
        nodegroup = make_accepted_NodeGroup()
 
101
        tag_name = factory.make_name("tag")
 
102
        clusters = [(nodegroup.uuid, nodegroup.cluster_name)]
 
103
        with FakeLogger("maas") as log:
 
104
            _get_clients_for_populating_tags(clusters, tag_name)
 
105
        self.assertDocTestMatches(
 
106
            "Cannot evaluate tag ... on cluster ... (...): ... surmise",
 
107
            log.output)
 
108
 
 
109
    def test__waits_for_clients_for_30_seconds_by_default(self):
 
110
        getClientFor = self.patch_getClientFor()
 
111
        getClientFor.side_effect = always_succeed_with(sentinel.client)
 
112
        nodegroup = make_accepted_NodeGroup()
 
113
        tag_name = factory.make_name("tag")
 
114
        clusters = [(nodegroup.uuid, nodegroup.cluster_name)]
 
115
        clients = _get_clients_for_populating_tags(clusters, tag_name)
 
116
        self.assertEqual([sentinel.client], clients)
 
117
        self.assertThat(
 
118
            getClientFor, MockCalledOnceWith(
 
119
                nodegroup.uuid, timeout=30))
 
120
 
 
121
    def test__obtains_multiple_clients(self):
 
122
        getClientFor = self.patch_getClientFor()
 
123
        # Return a 2-tuple as a stand-in for a real client.
 
124
        getClientFor.side_effect = lambda uuid, timeout: (
 
125
            defer.succeed((sentinel.client, uuid)))
 
126
        nodegroups = [make_accepted_NodeGroup() for _ in xrange(3)]
 
127
        tag_name = factory.make_name("tag")
 
128
        clusters = [(ng.uuid, ng.cluster_name) for ng in nodegroups]
 
129
        clients = _get_clients_for_populating_tags(clusters, tag_name)
 
130
        self.assertItemsEqual(
 
131
            [(sentinel.client, nodegroup.uuid) for nodegroup in nodegroups],
 
132
            clients)
 
133
 
 
134
 
 
135
class TestDoPopulateTags(MAASServerTestCase):
 
136
 
 
137
    def patch_clients(self, nodegroups):
 
138
        clients = [create_autospec(Client, instance=True) for _ in nodegroups]
 
139
        for nodegroup, client in izip(nodegroups, clients):
 
140
            client.side_effect = always_succeed_with(None)
 
141
            client.ident = nodegroup.uuid
 
142
 
 
143
        _get_clients = self.patch_autospec(
 
144
            populate_tags_module, "_get_clients_for_populating_tags")
 
145
        _get_clients.return_value = defer.succeed(clients)
 
146
 
 
147
        return clients
 
148
 
 
149
    def test__makes_calls_to_each_client_given(self):
 
150
        nodegroups = [make_accepted_NodeGroup() for _ in xrange(3)]
 
151
        clients = self.patch_clients(nodegroups)
 
152
 
 
153
        tag_name = factory.make_name("tag")
 
154
        tag_definition = factory.make_name("definition")
 
155
        tag_nsmap_prefix = factory.make_name("prefix")
 
156
        tag_nsmap_uri = factory.make_name("uri")
 
157
        tag_nsmap = {tag_nsmap_prefix: tag_nsmap_uri}
 
158
 
 
159
        clusters = list(
 
160
            (ng.uuid, ng.cluster_name, ng.api_credentials)
 
161
            for ng in nodegroups)
 
162
 
 
163
        [d] = _do_populate_tags(
 
164
            clusters, tag_name, tag_definition, tag_nsmap)
 
165
 
 
166
        self.assertIsNone(extract_result(d))
 
167
 
 
168
        for nodegroup, client in izip(nodegroups, clients):
 
169
            self.expectThat(client, MockCalledOnceWith(
 
170
                EvaluateTag, tag_name=tag_name, tag_definition=tag_definition,
 
171
                tag_nsmap=[{"prefix": tag_nsmap_prefix, "uri": tag_nsmap_uri}],
 
172
                credentials=nodegroup.api_credentials))
 
173
 
 
174
    def test__logs_successes(self):
 
175
        nodegroups = [make_accepted_NodeGroup()]
 
176
        self.patch_clients(nodegroups)
 
177
 
 
178
        tag_name = factory.make_name("tag")
 
179
        tag_definition = factory.make_name("definition")
 
180
        tag_nsmap = {}
 
181
 
 
182
        clusters = list(
 
183
            (ng.uuid, ng.cluster_name, ng.api_credentials)
 
184
            for ng in nodegroups)
 
185
 
 
186
        with FakeLogger("maas") as log:
 
187
            [d] = _do_populate_tags(
 
188
                clusters, tag_name, tag_definition, tag_nsmap)
 
189
            self.assertIsNone(extract_result(d))
 
190
 
 
191
        self.assertDocTestMatches(
 
192
            "Tag tag-... (definition-...) evaluated on cluster ... (...)",
 
193
            log.output)
 
194
 
 
195
    def test__logs_failures(self):
 
196
        nodegroups = [make_accepted_NodeGroup()]
 
197
        [client] = self.patch_clients(nodegroups)
 
198
        client.side_effect = always_fail_with(
 
199
            ZeroDivisionError("splendid day for a spot of cricket"))
 
200
 
 
201
        tag_name = factory.make_name("tag")
 
202
        tag_definition = factory.make_name("definition")
 
203
        tag_nsmap = {}
 
204
 
 
205
        clusters = list(
 
206
            (ng.uuid, ng.cluster_name, ng.api_credentials)
 
207
            for ng in nodegroups)
 
208
 
 
209
        with FakeLogger("maas") as log:
 
210
            [d] = _do_populate_tags(
 
211
                clusters, tag_name, tag_definition, tag_nsmap)
 
212
            self.assertIsNone(extract_result(d))
 
213
 
 
214
        self.assertDocTestMatches(
 
215
            "Tag tag-... (definition-...) could not be evaluated ... (...): "
 
216
            "splendid day for a spot of cricket", log.output)
28
217
 
29
218
 
30
219
class TestPopulateTags(MAASServerTestCase):
31
220
 
32
 
    def test_populate_tags_task_routed_to_nodegroup_worker(self):
33
 
        nodegroup = factory.make_node_group()
34
 
        tag = factory.make_tag()
35
 
        task = self.patch(populate_tags_module, 'update_node_tags')
36
 
        populate_tags(tag)
37
 
        args, kwargs = task.apply_async.call_args
38
 
        self.assertEqual(nodegroup.work_queue, kwargs['queue'])
39
 
 
40
 
    def test_populate_tags_task_routed_to_all_nodegroup_workers(self):
41
 
        nodegroups = [factory.make_node_group() for i in range(5)]
42
 
        tag = factory.make_tag()
43
 
        refresh = self.patch(populate_tags_module, 'refresh_worker')
44
 
        task = self.patch(populate_tags_module, 'update_node_tags')
45
 
        populate_tags(tag)
46
 
        refresh_calls = [mock.call(nodegroup) for nodegroup in nodegroups]
47
 
        refresh.assert_has_calls(refresh_calls, any_order=True)
48
 
        task_calls = [
49
 
            mock.call(
50
 
                queue=nodegroup.work_queue,
51
 
                kwargs={
52
 
                    'tag_name': tag.name,
53
 
                    'tag_definition': tag.definition,
54
 
                    'tag_nsmap': tag_nsmap,
55
 
                },
56
 
            )
57
 
            for nodegroup in nodegroups
58
 
        ]
59
 
        task.apply_async.assert_has_calls(task_calls, any_order=True)
 
221
    def patch_do_populate_tags(self):
 
222
        do_populate_tags = self.patch_autospec(
 
223
            populate_tags_module, "_do_populate_tags")
 
224
        do_populate_tags.return_value = [sentinel.d]
 
225
        return do_populate_tags
 
226
 
 
227
    def test__calls_do_populate_tags_with_no_clusters(self):
 
228
        do_populate_tags = self.patch_do_populate_tags()
 
229
        tag = make_Tag_without_populating()
 
230
        populate_tags(tag)
 
231
        self.assertThat(do_populate_tags, MockCalledOnceWith(
 
232
            (), tag.name, tag.definition, populate_tags_module.tag_nsmap))
 
233
 
 
234
    def test__calls_do_populate_tags_with_clusters(self):
 
235
        do_populate_tags = self.patch_do_populate_tags()
 
236
        nodegroups = [make_accepted_NodeGroup() for _ in xrange(3)]
 
237
        tag = make_Tag_without_populating()
 
238
        populate_tags(tag)
 
239
        clusters_expected = tuple(
 
240
            (ng.uuid, ng.cluster_name, ng.api_credentials)
 
241
            for ng in nodegroups)
 
242
        self.assertThat(do_populate_tags, MockCalledOnceWith(
 
243
            clusters_expected, tag.name, tag.definition,
 
244
            populate_tags_module.tag_nsmap))
 
245
 
 
246
 
 
247
class TestPopulateTagsEndToNearlyEnd(MAASServerTestCase):
 
248
 
 
249
    def prepare_live_rpc(self):
 
250
        self.useFixture(RegionEventLoopFixture("rpc"))
 
251
        self.useFixture(RunningEventLoopFixture())
 
252
        return self.useFixture(MockLiveRegionToClusterRPCFixture())
 
253
 
 
254
    def test__calls_are_made_to_all_clusters(self):
 
255
        rpc_fixture = self.prepare_live_rpc()
 
256
        nodegroups = [make_accepted_NodeGroup() for _ in xrange(3)]
 
257
        protocols = []
 
258
        for nodegroup in nodegroups:
 
259
            protocol = rpc_fixture.makeCluster(nodegroup, EvaluateTag)
 
260
            protocol.EvaluateTag.side_effect = always_succeed_with({})
 
261
            protocols.append(protocol)
 
262
        tag = make_Tag_without_populating()
 
263
 
 
264
        d = populate_tags(tag)
 
265
 
 
266
        # `d` is a testing-only convenience. We must wait for it to fire, and
 
267
        # we must do that from the reactor thread.
 
268
        wait_for_populate = asynchronous(lambda: d)
 
269
        wait_for_populate().wait(10)
 
270
 
 
271
        for nodegroup, protocol in izip(nodegroups, protocols):
 
272
            self.expectThat(protocol.EvaluateTag, MockCalledOnceWith(
 
273
                protocol, tag_name=tag.name, tag_definition=tag.definition,
 
274
                tag_nsmap=ANY, credentials=nodegroup.api_credentials))
60
275
 
61
276
 
62
277
class TestPopulateTagsForSingleNode(MAASServerTestCase):
63
278
 
64
279
    def test_updates_node_with_all_applicable_tags(self):
65
 
        node = factory.make_node()
66
 
        factory.make_node_commission_result(
 
280
        node = factory.make_Node()
 
281
        factory.make_NodeResult_for_commissioning(
67
282
            node, commissioningscript.LSHW_OUTPUT_NAME, 0, b"<foo/>")
68
 
        factory.make_node_commission_result(
 
283
        factory.make_NodeResult_for_commissioning(
69
284
            node, commissioningscript.LLDP_OUTPUT_NAME, 0, b"<bar/>")
70
285
        tags = [
71
 
            factory.make_tag("foo", "/foo"),
72
 
            factory.make_tag("bar", "//lldp:bar"),
73
 
            factory.make_tag("baz", "/foo/bar"),
 
286
            factory.make_Tag("foo", "/foo"),
 
287
            factory.make_Tag("bar", "//lldp:bar"),
 
288
            factory.make_Tag("baz", "/foo/bar"),
74
289
            ]
75
290
        populate_tags_for_single_node(tags, node)
76
291
        self.assertItemsEqual(
77
292
            ["foo", "bar"], [tag.name for tag in node.tags.all()])
78
293
 
79
294
    def test_ignores_tags_with_unrecognised_namespaces(self):
80
 
        node = factory.make_node()
81
 
        factory.make_node_commission_result(
 
295
        node = factory.make_Node()
 
296
        factory.make_NodeResult_for_commissioning(
82
297
            node, commissioningscript.LSHW_OUTPUT_NAME, 0, b"<foo/>")
83
298
        tags = [
84
 
            factory.make_tag("foo", "/foo"),
85
 
            factory.make_tag("lou", "//nge:bar"),
 
299
            factory.make_Tag("foo", "/foo"),
 
300
            factory.make_Tag("lou", "//nge:bar"),
86
301
            ]
87
302
        populate_tags_for_single_node(tags, node)  # Look mom, no exception!
88
303
        self.assertSequenceEqual(
89
304
            ["foo"], [tag.name for tag in node.tags.all()])
90
305
 
91
306
    def test_ignores_tags_without_definition(self):
92
 
        node = factory.make_node()
93
 
        factory.make_node_commission_result(
 
307
        node = factory.make_Node()
 
308
        factory.make_NodeResult_for_commissioning(
94
309
            node, commissioningscript.LSHW_OUTPUT_NAME, 0, b"<foo/>")
95
310
        tags = [
96
 
            factory.make_tag("foo", "/foo"),
 
311
            factory.make_Tag("foo", "/foo"),
97
312
            Tag(name="empty", definition=""),
98
313
            Tag(name="null", definition=None),
99
314
            ]