Merge lp:~allenap/maas/rpc-get-preseed-data-helper into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 2597
Proposed branch: lp:~allenap/maas/rpc-get-preseed-data-helper
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 333 lines (+146/-44)
5 files modified
src/apiclient/tests/test_maas_client.py (+2/-9)
src/maasserver/clusterrpc/osystems.py (+40/-2)
src/maasserver/clusterrpc/tests/test_osystems.py (+57/-14)
src/maasserver/tests/test_preseed.py (+2/-10)
src/maastesting/factory.py (+45/-9)
To merge this branch: bzr merge lp:~allenap/maas/rpc-get-preseed-data-helper
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+228086@code.launchpad.net

Commit message

Helper function to call GetPreseedData on the cluster from the region.

Also cleans up several implementations of make_url().

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) :
review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Thanks for the review!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/apiclient/tests/test_maas_client.py'
--- src/apiclient/tests/test_maas_client.py 2014-07-16 14:12:13 +0000
+++ src/apiclient/tests/test_maas_client.py 2014-07-24 15:53:02 +0000
@@ -138,14 +138,6 @@
138 self.assertEqual(content, read_content)138 self.assertEqual(content, read_content)
139139
140140
141def make_url():
142 """Create an arbitrary URL."""
143 return 'http://example.com:%d/%s/' % (
144 factory.pick_port(),
145 factory.make_string(),
146 )
147
148
149def make_path():141def make_path():
150 """Create an arbitrary resource path."""142 """Create an arbitrary resource path."""
151 return "/" + '/'.join(factory.make_string() for counter in range(2))143 return "/" + '/'.join(factory.make_string() for counter in range(2))
@@ -172,7 +164,8 @@
172def make_client(root=None, result=None):164def make_client(root=None, result=None):
173 """Create a MAASClient."""165 """Create a MAASClient."""
174 if root is None:166 if root is None:
175 root = make_url()167 root = factory.make_simple_http_url(
168 path=factory.make_name("path") + "/")
176 auth = MAASOAuth(169 auth = MAASOAuth(
177 factory.make_string(), factory.make_string(),170 factory.make_string(), factory.make_string(),
178 factory.make_string())171 factory.make_string())
179172
=== modified file 'src/maasserver/clusterrpc/osystems.py'
--- src/maasserver/clusterrpc/osystems.py 2014-07-16 22:11:14 +0000
+++ src/maasserver/clusterrpc/osystems.py 2014-07-24 15:53:02 +0000
@@ -14,14 +14,23 @@
14__metaclass__ = type14__metaclass__ = type
15__all__ = [15__all__ = [
16 "gen_all_known_operating_systems",16 "gen_all_known_operating_systems",
17 "get_preseed_data",
17]18]
1819
19from collections import defaultdict20from collections import defaultdict
20from functools import partial21from functools import partial
22from urlparse import urlparse
2123
22from maasserver.rpc import getAllClients24from maasserver.rpc import (
25 getAllClients,
26 getClientFor,
27 )
23from maasserver.utils import async28from maasserver.utils import async
24from provisioningserver.rpc.cluster import ListOperatingSystems29from provisioningserver.rpc.cluster import (
30 GetPreseedData,
31 ListOperatingSystems,
32 )
33from provisioningserver.utils import synchronous
25from twisted.python.failure import Failure34from twisted.python.failure import Failure
2635
2736
@@ -35,6 +44,7 @@
35 yield response44 yield response
3645
3746
47@synchronous
38def gen_all_known_operating_systems():48def gen_all_known_operating_systems():
39 """Generator yielding details on OSes supported by any cluster.49 """Generator yielding details on OSes supported by any cluster.
4050
@@ -52,3 +62,31 @@
52 if osystem not in seen[name]:62 if osystem not in seen[name]:
53 seen[name].append(osystem)63 seen[name].append(osystem)
54 yield osystem64 yield osystem
65
66
67@synchronous
68def get_preseed_data(preseed_type, node, token, metadata_url):
69 """Obtain optional preseed data for this OS, preseed type, and node.
70
71 :param preseed_type: The type of preseed to compose.
72 :param node: The node model instance.
73 :param token: The token model instance.
74 :param metadata_url: The URL where this node's metadata will be made
75 available.
76
77 :raises NoConnectionsAvailable: When no connections to the node's
78 cluster are available for use.
79 :raises NoSuchOperatingSystem: When the node's declared operating
80 system is not known to its cluster.
81 :raises NotImplementedError: When this node's OS does not want to
82 define any OS-specific preseed data.
83 :raises TimeoutError: If a response has not been received within 30
84 seconds.
85 """
86 client = getClientFor(node.nodegroup.uuid).wait(5)
87 call = client(
88 GetPreseedData, osystem=node.get_osystem(), preseed_type=preseed_type,
89 node_system_id=node.system_id, node_hostname=node.hostname,
90 consumer_key=token.consumer.key, token_key=token.key,
91 token_secret=token.secret, metadata_url=urlparse(metadata_url))
92 return call.wait(30)
5593
=== modified file 'src/maasserver/clusterrpc/tests/test_osystems.py'
--- src/maasserver/clusterrpc/tests/test_osystems.py 2014-07-21 12:28:45 +0000
+++ src/maasserver/clusterrpc/tests/test_osystems.py 2014-07-24 15:53:02 +0000
@@ -20,12 +20,18 @@
20 )20 )
2121
22from maasserver import eventloop22from maasserver import eventloop
23from maasserver.clusterrpc.osystems import gen_all_known_operating_systems23from maasserver.clusterrpc.osystems import (
24 gen_all_known_operating_systems,
25 get_preseed_data,
26 )
27from maasserver.enum import PRESEED_TYPE
24from maasserver.rpc import getAllClients28from maasserver.rpc import getAllClients
25from maasserver.rpc.testing.fixtures import ClusterRPCFixture29from maasserver.rpc.testing.fixtures import ClusterRPCFixture
26from maasserver.testing.eventloop import RegionEventLoopFixture30from maasserver.testing.eventloop import RegionEventLoopFixture
27from maasserver.testing.factory import factory31from maasserver.testing.factory import factory
28from maasserver.testing.testcase import MAASServerTestCase32from maasserver.testing.testcase import MAASServerTestCase
33from metadataserver.models import NodeKey
34from provisioningserver.rpc.exceptions import NoSuchOperatingSystem
29from testtools.matchers import (35from testtools.matchers import (
30 AfterPreprocessing,36 AfterPreprocessing,
31 AllMatch,37 AllMatch,
@@ -37,21 +43,22 @@
37from twisted.internet.defer import succeed43from twisted.internet.defer import succeed
3844
3945
46def fake_cluster_rpc(test):
47 # Set-up the event-loop with only the RPC service running, then
48 # layer on a fake cluster RPC implementation.
49 test.useFixture(RegionEventLoopFixture("rpc"))
50 test.addCleanup(lambda: eventloop.reset().wait(5))
51 eventloop.start().wait(5)
52 test.useFixture(ClusterRPCFixture())
53
54
40class TestGenAllKnownOperatingSystems(MAASServerTestCase):55class TestGenAllKnownOperatingSystems(MAASServerTestCase):
41 """Tests for `gen_all_known_operating_systems`."""56 """Tests for `gen_all_known_operating_systems`."""
4257
43 def fake_cluster_rpc(self):
44 # Set-up the event-loop with only the RPC service running, then
45 # layer on a fake cluster RPC implementation.
46 self.useFixture(RegionEventLoopFixture("rpc"))
47 self.addCleanup(lambda: eventloop.reset().wait(5))
48 eventloop.start().wait(5)
49 self.useFixture(ClusterRPCFixture())
50
51 def test_yields_oses_known_to_a_cluster(self):58 def test_yields_oses_known_to_a_cluster(self):
52 # The operating systems known to a single node are returned.59 # The operating systems known to a single node are returned.
53 factory.make_node_group().accept()60 factory.make_node_group().accept()
54 self.fake_cluster_rpc()61 fake_cluster_rpc(self)
55 osystems = gen_all_known_operating_systems()62 osystems = gen_all_known_operating_systems()
56 self.assertIsInstance(osystems, Iterator)63 self.assertIsInstance(osystems, Iterator)
57 osystems = list(osystems)64 osystems = list(osystems)
@@ -61,7 +68,7 @@
61 def test_yields_oses_known_to_multiple_clusters(self):68 def test_yields_oses_known_to_multiple_clusters(self):
62 factory.make_node_group().accept()69 factory.make_node_group().accept()
63 factory.make_node_group().accept()70 factory.make_node_group().accept()
64 self.fake_cluster_rpc()71 fake_cluster_rpc(self)
65 osystems = gen_all_known_operating_systems()72 osystems = gen_all_known_operating_systems()
66 self.assertIsInstance(osystems, Iterator)73 self.assertIsInstance(osystems, Iterator)
67 osystems = list(osystems)74 osystems = list(osystems)
@@ -73,7 +80,7 @@
73 # every cluster will have several (or all) OSes in common.80 # every cluster will have several (or all) OSes in common.
74 factory.make_node_group().accept()81 factory.make_node_group().accept()
75 factory.make_node_group().accept()82 factory.make_node_group().accept()
76 self.fake_cluster_rpc()83 fake_cluster_rpc(self)
77 counter = Counter(84 counter = Counter(
78 osystem["name"] for osystem in85 osystem["name"] for osystem in
79 gen_all_known_operating_systems())86 gen_all_known_operating_systems())
@@ -88,7 +95,7 @@
8895
89 def test_os_data_is_passed_through_unmolested(self):96 def test_os_data_is_passed_through_unmolested(self):
90 factory.make_node_group().accept()97 factory.make_node_group().accept()
91 self.fake_cluster_rpc()98 fake_cluster_rpc(self)
92 example = {99 example = {
93 "osystems": [100 "osystems": [
94 {101 {
@@ -109,7 +116,7 @@
109 factory.make_node_group().accept()116 factory.make_node_group().accept()
110 factory.make_node_group().accept()117 factory.make_node_group().accept()
111 factory.make_node_group().accept()118 factory.make_node_group().accept()
112 self.fake_cluster_rpc()119 fake_cluster_rpc(self)
113120
114 clients = getAllClients().wait()121 clients = getAllClients().wait()
115 for index, client in enumerate(clients):122 for index, client in enumerate(clients):
@@ -129,3 +136,39 @@
129 self.assertItemsEqual(136 self.assertItemsEqual(
130 [{"name": clients[0].ident}],137 [{"name": clients[0].ident}],
131 gen_all_known_operating_systems())138 gen_all_known_operating_systems())
139
140
141class TestGetPreseedData(MAASServerTestCase):
142 """Tests for `get_preseed_data`."""
143
144 def test_returns_preseed_data(self):
145 # The Windows driver is known to provide custom preseed data.
146 node = factory.make_node(osystem="windows")
147 node.nodegroup.accept()
148 fake_cluster_rpc(self)
149 preseed_data = get_preseed_data(
150 PRESEED_TYPE.COMMISSIONING, node,
151 token=NodeKey.objects.get_token_for_node(node),
152 metadata_url=factory.make_url())
153 self.assertThat(preseed_data, IsInstance(dict))
154 self.assertThat(preseed_data, Not(HasLength(0)))
155
156 def test_propagates_NotImplementedError(self):
157 # The Windows driver is known to *not* provide custom preseed
158 # data when using Curtin.
159 node = factory.make_node(osystem="windows")
160 node.nodegroup.accept()
161 fake_cluster_rpc(self)
162 self.assertRaises(
163 NotImplementedError, get_preseed_data, PRESEED_TYPE.CURTIN,
164 node, token=NodeKey.objects.get_token_for_node(node),
165 metadata_url=factory.make_url())
166
167 def test_propagates_NoSuchOperatingSystem(self):
168 node = factory.make_node(osystem=factory.make_name("foo"))
169 node.nodegroup.accept()
170 fake_cluster_rpc(self)
171 self.assertRaises(
172 NoSuchOperatingSystem, get_preseed_data, PRESEED_TYPE.CURTIN,
173 node, token=NodeKey.objects.get_token_for_node(node),
174 metadata_url=factory.make_url())
132175
=== modified file 'src/maasserver/tests/test_preseed.py'
--- src/maasserver/tests/test_preseed.py 2014-07-18 15:44:55 +0000
+++ src/maasserver/tests/test_preseed.py 2014-07-24 15:53:02 +0000
@@ -442,14 +442,6 @@
442 pick_cluster_controller_address(node))442 pick_cluster_controller_address(node))
443443
444444
445def make_url(name):
446 """Create a fake archive URL."""
447 return "http://%s.example.com/%s/" % (
448 factory.make_name(name),
449 factory.make_name('path'),
450 )
451
452
453class TestPreseedContext(MAASServerTestCase):445class TestPreseedContext(MAASServerTestCase):
454 """Tests for `get_preseed_context`."""446 """Tests for `get_preseed_context`."""
455447
@@ -467,8 +459,8 @@
467 def test_get_preseed_context_archive_refs(self):459 def test_get_preseed_context_archive_refs(self):
468 # urlparse lowercases the hostnames. That should not have any460 # urlparse lowercases the hostnames. That should not have any
469 # impact but for testing, create lower-case hostnames.461 # impact but for testing, create lower-case hostnames.
470 main_archive = make_url('main_archive')462 main_archive = factory.make_url(netloc="main-archive.example.com")
471 ports_archive = make_url('ports_archive')463 ports_archive = factory.make_url(netloc="ports-archive.example.com")
472 Config.objects.set_config('main_archive', main_archive)464 Config.objects.set_config('main_archive', main_archive)
473 Config.objects.set_config('ports_archive', ports_archive)465 Config.objects.set_config('ports_archive', ports_archive)
474 nodegroup = factory.make_node_group(maas_url=factory.make_string())466 nodegroup = factory.make_node_group(maas_url=factory.make_string())
475467
=== modified file 'src/maastesting/factory.py'
--- src/maastesting/factory.py 2014-07-16 14:12:13 +0000
+++ src/maastesting/factory.py 2014-07-24 15:53:02 +0000
@@ -322,23 +322,59 @@
322 scheme for scheme in urlparse.uses_params322 scheme for scheme in urlparse.uses_params
323 if scheme != "")323 if scheme != "")
324324
325 def make_parsed_url(self):325 def make_parsed_url(
326 self, scheme=None, netloc=None, path=None, params=None,
327 query=None, fragment=None):
326 """Generate a random parsed URL object.328 """Generate a random parsed URL object.
327329
330 Contains randomly generated values for all parts of a URL: scheme,
331 location, path, parameters, query, and fragment. However, each part
332 can be overridden individually.
333
328 :return: Instance of :py:class:`urlparse.ParseResult`.334 :return: Instance of :py:class:`urlparse.ParseResult`.
329 """335 """
330 return urlparse.ParseResult(336 if scheme is None:
331 # Select a scheme that allows parameters; see above.337 # Select a scheme that allows parameters; see above.
332 random.choice(self._make_parsed_url_schemes),338 scheme = random.choice(self._make_parsed_url_schemes)
333 self.make_name("netloc").lower(),339 if netloc is None:
340 netloc = "%s.example.com" % self.make_name("netloc").lower()
341 if path is None:
334 # A leading forward-slash will be added in geturl() if we342 # A leading forward-slash will be added in geturl() if we
335 # don't, so ensure it's here now so tests can compare URLs343 # don't, so ensure it's here now so tests can compare URLs
336 # without worrying about it.344 # without worrying about it.
337 self.make_name("/path"),345 path = self.make_name("/path")
338 self.make_name("params"),346 else:
339 self.make_name("query"),347 # Same here with the forward-slash prefix.
340 self.make_name("fragment"),348 if not path.startswith("/"):
341 )349 path = "/" + path
350 if params is None:
351 params = self.make_name("params")
352 if query is None:
353 query = self.make_name("query")
354 if fragment is None:
355 fragment = self.make_name("fragment")
356 return urlparse.ParseResult(
357 scheme, netloc, path, params, query, fragment)
358
359 def make_url(
360 self, scheme=None, netloc=None, path=None, params=None,
361 query=None, fragment=None):
362 """Generate a random URL.
363
364 Contains randomly generated values for all parts of a URL: scheme,
365 location, path, parameters, query, and fragment. However, each part
366 can be overridden individually.
367
368 :return: string
369 """
370 return self.make_parsed_url(
371 scheme, netloc, path, params, query, fragment).geturl()
372
373 def make_simple_http_url(self, netloc=None, path=None):
374 """Create an arbitrary HTTP URL with only a location and path."""
375 return self.make_parsed_url(
376 scheme="http", netloc=netloc, path=path, params="", query="",
377 fragment="").geturl()
342378
343 def make_names(self, *prefixes):379 def make_names(self, *prefixes):
344 """Generate random names.380 """Generate random names.