Merge lp:~blake-rouse/maas/rpc-client-now-2.1 into lp:maas/2.1

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 5585
Proposed branch: lp:~blake-rouse/maas/rpc-client-now-2.1
Merge into: lp:maas/2.1
Diff against target: 441 lines (+134/-46)
15 files modified
src/provisioningserver/rackdservices/dhcp_probe_service.py (+1/-1)
src/provisioningserver/rackdservices/image_download_service.py (+1/-1)
src/provisioningserver/rackdservices/lease_socket_service.py (+1/-1)
src/provisioningserver/rackdservices/networks_monitoring_service.py (+28/-18)
src/provisioningserver/rackdservices/ntp.py (+1/-1)
src/provisioningserver/rackdservices/service_monitor_service.py (+1/-1)
src/provisioningserver/rackdservices/tests/test_dhcp_probe_service.py (+8/-7)
src/provisioningserver/rackdservices/tests/test_image_download_service.py (+2/-2)
src/provisioningserver/rackdservices/tests/test_lease_socket_service.py (+1/-1)
src/provisioningserver/rackdservices/tests/test_ntp.py (+1/-1)
src/provisioningserver/rackdservices/tests/test_service_monitor_service.py (+1/-1)
src/provisioningserver/rackdservices/tests/test_tftp.py (+8/-6)
src/provisioningserver/rackdservices/tftp.py (+10/-5)
src/provisioningserver/rpc/clusterservice.py (+21/-0)
src/provisioningserver/rpc/tests/test_clusterservice.py (+49/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/rpc-client-now-2.1
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+317249@code.launchpad.net

This proposal supersedes a proposal from 2017-02-14.

Commit message

Backport r5725: Add new getClientNow that ensures a connection to a region controller before returning a client. Use the new getClientNow across the rack controller getClient calls.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0. Got: .

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Self-approving.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/provisioningserver/rackdservices/dhcp_probe_service.py'
2--- src/provisioningserver/rackdservices/dhcp_probe_service.py 2016-10-28 15:58:32 +0000
3+++ src/provisioningserver/rackdservices/dhcp_probe_service.py 2017-02-14 18:27:19 +0000
4@@ -107,7 +107,7 @@
5 client = None
6 for elapsed, remaining, wait in retries(15, 5, self.clock):
7 try:
8- client = self.client_service.getClient()
9+ client = yield self.client_service.getClientNow()
10 break
11 except NoConnectionsAvailable:
12 yield pause(wait, self.clock)
13
14=== modified file 'src/provisioningserver/rackdservices/image_download_service.py'
15--- src/provisioningserver/rackdservices/image_download_service.py 2016-10-28 15:58:32 +0000
16+++ src/provisioningserver/rackdservices/image_download_service.py 2017-02-14 18:27:19 +0000
17@@ -92,7 +92,7 @@
18 # the RPC service.
19 for elapsed, remaining, wait in retries(15, 5, self.clock):
20 try:
21- client = self.client_service.getClient()
22+ client = yield self.client_service.getClientNow()
23 break
24 except NoConnectionsAvailable:
25 yield pause(wait, self.clock)
26
27=== modified file 'src/provisioningserver/rackdservices/lease_socket_service.py'
28--- src/provisioningserver/rackdservices/lease_socket_service.py 2016-03-28 13:54:47 +0000
29+++ src/provisioningserver/rackdservices/lease_socket_service.py 2017-02-14 18:27:19 +0000
30@@ -106,7 +106,7 @@
31 client = None
32 for elapsed, remaining, wait in retries(30, 10, clock):
33 try:
34- client = self.client_service.getClient()
35+ client = yield self.client_service.getClientNow()
36 break
37 except NoConnectionsAvailable:
38 yield pause(wait, self.clock)
39
40=== modified file 'src/provisioningserver/rackdservices/networks_monitoring_service.py'
41--- src/provisioningserver/rackdservices/networks_monitoring_service.py 2016-10-25 13:57:02 +0000
42+++ src/provisioningserver/rackdservices/networks_monitoring_service.py 2017-02-14 18:27:19 +0000
43@@ -30,36 +30,46 @@
44
45 def getDiscoveryState(self):
46 """Get the discovery state from the region."""
47- client = self.clientService.getClient()
48 if self._recorded is None:
49 # Wait until the rack has refreshed.
50 return {}
51 else:
52- d = client(
53- GetDiscoveryState, system_id=client.localIdent)
54- d.addCallback(lambda args: args['interfaces'])
55+ def getState(client):
56+ d = client(
57+ GetDiscoveryState, system_id=client.localIdent)
58+ d.addCallback(lambda args: args['interfaces'])
59+ return d
60+
61+ d = self.clientService.getClientNow()
62+ d.addCallback(getState)
63 return d
64
65 def recordInterfaces(self, interfaces):
66 """Record the interfaces information to the region."""
67- client = self.clientService.getClient()
68- # On first run perform a refresh
69- if self._recorded is None:
70- return client(RequestRackRefresh, system_id=client.localIdent)
71- else:
72- return client(
73- UpdateInterfaces, system_id=client.localIdent,
74- interfaces=interfaces)
75+ def record(client):
76+ # On first run perform a refresh
77+ if self._recorded is None:
78+ return client(RequestRackRefresh, system_id=client.localIdent)
79+ else:
80+ return client(
81+ UpdateInterfaces, system_id=client.localIdent,
82+ interfaces=interfaces)
83+
84+ d = self.clientService.getClientNow()
85+ d.addCallback(record)
86+ return d
87
88 def reportNeighbours(self, neighbours):
89 """Report neighbour information to the region."""
90- client = self.clientService.getClient()
91- return client(
92+ d = self.clientService.getClientNow()
93+ d.addCallback(lambda client: client(
94 ReportNeighbours, system_id=client.localIdent,
95- neighbours=neighbours)
96+ neighbours=neighbours))
97+ return d
98
99 def reportMDNSEntries(self, mdns):
100 """Report mDNS entries to the region."""
101- client = self.clientService.getClient()
102- return client(
103- ReportMDNSEntries, system_id=client.localIdent, mdns=mdns)
104+ d = self.clientService.getClientNow()
105+ d.addCleanup(lambda client: client(
106+ ReportMDNSEntries, system_id=client.localIdent, mdns=mdns))
107+ return d
108
109=== modified file 'src/provisioningserver/rackdservices/ntp.py'
110--- src/provisioningserver/rackdservices/ntp.py 2016-10-28 15:58:32 +0000
111+++ src/provisioningserver/rackdservices/ntp.py 2017-02-14 18:27:19 +0000
112@@ -66,7 +66,7 @@
113 subsequently obtained configuration objects, allowing this service to
114 determine whether a change needs to be applied to the NTP server.
115 """
116- client = self._rpc_service.getClient()
117+ client = yield self._rpc_service.getClientNow()
118 time_configuation = yield client(
119 GetTimeConfiguration, system_id=client.localIdent)
120 controller_type = yield client(
121
122=== modified file 'src/provisioningserver/rackdservices/service_monitor_service.py'
123--- src/provisioningserver/rackdservices/service_monitor_service.py 2016-10-28 15:58:32 +0000
124+++ src/provisioningserver/rackdservices/service_monitor_service.py 2017-02-14 18:27:19 +0000
125@@ -82,7 +82,7 @@
126 client = None
127 for elapsed, remaining, wait in retries(30, 10, self.clock):
128 try:
129- client = self.client_service.getClient()
130+ client = yield self.client_service.getClientNow()
131 break
132 except NoConnectionsAvailable:
133 yield pause(wait, self.clock)
134
135=== modified file 'src/provisioningserver/rackdservices/tests/test_dhcp_probe_service.py'
136--- src/provisioningserver/rackdservices/tests/test_dhcp_probe_service.py 2016-10-21 18:49:40 +0000
137+++ src/provisioningserver/rackdservices/tests/test_dhcp_probe_service.py 2017-02-14 18:27:19 +0000
138@@ -103,7 +103,8 @@
139 region.ReportForeignDHCPServer.commandName]
140
141 rpc_service = Mock()
142- rpc_service.getClient.return_value = getRegionClient()
143+ rpc_service.getClientNow.return_value = defer.succeed(
144+ getRegionClient())
145 service = DHCPProbeService(
146 rpc_service, clock)
147 yield service.startService()
148@@ -160,7 +161,7 @@
149 service = DHCPProbeService(
150 sentinel.service, clock)
151 try_get_client = self.patch(service, '_tryGetClient')
152- try_get_client.getClient = Mock()
153+ try_get_client.getClientNow = Mock()
154 probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
155 yield service.startService()
156 yield service.stopService()
157@@ -182,7 +183,7 @@
158 service = DHCPProbeService(
159 sentinel.service, clock)
160 try_get_client = self.patch(service, '_tryGetClient')
161- try_get_client.getClient = Mock()
162+ try_get_client.getClientNow = Mock()
163 probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
164 yield service.startService()
165 yield service.stopService()
166@@ -204,7 +205,7 @@
167 service = DHCPProbeService(
168 sentinel.service, clock)
169 try_get_client = self.patch(service, '_tryGetClient')
170- try_get_client.getClient = Mock()
171+ try_get_client.getClientNow = Mock()
172 probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
173 yield service.startService()
174 yield service.stopService()
175@@ -226,7 +227,7 @@
176 service = DHCPProbeService(
177 sentinel.service, clock)
178 try_get_client = self.patch(service, '_tryGetClient')
179- try_get_client.getClient = Mock()
180+ try_get_client.getClientNow = Mock()
181 probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
182 yield service.startService()
183 yield service.stopService()
184@@ -257,7 +258,7 @@
185 probe_interface.return_value = [foreign_dhcp_ip]
186 client = getRegionClient()
187 rpc_service = Mock()
188- rpc_service.getClient.return_value = client
189+ rpc_service.getClientNow.return_value = defer.succeed(client)
190
191 service = DHCPProbeService(
192 rpc_service, clock)
193@@ -297,7 +298,7 @@
194
195 client = getRegionClient()
196 rpc_service = Mock()
197- rpc_service.getClient.return_value = client
198+ rpc_service.getClientNow.return_value = defer.succeed(client)
199 service = DHCPProbeService(
200 rpc_service, clock)
201 yield service.startService()
202
203=== modified file 'src/provisioningserver/rackdservices/tests/test_image_download_service.py'
204--- src/provisioningserver/rackdservices/tests/test_image_download_service.py 2016-10-18 11:55:44 +0000
205+++ src/provisioningserver/rackdservices/tests/test_image_download_service.py 2017-02-14 18:27:19 +0000
206@@ -145,7 +145,7 @@
207 http=urlparse(http_proxy),
208 https=urlparse(https_proxy))),
209 ]
210- rpc_client.getClient.return_value = client_call
211+ rpc_client.getClientNow.return_value = defer.succeed(client_call)
212
213 # We could patch out 'import_boot_images' instead here but I
214 # don't do that for 2 reasons:
215@@ -165,7 +165,7 @@
216 def test_no_download_if_no_rpc_connections(self):
217 rpc_client = Mock()
218 failure = NoConnectionsAvailable()
219- rpc_client.getClient.side_effect = failure
220+ rpc_client.getClientNow.return_value = defer.fail(failure)
221
222 deferToThread = self.patch(boot_images, 'deferToThread')
223 service = ImageDownloadService(
224
225=== modified file 'src/provisioningserver/rackdservices/tests/test_lease_socket_service.py'
226--- src/provisioningserver/rackdservices/tests/test_lease_socket_service.py 2016-10-18 11:55:44 +0000
227+++ src/provisioningserver/rackdservices/tests/test_lease_socket_service.py 2017-02-14 18:27:19 +0000
228@@ -203,7 +203,7 @@
229
230 client = getRegionClient()
231 rpc_service = MagicMock()
232- rpc_service.getClient.return_value = client
233+ rpc_service.getClientNow.return_value = defer.succeed(client)
234 service = LeaseSocketService(
235 rpc_service, reactor)
236
237
238=== modified file 'src/provisioningserver/rackdservices/tests/test_ntp.py'
239--- src/provisioningserver/rackdservices/tests/test_ntp.py 2016-10-04 11:50:27 +0000
240+++ src/provisioningserver/rackdservices/tests/test_ntp.py 2017-02-14 18:27:19 +0000
241@@ -70,7 +70,7 @@
242 class StubClusterClientService:
243 """A stub `ClusterClientService` service that's never connected."""
244
245- def getClient(self):
246+ def getClientNow(self):
247 raise exceptions.NoConnectionsAvailable()
248
249
250
251=== modified file 'src/provisioningserver/rackdservices/tests/test_service_monitor_service.py'
252--- src/provisioningserver/rackdservices/tests/test_service_monitor_service.py 2016-10-18 15:51:09 +0000
253+++ src/provisioningserver/rackdservices/tests/test_service_monitor_service.py 2017-02-14 18:27:19 +0000
254@@ -129,7 +129,7 @@
255
256 client = getRegionClient()
257 rpc_service = Mock()
258- rpc_service.getClient.return_value = client
259+ rpc_service.getClientNow.return_value = succeed(client)
260 monitor_service = sms.ServiceMonitorService(
261 rpc_service, Clock())
262 yield monitor_service.startService()
263
264=== modified file 'src/provisioningserver/rackdservices/tests/test_tftp.py'
265--- src/provisioningserver/rackdservices/tests/test_tftp.py 2016-11-01 16:29:24 +0000
266+++ src/provisioningserver/rackdservices/tests/test_tftp.py 2017-02-14 18:27:19 +0000
267@@ -286,7 +286,7 @@
268 client.localIdent = factory.make_name("system_id")
269 client.return_value = fail(BootConfigNoResponse())
270 client_service = Mock()
271- client_service.getClient.return_value = client
272+ client_service.getClientNow.return_value = succeed(client)
273 backend = TFTPBackend(
274 self.make_dir(), client_service)
275
276@@ -301,7 +301,7 @@
277 client.localIdent = factory.make_name("system_id")
278 client.return_value = fail(exception_type(exception_message))
279 client_service = Mock()
280- client_service.getClient.return_value = client
281+ client_service.getClientNow.return_value = succeed(client)
282 backend = TFTPBackend(
283 self.make_dir(), client_service)
284
285@@ -405,7 +405,7 @@
286 client.localIdent = factory.make_name("system_id")
287 client.return_value = succeed(fake_params)
288 client_service = Mock()
289- client_service.getClient.return_value = client
290+ client_service.getClientNow.return_value = succeed(client)
291
292 # get_boot_method_reader() takes a dict() of parameters and returns an
293 # `IReader` of a PXE configuration, rendered by
294@@ -446,7 +446,7 @@
295 client.localIdent = factory.make_name("system_id")
296 client.return_value = succeed(fake_params)
297 client_service = Mock()
298- client_service.getClient.return_value = client
299+ client_service.getClientNow.return_value = succeed(client)
300
301 # get_boot_method_reader() takes a dict() of parameters and returns an
302 # `IReader` of a PXE configuration, rendered by
303@@ -517,9 +517,11 @@
304 params_all = params_okay.copy()
305 params_all.update(params_other)
306
307- client_service = Mock()
308- client = client_service.getClient.return_value
309+ client = Mock()
310 client.localIdent = params_okay["system_id"]
311+ client_service = Mock()
312+ client_service.getClientNow.return_value = succeed(client)
313+
314 backend = TFTPBackend(self.make_dir(), client_service)
315 backend.fetcher = Mock()
316
317
318=== modified file 'src/provisioningserver/rackdservices/tftp.py'
319--- src/provisioningserver/rackdservices/tftp.py 2016-11-01 16:29:24 +0000
320+++ src/provisioningserver/rackdservices/tftp.py 2017-02-14 18:27:19 +0000
321@@ -235,11 +235,16 @@
322 name: params[name] for name in arguments
323 if name in params
324 }
325- client = self.client_service.getClient()
326- params["system_id"] = client.localIdent
327- d = self.fetcher(client, GetBootConfig, **params)
328- d.addCallback(self.get_boot_image, client)
329- d.addCallback(lambda data: KernelParameters(**data))
330+
331+ def fetch(client, params):
332+ params["system_id"] = client.localIdent
333+ d = self.fetcher(client, GetBootConfig, **params)
334+ d.addCallback(self.get_boot_image, client)
335+ d.addCallback(lambda data: KernelParameters(**data))
336+ return d
337+
338+ d = self.client_service.getClientNow()
339+ d.addCallback(fetch, params)
340 return d
341
342 @deferred
343
344=== modified file 'src/provisioningserver/rpc/clusterservice.py'
345--- src/provisioningserver/rpc/clusterservice.py 2016-10-28 15:58:32 +0000
346+++ src/provisioningserver/rpc/clusterservice.py 2017-02-14 18:27:19 +0000
347@@ -96,6 +96,7 @@
348 )
349 from provisioningserver.utils.twisted import (
350 callOut,
351+ deferred,
352 DeferredValue,
353 makeDeferredWithProcessProtocol,
354 )
355@@ -930,6 +931,26 @@
356 else:
357 return common.Client(random.choice(conns))
358
359+ @deferred
360+ def getClientNow(self):
361+ """Returns a `Defer` that resolves to a :class:`common.Client`
362+ connected to a region.
363+
364+ If a connection already exists to the region then this method
365+ will just return that current connection. If no connections exists
366+ this method will try its best to make a connection before returning
367+ the client.
368+
369+ :raises: :py:class:`~.exceptions.NoConnectionsAvailable` when
370+ there no connections can be made to a region controller.
371+ """
372+ try:
373+ return self.getClient()
374+ except exceptions.NoConnectionsAvailable:
375+ d = self._tryUpdate()
376+ d.addCallback(lambda _: self.getClient())
377+ return d
378+
379 def getAllClients(self):
380 """Return a list of all connected :class:`common.Client`s."""
381 return [common.Client(conn) for conn in self.connections.values()]
382
383=== modified file 'src/provisioningserver/rpc/tests/test_clusterservice.py'
384--- src/provisioningserver/rpc/tests/test_clusterservice.py 2016-10-28 15:58:32 +0000
385+++ src/provisioningserver/rpc/tests/test_clusterservice.py 2017-02-14 18:27:19 +0000
386@@ -801,6 +801,55 @@
387 exceptions.NoConnectionsAvailable,
388 service.getClient)
389
390+ @inlineCallbacks
391+ def test_getClientNow_returns_current_connection(self):
392+ service = ClusterClientService(Clock())
393+ service.connections = {
394+ sentinel.eventloop01: DummyConnection(),
395+ sentinel.eventloop02: DummyConnection(),
396+ sentinel.eventloop03: DummyConnection(),
397+ }
398+ client = yield service.getClientNow()
399+ self.assertIn(
400+ client, {
401+ common.Client(conn)
402+ for conn in service.connections.values()
403+ })
404+
405+ @inlineCallbacks
406+ def test_getClientNow_calls__tryUpdate_when_there_are_no_connections(self):
407+ service = ClusterClientService(Clock())
408+ service.connections = {}
409+
410+ def addConnections():
411+ service.connections = {
412+ sentinel.eventloop01: DummyConnection(),
413+ sentinel.eventloop02: DummyConnection(),
414+ sentinel.eventloop03: DummyConnection(),
415+ }
416+ return succeed(None)
417+
418+ self.patch(service, "_tryUpdate").side_effect = addConnections
419+ client = yield service.getClientNow()
420+ self.assertIn(
421+ client, {
422+ common.Client(conn)
423+ for conn in service.connections.values()
424+ })
425+
426+ def test_getClientNow_raises_exception_when_no_clients(self):
427+ service = ClusterClientService(Clock())
428+ service.connections = {}
429+
430+ self.patch(service, "_tryUpdate").return_value = succeed(None)
431+ d = service.getClientNow()
432+ d.addCallback(
433+ lambda _: self.fail("Errback should have been called."))
434+ d.addErrback(
435+ lambda failure: self.assertIsInstance(
436+ failure.value, exceptions.NoConnectionsAvailable))
437+ return d
438+
439 def test_getAllClients(self):
440 service = ClusterClientService(Clock())
441 uuid1 = factory.make_UUID()

Subscribers

People subscribed via source and target branches

to all changes: