Merge ~mpontillo/maas:version-info-updates-and-triggers into maas:master
- Git
- lp:~mpontillo/maas
- version-info-updates-and-triggers
- Merge into master
Proposed by
Mike Pontillo
Status: | Merged |
---|---|
Approved by: | Mike Pontillo |
Approved revision: | b040b7ccab7cffe27fbce2ac149ac6d012fc2d5a |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~mpontillo/maas:version-info-updates-and-triggers |
Merge into: | maas:master |
Diff against target: |
908 lines (+312/-207) 11 files modified
src/maasserver/models/node.py (+18/-8) src/maasserver/static/partials/node-details.html (+7/-0) src/maasserver/triggers/__init__.py (+102/-10) src/maasserver/triggers/tests/test_init.py (+3/-0) src/maasserver/triggers/tests/test_websocket_listener.py (+105/-0) src/maasserver/triggers/websocket.py (+54/-186) src/maasserver/websockets/handlers/controller.py (+4/-1) src/maasserver/websockets/handlers/node.py (+2/-2) src/maasserver/websockets/handlers/tests/test_controller.py (+10/-0) src/provisioningserver/refresh/__init__.py (+3/-0) src/provisioningserver/refresh/tests/test_refresh.py (+4/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lee Trager (community) | Approve | ||
Review via email: mp+329864@code.launchpad.net |
Commit message
Trigger and websocket work for controller verison
* Add triggers for ControllerInfo model. (Only fire triggers
if the version was updated.)
* Add version information to Controller handler.
* Add version reporting to controller refresh.
* Add version display on controller details page.
* Refactor trigger addition code to add register_triggers() method.
* Drive-by fix to process_sys_info() to prevent updating fields
that did not change.
Description of the change
To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote : | # |
Revision history for this message
Mike Pontillo (mpontillo) wrote : | # |
Quick reply; thanks.
Revision history for this message
Lee Trager (ltrager) wrote : | # |
Looks good just one question below.
review:
Needs Information
Revision history for this message
Mike Pontillo (mpontillo) wrote : | # |
Thanks for taking a look; I've replied to your question inline below.
Revision history for this message
Lee Trager (ltrager) wrote : | # |
Thanks for the explanation. LGTM!
review:
Approve
There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py |
2 | index 63e689b..1ec195c 100644 |
3 | --- a/src/maasserver/models/node.py |
4 | +++ b/src/maasserver/models/node.py |
5 | @@ -4822,23 +4822,33 @@ class Controller(Node): |
6 | @transactional |
7 | def _process_sys_info(self, response): |
8 | update_fields = [] |
9 | - if response['hostname'] != '': |
10 | - self.hostname = response['hostname'] |
11 | + hostname = response.get('hostname') |
12 | + if hostname and self.hostname != hostname: |
13 | + self.hostname = hostname |
14 | update_fields.append('hostname') |
15 | - if response['architecture'] != '': |
16 | - self.architecture = response['architecture'] |
17 | + architecture = response.get('architecture') |
18 | + if architecture and self.architecture != architecture: |
19 | + self.architecture = architecture |
20 | update_fields.append('architecture') |
21 | - if response['osystem'] != '': |
22 | - self.osystem = response['osystem'] |
23 | + osystem = response.get('osystem') |
24 | + if osystem and self.osystem != osystem: |
25 | + self.osystem = osystem |
26 | update_fields.append('osystem') |
27 | - if response['distro_series'] != '': |
28 | + distro_series = response.get('distro_series') |
29 | + if distro_series and self.distro_series != distro_series: |
30 | self.distro_series = response['distro_series'] |
31 | update_fields.append('distro_series') |
32 | + maas_version = response.get('maas_version') |
33 | + if maas_version and self.version != maas_version: |
34 | + # Circular imports. |
35 | + from maasserver.models import ControllerInfo |
36 | + ControllerInfo.objects.set_version(self, maas_version) |
37 | # MAAS 2.3+ will send an empty dictionary on purpose, but older |
38 | # versions of the MAAS rack will send real data (and it might arrive |
39 | # in a more timely manner than the UpdateInterfaces call from the |
40 | # NetworksMonitoringService). |
41 | - if response['interfaces'] != {}: |
42 | + interfaces = response.get('interfaces', {}) |
43 | + if len(interfaces) > 0: |
44 | self.update_interfaces(response['interfaces']) |
45 | if len(update_fields) > 0: |
46 | self.save(update_fields=update_fields) |
47 | diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html |
48 | index ccaffa0..66619a1 100755 |
49 | --- a/src/maasserver/static/partials/node-details.html |
50 | +++ b/src/maasserver/static/partials/node-details.html |
51 | @@ -321,6 +321,13 @@ |
52 | aria-label="A region controller controls MAAS services like DNS and runs the database.

A rack controller controls hosts and images and runs network services
like DHCP for connected VLANs." |
53 | data-ng-show="node.node_type == 4"></i> |
54 | </dd> |
55 | + <dt class="two-col">MAAS Version</dt> |
56 | + <dd class="four-col last-col" data-ng-show="node.version"> |
57 | + {$ node.version $} |
58 | + </dd> |
59 | + <dd class="four-col last-col u-text--subtle" data-ng-show="!node.version"> |
60 | + Unknown (< 2.3.0) |
61 | + </dd> |
62 | <dt class="two-col" data-ng-show="node.node_type == 2 || node.node_type == 4">Last image sync</dt> |
63 | <dd class="four-col last-col" data-ng-show="node.node_type == 2 || node.node_type == 4"> |
64 | {$ node.last_image_sync || 'never' $} |
65 | diff --git a/src/maasserver/triggers/__init__.py b/src/maasserver/triggers/__init__.py |
66 | index 88323f2..f84a1db 100644 |
67 | --- a/src/maasserver/triggers/__init__.py |
68 | +++ b/src/maasserver/triggers/__init__.py |
69 | @@ -29,21 +29,113 @@ def register_procedure(procedure): |
70 | cursor.execute(procedure) |
71 | |
72 | |
73 | -def register_trigger(table, procedure, event, params=None, when="after"): |
74 | +# Mappings for (postgres_event_type, maas_notification_type, pg_obj) for |
75 | +# trigger notification events. Three conventions are currently in-use in MAAS. |
76 | + |
77 | +# Event names: create/update/delete. |
78 | +# The majority of triggers use this convention; this is the default. |
79 | +EVENTS_CUD = ( |
80 | + ('insert', 'create', 'NEW'), |
81 | + ('update', 'update', 'NEW'), |
82 | + ('delete', 'delete', 'OLD'), |
83 | +) |
84 | + |
85 | +# Event names: insert/update/delete. |
86 | +EVENTS_IUD = ( |
87 | + ('insert', 'insert', 'NEW'), |
88 | + ('update', 'update', 'NEW'), |
89 | + ('delete', 'delete', 'OLD'), |
90 | +) |
91 | + |
92 | +# Event names: link/update/unlink. |
93 | +EVENTS_LUU = ( |
94 | + ('insert', 'link', 'NEW'), |
95 | + ('update', 'update', 'NEW'), |
96 | + ('delete', 'unlink', 'OLD'), |
97 | +) |
98 | + |
99 | + |
100 | +def register_triggers( |
101 | + table, event_prefix, params=None, fields=None, events=None, |
102 | + when="after"): |
103 | + """Registers a set of triggers for insert, update, and delete. |
104 | + |
105 | + Event names will be determined based on MAAS convention, unless the |
106 | + convention is passed in via the `events` parameter. Predefined conventions |
107 | + in-use in MAAS are provided via the EVENTS_* constants. |
108 | + |
109 | + :param table: The table name to create the trigger on. |
110 | + :param event_prefix: The event prefix for the trigger. For example, if |
111 | + the table is maasserver_subnet, 'subnet' might be an appropriate event |
112 | + prefix. |
113 | + :param params: A dictionary of parameters that should be ANDed together to |
114 | + form the initial WHEN clause. |
115 | + :param fields: A list of fields whose values will be checked for changes |
116 | + before the trigger fires. If None is specified, all fields in the row |
117 | + will be checked. |
118 | + :param events: A tuple in the format of the EVENTS_* constants indicating |
119 | + which convention to use for notification names. |
120 | + :param when: When the trigger should fire relative to the row update. The |
121 | + default is AFTER, but postgresql also supports BEFORE and INSTEAD OF. |
122 | + """ |
123 | + if events is None: |
124 | + events = EVENTS_CUD |
125 | + for pg_event, maas_event_type, pg_obj in events: |
126 | + event_params = None |
127 | + if params is not None: |
128 | + event_params = {} |
129 | + for key, value in params.items(): |
130 | + event_params['%s.%s' % (pg_obj, key)] = value |
131 | + register_trigger( |
132 | + table, '%s_%s_notify' % (event_prefix, maas_event_type), |
133 | + pg_event, event_params, fields, when) |
134 | + |
135 | + |
136 | +def _make_when_clause(is_update, params, fields): |
137 | + """Generates a WHEN clause for the trigger. |
138 | + |
139 | + :param is_update: If true, this trigger is for update. (not insert/delete) |
140 | + :param params: A dictionary of parameters that should be ANDed together to |
141 | + form the initial WHEN clause. |
142 | + :param fields: A list of fields whose values will be checked for changes |
143 | + before the trigger fires. If None is specified, all fields in the row |
144 | + will be checked. |
145 | + :return: the WHEN clause to use in the trigger. |
146 | + """ |
147 | + when_clause = '' |
148 | + if params is not None or (fields is not None and is_update): |
149 | + if params is None: |
150 | + params = {} |
151 | + if fields is None: |
152 | + fields = [] |
153 | + when_clause = 'WHEN (' |
154 | + when_clause += ' AND '.join([ |
155 | + "%s = '%s'" % (key, value) |
156 | + for key, value in params.items() |
157 | + ]) |
158 | + if is_update and len(fields) > 0: |
159 | + if len(params) > 0: |
160 | + when_clause += " AND (" |
161 | + when_clause += ' OR '.join([ |
162 | + "NEW.%s IS DISTINCT FROM OLD.%s" % (field, field) |
163 | + for field in fields |
164 | + ]) |
165 | + if len(params) > 0: |
166 | + when_clause += ")" |
167 | + when_clause += ")" |
168 | + return when_clause |
169 | + |
170 | + |
171 | +def register_trigger( |
172 | + table, procedure, event, params=None, fields=None, when="after"): |
173 | """Register `trigger` on `table` if it doesn't exist.""" |
174 | # Strip the "maasserver_" off the front of the table name. |
175 | table_name = table |
176 | if table.startswith("maasserver_"): |
177 | table_name = table_name[11:] |
178 | trigger_name = "%s_%s" % (table_name, procedure) |
179 | - if params is not None: |
180 | - filter = 'WHEN (' + ''.join( |
181 | - [ |
182 | - "%s = '%s'" % (key, value) |
183 | - for key, value in params.items() |
184 | - ]) + ')' |
185 | - else: |
186 | - filter = '' |
187 | + is_update = (event == "update") |
188 | + when_clause = _make_when_clause(is_update, params, fields) |
189 | trigger_sql = dedent("""\ |
190 | DROP TRIGGER IF EXISTS %s ON %s; |
191 | CREATE TRIGGER %s |
192 | @@ -58,7 +150,7 @@ def register_trigger(table, procedure, event, params=None, when="after"): |
193 | when.upper(), |
194 | event.upper(), |
195 | table, |
196 | - filter, |
197 | + when_clause, |
198 | procedure, |
199 | ) |
200 | with closing(connection.cursor()) as cursor: |
201 | diff --git a/src/maasserver/triggers/tests/test_init.py b/src/maasserver/triggers/tests/test_init.py |
202 | index a0256c4..3f291cb 100644 |
203 | --- a/src/maasserver/triggers/tests/test_init.py |
204 | +++ b/src/maasserver/triggers/tests/test_init.py |
205 | @@ -125,6 +125,9 @@ class TestTriggersUsed(MAASServerTestCase): |
206 | "config_config_update_notify", |
207 | "config_sys_proxy_config_use_peer_proxy_insert", |
208 | "config_sys_proxy_config_use_peer_proxy_update", |
209 | + 'controllerinfo_controllerinfo_link_notify', |
210 | + 'controllerinfo_controllerinfo_unlink_notify', |
211 | + 'controllerinfo_controllerinfo_update_notify', |
212 | "dhcpsnippet_dhcpsnippet_create_notify", |
213 | "dhcpsnippet_dhcpsnippet_delete_notify", |
214 | "dhcpsnippet_dhcpsnippet_update_notify", |
215 | diff --git a/src/maasserver/triggers/tests/test_websocket_listener.py b/src/maasserver/triggers/tests/test_websocket_listener.py |
216 | index 8877fcc..c40a3c6 100644 |
217 | --- a/src/maasserver/triggers/tests/test_websocket_listener.py |
218 | +++ b/src/maasserver/triggers/tests/test_websocket_listener.py |
219 | @@ -18,6 +18,7 @@ from maasserver.enum import ( |
220 | NODE_TYPE, |
221 | ) |
222 | from maasserver.listener import PostgresListenerService |
223 | +from maasserver.models import ControllerInfo |
224 | from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE |
225 | from maasserver.models.config import Config |
226 | from maasserver.models.node import Node |
227 | @@ -36,7 +37,9 @@ from provisioningserver.utils.twisted import ( |
228 | FOREVER, |
229 | synchronous, |
230 | ) |
231 | +from testtools import ExpectedException |
232 | from twisted.internet.defer import ( |
233 | + CancelledError, |
234 | DeferredList, |
235 | DeferredQueue, |
236 | inlineCallbacks, |
237 | @@ -219,6 +222,108 @@ class TestNodeListener( |
238 | yield listener.stopService() |
239 | |
240 | |
241 | +class TestControllerListener( |
242 | + MAASTransactionServerTestCase, TransactionalHelpersMixin): |
243 | + """End-to-end test of both the listeners code and the triggers code.""" |
244 | + |
245 | + scenarios = ( |
246 | + ('rack', { |
247 | + 'params': {'node_type': NODE_TYPE.RACK_CONTROLLER}, |
248 | + 'listener': 'controller', |
249 | + }), |
250 | + ('region_and_rack', { |
251 | + 'params': {'node_type': NODE_TYPE.REGION_AND_RACK_CONTROLLER}, |
252 | + 'listener': 'controller', |
253 | + }), |
254 | + ('region', { |
255 | + 'params': {'node_type': NODE_TYPE.REGION_CONTROLLER}, |
256 | + 'listener': 'controller', |
257 | + }), |
258 | + ) |
259 | + |
260 | + def set_version(self, controller, version): |
261 | + ControllerInfo.objects.set_version(controller, version) |
262 | + |
263 | + def set_interface_update_info(self, controller, interfaces, hints): |
264 | + ControllerInfo.objects.set_interface_update_info( |
265 | + controller, interfaces, hints) |
266 | + |
267 | + def delete_controllerinfo(self, controller): |
268 | + ControllerInfo.objects.filter(node=controller).delete() |
269 | + |
270 | + @wait_for_reactor |
271 | + @inlineCallbacks |
272 | + def test__calls_handler_on_controllerinfo_insert(self): |
273 | + yield deferToDatabase(register_websocket_triggers) |
274 | + listener = self.make_listener_without_delay() |
275 | + dv = DeferredValue() |
276 | + params = self.params.copy() |
277 | + controller = yield deferToDatabase(self.create_node, params) |
278 | + listener.register(self.listener, lambda *args: dv.set(args)) |
279 | + yield listener.startService() |
280 | + try: |
281 | + yield deferToDatabase( |
282 | + self.set_version, controller, factory.make_string()) |
283 | + yield dv.get(timeout=2) |
284 | + finally: |
285 | + yield listener.stopService() |
286 | + |
287 | + @wait_for_reactor |
288 | + @inlineCallbacks |
289 | + def test__calls_handler_on_controllerinfo_version_update(self): |
290 | + yield deferToDatabase(register_websocket_triggers) |
291 | + listener = self.make_listener_without_delay() |
292 | + dv = DeferredValue() |
293 | + params = self.params.copy() |
294 | + controller = yield deferToDatabase(self.create_node, params) |
295 | + yield deferToDatabase(self.set_version, controller, '') |
296 | + listener.register(self.listener, lambda *args: dv.set(args)) |
297 | + yield listener.startService() |
298 | + try: |
299 | + yield deferToDatabase( |
300 | + self.set_version, controller, factory.make_string()) |
301 | + yield dv.get(timeout=2) |
302 | + finally: |
303 | + yield listener.stopService() |
304 | + |
305 | + @wait_for_reactor |
306 | + @inlineCallbacks |
307 | + def test__skips_notify_on_controllerinfo_interface_update(self): |
308 | + yield deferToDatabase(register_websocket_triggers) |
309 | + listener = self.make_listener_without_delay() |
310 | + dv = DeferredValue() |
311 | + params = self.params.copy() |
312 | + controller = yield deferToDatabase(self.create_node, params) |
313 | + yield deferToDatabase(self.set_version, controller, '') |
314 | + listener.register(self.listener, lambda *args: dv.set(args)) |
315 | + yield listener.startService() |
316 | + try: |
317 | + yield deferToDatabase( |
318 | + self.set_interface_update_info, controller, '{]', '{}') |
319 | + with ExpectedException(CancelledError): |
320 | + yield dv.get(timeout=0.2) |
321 | + finally: |
322 | + yield listener.stopService() |
323 | + |
324 | + @wait_for_reactor |
325 | + @inlineCallbacks |
326 | + def test__calls_handler_on_controllerinfo_delete(self): |
327 | + yield deferToDatabase(register_websocket_triggers) |
328 | + listener = self.make_listener_without_delay() |
329 | + dv = DeferredValue() |
330 | + params = self.params.copy() |
331 | + controller = yield deferToDatabase(self.create_node, params) |
332 | + yield deferToDatabase(self.set_version, controller, '') |
333 | + listener.register(self.listener, lambda *args: dv.set(args)) |
334 | + yield listener.startService() |
335 | + try: |
336 | + yield deferToDatabase( |
337 | + self.delete_controllerinfo, controller) |
338 | + yield dv.get(timeout=2) |
339 | + finally: |
340 | + yield listener.stopService() |
341 | + |
342 | + |
343 | class TestDeviceWithParentListener( |
344 | MAASTransactionServerTestCase, TransactionalHelpersMixin): |
345 | """End-to-end test of both the listeners code and the triggers code.""" |
346 | diff --git a/src/maasserver/triggers/websocket.py b/src/maasserver/triggers/websocket.py |
347 | index 0e9df27..0ec50e4 100644 |
348 | --- a/src/maasserver/triggers/websocket.py |
349 | +++ b/src/maasserver/triggers/websocket.py |
350 | @@ -22,8 +22,11 @@ from maasserver.enum import ( |
351 | NODE_TYPE, |
352 | ) |
353 | from maasserver.triggers import ( |
354 | + EVENTS_IUD, |
355 | + EVENTS_LUU, |
356 | register_procedure, |
357 | register_trigger, |
358 | + register_triggers, |
359 | ) |
360 | from maasserver.utils.orm import transactional |
361 | |
362 | @@ -1175,21 +1178,27 @@ def register_websocket_triggers(): |
363 | '%s_delete_notify' % proc_name_prefix, |
364 | '%s_delete' % event_name_prefix, |
365 | 'OLD.system_id')) |
366 | - register_trigger( |
367 | - "maasserver_node", |
368 | - "%s_create_notify" % proc_name_prefix, |
369 | - "insert", |
370 | - {'NEW.node_type': node_type}) |
371 | - register_trigger( |
372 | - "maasserver_node", |
373 | - "%s_update_notify" % proc_name_prefix, |
374 | - "update", |
375 | - {'NEW.node_type': node_type}) |
376 | - register_trigger( |
377 | - "maasserver_node", |
378 | - "%s_delete_notify" % proc_name_prefix, |
379 | - "delete", |
380 | - {'OLD.node_type': node_type}) |
381 | + register_triggers( |
382 | + "maasserver_node", proc_name_prefix, {'node_type': node_type}) |
383 | + |
384 | + # ControllerInfo notifications |
385 | + register_procedure( |
386 | + render_node_related_notification_procedure( |
387 | + 'controllerinfo_link_notify', 'NEW.node_id')) |
388 | + register_procedure( |
389 | + render_node_related_notification_procedure( |
390 | + 'controllerinfo_update_notify', 'NEW.node_id')) |
391 | + register_procedure( |
392 | + render_node_related_notification_procedure( |
393 | + 'controllerinfo_unlink_notify', 'OLD.node_id')) |
394 | + register_triggers( |
395 | + "maasserver_controllerinfo", |
396 | + "controllerinfo", |
397 | + # Trigger only fires for version information on update; it doesn't |
398 | + # care when the other metadata is updated. |
399 | + fields=('version',), |
400 | + events=EVENTS_LUU |
401 | + ) |
402 | |
403 | # Config table |
404 | register_procedure( |
405 | @@ -1201,12 +1210,7 @@ def register_websocket_triggers(): |
406 | register_procedure( |
407 | render_notification_procedure( |
408 | 'config_delete_notify', 'config_delete', 'OLD.id')) |
409 | - register_trigger( |
410 | - "maasserver_config", "config_create_notify", "insert") |
411 | - register_trigger( |
412 | - "maasserver_config", "config_update_notify", "update") |
413 | - register_trigger( |
414 | - "maasserver_config", "config_delete_notify", "delete") |
415 | + register_triggers("maasserver_config", "config") |
416 | |
417 | # Device Node types |
418 | register_procedure( |
419 | @@ -1218,15 +1222,8 @@ def register_websocket_triggers(): |
420 | register_procedure( |
421 | render_device_notification_procedure( |
422 | 'device_delete_notify', 'device_delete', 'OLD')) |
423 | - register_trigger( |
424 | - "maasserver_node", "device_create_notify", "insert", |
425 | - {'NEW.node_type': NODE_TYPE.DEVICE}) |
426 | - register_trigger( |
427 | - "maasserver_node", "device_update_notify", "update", |
428 | - {'NEW.node_type': NODE_TYPE.DEVICE}) |
429 | - register_trigger( |
430 | - "maasserver_node", "device_delete_notify", "delete", |
431 | - {'OLD.node_type': NODE_TYPE.DEVICE}) |
432 | + register_triggers( |
433 | + "maasserver_node", "device", {'node_type': NODE_TYPE.DEVICE}) |
434 | |
435 | # VLAN table |
436 | register_procedure( |
437 | @@ -1238,12 +1235,7 @@ def register_websocket_triggers(): |
438 | register_procedure( |
439 | render_notification_procedure( |
440 | 'vlan_delete_notify', 'vlan_delete', 'OLD.id')) |
441 | - register_trigger( |
442 | - "maasserver_vlan", "vlan_create_notify", "insert") |
443 | - register_trigger( |
444 | - "maasserver_vlan", "vlan_update_notify", "update") |
445 | - register_trigger( |
446 | - "maasserver_vlan", "vlan_delete_notify", "delete") |
447 | + register_triggers("maasserver_vlan", "vlan") |
448 | |
449 | # IPRange table |
450 | register_procedure( |
451 | @@ -1255,12 +1247,7 @@ def register_websocket_triggers(): |
452 | register_procedure( |
453 | render_notification_procedure( |
454 | 'iprange_delete_notify', 'iprange_delete', 'OLD.id')) |
455 | - register_trigger( |
456 | - "maasserver_iprange", "iprange_create_notify", "insert") |
457 | - register_trigger( |
458 | - "maasserver_iprange", "iprange_update_notify", "update") |
459 | - register_trigger( |
460 | - "maasserver_iprange", "iprange_delete_notify", "delete") |
461 | + register_triggers("maasserver_iprange", "iprange") |
462 | |
463 | # Neighbour table |
464 | register_procedure( |
465 | @@ -1272,12 +1259,7 @@ def register_websocket_triggers(): |
466 | register_procedure( |
467 | render_notification_procedure( |
468 | 'neighbour_delete_notify', 'neighbour_delete', 'OLD.ip')) |
469 | - register_trigger( |
470 | - "maasserver_neighbour", "neighbour_create_notify", "insert") |
471 | - register_trigger( |
472 | - "maasserver_neighbour", "neighbour_update_notify", "update") |
473 | - register_trigger( |
474 | - "maasserver_neighbour", "neighbour_delete_notify", "delete") |
475 | + register_triggers("maasserver_neighbour", "neighbour") |
476 | |
477 | # StaticRoute table |
478 | register_procedure( |
479 | @@ -1289,12 +1271,7 @@ def register_websocket_triggers(): |
480 | register_procedure( |
481 | render_notification_procedure( |
482 | 'staticroute_delete_notify', 'staticroute_delete', 'OLD.id')) |
483 | - register_trigger( |
484 | - "maasserver_staticroute", "staticroute_create_notify", "insert") |
485 | - register_trigger( |
486 | - "maasserver_staticroute", "staticroute_update_notify", "update") |
487 | - register_trigger( |
488 | - "maasserver_staticroute", "staticroute_delete_notify", "delete") |
489 | + register_triggers("maasserver_staticroute", "staticroute") |
490 | |
491 | # Fabric table |
492 | register_procedure( |
493 | @@ -1306,12 +1283,7 @@ def register_websocket_triggers(): |
494 | register_procedure( |
495 | render_notification_procedure( |
496 | 'fabric_delete_notify', 'fabric_delete', 'OLD.id')) |
497 | - register_trigger( |
498 | - "maasserver_fabric", "fabric_create_notify", "insert") |
499 | - register_trigger( |
500 | - "maasserver_fabric", "fabric_update_notify", "update") |
501 | - register_trigger( |
502 | - "maasserver_fabric", "fabric_delete_notify", "delete") |
503 | + register_triggers("maasserver_fabric", "fabric") |
504 | |
505 | # Space table |
506 | register_procedure( |
507 | @@ -1323,12 +1295,7 @@ def register_websocket_triggers(): |
508 | register_procedure( |
509 | render_notification_procedure( |
510 | 'space_delete_notify', 'space_delete', 'OLD.id')) |
511 | - register_trigger( |
512 | - "maasserver_space", "space_create_notify", "insert") |
513 | - register_trigger( |
514 | - "maasserver_space", "space_update_notify", "update") |
515 | - register_trigger( |
516 | - "maasserver_space", "space_delete_notify", "delete") |
517 | + register_triggers("maasserver_space", "space") |
518 | |
519 | # Subnet table |
520 | register_procedure( |
521 | @@ -1340,12 +1307,7 @@ def register_websocket_triggers(): |
522 | register_procedure( |
523 | render_notification_procedure( |
524 | 'subnet_delete_notify', 'subnet_delete', 'OLD.id')) |
525 | - register_trigger( |
526 | - "maasserver_subnet", "subnet_create_notify", "insert") |
527 | - register_trigger( |
528 | - "maasserver_subnet", "subnet_update_notify", "update") |
529 | - register_trigger( |
530 | - "maasserver_subnet", "subnet_delete_notify", "delete") |
531 | + register_triggers("maasserver_subnet", "subnet") |
532 | |
533 | # Subnet node notifications |
534 | register_procedure( |
535 | @@ -1421,15 +1383,8 @@ def register_websocket_triggers(): |
536 | register_procedure( |
537 | STATIC_IP_ADDRESS_DOMAIN_NOTIFY % ( |
538 | 'ipaddress_domain_delete_notify', 'OLD.id')) |
539 | - register_trigger( |
540 | - "maasserver_staticipaddress", |
541 | - "ipaddress_domain_insert_notify", "insert") |
542 | - register_trigger( |
543 | - "maasserver_staticipaddress", |
544 | - "ipaddress_domain_update_notify", "update") |
545 | - register_trigger( |
546 | - "maasserver_staticipaddress", |
547 | - "ipaddress_domain_delete_notify", "delete") |
548 | + register_triggers( |
549 | + "maasserver_staticipaddress", "ipaddress_domain", events=EVENTS_IUD) |
550 | |
551 | # IP range subnet notifications |
552 | register_procedure( |
553 | @@ -1438,15 +1393,8 @@ def register_websocket_triggers(): |
554 | IP_RANGE_SUBNET_UPDATE_NOTIFY % 'iprange_subnet_update_notify') |
555 | register_procedure( |
556 | IP_RANGE_SUBNET_DELETE_NOTIFY % 'iprange_subnet_delete_notify') |
557 | - register_trigger( |
558 | - "maasserver_iprange", |
559 | - "iprange_subnet_insert_notify", "insert") |
560 | - register_trigger( |
561 | - "maasserver_iprange", |
562 | - "iprange_subnet_update_notify", "update") |
563 | - register_trigger( |
564 | - "maasserver_iprange", |
565 | - "iprange_subnet_delete_notify", "delete") |
566 | + register_triggers( |
567 | + "maasserver_iprange", "iprange_subnet", events=EVENTS_IUD) |
568 | |
569 | # Pod notifications |
570 | register_procedure( |
571 | @@ -1457,15 +1405,7 @@ def register_websocket_triggers(): |
572 | BMC_TYPE.POD, BMC_TYPE.POD, BMC_TYPE.BMC)) |
573 | register_procedure( |
574 | POD_DELETE_NOTIFY % ('pod_delete_notify', BMC_TYPE.POD)) |
575 | - register_trigger( |
576 | - "maasserver_bmc", |
577 | - "pod_insert_notify", "insert") |
578 | - register_trigger( |
579 | - "maasserver_bmc", |
580 | - "pod_update_notify", "update") |
581 | - register_trigger( |
582 | - "maasserver_bmc", |
583 | - "pod_delete_notify", "delete") |
584 | + register_triggers("maasserver_bmc", "pod", events=EVENTS_IUD) |
585 | |
586 | # Node pod notifications |
587 | register_procedure( |
588 | @@ -1475,15 +1415,7 @@ def register_websocket_triggers(): |
589 | 'node_pod_update_notify', BMC_TYPE.POD, BMC_TYPE.POD)) |
590 | register_procedure( |
591 | NODE_POD_DELETE_NOTIFY % ('node_pod_delete_notify', BMC_TYPE.POD)) |
592 | - register_trigger( |
593 | - "maasserver_node", |
594 | - "node_pod_insert_notify", "insert") |
595 | - register_trigger( |
596 | - "maasserver_node", |
597 | - "node_pod_update_notify", "update") |
598 | - register_trigger( |
599 | - "maasserver_node", |
600 | - "node_pod_delete_notify", "delete") |
601 | + register_triggers("maasserver_node", "node_pod", events=EVENTS_IUD) |
602 | |
603 | # DNSData table |
604 | register_procedure( |
605 | @@ -1496,15 +1428,8 @@ def register_websocket_triggers(): |
606 | register_procedure( |
607 | DNSDATA_DOMAIN_NOTIFY % ( |
608 | 'dnsdata_domain_delete_notify', 'OLD.dnsresource_id')) |
609 | - register_trigger( |
610 | - "maasserver_dnsdata", |
611 | - "dnsdata_domain_insert_notify", "insert") |
612 | - register_trigger( |
613 | - "maasserver_dnsdata", |
614 | - "dnsdata_domain_update_notify", "update") |
615 | - register_trigger( |
616 | - "maasserver_dnsdata", |
617 | - "dnsdata_domain_delete_notify", "delete") |
618 | + register_triggers( |
619 | + "maasserver_dnsdata", "dnsdata_domain", events=EVENTS_IUD) |
620 | |
621 | # DNSResource table |
622 | register_procedure( |
623 | @@ -1515,15 +1440,8 @@ def register_websocket_triggers(): |
624 | register_procedure( |
625 | DNSRESOURCE_DOMAIN_NOTIFY % ( |
626 | 'dnsresource_domain_delete_notify', 'OLD')) |
627 | - register_trigger( |
628 | - "maasserver_dnsresource", |
629 | - "dnsresource_domain_insert_notify", "insert") |
630 | - register_trigger( |
631 | - "maasserver_dnsresource", |
632 | - "dnsresource_domain_update_notify", "update") |
633 | - register_trigger( |
634 | - "maasserver_dnsresource", |
635 | - "dnsresource_domain_delete_notify", "delete") |
636 | + register_triggers( |
637 | + "maasserver_dnsresource", "dnsresource_domain", events=EVENTS_IUD) |
638 | |
639 | # Domain table |
640 | register_procedure( |
641 | @@ -1542,12 +1460,7 @@ def register_websocket_triggers(): |
642 | 'domain_delete_notify', 'domain_delete', 'OLD.id')) |
643 | register_trigger( |
644 | "maasserver_domain", "domain_node_update_notify", "update") |
645 | - register_trigger( |
646 | - "maasserver_domain", "domain_create_notify", "insert") |
647 | - register_trigger( |
648 | - "maasserver_domain", "domain_update_notify", "update") |
649 | - register_trigger( |
650 | - "maasserver_domain", "domain_delete_notify", "delete") |
651 | + register_triggers("maasserver_domain", "domain") |
652 | |
653 | # MAC static ip address table, update to linked domain via dnsresource |
654 | register_procedure( |
655 | @@ -1573,12 +1486,7 @@ def register_websocket_triggers(): |
656 | register_procedure( |
657 | render_notification_procedure( |
658 | 'zone_delete_notify', 'zone_delete', 'OLD.id')) |
659 | - register_trigger( |
660 | - "maasserver_zone", "zone_create_notify", "insert") |
661 | - register_trigger( |
662 | - "maasserver_zone", "zone_update_notify", "update") |
663 | - register_trigger( |
664 | - "maasserver_zone", "zone_delete_notify", "delete") |
665 | + register_triggers("maasserver_zone", "zone") |
666 | |
667 | # Service table |
668 | register_procedure( |
669 | @@ -1590,12 +1498,7 @@ def register_websocket_triggers(): |
670 | register_procedure( |
671 | render_notification_procedure( |
672 | 'service_delete_notify', 'service_delete', 'OLD.id')) |
673 | - register_trigger( |
674 | - "maasserver_service", "service_create_notify", "insert") |
675 | - register_trigger( |
676 | - "maasserver_service", "service_update_notify", "update") |
677 | - register_trigger( |
678 | - "maasserver_service", "service_delete_notify", "delete") |
679 | + register_triggers("maasserver_service", "service") |
680 | |
681 | # Tag table |
682 | register_procedure( |
683 | @@ -1607,12 +1510,7 @@ def register_websocket_triggers(): |
684 | register_procedure( |
685 | render_notification_procedure( |
686 | 'tag_delete_notify', 'tag_delete', 'OLD.id')) |
687 | - register_trigger( |
688 | - "maasserver_tag", "tag_create_notify", "insert") |
689 | - register_trigger( |
690 | - "maasserver_tag", "tag_update_notify", "update") |
691 | - register_trigger( |
692 | - "maasserver_tag", "tag_delete_notify", "delete") |
693 | + register_triggers("maasserver_tag", "tag") |
694 | |
695 | # Node tag link table |
696 | register_procedure( |
697 | @@ -1647,12 +1545,7 @@ def register_websocket_triggers(): |
698 | register_procedure( |
699 | render_notification_procedure( |
700 | 'user_delete_notify', 'user_delete', 'OLD.id')) |
701 | - register_trigger( |
702 | - "auth_user", "user_create_notify", "insert") |
703 | - register_trigger( |
704 | - "auth_user", "user_update_notify", "update") |
705 | - register_trigger( |
706 | - "auth_user", "user_delete_notify", "delete") |
707 | + register_triggers("auth_user", "user") |
708 | |
709 | # Events table |
710 | register_procedure( |
711 | @@ -1928,12 +1821,7 @@ def register_websocket_triggers(): |
712 | register_procedure( |
713 | render_notification_procedure( |
714 | 'sshkey_delete_notify', 'sshkey_delete', 'OLD.id')) |
715 | - register_trigger( |
716 | - "maasserver_sshkey", "sshkey_create_notify", "insert") |
717 | - register_trigger( |
718 | - "maasserver_sshkey", "sshkey_update_notify", "update") |
719 | - register_trigger( |
720 | - "maasserver_sshkey", "sshkey_delete_notify", "delete") |
721 | + register_triggers("maasserver_sshkey", "sshkey") |
722 | |
723 | # SSL key table, update to linked user. |
724 | register_procedure( |
725 | @@ -1957,12 +1845,7 @@ def register_websocket_triggers(): |
726 | register_procedure( |
727 | render_notification_procedure( |
728 | 'dhcpsnippet_delete_notify', 'dhcpsnippet_delete', 'OLD.id')) |
729 | - register_trigger( |
730 | - "maasserver_dhcpsnippet", "dhcpsnippet_create_notify", "insert") |
731 | - register_trigger( |
732 | - "maasserver_dhcpsnippet", "dhcpsnippet_update_notify", "update") |
733 | - register_trigger( |
734 | - "maasserver_dhcpsnippet", "dhcpsnippet_delete_notify", "delete") |
735 | + register_triggers("maasserver_dhcpsnippet", "dhcpsnippet") |
736 | |
737 | # PackageRepository table |
738 | register_procedure( |
739 | @@ -1977,15 +1860,7 @@ def register_websocket_triggers(): |
740 | render_notification_procedure( |
741 | 'packagerepository_delete_notify', 'packagerepository_delete', |
742 | 'OLD.id')) |
743 | - register_trigger( |
744 | - "maasserver_packagerepository", "packagerepository_create_notify", |
745 | - "insert") |
746 | - register_trigger( |
747 | - "maasserver_packagerepository", "packagerepository_update_notify", |
748 | - "update") |
749 | - register_trigger( |
750 | - "maasserver_packagerepository", "packagerepository_delete_notify", |
751 | - "delete") |
752 | + register_triggers("maasserver_packagerepository", "packagerepository") |
753 | |
754 | # Node type change. |
755 | register_procedure(node_type_change()) |
756 | @@ -2002,12 +1877,7 @@ def register_websocket_triggers(): |
757 | register_procedure( |
758 | render_notification_procedure( |
759 | 'notification_delete_notify', 'notification_delete', 'OLD.id')) |
760 | - register_trigger( |
761 | - "maasserver_notification", "notification_create_notify", "insert") |
762 | - register_trigger( |
763 | - "maasserver_notification", "notification_update_notify", "update") |
764 | - register_trigger( |
765 | - "maasserver_notification", "notification_delete_notify", "delete") |
766 | + register_triggers("maasserver_notification", "notification") |
767 | |
768 | # NotificationDismissal table. |
769 | register_procedure( |
770 | @@ -2028,6 +1898,4 @@ def register_websocket_triggers(): |
771 | register_procedure( |
772 | render_notification_procedure( |
773 | 'script_delete_notify', 'script_delete', 'OLD.id')) |
774 | - register_trigger('metadataserver_script', 'script_create_notify', 'insert') |
775 | - register_trigger('metadataserver_script', 'script_update_notify', 'update') |
776 | - register_trigger('metadataserver_script', 'script_delete_notify', 'delete') |
777 | + register_triggers('metadataserver_script', 'script') |
778 | diff --git a/src/maasserver/websockets/handlers/controller.py b/src/maasserver/websockets/handlers/controller.py |
779 | index ca85333..6edc6f0 100644 |
780 | --- a/src/maasserver/websockets/handlers/controller.py |
781 | +++ b/src/maasserver/websockets/handlers/controller.py |
782 | @@ -23,7 +23,9 @@ class ControllerHandler(MachineHandler): |
783 | class Meta(MachineHandler.Meta): |
784 | abstract = False |
785 | queryset = node_prefetch( |
786 | - Controller.controllers.all().prefetch_related("interface_set")) |
787 | + Controller.controllers.all().prefetch_related("interface_set"), |
788 | + 'controllerinfo' |
789 | + ) |
790 | allowed_methods = [ |
791 | 'list', |
792 | 'get', |
793 | @@ -89,6 +91,7 @@ class ControllerHandler(MachineHandler): |
794 | |
795 | def dehydrate(self, obj, data, for_list=False): |
796 | data = super().dehydrate(obj, data, for_list=for_list) |
797 | + data["version"] = obj.version |
798 | data["vlan_ids"] = [ |
799 | interface.vlan_id |
800 | for interface in obj.interface_set.all() |
801 | diff --git a/src/maasserver/websockets/handlers/node.py b/src/maasserver/websockets/handlers/node.py |
802 | index 12bb3c3..5014fa8 100644 |
803 | --- a/src/maasserver/websockets/handlers/node.py |
804 | +++ b/src/maasserver/websockets/handlers/node.py |
805 | @@ -45,10 +45,10 @@ from metadataserver.enum import RESULT_TYPE |
806 | from provisioningserver.tags import merge_details_cleanly |
807 | |
808 | |
809 | -def node_prefetch(queryset): |
810 | +def node_prefetch(queryset, *args): |
811 | return ( |
812 | queryset |
813 | - .select_related('boot_interface', 'owner', 'zone', 'domain') |
814 | + .select_related('boot_interface', 'owner', 'zone', 'domain', *args) |
815 | .prefetch_related('blockdevice_set__iscsiblockdevice') |
816 | .prefetch_related('blockdevice_set__physicalblockdevice') |
817 | .prefetch_related('blockdevice_set__virtualblockdevice') |
818 | diff --git a/src/maasserver/websockets/handlers/tests/test_controller.py b/src/maasserver/websockets/handlers/tests/test_controller.py |
819 | index 111ed57..ad628e9 100644 |
820 | --- a/src/maasserver/websockets/handlers/tests/test_controller.py |
821 | +++ b/src/maasserver/websockets/handlers/tests/test_controller.py |
822 | @@ -7,6 +7,7 @@ __all__ = [] |
823 | |
824 | from maasserver.enum import NODE_TYPE |
825 | from maasserver.forms import ControllerForm |
826 | +from maasserver.models import ControllerInfo |
827 | from maasserver.testing.factory import factory |
828 | from maasserver.testing.testcase import MAASServerTestCase |
829 | from maasserver.websockets.base import dehydrate_datetime |
830 | @@ -96,6 +97,15 @@ class TestControllerHandler(MAASServerTestCase): |
831 | handler = ControllerHandler(owner, {}) |
832 | self.assertTrue(handler.dehydrate_show_os_info(rack)) |
833 | |
834 | + def test_dehydrate_includes_version(self): |
835 | + owner = factory.make_admin() |
836 | + handler = ControllerHandler(owner, {}) |
837 | + rack = factory.make_RackController() |
838 | + version = factory.make_string() |
839 | + ControllerInfo.objects.set_version(rack, version) |
840 | + result = handler.list({}) |
841 | + self.assertEqual(version, result[0].get('version')) |
842 | + |
843 | |
844 | class TestControllerHandlerScenarios(MAASServerTestCase): |
845 | |
846 | diff --git a/src/provisioningserver/refresh/__init__.py b/src/provisioningserver/refresh/__init__.py |
847 | index a377038..bf05f10 100644 |
848 | --- a/src/provisioningserver/refresh/__init__.py |
849 | +++ b/src/provisioningserver/refresh/__init__.py |
850 | @@ -2,6 +2,7 @@ |
851 | # GNU Affero General Public License version 3 (see the file LICENSE). |
852 | |
853 | """Functionality to refresh rack controller hardware and networking details.""" |
854 | + |
855 | import os |
856 | import socket |
857 | import stat |
858 | @@ -25,6 +26,7 @@ from provisioningserver.utils.shell import ( |
859 | ExternalProcessError, |
860 | ) |
861 | from provisioningserver.utils.twisted import synchronous |
862 | +from provisioningserver.utils.version import get_maas_version |
863 | |
864 | |
865 | maaslog = get_maas_logger("refresh") |
866 | @@ -77,6 +79,7 @@ def get_sys_info(): |
867 | 'architecture': get_architecture(), |
868 | 'osystem': osystem, |
869 | 'distro_series': distro_series, |
870 | + 'maas_version': get_maas_version(), |
871 | # In MAAS 2.3+, the NetworksMonitoringService is solely responsible for |
872 | # interface update, because interface updates need to include beaconing |
873 | # data. The key and empty dictionary are necessary for backward |
874 | diff --git a/src/provisioningserver/refresh/tests/test_refresh.py b/src/provisioningserver/refresh/tests/test_refresh.py |
875 | index ff6b759..d03cac1 100644 |
876 | --- a/src/provisioningserver/refresh/tests/test_refresh.py |
877 | +++ b/src/provisioningserver/refresh/tests/test_refresh.py |
878 | @@ -25,6 +25,7 @@ from provisioningserver.refresh.maas_api_helper import ( |
879 | MD_VERSION, |
880 | SignalException, |
881 | ) |
882 | +from provisioningserver.utils.version import get_maas_version |
883 | from testtools.matchers import ( |
884 | Contains, |
885 | DirExists, |
886 | @@ -82,6 +83,7 @@ class TestHelpers(MAASTestCase): |
887 | 'architecture': architecture, |
888 | 'osystem': osystem, |
889 | 'distro_series': distro_series, |
890 | + 'maas_version': get_maas_version(), |
891 | 'interfaces': {}, |
892 | }, Equals(refresh.get_sys_info())) |
893 | |
894 | @@ -105,6 +107,7 @@ class TestHelpers(MAASTestCase): |
895 | 'architecture': architecture, |
896 | 'osystem': osystem, |
897 | 'distro_series': distro_series, |
898 | + 'maas_version': get_maas_version(), |
899 | 'interfaces': {}, |
900 | }, Equals(refresh.get_sys_info())) |
901 | |
902 | @@ -120,6 +123,7 @@ class TestHelpers(MAASTestCase): |
903 | 'architecture': architecture, |
904 | 'osystem': '', |
905 | 'distro_series': '', |
906 | + 'maas_version': get_maas_version(), |
907 | 'interfaces': {}, |
908 | }, Equals(refresh.get_sys_info())) |
909 |
quick comment :)