Merge lp:~gmb/maas/port-add-seamicro-to-RPC into lp:~maas-committers/maas/trunk

Proposed by Graham Binns
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 2950
Proposed branch: lp:~gmb/maas/port-add-seamicro-to-RPC
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~gmb/maas/port-add-virsh-to-rpc
Diff against target: 341 lines (+198/-21)
7 files modified
src/maasserver/models/nodegroup.py (+18/-3)
src/maasserver/models/tests/test_nodegroup.py (+54/-0)
src/provisioningserver/rpc/cluster.py (+17/-0)
src/provisioningserver/rpc/clusterservice.py (+26/-0)
src/provisioningserver/rpc/exceptions.py (+4/-0)
src/provisioningserver/rpc/tests/test_clusterservice.py (+79/-0)
src/provisioningserver/tasks.py (+0/-18)
To merge this branch: bzr merge lp:~gmb/maas/port-add-seamicro-to-RPC
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+234148@code.launchpad.net

Commit message

Port NodeGroup.add_seamicro15k() to use RPC rather than celery.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good, but I think not finding the IP address for the given MAC is worthy of an error.

review: Approve
Revision history for this message
Graham Binns (gmb) wrote :

> Looks good, but I think not finding the IP address for the given MAC is worthy
> of an error.

Yep. Exception added.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/nodegroup.py'
2--- src/maasserver/models/nodegroup.py 2014-09-10 14:40:46 +0000
3+++ src/maasserver/models/nodegroup.py 2014-09-10 16:44:53 +0000
4@@ -39,12 +39,12 @@
5 )
6 from provisioningserver.dhcp.omshell import generate_omapi_key
7 from provisioningserver.rpc.cluster import (
8+ AddSeaMicro15k,
9 AddVirsh,
10 ImportBootImages,
11 )
12 from provisioningserver.rpc.exceptions import NoConnectionsAvailable
13 from provisioningserver.tasks import (
14- add_seamicro15k,
15 enlist_nodes_from_mscm,
16 enlist_nodes_from_ucsm,
17 )
18@@ -262,15 +262,30 @@
19 :param password: password for power controller
20 :param power_control: optional specify the power control method,
21 either ipmi (default), restapi, or restapi2.
22+
23+ :raises NoConnectionsAvailable: If no connections to the cluster
24+ are available.
25 """
26- args = (mac, username, password, power_control)
27- add_seamicro15k.apply_async(queue=self.uuid, args=args)
28+ try:
29+ client = getClientFor(self.uuid, timeout=1)
30+ except NoConnectionsAvailable:
31+ # No connection to the cluster so we can't do anything. We
32+ # let the caller handle the error, since we don't want to
33+ # just drop it.
34+ raise
35+ else:
36+ return client(
37+ AddSeaMicro15k, mac=mac, username=username,
38+ password=password, power_control=power_control)
39
40 def add_virsh(self, poweraddr, password=None):
41 """ Add all of the virtual machines inside a virsh controller.
42
43 :param poweraddr: virsh connection string
44 :param password: ssh password
45+
46+ :raises NoConnectionsAvailable: If no connections to the cluster
47+ are available.
48 """
49 try:
50 client = getClientFor(self.uuid, timeout=1)
51
52=== modified file 'src/maasserver/models/tests/test_nodegroup.py'
53--- src/maasserver/models/tests/test_nodegroup.py 2014-09-10 14:40:46 +0000
54+++ src/maasserver/models/tests/test_nodegroup.py 2014-09-10 16:44:53 +0000
55@@ -47,6 +47,7 @@
56 )
57 from provisioningserver.dhcp.omshell import generate_omapi_key
58 from provisioningserver.rpc.cluster import (
59+ AddSeaMicro15k,
60 AddVirsh,
61 ImportBootImages,
62 )
63@@ -459,3 +460,56 @@
64 self.assertRaises(
65 NoConnectionsAvailable, nodegroup.add_virsh, poweraddr,
66 password)
67+
68+ def test_add_seamicro15k_end_to_end(self):
69+ nodegroup = factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED)
70+
71+ self.useFixture(RegionEventLoopFixture("rpc"))
72+ self.useFixture(RunningEventLoopFixture())
73+ fixture = self.useFixture(MockLiveRegionToClusterRPCFixture())
74+ protocol = fixture.makeCluster(nodegroup, AddSeaMicro15k)
75+ protocol.AddSeaMicro15k.return_value = defer.succeed({})
76+
77+ mac = factory.getRandomMACAddress()
78+ username = factory.make_name('user')
79+ password = factory.make_name('password')
80+ power_control = factory.make_name('power_control')
81+ nodegroup.add_seamicro15k(
82+ mac, username, password, power_control).wait(10)
83+
84+ self.expectThat(
85+ protocol.AddSeaMicro15k,
86+ MockCalledOnceWith(
87+ ANY, mac=mac, username=username, password=password,
88+ power_control=power_control))
89+
90+ def test_add_seamicro15k_calls_client_with_resource_endpoint(self):
91+ getClientFor = self.patch(nodegroup_module, 'getClientFor')
92+ client = getClientFor.return_value
93+ nodegroup = factory.make_NodeGroup()
94+
95+ mac = factory.getRandomMACAddress()
96+ username = factory.make_name('user')
97+ password = factory.make_name('password')
98+ power_control = factory.make_name('power_control')
99+ nodegroup.add_seamicro15k(
100+ mac, username, password, power_control)
101+
102+ self.expectThat(
103+ client,
104+ MockCalledOnceWith(
105+ AddSeaMicro15k, mac=mac, username=username,
106+ password=password, power_control=power_control))
107+
108+ def test_add_seamicro15k_raises_if_no_connection_to_cluster(self):
109+ getClientFor = self.patch(nodegroup_module, 'getClientFor')
110+ getClientFor.side_effect = NoConnectionsAvailable()
111+ nodegroup = factory.make_NodeGroup()
112+
113+ mac = factory.getRandomMACAddress()
114+ username = factory.make_name('user')
115+ password = factory.make_name('password')
116+ power_control = factory.make_name('power_control')
117+ self.assertRaises(
118+ NoConnectionsAvailable, nodegroup.add_seamicro15k,
119+ mac, username, password, power_control)
120
121=== modified file 'src/provisioningserver/rpc/cluster.py'
122--- src/provisioningserver/rpc/cluster.py 2014-09-10 15:06:39 +0000
123+++ src/provisioningserver/rpc/cluster.py 2014-09-10 16:44:53 +0000
124@@ -360,3 +360,20 @@
125 ]
126 response = []
127 errors = []
128+
129+
130+class AddSeaMicro15k(amp.Command):
131+ """Probe for and enlist seamicro15k machines attached to the cluster.
132+
133+ :since: 1.7
134+ """
135+ arguments = [
136+ (b"mac", amp.Unicode()),
137+ (b"username", amp.Unicode()),
138+ (b"password", amp.Unicode()),
139+ (b"power_control", amp.Unicode(optional=True)),
140+ ]
141+ response = []
142+ errors = {
143+ exceptions.NoIPFoundForMACAddress: b"NoIPFoundForMACAddress",
144+ }
145
146=== modified file 'src/provisioningserver/rpc/clusterservice.py'
147--- src/provisioningserver/rpc/clusterservice.py 2014-09-10 15:06:39 +0000
148+++ src/provisioningserver/rpc/clusterservice.py 2014-09-10 16:44:53 +0000
149@@ -29,7 +29,11 @@
150 ArchitectureRegistry,
151 PowerTypeRegistry,
152 )
153+from provisioningserver.drivers.hardware.seamicro import (
154+ probe_seamicro15k_and_enlist,
155+ )
156 from provisioningserver.drivers.hardware.virsh import probe_virsh_and_enlist
157+from provisioningserver.logger.log import get_maas_logger
158 from provisioningserver.rpc import (
159 cluster,
160 common,
161@@ -61,6 +65,7 @@
162 cancel_timer,
163 start_timers,
164 )
165+from provisioningserver.utils.network import find_ip_via_arp
166 from twisted.application.internet import TimerService
167 from twisted.internet.defer import inlineCallbacks
168 from twisted.internet.endpoints import (
169@@ -74,6 +79,9 @@
170 from zope.interface import implementer
171
172
173+maaslog = get_maas_logger("rpc.cluster")
174+
175+
176 class Cluster(RPCProtocol):
177 """The RPC protocol supported by a cluster controller.
178
179@@ -241,6 +249,24 @@
180 probe_virsh_and_enlist(poweraddr, password)
181 return {}
182
183+ @cluster.AddSeaMicro15k.responder
184+ def add_seamicro15k(self, mac, username, password, power_control=None):
185+ """add_virsh()
186+
187+ Implementation of
188+ :py:class:`~provisioningserver.rpc.cluster.AddSeaMicro15k`.
189+ """
190+ ip = find_ip_via_arp(mac)
191+ if ip is not None:
192+ probe_seamicro15k_and_enlist(
193+ ip, username, password,
194+ power_control=power_control)
195+ else:
196+ message = "Couldn't find IP address for MAC %s" % mac
197+ maaslog.warning(message)
198+ raise exceptions.NoIPFoundForMACAddress(message)
199+ return {}
200+
201
202 @implementer(IConnection)
203 class ClusterClient(Cluster):
204
205=== modified file 'src/provisioningserver/rpc/exceptions.py'
206--- src/provisioningserver/rpc/exceptions.py 2014-09-10 12:47:59 +0000
207+++ src/provisioningserver/rpc/exceptions.py 2014-09-10 16:44:53 +0000
208@@ -97,3 +97,7 @@
209
210 class NodeAlreadyExists(Exception):
211 """A node already exists with a given MAC address."""
212+
213+
214+class NoIPFoundForMACAddress(Exception):
215+ """No IP was found for a given MAC address."""
216
217=== modified file 'src/provisioningserver/rpc/tests/test_clusterservice.py'
218--- src/provisioningserver/rpc/tests/test_clusterservice.py 2014-09-10 15:06:39 +0000
219+++ src/provisioningserver/rpc/tests/test_clusterservice.py 2014-09-10 16:44:53 +0000
220@@ -1175,3 +1175,82 @@
221 self.assertThat(
222 probe_virsh_and_enlist, MockCalledOnceWith(
223 poweraddr, None))
224+
225+
226+class TestClusterProtocol_AddSeaMicro15k(MAASTestCase):
227+
228+ def test__is_registered(self):
229+ protocol = Cluster()
230+ responder = protocol.locateResponder(
231+ cluster.AddSeaMicro15k.commandName)
232+ self.assertIsNot(responder, None)
233+
234+ def test__calls_find_ip_via_arp(self):
235+ # Prevent any actual probing from happing.
236+ self.patch_autospec(
237+ clusterservice, 'probe_seamicro15k_and_enlist')
238+ find_ip_via_arp = self.patch_autospec(
239+ clusterservice, 'find_ip_via_arp')
240+ find_ip_via_arp.return_value = factory.getRandomIPAddress()
241+
242+ mac = factory.getRandomMACAddress()
243+ username = factory.make_name('user')
244+ password = factory.make_name('password')
245+ power_control = factory.make_name('power_control')
246+ call_responder(Cluster(), cluster.AddSeaMicro15k, {
247+ "mac": mac,
248+ "username": username,
249+ "password": password,
250+ "power_control": power_control
251+ })
252+
253+ self.assertThat(
254+ find_ip_via_arp, MockCalledOnceWith(mac))
255+
256+ @inlineCallbacks
257+ def test__raises_and_logs_warning_if_no_ip_found_for_mac(self):
258+ maaslog = self.patch(clusterservice, 'maaslog')
259+ find_ip_via_arp = self.patch_autospec(
260+ clusterservice, 'find_ip_via_arp')
261+ find_ip_via_arp.return_value = None
262+
263+ mac = factory.getRandomMACAddress()
264+ username = factory.make_name('user')
265+ password = factory.make_name('password')
266+ power_control = factory.make_name('power_control')
267+
268+ with ExpectedException(exceptions.NoIPFoundForMACAddress):
269+ yield call_responder(Cluster(), cluster.AddSeaMicro15k, {
270+ "mac": mac,
271+ "username": username,
272+ "password": password,
273+ "power_control": power_control
274+ })
275+
276+ self.assertThat(
277+ maaslog.warning,
278+ MockCalledOnceWith(
279+ "Couldn't find IP address for MAC %s" % mac))
280+
281+ def test__calls_probe_seamicro15k_and_enlist(self):
282+ probe_seamicro15k_and_enlist = self.patch_autospec(
283+ clusterservice, 'probe_seamicro15k_and_enlist')
284+ find_ip_via_arp = self.patch_autospec(
285+ clusterservice, 'find_ip_via_arp')
286+ find_ip_via_arp.return_value = factory.getRandomIPAddress()
287+
288+ mac = factory.getRandomMACAddress()
289+ username = factory.make_name('user')
290+ password = factory.make_name('password')
291+ power_control = factory.make_name('power_control')
292+ call_responder(Cluster(), cluster.AddSeaMicro15k, {
293+ "mac": mac,
294+ "username": username,
295+ "password": password,
296+ "power_control": power_control
297+ })
298+
299+ self.assertThat(
300+ probe_seamicro15k_and_enlist, MockCalledOnceWith(
301+ find_ip_via_arp.return_value, username, password,
302+ power_control=power_control))
303
304=== modified file 'src/provisioningserver/tasks.py'
305--- src/provisioningserver/tasks.py 2014-09-10 15:06:39 +0000
306+++ src/provisioningserver/tasks.py 2014-09-10 16:44:53 +0000
307@@ -52,13 +52,9 @@
308 setup_rndc,
309 )
310 from provisioningserver.drivers.hardware.mscm import probe_and_enlist_mscm
311-from provisioningserver.drivers.hardware.seamicro import (
312- probe_seamicro15k_and_enlist,
313- )
314 from provisioningserver.drivers.hardware.ucsm import probe_and_enlist_ucsm
315 from provisioningserver.logger import get_maas_logger
316 from provisioningserver.utils.fs import sudo_write_file
317-from provisioningserver.utils.network import find_ip_via_arp
318
319 # For each item passed to refresh_secrets, a refresh function to give it to.
320 refresh_functions = {
321@@ -392,20 +388,6 @@
322 @task
323 @log_task_events()
324 @log_exception_text
325-def add_seamicro15k(mac, username, password, power_control=None):
326- """ See `maasserver.api.NodeGroup.add_seamicro15k`. """
327- ip = find_ip_via_arp(mac)
328- if ip is not None:
329- probe_seamicro15k_and_enlist(
330- ip, username, password,
331- power_control=power_control)
332- else:
333- maaslog.warning("Couldn't find IP address for MAC %s" % mac)
334-
335-
336-@task
337-@log_task_events()
338-@log_exception_text
339 def enlist_nodes_from_ucsm(url, username, password):
340 """ See `maasserver.api.NodeGroupHandler.enlist_nodes_from_ucsm`. """
341 probe_and_enlist_ucsm(url, username, password)