Merge lp:~ltrager/maas/push_rack_refresh into lp:~ltrager/maas/refresh_locking
- push_rack_refresh
- Merge into refresh_locking
Status: | Superseded |
---|---|
Proposed branch: | lp:~ltrager/maas/push_rack_refresh |
Merge into: | lp:~ltrager/maas/refresh_locking |
Diff against target: |
673 lines (+116/-218) 15 files modified
src/maasserver/api/rackcontrollers.py (+0/-15) src/maasserver/api/tests/test_rackcontroller.py (+0/-17) src/maasserver/models/node.py (+5/-1) src/maasserver/node_action.py (+0/-20) src/maasserver/rpc/rackcontrollers.py (+13/-43) src/maasserver/rpc/regionservice.py (+13/-8) src/maasserver/rpc/testing/fixtures.py (+1/-1) src/maasserver/rpc/tests/test_rackcontrollers.py (+12/-59) src/maasserver/rpc/tests/test_regionservice.py (+1/-1) src/maasserver/rpc/tests/test_regionservice_calls.py (+24/-0) src/maasserver/tests/test_node_action.py (+1/-47) src/maasserver/websockets/handlers/tests/test_general.py (+2/-2) src/provisioningserver/pserv_services/networks_monitoring_service.py (+11/-4) src/provisioningserver/pserv_services/tests/test_networks_monitoring_service.py (+20/-0) src/provisioningserver/rpc/region.py (+13/-0) |
To merge this branch: | bzr merge lp:~ltrager/maas/push_rack_refresh |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Lee Trager | Pending | ||
Review via email: mp+296865@code.launchpad.net |
This proposal has been superseded by a proposal from 2016-06-09.
Commit message
Have the rack request a refresh on connect instead of the region determining if one is needed.
Description of the change
This modifies the rack registration process so the region no longer determines if a refresh is needed. The network monitoring service will now request a refresh the first time it can connect to a region. As the network monitoring service runs every 30 seconds the refresh typically doesn't occur until the rack has been running for 30 seconds. This is because as soon as the rack starts the network monitoring service tries and fails to obtain an RPC client for the region as the connection hasn't been made yet. lp:~ltrager/maas/region_refresh modifies the region to refresh itself on start which will ensure region and rack controllers are refreshed as soon as the region service comes up.
As discussed this also removes the refresh API and websockets. Users who want to run a refresh on a controller will have to restart the maas-regiond or maas-rackd service.
Preview Diff
1 | === modified file 'src/maasserver/api/rackcontrollers.py' | |||
2 | --- src/maasserver/api/rackcontrollers.py 2016-05-16 08:36:33 +0000 | |||
3 | +++ src/maasserver/api/rackcontrollers.py 2016-06-09 19:53:21 +0000 | |||
4 | @@ -124,21 +124,6 @@ | |||
5 | 124 | 124 | ||
6 | 125 | @admin_method | 125 | @admin_method |
7 | 126 | @operation(idempotent=False) | 126 | @operation(idempotent=False) |
8 | 127 | def refresh(self, request, system_id): | ||
9 | 128 | """Refresh the hardware information for a specific rack controller. | ||
10 | 129 | |||
11 | 130 | Returns 404 if the rack-controller is not found. | ||
12 | 131 | Returns 403 if the user does not have permission to refresh the rack. | ||
13 | 132 | """ | ||
14 | 133 | rack = self.model.objects.get_node_or_404( | ||
15 | 134 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) | ||
16 | 135 | rack.refresh() | ||
17 | 136 | return HttpResponse( | ||
18 | 137 | "Refresh of %s has begun" % rack.hostname, | ||
19 | 138 | content_type=("text/plain; charset=%s" % settings.DEFAULT_CHARSET)) | ||
20 | 139 | |||
21 | 140 | @admin_method | ||
22 | 141 | @operation(idempotent=False) | ||
23 | 142 | def import_boot_images(self, request, system_id): | 127 | def import_boot_images(self, request, system_id): |
24 | 143 | """Import the boot images on this rack controller. | 128 | """Import the boot images on this rack controller. |
25 | 144 | 129 | ||
26 | 145 | 130 | ||
27 | === modified file 'src/maasserver/api/tests/test_rackcontroller.py' | |||
28 | --- src/maasserver/api/tests/test_rackcontroller.py 2016-05-24 22:05:45 +0000 | |||
29 | +++ src/maasserver/api/tests/test_rackcontroller.py 2016-06-09 19:53:21 +0000 | |||
30 | @@ -7,7 +7,6 @@ | |||
31 | 7 | 7 | ||
32 | 8 | from django.core.urlresolvers import reverse | 8 | from django.core.urlresolvers import reverse |
33 | 9 | from maasserver.api import rackcontrollers | 9 | from maasserver.api import rackcontrollers |
34 | 10 | from maasserver.models import node as node_module | ||
35 | 11 | from maasserver.testing.api import ( | 10 | from maasserver.testing.api import ( |
36 | 12 | APITestCase, | 11 | APITestCase, |
37 | 13 | explain_unexpected_response, | 12 | explain_unexpected_response, |
38 | @@ -49,22 +48,6 @@ | |||
39 | 49 | response = self.client.put(self.get_rack_uri(rack), {}) | 48 | response = self.client.put(self.get_rack_uri(rack), {}) |
40 | 50 | self.assertEqual(http.client.FORBIDDEN, response.status_code) | 49 | self.assertEqual(http.client.FORBIDDEN, response.status_code) |
41 | 51 | 50 | ||
42 | 52 | def test_POST_refresh_checks_permission(self): | ||
43 | 53 | self.patch(node_module.RackController, 'refresh') | ||
44 | 54 | rack = factory.make_RackController(owner=factory.make_User()) | ||
45 | 55 | response = self.client.post(self.get_rack_uri(rack), {'op': 'refresh'}) | ||
46 | 56 | self.assertEqual(http.client.FORBIDDEN, response.status_code) | ||
47 | 57 | |||
48 | 58 | def test_POST_refresh_returns_null(self): | ||
49 | 59 | self.patch(node_module.RackController, 'refresh') | ||
50 | 60 | self.become_admin() | ||
51 | 61 | rack = factory.make_RackController(owner=factory.make_User()) | ||
52 | 62 | response = self.client.post(self.get_rack_uri(rack), {'op': 'refresh'}) | ||
53 | 63 | self.assertEqual(http.client.OK, response.status_code) | ||
54 | 64 | self.assertEqual( | ||
55 | 65 | ('Refresh of %s has begun' % rack.hostname).encode('utf-8'), | ||
56 | 66 | response.content) | ||
57 | 67 | |||
58 | 68 | def test_POST_import_boot_images_import_to_rack_controllers(self): | 51 | def test_POST_import_boot_images_import_to_rack_controllers(self): |
59 | 69 | from maasserver.clusterrpc import boot_images | 52 | from maasserver.clusterrpc import boot_images |
60 | 70 | self.patch(boot_images, "RackControllersImporter") | 53 | self.patch(boot_images, "RackControllersImporter") |
61 | 71 | 54 | ||
62 | === modified file 'src/maasserver/models/node.py' | |||
63 | --- src/maasserver/models/node.py 2016-06-08 08:37:14 +0000 | |||
64 | +++ src/maasserver/models/node.py 2016-06-09 19:53:21 +0000 | |||
65 | @@ -3606,7 +3606,11 @@ | |||
66 | 3606 | def _get_token_for_controller(self): | 3606 | def _get_token_for_controller(self): |
67 | 3607 | # Avoid circular imports. | 3607 | # Avoid circular imports. |
68 | 3608 | from metadataserver.models import NodeKey | 3608 | from metadataserver.models import NodeKey |
70 | 3609 | return NodeKey.objects.get_token_for_node(self) | 3609 | token = NodeKey.objects.get_token_for_node(self) |
71 | 3610 | # Pull consumer into memory so it can be accessed outside a | ||
72 | 3611 | # database thread | ||
73 | 3612 | token.consumer | ||
74 | 3613 | return token | ||
75 | 3610 | 3614 | ||
76 | 3611 | @transactional | 3615 | @transactional |
77 | 3612 | def _get_current_node_result_ids(self): | 3616 | def _get_current_node_result_ids(self): |
78 | 3613 | 3617 | ||
79 | === modified file 'src/maasserver/node_action.py' | |||
80 | --- src/maasserver/node_action.py 2016-05-26 06:47:45 +0000 | |||
81 | +++ src/maasserver/node_action.py 2016-06-09 19:53:21 +0000 | |||
82 | @@ -482,25 +482,6 @@ | |||
83 | 482 | raise NodeActionError(exception) | 482 | raise NodeActionError(exception) |
84 | 483 | 483 | ||
85 | 484 | 484 | ||
86 | 485 | class Refresh(NodeAction): | ||
87 | 486 | """Refresh a rack or region and rack controller.""" | ||
88 | 487 | name = "refresh" | ||
89 | 488 | display = "Refresh" | ||
90 | 489 | display_sentence = "refreshed" | ||
91 | 490 | permission = NODE_PERMISSION.ADMIN | ||
92 | 491 | for_type = { | ||
93 | 492 | NODE_TYPE.RACK_CONTROLLER, | ||
94 | 493 | NODE_TYPE.REGION_AND_RACK_CONTROLLER | ||
95 | 494 | } | ||
96 | 495 | |||
97 | 496 | def execute(self): | ||
98 | 497 | """See `NodeAction.execute`.""" | ||
99 | 498 | try: | ||
100 | 499 | self.node.refresh() | ||
101 | 500 | except RPC_EXCEPTIONS + (ExternalProcessError,) as exception: | ||
102 | 501 | raise NodeActionError(exception) | ||
103 | 502 | |||
104 | 503 | |||
105 | 504 | ACTION_CLASSES = ( | 485 | ACTION_CLASSES = ( |
106 | 505 | Commission, | 486 | Commission, |
107 | 506 | Acquire, | 487 | Acquire, |
108 | @@ -514,7 +495,6 @@ | |||
109 | 514 | SetZone, | 495 | SetZone, |
110 | 515 | Delete, | 496 | Delete, |
111 | 516 | ImportImages, | 497 | ImportImages, |
112 | 517 | Refresh, | ||
113 | 518 | ) | 498 | ) |
114 | 519 | 499 | ||
115 | 520 | ACTIONS_DICT = OrderedDict((action.name, action) for action in ACTION_CLASSES) | 500 | ACTIONS_DICT = OrderedDict((action.name, action) for action in ACTION_CLASSES) |
116 | 521 | 501 | ||
117 | === modified file 'src/maasserver/rpc/rackcontrollers.py' | |||
118 | --- src/maasserver/rpc/rackcontrollers.py 2016-05-31 19:15:43 +0000 | |||
119 | +++ src/maasserver/rpc/rackcontrollers.py 2016-06-09 19:53:21 +0000 | |||
120 | @@ -80,7 +80,7 @@ | |||
121 | 80 | registered and successfully connected we will refresh all commissioning | 80 | registered and successfully connected we will refresh all commissioning |
122 | 81 | data. | 81 | data. |
123 | 82 | 82 | ||
125 | 83 | :return: A ``(rack-controller, refresh-hint)`` tuple. | 83 | :return: A ``rack-controller``. |
126 | 84 | """ | 84 | """ |
127 | 85 | if interfaces is None: | 85 | if interfaces is None: |
128 | 86 | interfaces = {} | 86 | interfaces = {} |
129 | @@ -91,13 +91,18 @@ | |||
130 | 91 | maaslog.info("Created new rack controller %s.", node.hostname) | 91 | maaslog.info("Created new rack controller %s.", node.hostname) |
131 | 92 | elif node.is_rack_controller: | 92 | elif node.is_rack_controller: |
132 | 93 | maaslog.info("Registering existing rack controller %s.", node.hostname) | 93 | maaslog.info("Registering existing rack controller %s.", node.hostname) |
133 | 94 | elif node.is_region_controller: | ||
134 | 95 | maaslog.info( | ||
135 | 96 | "Converting %s into a region and rack controller.", node.hostname) | ||
136 | 97 | node.node_type = NODE_TYPE.REGION_AND_RACK_CONTROLLER | ||
137 | 98 | node.save() | ||
138 | 94 | else: | 99 | else: |
145 | 95 | maaslog.info( | 100 | maaslog.info("Converting %s into a rack controller.", node.hostname) |
146 | 96 | "Found existing node %s as candidate for rack controller.", | 101 | node.node_type = NODE_TYPE.RACK_CONTROLLER |
147 | 97 | node.hostname) | 102 | node.save() |
148 | 98 | 103 | ||
149 | 99 | # This may be a no-op, but it does provide us with a refresh hint. | 104 | rackcontroller = typecast_node(node, RackController) |
150 | 100 | rackcontroller, needs_refresh = upgrade(node) | 105 | |
151 | 101 | # Update `rackcontroller.url` from the given URL, but only when the | 106 | # Update `rackcontroller.url` from the given URL, but only when the |
152 | 102 | # hostname is not 'localhost' (i.e. the default value used when the master | 107 | # hostname is not 'localhost' (i.e. the default value used when the master |
153 | 103 | # cluster connects). | 108 | # cluster connects). |
154 | @@ -112,8 +117,7 @@ | |||
155 | 112 | rackcontroller.save(update_fields=update_fields) | 117 | rackcontroller.save(update_fields=update_fields) |
156 | 113 | # Update networking information every time we see a rack. | 118 | # Update networking information every time we see a rack. |
157 | 114 | rackcontroller.update_interfaces(interfaces) | 119 | rackcontroller.update_interfaces(interfaces) |
160 | 115 | # Hint to callers whether or not this rack needs to be refreshed. | 120 | return rackcontroller |
159 | 116 | return rackcontroller, needs_refresh | ||
161 | 117 | 121 | ||
162 | 118 | 122 | ||
163 | 119 | @typed | 123 | @typed |
164 | @@ -136,40 +140,6 @@ | |||
165 | 136 | return Node.objects.filter(query).first() | 140 | return Node.objects.filter(query).first() |
166 | 137 | 141 | ||
167 | 138 | 142 | ||
168 | 139 | def upgrade(node): | ||
169 | 140 | """Upgrade `node` to a rack controller if it isn't already. | ||
170 | 141 | |||
171 | 142 | Return a hint as to whether a refresh is necessary. | ||
172 | 143 | |||
173 | 144 | :return: A ``(rack-controller, refresh-hint)`` tuple. | ||
174 | 145 | """ | ||
175 | 146 | # Refresh whenever an existing node is converted for use as a rack | ||
176 | 147 | # controller. This is needed for two reasons. First, when the region starts | ||
177 | 148 | # it creates a node for itself but only gathers networking information. | ||
178 | 149 | # Second, information about the node may have changed since its last use. | ||
179 | 150 | needs_refresh = True | ||
180 | 151 | |||
181 | 152 | if node.is_rack_controller: | ||
182 | 153 | # We don't want to refresh existing rack controllers as each time a | ||
183 | 154 | # rack controller connects to a region it creates four connections. | ||
184 | 155 | # This means for every region we connect to we would refresh | ||
185 | 156 | # 4 * regions every time the rack controller restarts. If the cpu_count | ||
186 | 157 | # and memory is non-zero our information at this point should be | ||
187 | 158 | # current and the user can always manually refresh. | ||
188 | 159 | needs_refresh = (node.cpu_count == 0 or node.memory == 0) | ||
189 | 160 | elif node.is_region_controller: | ||
190 | 161 | maaslog.info( | ||
191 | 162 | "Converting %s into a region and rack controller.", node.hostname) | ||
192 | 163 | node.node_type = NODE_TYPE.REGION_AND_RACK_CONTROLLER | ||
193 | 164 | node.save() | ||
194 | 165 | else: | ||
195 | 166 | maaslog.info("Converting %s into a rack controller.", node.hostname) | ||
196 | 167 | node.node_type = NODE_TYPE.RACK_CONTROLLER | ||
197 | 168 | node.save() | ||
198 | 169 | |||
199 | 170 | return typecast_node(node, RackController), needs_refresh | ||
200 | 171 | |||
201 | 172 | |||
202 | 173 | @transactional | 143 | @transactional |
203 | 174 | def update_foreign_dhcp(system_id, interface_name, dhcp_ip=None): | 144 | def update_foreign_dhcp(system_id, interface_name, dhcp_ip=None): |
204 | 175 | """Update the external_dhcp field of the VLAN for the interface. | 145 | """Update the external_dhcp field of the VLAN for the interface. |
205 | 176 | 146 | ||
206 | === modified file 'src/maasserver/rpc/regionservice.py' | |||
207 | --- src/maasserver/rpc/regionservice.py 2016-05-27 13:14:01 +0000 | |||
208 | +++ src/maasserver/rpc/regionservice.py 2016-06-09 19:53:21 +0000 | |||
209 | @@ -445,6 +445,18 @@ | |||
210 | 445 | return deferToDatabase( | 445 | return deferToDatabase( |
211 | 446 | update_services, system_id, services) | 446 | update_services, system_id, services) |
212 | 447 | 447 | ||
213 | 448 | @region.RequestRackRefresh.responder | ||
214 | 449 | def request_rack_refresh(self, system_id): | ||
215 | 450 | """Request a refresh of the rack | ||
216 | 451 | |||
217 | 452 | Implementation of | ||
218 | 453 | :py:class:`~provisioningserver.rpc.region.RequestRackRefresh`. | ||
219 | 454 | """ | ||
220 | 455 | d = deferToDatabase(RackController.objects.get, system_id=system_id) | ||
221 | 456 | d.addCallback(lambda rack: rack.refresh()) | ||
222 | 457 | d.addCallback(lambda _: {}) | ||
223 | 458 | return d | ||
224 | 459 | |||
225 | 448 | 460 | ||
226 | 449 | @transactional | 461 | @transactional |
227 | 450 | def registerConnection(region_id, rack_controller, host): | 462 | def registerConnection(region_id, rack_controller, host): |
228 | @@ -534,7 +546,7 @@ | |||
229 | 534 | 546 | ||
230 | 535 | try: | 547 | try: |
231 | 536 | # Register, which includes updating interfaces. | 548 | # Register, which includes updating interfaces. |
233 | 537 | rack_controller, needs_refresh = yield deferToDatabase( | 549 | rack_controller = yield deferToDatabase( |
234 | 538 | rackcontrollers.register, system_id=system_id, | 550 | rackcontrollers.register, system_id=system_id, |
235 | 539 | hostname=hostname, interfaces=interfaces, url=url) | 551 | hostname=hostname, interfaces=interfaces, url=url) |
236 | 540 | 552 | ||
237 | @@ -568,13 +580,6 @@ | |||
238 | 568 | # the information about the rack controller if needed. | 580 | # the information about the rack controller if needed. |
239 | 569 | log.msg( | 581 | log.msg( |
240 | 570 | "Rack controller '%s' has been registered." % self.ident) | 582 | "Rack controller '%s' has been registered." % self.ident) |
241 | 571 | if self.hostIsRemote and needs_refresh: | ||
242 | 572 | # Needs to be refresh. Perform this operation in a thread but | ||
243 | 573 | # we ignore when it is done. | ||
244 | 574 | log.msg( | ||
245 | 575 | "Rack controller '%s' needs to be refreshed; " | ||
246 | 576 | "performing the refresh operation." % self.ident) | ||
247 | 577 | deferToDatabase(rack_controller.refresh) | ||
248 | 578 | 583 | ||
249 | 579 | # Done registering the rack controller and connection. | 584 | # Done registering the rack controller and connection. |
250 | 580 | return {'system_id': self.ident} | 585 | return {'system_id': self.ident} |
251 | 581 | 586 | ||
252 | === modified file 'src/maasserver/rpc/testing/fixtures.py' | |||
253 | --- src/maasserver/rpc/testing/fixtures.py 2016-05-27 13:14:01 +0000 | |||
254 | +++ src/maasserver/rpc/testing/fixtures.py 2016-06-09 19:53:21 +0000 | |||
255 | @@ -321,7 +321,7 @@ | |||
256 | 321 | # Mock the registration into the database, as the rack controller is | 321 | # Mock the registration into the database, as the rack controller is |
257 | 322 | # already created. We reset this once registration is complete so as | 322 | # already created. We reset this once registration is complete so as |
258 | 323 | # to not interfere with other tests. | 323 | # to not interfere with other tests. |
260 | 324 | registered = rack_controller, False # Hint that no refresh needed. | 324 | registered = rack_controller |
261 | 325 | patcher = MonkeyPatcher() | 325 | patcher = MonkeyPatcher() |
262 | 326 | patcher.add_patch( | 326 | patcher.add_patch( |
263 | 327 | rackcontrollers, "register", | 327 | rackcontrollers, "register", |
264 | 328 | 328 | ||
265 | === modified file 'src/maasserver/rpc/tests/test_rackcontrollers.py' | |||
266 | --- src/maasserver/rpc/tests/test_rackcontrollers.py 2016-05-31 19:17:13 +0000 | |||
267 | +++ src/maasserver/rpc/tests/test_rackcontrollers.py 2016-06-09 19:53:21 +0000 | |||
268 | @@ -108,23 +108,23 @@ | |||
269 | 108 | 108 | ||
270 | 109 | def test_sets_owner_to_worker_when_none(self): | 109 | def test_sets_owner_to_worker_when_none(self): |
271 | 110 | node = factory.make_Node() | 110 | node = factory.make_Node() |
273 | 111 | rack_registered, needs_refresh = register(system_id=node.system_id) | 111 | rack_registered = register(system_id=node.system_id) |
274 | 112 | self.assertEqual(worker_user.get_worker_user(), rack_registered.owner) | 112 | self.assertEqual(worker_user.get_worker_user(), rack_registered.owner) |
275 | 113 | 113 | ||
276 | 114 | def test_leaves_owner_when_owned(self): | 114 | def test_leaves_owner_when_owned(self): |
277 | 115 | user = factory.make_User() | 115 | user = factory.make_User() |
278 | 116 | node = factory.make_Machine(owner=user) | 116 | node = factory.make_Machine(owner=user) |
280 | 117 | rack_registered, needs_refresh = register(system_id=node.system_id) | 117 | rack_registered = register(system_id=node.system_id) |
281 | 118 | self.assertEqual(user, rack_registered.owner) | 118 | self.assertEqual(user, rack_registered.owner) |
282 | 119 | 119 | ||
283 | 120 | def test_finds_existing_node_by_system_id(self): | 120 | def test_finds_existing_node_by_system_id(self): |
284 | 121 | node = factory.make_Node() | 121 | node = factory.make_Node() |
286 | 122 | rack_registered, needs_refresh = register(system_id=node.system_id) | 122 | rack_registered = register(system_id=node.system_id) |
287 | 123 | self.assertEqual(node.system_id, rack_registered.system_id) | 123 | self.assertEqual(node.system_id, rack_registered.system_id) |
288 | 124 | 124 | ||
289 | 125 | def test_finds_existing_node_by_hostname(self): | 125 | def test_finds_existing_node_by_hostname(self): |
290 | 126 | node = factory.make_Node() | 126 | node = factory.make_Node() |
292 | 127 | rack_registered, needs_refresh = register(hostname=node.hostname) | 127 | rack_registered = register(hostname=node.hostname) |
293 | 128 | self.assertEqual(node.system_id, rack_registered.system_id) | 128 | self.assertEqual(node.system_id, rack_registered.system_id) |
294 | 129 | 129 | ||
295 | 130 | def test_finds_existing_node_by_mac(self): | 130 | def test_finds_existing_node_by_mac(self): |
296 | @@ -140,39 +140,9 @@ | |||
297 | 140 | "enabled": True, | 140 | "enabled": True, |
298 | 141 | } | 141 | } |
299 | 142 | } | 142 | } |
301 | 143 | rack_registered, needs_refresh = register(interfaces=interfaces) | 143 | rack_registered = register(interfaces=interfaces) |
302 | 144 | self.assertEqual(node.system_id, rack_registered.system_id) | 144 | self.assertEqual(node.system_id, rack_registered.system_id) |
303 | 145 | 145 | ||
304 | 146 | def test_finds_existing_controller_needs_refresh_with_bad_info(self): | ||
305 | 147 | node_type = random.choice([ | ||
306 | 148 | NODE_TYPE.RACK_CONTROLLER, | ||
307 | 149 | NODE_TYPE.REGION_AND_RACK_CONTROLLER, | ||
308 | 150 | ]) | ||
309 | 151 | node = factory.make_Node(node_type=node_type) | ||
310 | 152 | rack_registered, needs_refresh = register(system_id=node.system_id) | ||
311 | 153 | self.assertTrue(needs_refresh) | ||
312 | 154 | |||
313 | 155 | def test_finds_existing_controller_doesnt_need_refresh_good_info(self): | ||
314 | 156 | node_type = random.choice([ | ||
315 | 157 | NODE_TYPE.RACK_CONTROLLER, | ||
316 | 158 | NODE_TYPE.REGION_AND_RACK_CONTROLLER, | ||
317 | 159 | ]) | ||
318 | 160 | node = factory.make_Node( | ||
319 | 161 | node_type=node_type, cpu_count=random.randint(1, 32), | ||
320 | 162 | memory=random.randint(1024, 8096)) | ||
321 | 163 | rack_registered, needs_refresh = register(system_id=node.system_id) | ||
322 | 164 | self.assertFalse(needs_refresh) | ||
323 | 165 | |||
324 | 166 | def test_converts_existing_node_sets_needs_refresh_to_true(self): | ||
325 | 167 | node_type = random.choice([ | ||
326 | 168 | NODE_TYPE.MACHINE, | ||
327 | 169 | NODE_TYPE.DEVICE, | ||
328 | 170 | NODE_TYPE.REGION_CONTROLLER, | ||
329 | 171 | ]) | ||
330 | 172 | node = factory.make_Node(node_type=node_type) | ||
331 | 173 | rack_registered, needs_refresh = register(system_id=node.system_id) | ||
332 | 174 | self.assertTrue(needs_refresh) | ||
333 | 175 | |||
334 | 176 | def test_find_existing_keeps_type(self): | 146 | def test_find_existing_keeps_type(self): |
335 | 177 | node_type = random.choice( | 147 | node_type = random.choice( |
336 | 178 | (NODE_TYPE.RACK_CONTROLLER, NODE_TYPE.REGION_AND_RACK_CONTROLLER)) | 148 | (NODE_TYPE.RACK_CONTROLLER, NODE_TYPE.REGION_AND_RACK_CONTROLLER)) |
337 | @@ -190,7 +160,7 @@ | |||
338 | 190 | 160 | ||
339 | 191 | def test_converts_region_controller(self): | 161 | def test_converts_region_controller(self): |
340 | 192 | node = factory.make_Node(node_type=NODE_TYPE.REGION_CONTROLLER) | 162 | node = factory.make_Node(node_type=NODE_TYPE.REGION_CONTROLLER) |
342 | 193 | rack_registered, needs_refresh = register(system_id=node.system_id) | 163 | rack_registered = register(system_id=node.system_id) |
343 | 194 | self.assertEqual( | 164 | self.assertEqual( |
344 | 195 | rack_registered.node_type, NODE_TYPE.REGION_AND_RACK_CONTROLLER) | 165 | rack_registered.node_type, NODE_TYPE.REGION_AND_RACK_CONTROLLER) |
345 | 196 | 166 | ||
346 | @@ -199,14 +169,12 @@ | |||
347 | 199 | node = factory.make_Node(node_type=NODE_TYPE.REGION_CONTROLLER) | 169 | node = factory.make_Node(node_type=NODE_TYPE.REGION_CONTROLLER) |
348 | 200 | register(system_id=node.system_id) | 170 | register(system_id=node.system_id) |
349 | 201 | self.assertEqual( | 171 | self.assertEqual( |
354 | 202 | "Found existing node %s as candidate for rack controller.\n" | 172 | "Converting %s into a region and rack controller.\n" % |
355 | 203 | "Converting %s into a region and rack controller.\n" | 173 | node.hostname, logger.output) |
352 | 204 | % (node.hostname, node.hostname), | ||
353 | 205 | logger.output) | ||
356 | 206 | 174 | ||
357 | 207 | def test_converts_existing_node(self): | 175 | def test_converts_existing_node(self): |
358 | 208 | node = factory.make_Node(node_type=NODE_TYPE.MACHINE) | 176 | node = factory.make_Node(node_type=NODE_TYPE.MACHINE) |
360 | 209 | rack_registered, needs_refresh = register(system_id=node.system_id) | 177 | rack_registered = register(system_id=node.system_id) |
361 | 210 | self.assertEqual(rack_registered.node_type, NODE_TYPE.RACK_CONTROLLER) | 178 | self.assertEqual(rack_registered.node_type, NODE_TYPE.RACK_CONTROLLER) |
362 | 211 | 179 | ||
363 | 212 | def test_logs_converting_existing_node(self): | 180 | def test_logs_converting_existing_node(self): |
364 | @@ -214,9 +182,7 @@ | |||
365 | 214 | node = factory.make_Node(node_type=NODE_TYPE.MACHINE) | 182 | node = factory.make_Node(node_type=NODE_TYPE.MACHINE) |
366 | 215 | register(system_id=node.system_id) | 183 | register(system_id=node.system_id) |
367 | 216 | self.assertEqual( | 184 | self.assertEqual( |
371 | 217 | "Found existing node %s as candidate for rack controller.\n" | 185 | "Converting %s into a rack controller.\n" % node.hostname, |
369 | 218 | "Converting %s into a rack controller.\n" | ||
370 | 219 | % (node.hostname, node.hostname), | ||
372 | 220 | logger.output) | 186 | logger.output) |
373 | 221 | 187 | ||
374 | 222 | def test_creates_new_rackcontroller(self): | 188 | def test_creates_new_rackcontroller(self): |
375 | @@ -234,19 +200,6 @@ | |||
376 | 234 | register(interfaces=interfaces) | 200 | register(interfaces=interfaces) |
377 | 235 | self.assertEqual(node_count + 1, len(Node.objects.all())) | 201 | self.assertEqual(node_count + 1, len(Node.objects.all())) |
378 | 236 | 202 | ||
379 | 237 | def test_creates_new_rackcontroller_sets_needs_refresh_to_true(self): | ||
380 | 238 | interfaces = { | ||
381 | 239 | factory.make_name("eth0"): { | ||
382 | 240 | "type": "physical", | ||
383 | 241 | "mac_address": factory.make_mac_address(), | ||
384 | 242 | "parents": [], | ||
385 | 243 | "links": [], | ||
386 | 244 | "enabled": True, | ||
387 | 245 | } | ||
388 | 246 | } | ||
389 | 247 | rack_registered, needs_refresh = register(interfaces=interfaces) | ||
390 | 248 | self.assertTrue(needs_refresh) | ||
391 | 249 | |||
392 | 250 | def test_logs_creating_new_rackcontroller(self): | 203 | def test_logs_creating_new_rackcontroller(self): |
393 | 251 | logger = self.useFixture(FakeLogger("maas")) | 204 | logger = self.useFixture(FakeLogger("maas")) |
394 | 252 | hostname = factory.make_name("hostname") | 205 | hostname = factory.make_name("hostname") |
395 | @@ -266,7 +219,7 @@ | |||
396 | 266 | "enabled": True, | 219 | "enabled": True, |
397 | 267 | } | 220 | } |
398 | 268 | } | 221 | } |
400 | 269 | rack_registered, needs_refresh = register(interfaces=interfaces) | 222 | rack_registered = register(interfaces=interfaces) |
401 | 270 | self.assertThat( | 223 | self.assertThat( |
402 | 271 | rack_registered.interface_set.all(), | 224 | rack_registered.interface_set.all(), |
403 | 272 | MatchesSetwise(*( | 225 | MatchesSetwise(*( |
404 | @@ -293,7 +246,7 @@ | |||
405 | 293 | "enabled": True, | 246 | "enabled": True, |
406 | 294 | } | 247 | } |
407 | 295 | } | 248 | } |
409 | 296 | rack_registered, needs_refresh = register( | 249 | rack_registered = register( |
410 | 297 | rack_controller.system_id, interfaces=interfaces) | 250 | rack_controller.system_id, interfaces=interfaces) |
411 | 298 | self.assertThat( | 251 | self.assertThat( |
412 | 299 | rack_registered.interface_set.all(), | 252 | rack_registered.interface_set.all(), |
413 | 300 | 253 | ||
414 | === modified file 'src/maasserver/rpc/tests/test_regionservice.py' | |||
415 | --- src/maasserver/rpc/tests/test_regionservice.py 2016-05-27 13:14:01 +0000 | |||
416 | +++ src/maasserver/rpc/tests/test_regionservice.py 2016-06-09 19:53:21 +0000 | |||
417 | @@ -523,7 +523,7 @@ | |||
418 | 523 | protocol.transport.getHost.return_value = host | 523 | protocol.transport.getHost.return_value = host |
419 | 524 | mock_deferToDatabase = self.patch(regionservice, "deferToDatabase") | 524 | mock_deferToDatabase = self.patch(regionservice, "deferToDatabase") |
420 | 525 | mock_deferToDatabase.side_effect = [ | 525 | mock_deferToDatabase.side_effect = [ |
422 | 526 | succeed((rack_controller, False)), | 526 | succeed(rack_controller), |
423 | 527 | succeed(None), | 527 | succeed(None), |
424 | 528 | ] | 528 | ] |
425 | 529 | yield call_responder( | 529 | yield call_responder( |
426 | 530 | 530 | ||
427 | === modified file 'src/maasserver/rpc/tests/test_regionservice_calls.py' | |||
428 | --- src/maasserver/rpc/tests/test_regionservice_calls.py 2016-05-25 21:03:50 +0000 | |||
429 | +++ src/maasserver/rpc/tests/test_regionservice_calls.py 2016-06-09 19:53:21 +0000 | |||
430 | @@ -59,6 +59,7 @@ | |||
431 | 59 | ) | 59 | ) |
432 | 60 | from maasserver.utils.threads import deferToDatabase | 60 | from maasserver.utils.threads import deferToDatabase |
433 | 61 | from maastesting.matchers import ( | 61 | from maastesting.matchers import ( |
434 | 62 | MockCalledOnce, | ||
435 | 62 | MockCalledOnceWith, | 63 | MockCalledOnceWith, |
436 | 63 | MockCalledWith, | 64 | MockCalledWith, |
437 | 64 | ) | 65 | ) |
438 | @@ -84,6 +85,7 @@ | |||
439 | 84 | ReportBootImages, | 85 | ReportBootImages, |
440 | 85 | ReportForeignDHCPServer, | 86 | ReportForeignDHCPServer, |
441 | 86 | RequestNodeInfoByMACAddress, | 87 | RequestNodeInfoByMACAddress, |
442 | 88 | RequestRackRefresh, | ||
443 | 87 | SendEvent, | 89 | SendEvent, |
444 | 88 | SendEventMACAddress, | 90 | SendEventMACAddress, |
445 | 89 | UpdateInterfaces, | 91 | UpdateInterfaces, |
446 | @@ -1233,3 +1235,25 @@ | |||
447 | 1233 | params = {'mac_address': factory.make_mac_address()} | 1235 | params = {'mac_address': factory.make_mac_address()} |
448 | 1234 | d = call_responder(Region(), RequestNodeInfoByMACAddress, params) | 1236 | d = call_responder(Region(), RequestNodeInfoByMACAddress, params) |
449 | 1235 | return assert_fails_with(d, NoSuchNode) | 1237 | return assert_fails_with(d, NoSuchNode) |
450 | 1238 | |||
451 | 1239 | |||
452 | 1240 | class TestRegionProtocol_RequestRefresh(MAASTransactionServerTestCase): | ||
453 | 1241 | |||
454 | 1242 | def test_request_refresh_is_registered(self): | ||
455 | 1243 | protocol = Region() | ||
456 | 1244 | responder = protocol.locateResponder( | ||
457 | 1245 | RequestRackRefresh.commandName) | ||
458 | 1246 | self.assertIsNotNone(responder) | ||
459 | 1247 | |||
460 | 1248 | @wait_for_reactor | ||
461 | 1249 | @inlineCallbacks | ||
462 | 1250 | def test_calls_refresh(self): | ||
463 | 1251 | rack = yield deferToDatabase(factory.make_RackController) | ||
464 | 1252 | self.patch(regionservice, 'deferToDatabase').return_value = succeed( | ||
465 | 1253 | rack) | ||
466 | 1254 | mock_refresh = self.patch(rack, 'refresh') | ||
467 | 1255 | response = yield call_responder( | ||
468 | 1256 | Region(), RequestRackRefresh, {'system_id': rack.system_id}) | ||
469 | 1257 | self.assertIsNotNone(response) | ||
470 | 1258 | |||
471 | 1259 | self.assertThat(mock_refresh, MockCalledOnce()) | ||
472 | 1236 | 1260 | ||
473 | === modified file 'src/maasserver/tests/test_node_action.py' | |||
474 | --- src/maasserver/tests/test_node_action.py 2016-05-26 06:47:45 +0000 | |||
475 | +++ src/maasserver/tests/test_node_action.py 2016-06-09 19:53:21 +0000 | |||
476 | @@ -5,7 +5,6 @@ | |||
477 | 5 | 5 | ||
478 | 6 | __all__ = [] | 6 | __all__ = [] |
479 | 7 | 7 | ||
480 | 8 | import random | ||
481 | 9 | from unittest.mock import ANY | 8 | from unittest.mock import ANY |
482 | 10 | 9 | ||
483 | 11 | from django.db import transaction | 10 | from django.db import transaction |
484 | @@ -28,10 +27,7 @@ | |||
485 | 28 | signals, | 27 | signals, |
486 | 29 | StaticIPAddress, | 28 | StaticIPAddress, |
487 | 30 | ) | 29 | ) |
492 | 31 | from maasserver.models.node import ( | 30 | from maasserver.models.node import typecast_to_node_type |
489 | 32 | RackController, | ||
490 | 33 | typecast_to_node_type, | ||
491 | 34 | ) | ||
493 | 35 | from maasserver.models.signals.testing import SignalsDisabled | 31 | from maasserver.models.signals.testing import SignalsDisabled |
494 | 36 | from maasserver.node_action import ( | 32 | from maasserver.node_action import ( |
495 | 37 | Abort, | 33 | Abort, |
496 | @@ -46,7 +42,6 @@ | |||
497 | 46 | NodeAction, | 42 | NodeAction, |
498 | 47 | PowerOff, | 43 | PowerOff, |
499 | 48 | PowerOn, | 44 | PowerOn, |
500 | 49 | Refresh, | ||
501 | 50 | Release, | 45 | Release, |
502 | 51 | RPC_EXCEPTIONS, | 46 | RPC_EXCEPTIONS, |
503 | 52 | SetZone, | 47 | SetZone, |
504 | @@ -854,33 +849,6 @@ | |||
505 | 854 | self.assertFalse(ImportImages(node, user).is_actionable()) | 849 | self.assertFalse(ImportImages(node, user).is_actionable()) |
506 | 855 | 850 | ||
507 | 856 | 851 | ||
508 | 857 | class TestRefresh(MAASServerTestCase): | ||
509 | 858 | |||
510 | 859 | def test_refresh(self): | ||
511 | 860 | user = factory.make_admin() | ||
512 | 861 | rack = factory.make_RackController() | ||
513 | 862 | mock_refresh = self.patch(rack, 'refresh') | ||
514 | 863 | |||
515 | 864 | with post_commit_hooks: | ||
516 | 865 | Refresh(rack, user).execute() | ||
517 | 866 | |||
518 | 867 | self.assertThat(mock_refresh, MockCalledOnce()) | ||
519 | 868 | |||
520 | 869 | def test_requires_admin_permission(self): | ||
521 | 870 | user = factory.make_User() | ||
522 | 871 | rack = factory.make_RackController() | ||
523 | 872 | self.assertFalse(Refresh(rack, user).is_permitted()) | ||
524 | 873 | |||
525 | 874 | def test_requires_rack(self): | ||
526 | 875 | user = factory.make_User() | ||
527 | 876 | node = factory.make_Node( | ||
528 | 877 | node_type=factory.pick_choice( | ||
529 | 878 | NODE_TYPE_CHOICES, but_not=[ | ||
530 | 879 | NODE_TYPE.RACK_CONTROLLER, | ||
531 | 880 | NODE_TYPE.REGION_AND_RACK_CONTROLLER])) | ||
532 | 881 | self.assertFalse(Refresh(node, user).is_actionable()) | ||
533 | 882 | |||
534 | 883 | |||
535 | 884 | class TestActionsErrorHandling(MAASServerTestCase): | 852 | class TestActionsErrorHandling(MAASServerTestCase): |
536 | 885 | """Tests for error handling in actions. | 853 | """Tests for error handling in actions. |
537 | 886 | 854 | ||
538 | @@ -904,8 +872,6 @@ | |||
539 | 904 | exception = self.make_exception() | 872 | exception = self.make_exception() |
540 | 905 | self.patch(node, '_start').side_effect = exception | 873 | self.patch(node, '_start').side_effect = exception |
541 | 906 | self.patch(node, '_stop').side_effect = exception | 874 | self.patch(node, '_stop').side_effect = exception |
542 | 907 | if isinstance(node, RackController): | ||
543 | 908 | self.patch(node, 'refresh').side_effect = exception | ||
544 | 909 | 875 | ||
545 | 910 | def make_action( | 876 | def make_action( |
546 | 911 | self, action_class, node_status, power_state=None, | 877 | self, action_class, node_status, power_state=None, |
547 | @@ -961,15 +927,3 @@ | |||
548 | 961 | self.assertEqual( | 927 | self.assertEqual( |
549 | 962 | get_error_message_for_exception(action.node._stop.side_effect), | 928 | get_error_message_for_exception(action.node._stop.side_effect), |
550 | 963 | str(exception)) | 929 | str(exception)) |
551 | 964 | |||
552 | 965 | def test_Refresh_handles_rpc_errors(self): | ||
553 | 966 | action = self.make_action( | ||
554 | 967 | Refresh, NODE_STATUS.ALLOCATED, power_state=POWER_STATE.ON, | ||
555 | 968 | node_type=random.choice([ | ||
556 | 969 | NODE_TYPE.RACK_CONTROLLER, | ||
557 | 970 | NODE_TYPE.REGION_AND_RACK_CONTROLLER])) | ||
558 | 971 | self.patch_rpc_methods(action.node) | ||
559 | 972 | exception = self.assertRaises(NodeActionError, action.execute) | ||
560 | 973 | self.assertEqual( | ||
561 | 974 | get_error_message_for_exception(action.node._stop.side_effect), | ||
562 | 975 | str(exception)) | ||
563 | 976 | 930 | ||
564 | === modified file 'src/maasserver/websockets/handlers/tests/test_general.py' | |||
565 | --- src/maasserver/websockets/handlers/tests/test_general.py 2016-05-26 06:47:45 +0000 | |||
566 | +++ src/maasserver/websockets/handlers/tests/test_general.py 2016-06-09 19:53:21 +0000 | |||
567 | @@ -144,7 +144,7 @@ | |||
568 | 144 | def test_rack_controller_actions_for_admin(self): | 144 | def test_rack_controller_actions_for_admin(self): |
569 | 145 | handler = GeneralHandler(factory.make_admin(), {}) | 145 | handler = GeneralHandler(factory.make_admin(), {}) |
570 | 146 | self.assertItemsEqual( | 146 | self.assertItemsEqual( |
572 | 147 | ['delete', 'import-images', 'off', 'on', 'refresh', 'set-zone'], | 147 | ['delete', 'import-images', 'off', 'on', 'set-zone'], |
573 | 148 | [action['name'] for action in handler.rack_controller_actions({})]) | 148 | [action['name'] for action in handler.rack_controller_actions({})]) |
574 | 149 | 149 | ||
575 | 150 | def test_rack_controller_actions_for_non_admin(self): | 150 | def test_rack_controller_actions_for_non_admin(self): |
576 | @@ -154,7 +154,7 @@ | |||
577 | 154 | def test_region_and_rack_controller_actions_for_admin(self): | 154 | def test_region_and_rack_controller_actions_for_admin(self): |
578 | 155 | handler = GeneralHandler(factory.make_admin(), {}) | 155 | handler = GeneralHandler(factory.make_admin(), {}) |
579 | 156 | self.assertItemsEqual( | 156 | self.assertItemsEqual( |
581 | 157 | ['set-zone', 'refresh', 'delete', 'import-images'], | 157 | ['set-zone', 'delete', 'import-images'], |
582 | 158 | [action['name'] | 158 | [action['name'] |
583 | 159 | for action in handler.region_and_rack_controller_actions({})]) | 159 | for action in handler.region_and_rack_controller_actions({})]) |
584 | 160 | 160 | ||
585 | 161 | 161 | ||
586 | === modified file 'src/provisioningserver/pserv_services/networks_monitoring_service.py' | |||
587 | --- src/provisioningserver/pserv_services/networks_monitoring_service.py 2016-06-03 17:31:53 +0000 | |||
588 | +++ src/provisioningserver/pserv_services/networks_monitoring_service.py 2016-06-09 19:53:21 +0000 | |||
589 | @@ -8,7 +8,10 @@ | |||
590 | 8 | ] | 8 | ] |
591 | 9 | 9 | ||
592 | 10 | from provisioningserver.logger.log import get_maas_logger | 10 | from provisioningserver.logger.log import get_maas_logger |
594 | 11 | from provisioningserver.rpc.region import UpdateInterfaces | 11 | from provisioningserver.rpc.region import ( |
595 | 12 | RequestRackRefresh, | ||
596 | 13 | UpdateInterfaces, | ||
597 | 14 | ) | ||
598 | 12 | from provisioningserver.utils.services import NetworksMonitoringService | 15 | from provisioningserver.utils.services import NetworksMonitoringService |
599 | 13 | 16 | ||
600 | 14 | 17 | ||
601 | @@ -25,6 +28,10 @@ | |||
602 | 25 | def recordInterfaces(self, interfaces): | 28 | def recordInterfaces(self, interfaces): |
603 | 26 | """Record the interfaces information.""" | 29 | """Record the interfaces information.""" |
604 | 27 | client = self.clientService.getClient() | 30 | client = self.clientService.getClient() |
608 | 28 | return client( | 31 | # On first run perform a refresh |
609 | 29 | UpdateInterfaces, system_id=client.localIdent, | 32 | if self._recorded is None: |
610 | 30 | interfaces=interfaces) | 33 | return client(RequestRackRefresh, system_id=client.localIdent) |
611 | 34 | else: | ||
612 | 35 | return client( | ||
613 | 36 | UpdateInterfaces, system_id=client.localIdent, | ||
614 | 37 | interfaces=interfaces) | ||
615 | 31 | 38 | ||
616 | === modified file 'src/provisioningserver/pserv_services/tests/test_networks_monitoring_service.py' | |||
617 | --- src/provisioningserver/pserv_services/tests/test_networks_monitoring_service.py 2016-06-04 08:51:04 +0000 | |||
618 | +++ src/provisioningserver/pserv_services/tests/test_networks_monitoring_service.py 2016-06-09 19:53:21 +0000 | |||
619 | @@ -29,6 +29,22 @@ | |||
620 | 29 | run_tests_with = MAASTwistedRunTest.make_factory(timeout=5) | 29 | run_tests_with = MAASTwistedRunTest.make_factory(timeout=5) |
621 | 30 | 30 | ||
622 | 31 | @inlineCallbacks | 31 | @inlineCallbacks |
623 | 32 | def test_runs_refresh_first_time(self): | ||
624 | 33 | fixture = self.useFixture(MockLiveClusterToRegionRPCFixture()) | ||
625 | 34 | protocol, connecting = fixture.makeEventLoop(region.RequestRackRefresh) | ||
626 | 35 | self.addCleanup((yield connecting)) | ||
627 | 36 | |||
628 | 37 | rpc_service = services.getServiceNamed('rpc') | ||
629 | 38 | service = RackNetworksMonitoringService(rpc_service, Clock()) | ||
630 | 39 | |||
631 | 40 | yield service.startService() | ||
632 | 41 | yield service.stopService() | ||
633 | 42 | |||
634 | 43 | self.assertThat( | ||
635 | 44 | protocol.RequestRackRefresh, MockCalledOnceWith( | ||
636 | 45 | protocol, system_id=rpc_service.getClient().localIdent)) | ||
637 | 46 | |||
638 | 47 | @inlineCallbacks | ||
639 | 32 | def test_reports_interfaces_to_region(self): | 48 | def test_reports_interfaces_to_region(self): |
640 | 33 | fixture = self.useFixture(MockLiveClusterToRegionRPCFixture()) | 49 | fixture = self.useFixture(MockLiveClusterToRegionRPCFixture()) |
641 | 34 | protocol, connecting = fixture.makeEventLoop(region.UpdateInterfaces) | 50 | protocol, connecting = fixture.makeEventLoop(region.UpdateInterfaces) |
642 | @@ -47,6 +63,10 @@ | |||
643 | 47 | rpc_service = services.getServiceNamed('rpc') | 63 | rpc_service = services.getServiceNamed('rpc') |
644 | 48 | service = RackNetworksMonitoringService(rpc_service, Clock()) | 64 | service = RackNetworksMonitoringService(rpc_service, Clock()) |
645 | 49 | service.getInterfaces = lambda: succeed(interfaces) | 65 | service.getInterfaces = lambda: succeed(interfaces) |
646 | 66 | # Put something in the cache. This tells recordInterfaces that refresh | ||
647 | 67 | # has already run but the interfaces have changed thus they need to be | ||
648 | 68 | # updated. | ||
649 | 69 | service._interfacesRecorded({}) | ||
650 | 50 | 70 | ||
651 | 51 | yield service.startService() | 71 | yield service.startService() |
652 | 52 | yield service.stopService() | 72 | yield service.stopService() |
653 | 53 | 73 | ||
654 | === modified file 'src/provisioningserver/rpc/region.py' | |||
655 | --- src/provisioningserver/rpc/region.py 2016-05-11 00:25:14 +0000 | |||
656 | +++ src/provisioningserver/rpc/region.py 2016-06-09 19:53:21 +0000 | |||
657 | @@ -480,3 +480,16 @@ | |||
658 | 480 | errors = { | 480 | errors = { |
659 | 481 | NoSuchCluster: b"NoSuchCluster", | 481 | NoSuchCluster: b"NoSuchCluster", |
660 | 482 | } | 482 | } |
661 | 483 | |||
662 | 484 | |||
663 | 485 | class RequestRackRefresh(amp.Command): | ||
664 | 486 | """Request a refresh of the rack from the region. | ||
665 | 487 | |||
666 | 488 | :since: 2.0 | ||
667 | 489 | """ | ||
668 | 490 | |||
669 | 491 | arguments = [ | ||
670 | 492 | (b"system_id", amp.Unicode()), | ||
671 | 493 | ] | ||
672 | 494 | response = [] | ||
673 | 495 | errors = [] |
Looks good. Just one comment.