Merge ~ltrager/maas:node_device_websocket into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Adam Collard
Approved revision: d6d84b1ebcbb150d596d9d4dff11ddbfe0a18e89
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:node_device_websocket
Merge into: maas:master
Prerequisite: ~ltrager/maas:node_device_api
Diff against target: 366 lines (+220/-6)
10 files modified
src/maasserver/triggers/testing.py (+17/-1)
src/maasserver/triggers/tests/test_init.py (+3/-0)
src/maasserver/triggers/tests/test_websocket_listener.py (+63/-0)
src/maasserver/triggers/websocket.py (+19/-1)
src/maasserver/websockets/handlers/__init__.py (+3/-2)
src/maasserver/websockets/handlers/node.py (+1/-1)
src/maasserver/websockets/handlers/node_device.py (+51/-0)
src/maasserver/websockets/handlers/tests/test_machine.py (+10/-1)
src/maasserver/websockets/handlers/tests/test_node_device.py (+51/-0)
src/maasserver/websockets/tests/test_protocol.py (+2/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Alberto Donato (community) Approve
Review via email: mp+395409@code.launchpad.net

Commit message

Add NodeDevice websocket

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

UNIT TESTS
-b node_device_websocket lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/8908/console
COMMIT: 4cc0b29b718e96d3a7c82ec7b88e1174c53d9003

review: Needs Fixing
Revision history for this message
Alberto Donato (ack) wrote :

LGTM

review: Approve
~ltrager/maas:node_device_websocket updated
525610a... by Lee Trager

Merge branch 'master' into node_device_model

b48316d... by Lee Trager

ack fix

2fbf073... by Lee Trager

Merge branch 'node_device_model' into process_node_device_data

4d8aa9f... by Lee Trager

Fix merge mistake

69c462f... by Lee Trager

Merge branch 'process_node_device_data' into node_device_api

f69a752... by Lee Trager

Merge branch 'node_device_api' into node_device_websocket

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b node_device_websocket lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/8923/console
COMMIT: f69a7522c82a1e51a1f29e3097902aff3cd37654

review: Needs Fixing
~ltrager/maas:node_device_websocket updated
72cf97b... by Lee Trager

Merge branch 'master' into process_node_device_data

1d81711... by Lee Trager

Merge branch 'process_node_device_data' into node_device_api

174a1d0... by Lee Trager

Merge branch 'node_device_api' into node_device_websocket

2b01786... by Lee Trager

Fix failing test

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b node_device_websocket lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/8949/console
COMMIT: 2b01786670299787dc01bae90e4a0b14851ec43b

review: Needs Fixing
~ltrager/maas:node_device_websocket updated
ad87d83... by Lee Trager

Merge branch 'master' into process_node_device_data

917084b... by Lee Trager

adam-collard fixes

f7b6424... by Lee Trager

Merge branch 'process_node_device_data' into node_device_api

d6d84b1... by Lee Trager

Merge branch 'node_device_api' into node_device_websocket

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b node_device_websocket lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: d6d84b1ebcbb150d596d9d4dff11ddbfe0a18e89

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/triggers/testing.py b/src/maasserver/triggers/testing.py
2index 0a52dbd..5525281 100644
3--- a/src/maasserver/triggers/testing.py
4+++ b/src/maasserver/triggers/testing.py
5@@ -1,4 +1,4 @@
6-# Copyright 2016 Canonical Ltd. This software is licensed under the
7+# Copyright 2016-2020 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 """Helper class for all tests using the `PostgresListenerService` under
11@@ -29,6 +29,7 @@ from maasserver.models.filesystemgroup import FilesystemGroup
12 from maasserver.models.interface import Interface
13 from maasserver.models.iprange import IPRange
14 from maasserver.models.node import Node, RackController, RegionController
15+from maasserver.models.nodedevice import NodeDevice
16 from maasserver.models.nodemetadata import NodeMetadata
17 from maasserver.models.packagerepository import PackageRepository
18 from maasserver.models.partition import Partition
19@@ -827,6 +828,21 @@ class TransactionalHelpersMixin:
20 config.value = value
21 config.save()
22
23+ @transactional
24+ def create_node_device(self, params=None):
25+ if params is None:
26+ params = {}
27+ return factory.make_NodeDevice(**params)
28+
29+ @transactional
30+ def update_node_device(self, id, params, **kwargs):
31+ return apply_update_to_model(NodeDevice, id, params, **kwargs)
32+
33+ @transactional
34+ def delete_node_device(self, id):
35+ node_device = NodeDevice.objects.get(id=id)
36+ node_device.delete()
37+
38
39 class DNSHelpersMixin:
40 """Helper to get the zone serial and to assert it was incremented."""
41diff --git a/src/maasserver/triggers/tests/test_init.py b/src/maasserver/triggers/tests/test_init.py
42index 165d325..93e749d 100644
43--- a/src/maasserver/triggers/tests/test_init.py
44+++ b/src/maasserver/triggers/tests/test_init.py
45@@ -187,6 +187,9 @@ class TestTriggersUsed(MAASServerTestCase):
46 "neighbour_neighbour_create_notify",
47 "neighbour_neighbour_delete_notify",
48 "neighbour_neighbour_update_notify",
49+ "nodedevice_nodedevice_create_notify",
50+ "nodedevice_nodedevice_delete_notify",
51+ "nodedevice_nodedevice_update_notify",
52 "node_device_create_notify",
53 "node_device_delete_notify",
54 "node_device_update_notify",
55diff --git a/src/maasserver/triggers/tests/test_websocket_listener.py b/src/maasserver/triggers/tests/test_websocket_listener.py
56index 8589e21..9b1f535 100644
57--- a/src/maasserver/triggers/tests/test_websocket_listener.py
58+++ b/src/maasserver/triggers/tests/test_websocket_listener.py
59@@ -5132,3 +5132,66 @@ class TestScriptListener(
60 self.assertEqual(("delete", "%s" % script.id), dv.value)
61 finally:
62 yield listener.stopService()
63+
64+
65+class TestNodeDeviceListener(
66+ MAASTransactionServerTestCase, TransactionalHelpersMixin
67+):
68+ @wait_for_reactor
69+ @inlineCallbacks
70+ def test_calls_handler_on_create_notification(self):
71+ yield deferToDatabase(register_websocket_triggers)
72+ node = yield deferToDatabase(self.create_node)
73+ listener = self.make_listener_without_delay()
74+ dv = DeferredValue()
75+ listener.register("nodedevice", lambda *args: dv.set(args))
76+ yield listener.startService()
77+ try:
78+ node_device = yield deferToDatabase(
79+ self.create_node_device, {"node": node}
80+ )
81+ yield dv.get(timeout=2)
82+ self.assertEqual(("create", "%s" % node_device.id), dv.value)
83+ finally:
84+ yield listener.stopService()
85+
86+ @wait_for_reactor
87+ @inlineCallbacks
88+ def test_calls_handler_on_update_notification(self):
89+ yield deferToDatabase(register_websocket_triggers)
90+ listener = self.make_listener_without_delay()
91+ dv = DeferredValue()
92+ listener.register("nodedevice", lambda *args: dv.set(args))
93+ node_device = yield deferToDatabase(self.create_node_device)
94+
95+ yield listener.startService()
96+ try:
97+ yield deferToDatabase(
98+ self.update_node_device,
99+ node_device.id,
100+ {
101+ "commissioning_driver": factory.make_name(
102+ "commissioning_driver"
103+ )
104+ },
105+ )
106+ yield dv.get(timeout=2)
107+ self.assertEqual(("update", "%s" % node_device.id), dv.value)
108+ finally:
109+ yield listener.stopService()
110+
111+ @wait_for_reactor
112+ @inlineCallbacks
113+ def test_calls_handler_on_delete_notification(self):
114+ yield deferToDatabase(register_websocket_triggers)
115+ listener = self.make_listener_without_delay()
116+ dv = DeferredValue()
117+ listener.register("nodedevice", lambda *args: dv.set(args))
118+ node_device = yield deferToDatabase(self.create_node_device)
119+ yield listener.startService()
120+ try:
121+ yield deferToDatabase(self.delete_node_device, node_device.id)
122+ yield dv.get(timeout=2)
123+ self.assertEqual(("delete", "%s" % node_device.id), dv.value)
124+ finally:
125+ yield listener.stopService()
126diff --git a/src/maasserver/triggers/websocket.py b/src/maasserver/triggers/websocket.py
127index bbd0861..ac2f9e2 100644
128--- a/src/maasserver/triggers/websocket.py
129+++ b/src/maasserver/triggers/websocket.py
130@@ -1,4 +1,4 @@
131-# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
132+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
133 # GNU Affero General Public License version 3 (see the file LICENSE).
134
135 """
136@@ -2724,3 +2724,21 @@ def register_websocket_triggers():
137 )
138 )
139 register_triggers("metadataserver_script", "script")
140+
141+ # NodeDevice table
142+ register_procedure(
143+ render_notification_procedure(
144+ "nodedevice_create_notify", "nodedevice_create", "NEW.id"
145+ )
146+ )
147+ register_procedure(
148+ render_notification_procedure(
149+ "nodedevice_update_notify", "nodedevice_update", "NEW.id"
150+ )
151+ )
152+ register_procedure(
153+ render_notification_procedure(
154+ "nodedevice_delete_notify", "nodedevice_delete", "OLD.id"
155+ )
156+ )
157+ register_triggers("maasserver_nodedevice", "nodedevice")
158diff --git a/src/maasserver/websockets/handlers/__init__.py b/src/maasserver/websockets/handlers/__init__.py
159index 87901f8..1ae365c 100644
160--- a/src/maasserver/websockets/handlers/__init__.py
161+++ b/src/maasserver/websockets/handlers/__init__.py
162@@ -1,4 +1,4 @@
163-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
164+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
165 # GNU Affero General Public License version 3 (see the file LICENSE).
166
167 """Handlers for the WebSocket connections."""
168@@ -19,7 +19,7 @@ __all__ = [
169 "IPRangeHandler",
170 "IPRangeHandler",
171 "MachineHandler",
172- "NodeResultHandler",
173+ "NodeDeviceHandler",
174 "NodeResultHandler",
175 "NotificationHandler",
176 "PackageRepositoryHandler",
177@@ -52,6 +52,7 @@ from maasserver.websockets.handlers.fabric import FabricHandler
178 from maasserver.websockets.handlers.general import GeneralHandler
179 from maasserver.websockets.handlers.iprange import IPRangeHandler
180 from maasserver.websockets.handlers.machine import MachineHandler
181+from maasserver.websockets.handlers.node_device import NodeDeviceHandler
182 from maasserver.websockets.handlers.node_result import NodeResultHandler
183 from maasserver.websockets.handlers.notification import NotificationHandler
184 from maasserver.websockets.handlers.packagerepository import (
185diff --git a/src/maasserver/websockets/handlers/node.py b/src/maasserver/websockets/handlers/node.py
186index 48b4e38..bca7d2f 100644
187--- a/src/maasserver/websockets/handlers/node.py
188+++ b/src/maasserver/websockets/handlers/node.py
189@@ -138,7 +138,7 @@ class NodeHandler(TimestampedModelHandler):
190 def dehydrate_numanode(self, numa_node):
191 details = {
192 attr: getattr(numa_node, attr)
193- for attr in ("index", "memory", "cores")
194+ for attr in ("id", "index", "memory", "cores")
195 }
196 details["hugepages_set"] = [
197 self.dehydrate_hugepages(hugepages)
198diff --git a/src/maasserver/websockets/handlers/node_device.py b/src/maasserver/websockets/handlers/node_device.py
199new file mode 100644
200index 0000000..c69409d
201--- /dev/null
202+++ b/src/maasserver/websockets/handlers/node_device.py
203@@ -0,0 +1,51 @@
204+# Copyright 2020 Canonical Ltd. This software is licensed under the
205+# GNU Affero General Public License version 3 (see the file LICENSE).
206+
207+"""The Script handler for the WebSocket connection."""
208+
209+from operator import attrgetter
210+
211+from maasserver.models import NodeDevice
212+from maasserver.websockets.base import HandlerPKError
213+from maasserver.websockets.handlers.timestampedmodel import (
214+ TimestampedModelHandler,
215+)
216+
217+
218+class NodeDeviceHandler(TimestampedModelHandler):
219+ class Meta:
220+ queryset = NodeDevice.objects.all()
221+ pk = "id"
222+ allowed_methods = ["list", "delete"]
223+ listen_channels = ["nodedevice"]
224+
225+ def dehydrate(self, obj, data, for_list=False):
226+ # When NodeDevices are loaded in the UI the client has already received
227+ # the keys below. Instead of reprocessing them make it clear the handler
228+ # is only returning the ids, not values,
229+ for key in [
230+ "physical_blockdevice",
231+ "physical_interface",
232+ "numa_node",
233+ "node",
234+ ]:
235+ id = data.pop(key)
236+ data[f"{key}_id"] = id
237+ return data
238+
239+ def list(self, params):
240+ """List NodeDevice objects.
241+
242+ :param system_id: `Node.system_id` for the NodeDevices.
243+ """
244+ if "system_id" not in params:
245+ raise HandlerPKError("Missing system_id in params")
246+ system_id = params["system_id"]
247+
248+ qs = self.get_queryset(for_list=True)
249+ qs = qs.filter(node__system_id=system_id)
250+
251+ objs = list(qs)
252+ getpk = attrgetter(self._meta.pk)
253+ self.cache["loaded_pks"].update(getpk(obj) for obj in objs)
254+ return [self.full_dehydrate(obj, for_list=True) for obj in objs]
255diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py
256index 6ce5614..62cd7d6 100644
257--- a/src/maasserver/websockets/handlers/tests/test_machine.py
258+++ b/src/maasserver/websockets/handlers/tests/test_machine.py
259@@ -2088,20 +2088,29 @@ class TestMachineHandler(MAASServerTestCase):
260 self.assertEqual(
261 result["numa_nodes"],
262 [
263- {"index": 0, "memory": 0, "cores": [], "hugepages_set": []},
264 {
265+ "id": numa_node.id - 3,
266+ "index": 0,
267+ "memory": 0,
268+ "cores": [],
269+ "hugepages_set": [],
270+ },
271+ {
272+ "id": numa_node.id - 2,
273 "index": 1,
274 "memory": 512,
275 "cores": [0, 1],
276 "hugepages_set": [{"page_size": 1024, "total": 1024}],
277 },
278 {
279+ "id": numa_node.id - 1,
280 "index": 2,
281 "memory": 1024,
282 "cores": [2, 3],
283 "hugepages_set": [{"page_size": 1024, "total": 2048}],
284 },
285 {
286+ "id": numa_node.id,
287 "index": 3,
288 "memory": 2048,
289 "cores": [4, 5],
290diff --git a/src/maasserver/websockets/handlers/tests/test_node_device.py b/src/maasserver/websockets/handlers/tests/test_node_device.py
291new file mode 100644
292index 0000000..beea5dd
293--- /dev/null
294+++ b/src/maasserver/websockets/handlers/tests/test_node_device.py
295@@ -0,0 +1,51 @@
296+# Copyright 2020 Canonical Ltd. This software is licensed under the
297+# GNU Affero General Public License version 3 (see the file LICENSE).
298+
299+"""Tests for `maasserver.websockets.handlers.node_device`"""
300+
301+from maasserver.testing.factory import factory
302+from maasserver.testing.testcase import MAASServerTestCase
303+from maasserver.websockets.base import dehydrate_datetime, HandlerPKError
304+from maasserver.websockets.handlers.node_device import NodeDeviceHandler
305+
306+
307+class TestNodeDeviceHandler(MAASServerTestCase):
308+ def dehydrate_node_device(self, node_device):
309+ return {
310+ "id": node_device.id,
311+ "created": dehydrate_datetime(node_device.created),
312+ "updated": dehydrate_datetime(node_device.updated),
313+ "bus": node_device.bus,
314+ "hardware_type": node_device.hardware_type,
315+ "vendor_id": node_device.vendor_id,
316+ "product_id": node_device.product_id,
317+ "vendor_name": node_device.vendor_name,
318+ "product_name": node_device.product_name,
319+ "commissioning_driver": node_device.commissioning_driver,
320+ "bus_number": node_device.bus_number,
321+ "device_number": node_device.device_number,
322+ "pci_address": node_device.pci_address,
323+ "physical_blockdevice_id": node_device.physical_blockdevice_id,
324+ "physical_interface_id": node_device.physical_interface_id,
325+ "numa_node_id": node_device.numa_node_id,
326+ "node_id": node_device.node_id,
327+ }
328+
329+ def test_list(self):
330+ user = factory.make_User()
331+ handler = NodeDeviceHandler(user, {}, None)
332+ node = factory.make_Node_with_Interface_on_Subnet()
333+ factory.make_Node()
334+
335+ self.assertEqual(
336+ [
337+ self.dehydrate_node_device(node_device)
338+ for node_device in node.node_devices.all()
339+ ],
340+ handler.list({"system_id": node.system_id}),
341+ )
342+
343+ def test_list_raises_error_if_no_system_id(self):
344+ user = factory.make_User()
345+ handler = NodeDeviceHandler(user, {}, None)
346+ self.assertRaises(HandlerPKError, handler.list, {})
347diff --git a/src/maasserver/websockets/tests/test_protocol.py b/src/maasserver/websockets/tests/test_protocol.py
348index 3007e43..b2cba6a 100644
349--- a/src/maasserver/websockets/tests/test_protocol.py
350+++ b/src/maasserver/websockets/tests/test_protocol.py
351@@ -775,6 +775,7 @@ ALL_NOTIFIERS = (
352 "fabric",
353 "iprange",
354 "machine",
355+ "nodedevice",
356 "notification",
357 "notificationdismissal",
358 "packagerepository",
359@@ -809,6 +810,7 @@ ALL_HANDLERS = (
360 "general",
361 "iprange",
362 "machine",
363+ "nodedevice",
364 "noderesult",
365 "notification",
366 "packagerepository",

Subscribers

People subscribed via source and target branches