7286: Compute "missing" based on "last_ping_at" instead of using API server's
[arvados.git] / services / nodemanager / tests / test_daemon.py
1 #!/usr/bin/env python
2
3 from __future__ import absolute_import, print_function
4
5 import time
6 import unittest
7
8 import mock
9 import pykka
10
11 import arvnodeman.daemon as nmdaemon
12 from arvnodeman.computenode.dispatch import ComputeNodeMonitorActor
13 from . import testutil
14
15 class NodeManagerDaemonActorTestCase(testutil.ActorTestMixin,
16                                      unittest.TestCase):
17     def new_setup_proxy(self):
18         # Make sure that every time the daemon starts a setup actor,
19         # it gets a new mock object back.
20         self.last_setup = mock.MagicMock(name='setup_proxy_mock')
21         return self.last_setup
22
23     def make_daemon(self, cloud_nodes=[], arvados_nodes=[], want_sizes=[],
24                     min_size=testutil.MockSize(1), min_nodes=0, max_nodes=8):
25         for name in ['cloud_nodes', 'arvados_nodes', 'server_wishlist']:
26             setattr(self, name + '_poller', mock.MagicMock(name=name + '_mock'))
27         self.arv_factory = mock.MagicMock(name='arvados_mock')
28         self.cloud_factory = mock.MagicMock(name='cloud_mock')
29         self.cloud_factory().node_start_time.return_value = time.time()
30         self.cloud_updates = mock.MagicMock(name='updates_mock')
31         self.timer = testutil.MockTimer(deliver_immediately=False)
32         self.node_setup = mock.MagicMock(name='setup_mock')
33         self.node_setup.start().proxy.side_effect = self.new_setup_proxy
34         self.node_setup.reset_mock()
35         self.node_shutdown = mock.MagicMock(name='shutdown_mock')
36         self.daemon = nmdaemon.NodeManagerDaemonActor.start(
37             self.server_wishlist_poller, self.arvados_nodes_poller,
38             self.cloud_nodes_poller, self.cloud_updates, self.timer,
39             self.arv_factory, self.cloud_factory,
40             [54, 5, 1], min_size, min_nodes, max_nodes, 600, 1800, 3600,
41             self.node_setup, self.node_shutdown).proxy()
42         if cloud_nodes is not None:
43             self.daemon.update_cloud_nodes(cloud_nodes).get(self.TIMEOUT)
44         if arvados_nodes is not None:
45             self.daemon.update_arvados_nodes(arvados_nodes).get(self.TIMEOUT)
46         if want_sizes is not None:
47             self.daemon.update_server_wishlist(want_sizes).get(self.TIMEOUT)
48
49     def monitor_list(self):
50         return pykka.ActorRegistry.get_by_class(ComputeNodeMonitorActor)
51
52     def monitored_arvados_nodes(self):
53         pairings = []
54         for future in [actor.proxy().arvados_node
55                        for actor in self.monitor_list()]:
56             try:
57                 pairings.append(future.get(self.TIMEOUT))
58             except pykka.ActorDeadError:
59                 pass
60         return pairings
61
62     def alive_monitor_count(self):
63         return len(self.monitored_arvados_nodes())
64
65     def assertShutdownCancellable(self, expected=True):
66         self.assertTrue(self.node_shutdown.start.called)
67         self.assertIs(expected,
68                       self.node_shutdown.start.call_args[1]['cancellable'],
69                       "ComputeNodeShutdownActor incorrectly cancellable")
70
71     def test_easy_node_creation(self):
72         size = testutil.MockSize(1)
73         self.make_daemon(want_sizes=[size])
74         self.stop_proxy(self.daemon)
75         self.assertTrue(self.node_setup.start.called)
76
77     def check_monitors_arvados_nodes(self, *arv_nodes):
78         self.assertItemsEqual(arv_nodes, self.monitored_arvados_nodes())
79
80     def test_node_pairing(self):
81         cloud_node = testutil.cloud_node_mock(1)
82         arv_node = testutil.arvados_node_mock(1)
83         self.make_daemon([cloud_node], [arv_node])
84         self.stop_proxy(self.daemon)
85         self.check_monitors_arvados_nodes(arv_node)
86
87     def test_node_pairing_after_arvados_update(self):
88         cloud_node = testutil.cloud_node_mock(2)
89         self.make_daemon([cloud_node],
90                          [testutil.arvados_node_mock(2, ip_address=None)])
91         arv_node = testutil.arvados_node_mock(2)
92         self.daemon.update_arvados_nodes([arv_node]).get(self.TIMEOUT)
93         self.stop_proxy(self.daemon)
94         self.check_monitors_arvados_nodes(arv_node)
95
96     def test_arvados_node_un_and_re_paired(self):
97         # We need to create the Arvados node mock after spinning up the daemon
98         # to make sure it's new enough to pair with the cloud node.
99         self.make_daemon([testutil.cloud_node_mock(3)], arvados_nodes=None)
100         arv_node = testutil.arvados_node_mock(3)
101         self.daemon.update_arvados_nodes([arv_node]).get(self.TIMEOUT)
102         self.check_monitors_arvados_nodes(arv_node)
103         self.daemon.update_cloud_nodes([]).get(self.TIMEOUT)
104         self.assertEqual(0, self.alive_monitor_count())
105         self.daemon.update_cloud_nodes([testutil.cloud_node_mock(3)])
106         self.stop_proxy(self.daemon)
107         self.check_monitors_arvados_nodes(arv_node)
108
109     def test_old_arvados_node_not_double_assigned(self):
110         arv_node = testutil.arvados_node_mock(3, age=9000)
111         size = testutil.MockSize(3)
112         self.make_daemon(arvados_nodes=[arv_node])
113         self.daemon.update_server_wishlist([size]).get(self.TIMEOUT)
114         self.daemon.update_server_wishlist([size, size]).get(self.TIMEOUT)
115         self.stop_proxy(self.daemon)
116         used_nodes = [call[1].get('arvados_node')
117                       for call in self.node_setup.start.call_args_list]
118         self.assertEqual(2, len(used_nodes))
119         self.assertIn(arv_node, used_nodes)
120         self.assertIn(None, used_nodes)
121
122     def test_node_count_satisfied(self):
123         self.make_daemon([testutil.cloud_node_mock()],
124                          want_sizes=[testutil.MockSize(1)])
125         self.stop_proxy(self.daemon)
126         self.assertFalse(self.node_setup.start.called)
127
128     def test_dont_count_missing_as_busy(self):
129         size = testutil.MockSize(1)
130         self.make_daemon(cloud_nodes=[testutil.cloud_node_mock(1),
131                                       testutil.cloud_node_mock(2)],
132                          arvados_nodes=[testutil.arvados_node_mock(1),
133                                       testutil.arvados_node_mock(2, last_ping_at='1970-01-01T01:02:03.04050607Z')],
134                          want_sizes=[size, size])
135         self.stop_proxy(self.daemon)
136         self.assertTrue(self.node_setup.start.called)
137
138     def test_missing_counts_towards_max(self):
139         size = testutil.MockSize(1)
140         self.make_daemon(cloud_nodes=[testutil.cloud_node_mock(1),
141                                       testutil.cloud_node_mock(2)],
142                          arvados_nodes=[testutil.arvados_node_mock(1),
143                                         testutil.arvados_node_mock(2, last_ping_at='1970-01-01T01:02:03.04050607Z')],
144                          want_sizes=[size, size],
145                          max_nodes=2)
146         self.stop_proxy(self.daemon)
147         self.assertFalse(self.node_setup.start.called)
148
149     def test_booting_nodes_counted(self):
150         cloud_node = testutil.cloud_node_mock(1)
151         arv_node = testutil.arvados_node_mock(1)
152         server_wishlist = [testutil.MockSize(1)] * 2
153         self.make_daemon([cloud_node], [arv_node], server_wishlist)
154         self.daemon.max_nodes.get(self.TIMEOUT)
155         self.assertTrue(self.node_setup.start.called)
156         self.daemon.update_server_wishlist(server_wishlist).get(self.TIMEOUT)
157         self.stop_proxy(self.daemon)
158         self.assertEqual(1, self.node_setup.start.call_count)
159
160     def test_boot_new_node_when_all_nodes_busy(self):
161         arv_node = testutil.arvados_node_mock(2, job_uuid=True)
162         self.make_daemon([testutil.cloud_node_mock(2)], [arv_node],
163                          [testutil.MockSize(2)])
164         self.stop_proxy(self.daemon)
165         self.assertTrue(self.node_setup.start.called)
166
167     def test_boot_new_node_below_min_nodes(self):
168         min_size = testutil.MockSize(1)
169         wish_size = testutil.MockSize(3)
170         self.make_daemon([], [], None, min_size=min_size, min_nodes=2)
171         self.daemon.update_server_wishlist([wish_size]).get(self.TIMEOUT)
172         self.daemon.update_cloud_nodes([]).get(self.TIMEOUT)
173         self.daemon.update_server_wishlist([wish_size]).get(self.TIMEOUT)
174         self.stop_proxy(self.daemon)
175         self.assertEqual([wish_size, min_size],
176                          [call[1].get('cloud_size')
177                           for call in self.node_setup.start.call_args_list])
178
179     def test_no_new_node_when_ge_min_nodes_busy(self):
180         cloud_nodes = [testutil.cloud_node_mock(n) for n in range(1, 4)]
181         arv_nodes = [testutil.arvados_node_mock(n, job_uuid=True)
182                      for n in range(1, 4)]
183         self.make_daemon(cloud_nodes, arv_nodes, [], min_nodes=2)
184         self.stop_proxy(self.daemon)
185         self.assertEqual(0, self.node_setup.start.call_count)
186
187     def test_no_new_node_when_max_nodes_busy(self):
188         self.make_daemon([testutil.cloud_node_mock(3)],
189                          [testutil.arvados_node_mock(3, job_uuid=True)],
190                          [testutil.MockSize(3)],
191                          max_nodes=1)
192         self.stop_proxy(self.daemon)
193         self.assertFalse(self.node_setup.start.called)
194
195     def start_node_boot(self, cloud_node=None, arv_node=None, id_num=1):
196         if cloud_node is None:
197             cloud_node = testutil.cloud_node_mock(id_num)
198         if arv_node is None:
199             arv_node = testutil.arvados_node_mock(id_num)
200         self.make_daemon(want_sizes=[testutil.MockSize(id_num)])
201         self.daemon.max_nodes.get(self.TIMEOUT)
202         self.assertEqual(1, self.node_setup.start.call_count)
203         self.last_setup.cloud_node.get.return_value = cloud_node
204         self.last_setup.arvados_node.get.return_value = arv_node
205         return self.last_setup
206
207     def test_no_new_node_when_booted_node_not_usable(self):
208         cloud_node = testutil.cloud_node_mock(4)
209         arv_node = testutil.arvados_node_mock(4, crunch_worker_state='down')
210         setup = self.start_node_boot(cloud_node, arv_node)
211         self.daemon.node_up(setup).get(self.TIMEOUT)
212         self.assertEqual(1, self.alive_monitor_count())
213         self.daemon.update_cloud_nodes([cloud_node])
214         self.daemon.update_arvados_nodes([arv_node])
215         self.daemon.update_server_wishlist(
216             [testutil.MockSize(1)]).get(self.TIMEOUT)
217         self.stop_proxy(self.daemon)
218         self.assertEqual(1, self.node_setup.start.call_count)
219
220     def test_no_duplication_when_booting_node_listed_fast(self):
221         # Test that we don't start two ComputeNodeMonitorActors when
222         # we learn about a booting node through a listing before we
223         # get the "node up" message from CloudNodeSetupActor.
224         cloud_node = testutil.cloud_node_mock(1)
225         setup = self.start_node_boot(cloud_node)
226         self.daemon.update_cloud_nodes([cloud_node])
227         self.daemon.node_up(setup).get(self.TIMEOUT)
228         self.assertEqual(1, self.alive_monitor_count())
229
230     def test_no_duplication_when_booted_node_listed(self):
231         cloud_node = testutil.cloud_node_mock(2)
232         setup = self.start_node_boot(cloud_node, id_num=2)
233         self.daemon.node_up(setup)
234         self.daemon.update_cloud_nodes([cloud_node]).get(self.TIMEOUT)
235         self.assertEqual(1, self.alive_monitor_count())
236
237     def test_node_counted_after_boot_with_slow_listing(self):
238         # Test that, after we boot a compute node, we assume it exists
239         # even it doesn't appear in the listing (e.g., because of delays
240         # propagating tags).
241         setup = self.start_node_boot()
242         self.daemon.node_up(setup).get(self.TIMEOUT)
243         self.assertEqual(1, self.alive_monitor_count())
244         self.daemon.update_cloud_nodes([]).get(self.TIMEOUT)
245         self.assertEqual(1, self.alive_monitor_count())
246
247     def test_booted_unlisted_node_counted(self):
248         setup = self.start_node_boot(id_num=1)
249         self.daemon.node_up(setup)
250         self.daemon.update_server_wishlist(
251             [testutil.MockSize(1)]).get(self.TIMEOUT)
252         self.stop_proxy(self.daemon)
253         self.assertEqual(1, self.node_setup.start.call_count)
254
255     def test_booted_node_can_shutdown(self):
256         setup = self.start_node_boot()
257         self.daemon.node_up(setup).get(self.TIMEOUT)
258         self.assertEqual(1, self.alive_monitor_count())
259         monitor = self.monitor_list()[0].proxy()
260         self.daemon.update_server_wishlist([])
261         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
262         self.stop_proxy(self.daemon)
263         self.assertTrue(self.node_shutdown.start.called,
264                         "daemon did not shut down booted node on offer")
265
266     def test_booted_node_lifecycle(self):
267         cloud_node = testutil.cloud_node_mock(6)
268         setup = self.start_node_boot(cloud_node, id_num=6)
269         self.daemon.node_up(setup).get(self.TIMEOUT)
270         self.assertEqual(1, self.alive_monitor_count())
271         monitor = self.monitor_list()[0].proxy()
272         self.daemon.update_server_wishlist([])
273         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
274         self.assertShutdownCancellable(True)
275         shutdown = self.node_shutdown.start().proxy()
276         shutdown.cloud_node.get.return_value = cloud_node
277         self.daemon.node_finished_shutdown(shutdown).get(self.TIMEOUT)
278         self.assertTrue(shutdown.stop.called,
279                         "shutdown actor not stopped after finishing")
280         self.assertTrue(monitor.actor_ref.actor_stopped.wait(self.TIMEOUT),
281                         "monitor for booted node not stopped after shutdown")
282         self.daemon.update_server_wishlist(
283             [testutil.MockSize(2)]).get(self.TIMEOUT)
284         self.stop_proxy(self.daemon)
285         self.assertTrue(self.node_setup.start.called,
286                         "second node not started after booted node stopped")
287
288     def test_booted_node_shut_down_when_never_listed(self):
289         setup = self.start_node_boot()
290         self.daemon.node_up(setup).get(self.TIMEOUT)
291         self.assertEqual(1, self.alive_monitor_count())
292         self.assertFalse(self.node_shutdown.start.called)
293         self.timer.deliver()
294         self.stop_proxy(self.daemon)
295         self.assertShutdownCancellable(False)
296
297     def test_booted_node_shut_down_when_never_paired(self):
298         cloud_node = testutil.cloud_node_mock(2)
299         setup = self.start_node_boot(cloud_node)
300         self.daemon.node_up(setup).get(self.TIMEOUT)
301         self.assertEqual(1, self.alive_monitor_count())
302         self.daemon.update_cloud_nodes([cloud_node])
303         self.timer.deliver()
304         self.stop_proxy(self.daemon)
305         self.assertShutdownCancellable(False)
306
307     def test_booted_node_shut_down_when_never_working(self):
308         cloud_node = testutil.cloud_node_mock(4)
309         arv_node = testutil.arvados_node_mock(4, crunch_worker_state='down')
310         setup = self.start_node_boot(cloud_node, arv_node)
311         self.daemon.node_up(setup).get(self.TIMEOUT)
312         self.assertEqual(1, self.alive_monitor_count())
313         self.daemon.update_cloud_nodes([cloud_node])
314         self.daemon.update_arvados_nodes([arv_node]).get(self.TIMEOUT)
315         self.timer.deliver()
316         self.stop_proxy(self.daemon)
317         self.assertShutdownCancellable(False)
318
319     def test_node_that_pairs_not_considered_failed_boot(self):
320         cloud_node = testutil.cloud_node_mock(3)
321         arv_node = testutil.arvados_node_mock(3)
322         setup = self.start_node_boot(cloud_node, arv_node)
323         self.daemon.node_up(setup).get(self.TIMEOUT)
324         self.assertEqual(1, self.alive_monitor_count())
325         self.daemon.update_cloud_nodes([cloud_node])
326         self.daemon.update_arvados_nodes([arv_node]).get(self.TIMEOUT)
327         self.timer.deliver()
328         self.stop_proxy(self.daemon)
329         self.assertFalse(self.node_shutdown.start.called)
330
331     def test_node_that_pairs_busy_not_considered_failed_boot(self):
332         cloud_node = testutil.cloud_node_mock(5)
333         arv_node = testutil.arvados_node_mock(5, job_uuid=True)
334         setup = self.start_node_boot(cloud_node, arv_node)
335         self.daemon.node_up(setup).get(self.TIMEOUT)
336         self.assertEqual(1, self.alive_monitor_count())
337         self.daemon.update_cloud_nodes([cloud_node])
338         self.daemon.update_arvados_nodes([arv_node]).get(self.TIMEOUT)
339         self.timer.deliver()
340         self.stop_proxy(self.daemon)
341         self.assertFalse(self.node_shutdown.start.called)
342
343     def test_booting_nodes_shut_down(self):
344         self.make_daemon(want_sizes=[testutil.MockSize(1)])
345         self.daemon.update_server_wishlist([]).get(self.TIMEOUT)
346         self.stop_proxy(self.daemon)
347         self.assertTrue(self.last_setup.stop_if_no_cloud_node.called)
348
349     def test_all_booting_nodes_tried_to_shut_down(self):
350         size = testutil.MockSize(2)
351         self.make_daemon(want_sizes=[size])
352         self.daemon.max_nodes.get(self.TIMEOUT)
353         setup1 = self.last_setup
354         setup1.stop_if_no_cloud_node().get.return_value = False
355         setup1.stop_if_no_cloud_node.reset_mock()
356         self.daemon.update_server_wishlist([size, size]).get(self.TIMEOUT)
357         self.daemon.max_nodes.get(self.TIMEOUT)
358         self.assertIsNot(setup1, self.last_setup)
359         self.last_setup.stop_if_no_cloud_node().get.return_value = True
360         self.last_setup.stop_if_no_cloud_node.reset_mock()
361         self.daemon.update_server_wishlist([]).get(self.TIMEOUT)
362         self.daemon.max_nodes.get(self.TIMEOUT)
363         self.stop_proxy(self.daemon)
364         self.assertEqual(1, self.last_setup.stop_if_no_cloud_node.call_count)
365         self.assertTrue(setup1.stop_if_no_cloud_node.called)
366
367     def test_shutdown_declined_at_wishlist_capacity(self):
368         cloud_node = testutil.cloud_node_mock(1)
369         size = testutil.MockSize(1)
370         self.make_daemon(cloud_nodes=[cloud_node], want_sizes=[size])
371         self.assertEqual(1, self.alive_monitor_count())
372         monitor = self.monitor_list()[0].proxy()
373         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
374         self.stop_proxy(self.daemon)
375         self.assertFalse(self.node_shutdown.start.called)
376
377     def test_shutdown_declined_below_min_nodes(self):
378         cloud_node = testutil.cloud_node_mock(1)
379         self.make_daemon(cloud_nodes=[cloud_node], min_nodes=1)
380         self.assertEqual(1, self.alive_monitor_count())
381         monitor = self.monitor_list()[0].proxy()
382         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
383         self.stop_proxy(self.daemon)
384         self.assertFalse(self.node_shutdown.start.called)
385
386     def test_shutdown_accepted_below_capacity(self):
387         self.make_daemon(cloud_nodes=[testutil.cloud_node_mock()])
388         self.assertEqual(1, self.alive_monitor_count())
389         monitor = self.monitor_list()[0].proxy()
390         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
391         self.stop_proxy(self.daemon)
392         self.assertTrue(self.node_shutdown.start.called)
393
394     def test_shutdown_declined_when_idle_and_job_queued(self):
395         cloud_nodes = [testutil.cloud_node_mock(n) for n in [3, 4]]
396         arv_nodes = [testutil.arvados_node_mock(3, job_uuid=True),
397                      testutil.arvados_node_mock(4, job_uuid=None)]
398         self.make_daemon(cloud_nodes, arv_nodes, [testutil.MockSize(1)])
399         self.assertEqual(2, self.alive_monitor_count())
400         for mon_ref in self.monitor_list():
401             monitor = mon_ref.proxy()
402             if monitor.cloud_node.get(self.TIMEOUT) is cloud_nodes[-1]:
403                 break
404         else:
405             self.fail("monitor for idle node not found")
406         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
407         self.stop_proxy(self.daemon)
408         self.assertFalse(self.node_shutdown.start.called)
409
410     def test_node_shutdown_after_cancelled_shutdown(self):
411         cloud_node = testutil.cloud_node_mock(5)
412         self.make_daemon([cloud_node], [testutil.arvados_node_mock(5)])
413         self.assertEqual(1, self.alive_monitor_count())
414         monitor = self.monitor_list()[0].proxy()
415         shutdown_proxy = self.node_shutdown.start().proxy
416         shutdown_proxy().cloud_node.get.return_value = cloud_node
417         shutdown_proxy().success.get.return_value = False
418         shutdown_proxy.reset_mock()
419         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
420         self.assertTrue(shutdown_proxy.called)
421         self.daemon.node_finished_shutdown(shutdown_proxy()).get(self.TIMEOUT)
422         shutdown_proxy().success.get.return_value = True
423         shutdown_proxy.reset_mock()
424         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
425         self.assertTrue(shutdown_proxy.called)
426
427     def test_nodes_shutting_down_replaced_below_max_nodes(self):
428         cloud_node = testutil.cloud_node_mock(6)
429         self.make_daemon([cloud_node], [testutil.arvados_node_mock(6)])
430         self.assertEqual(1, self.alive_monitor_count())
431         monitor = self.monitor_list()[0].proxy()
432         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
433         self.assertTrue(self.node_shutdown.start.called)
434         self.daemon.update_server_wishlist(
435             [testutil.MockSize(6)]).get(self.TIMEOUT)
436         self.stop_proxy(self.daemon)
437         self.assertTrue(self.node_setup.start.called)
438
439     def test_nodes_shutting_down_not_replaced_at_max_nodes(self):
440         cloud_node = testutil.cloud_node_mock(7)
441         self.make_daemon([cloud_node], [testutil.arvados_node_mock(7)],
442                          max_nodes=1)
443         self.assertEqual(1, self.alive_monitor_count())
444         monitor = self.monitor_list()[0].proxy()
445         self.daemon.node_can_shutdown(monitor).get(self.TIMEOUT)
446         self.assertTrue(self.node_shutdown.start.called)
447         self.daemon.update_server_wishlist(
448             [testutil.MockSize(7)]).get(self.TIMEOUT)
449         self.stop_proxy(self.daemon)
450         self.assertFalse(self.node_setup.start.called)
451
452     def test_nodes_shutting_down_count_against_excess(self):
453         cloud_nodes = [testutil.cloud_node_mock(n) for n in [8, 9]]
454         arv_nodes = [testutil.arvados_node_mock(n) for n in [8, 9]]
455         self.make_daemon(cloud_nodes, arv_nodes, [testutil.MockSize(8)])
456         self.assertEqual(2, self.alive_monitor_count())
457         for mon_ref in self.monitor_list():
458             self.daemon.node_can_shutdown(mon_ref.proxy()).get(self.TIMEOUT)
459         self.assertEqual(1, self.node_shutdown.start.call_count)
460
461     def test_clean_shutdown_waits_for_node_setup_finish(self):
462         new_node = self.start_node_boot()
463         new_node.stop_if_no_cloud_node().get.return_value = False
464         new_node.stop_if_no_cloud_node.reset_mock()
465         self.daemon.shutdown().get(self.TIMEOUT)
466         self.assertTrue(new_node.stop_if_no_cloud_node.called)
467         self.daemon.node_up(new_node).get(self.TIMEOUT)
468         self.assertTrue(new_node.stop.called)
469         self.timer.deliver()
470         self.assertTrue(
471             self.daemon.actor_ref.actor_stopped.wait(self.TIMEOUT))
472
473     def test_wishlist_ignored_after_shutdown(self):
474         new_node = self.start_node_boot()
475         new_node.stop_if_no_cloud_node().get.return_value = False
476         new_node.stop_if_no_cloud_node.reset_mock()
477         self.daemon.shutdown().get(self.TIMEOUT)
478         size = testutil.MockSize(2)
479         self.daemon.update_server_wishlist([size] * 2).get(self.TIMEOUT)
480         self.timer.deliver()
481         self.stop_proxy(self.daemon)
482         self.assertEqual(1, self.node_setup.start.call_count)