Merge ~ltrager/maas:lp1870171_2.3 into maas:2.3

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: c95e561d3333430526370220a3c340d3ce34fd42
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1870171_2.3
Merge into: maas:2.3
Diff against target: 564 lines (+153/-120)
10 files modified
src/maasserver/api/account.py (+1/-1)
src/maasserver/api/machines.py (+21/-9)
src/maasserver/api/tests/test_api.py (+15/-2)
src/maasserver/api/tests/test_machine.py (+40/-16)
src/maasserver/api/tests/test_machines.py (+13/-48)
src/maasserver/models/node.py (+10/-7)
src/maasserver/models/tests/test_node.py (+29/-32)
src/maasserver/models/tests/test_userprofile.py (+9/-1)
src/maasserver/models/userprofile.py (+7/-2)
src/maasserver/node_action.py (+8/-2)
Reviewer Review Type Date Requested Status
Lee Trager (community) Approve
Review via email: mp+382099@code.launchpad.net

Commit message

Backport of d0653c9 LP: #1870171 - Remove the token column from the Node table.

The token column is only used by the API when allocating and machine and
listing allocated machines. When a User Token is deleted this association
causes the Node to be deleted as well. Allocated machines are already
filtered by owner and status so the token column isn't needed.

In the backport Node.token is no longer used but the column remains to
avoid creating a migration.

To post a comment you must log in.
Revision history for this message
Lee Trager (ltrager) wrote :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/account.py b/src/maasserver/api/account.py
2index adc8722..ba6ed54 100644
3--- a/src/maasserver/api/account.py
4+++ b/src/maasserver/api/account.py
5@@ -1,4 +1,4 @@
6-# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
7+# Copyright 2014-2020 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 """API handler: `Account`."""
11diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
12index e8b671b..9482631 100644
13--- a/src/maasserver/api/machines.py
14+++ b/src/maasserver/api/machines.py
15@@ -1,4 +1,4 @@
16-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
17+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
18 # GNU Affero General Public License version 3 (see the file LICENSE).
19
20 __all__ = [
21@@ -41,7 +41,6 @@ from maasserver.api.support import (
22 )
23 from maasserver.api.utils import (
24 get_mandatory_param,
25- get_oauth_token,
26 get_optional_list,
27 get_optional_param,
28 )
29@@ -525,7 +524,7 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin):
30 "Request from user %s to acquire machine: %s (%s)",
31 request.user.username, machine.fqdn, machine.system_id)
32 machine.acquire(
33- request.user, get_oauth_token(request),
34+ request.user,
35 agent_name=agent_name, comment=comment,
36 bridge_all=bridge_all, bridge_stp=bridge_stp,
37 bridge_fd=bridge_fd)
38@@ -1314,11 +1313,24 @@ class MachinesHandler(NodesHandler, PowersMixin):
39
40 @operation(idempotent=True)
41 def list_allocated(self, request):
42- """Fetch Machines that were allocated to the User/oauth token."""
43- token = get_oauth_token(request)
44- match_ids = get_optional_list(request.GET, 'id')
45- machines = Machine.objects.get_allocated_visible_machines(
46- token, match_ids)
47+ """@description-title List allocated
48+ @description List machines that were allocated to the User.
49+
50+ @success (http-status-code) "200" 200
51+ @success (json) "success-json" A JSON object containing a list of
52+ allocated machines.
53+ @success-example "success-json" [exkey=machines-placeholder]
54+ placeholder text
55+ """
56+ # limit to machines that the user can view
57+ machines = Machine.objects.get_nodes(
58+ request.user, NODE_PERMISSION.VIEW)
59+ machines = machines.filter(
60+ owner=request.user, status=NODE_STATUS.ALLOCATED
61+ )
62+ system_ids = get_optional_list(request.GET, "id")
63+ if system_ids:
64+ machines = machines.filter(system_id__in=system_ids)
65 return machines.order_by('id')
66
67 @operation(idempotent=False)
68@@ -1606,7 +1618,7 @@ class MachinesHandler(NodesHandler, PowersMixin):
69 raise NodesNotAvailable(message)
70 if not dry_run:
71 machine.acquire(
72- request.user, get_oauth_token(request),
73+ request.user,
74 agent_name=agent_name, comment=comment,
75 bridge_all=bridge_all, bridge_stp=bridge_stp,
76 bridge_fd=bridge_fd)
77diff --git a/src/maasserver/api/tests/test_api.py b/src/maasserver/api/tests/test_api.py
78index 08892d9..4e8b9e0 100644
79--- a/src/maasserver/api/tests/test_api.py
80+++ b/src/maasserver/api/tests/test_api.py
81@@ -1,4 +1,4 @@
82-# Copyright 2012-2016 Canonical Ltd. This software is licensed under the
83+# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
84 # GNU Affero General Public License version 3 (see the file LICENSE).
85
86 """Test maasserver API."""
87@@ -45,7 +45,10 @@ from maasserver.testing.testclient import MAASSensibleOAuthClient
88 from maasserver.utils.converters import json_load_bytes
89 from maasserver.utils.django_urls import reverse
90 from maasserver.utils.keys import ImportSSHKeysError
91-from maasserver.utils.orm import get_one
92+from maasserver.utils.orm import (
93+ get_one,
94+ reload_object,
95+)
96 from maastesting.matchers import MockCalledOnceWith
97 from maastesting.testcase import MAASTestCase
98 from piston3.doc import generate_doc
99@@ -288,6 +291,16 @@ class AccountAPITest(APITestCase.ForUser):
100
101 self.assertEqual(http.client.BAD_REQUEST, response.status_code)
102
103+ def test_delete_authorisation_token_doesnt_delete_node(self):
104+ token = self.user.tokens.first()
105+ node = factory.make_Node(owner=self.user, token=token)
106+ response = self.client.post(
107+ reverse("account_handler"),
108+ {"op": "delete_authorisation_token", "token_key": token.key},
109+ )
110+ self.assertEqual(http.client.NO_CONTENT, response.status_code)
111+ self.assertIsNotNone(reload_object(node))
112+
113 def test_update_authorisation_token(self):
114 token_name_orig = 'Test_Token'
115 token_name_updated = 'Test_Token update'
116diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py
117index 5b55eaf..e0b45ab 100644
118--- a/src/maasserver/api/tests/test_machine.py
119+++ b/src/maasserver/api/tests/test_machine.py
120@@ -716,10 +716,16 @@ class TestMachineAPI(APITestCase.ForUser):
121 }
122 self.client.post(self.get_machine_uri(machine), request)
123 self.assertThat(
124- machine_method, MockCalledOnceWith(
125- ANY, ANY, agent_name=ANY,
126- bridge_all=False, bridge_fd=False,
127- bridge_stp=False, comment=request['comment']))
128+ machine_method,
129+ MockCalledOnceWith(
130+ ANY,
131+ agent_name=ANY,
132+ bridge_all=False,
133+ bridge_fd=0,
134+ bridge_stp=False,
135+ comment=request["comment"],
136+ ),
137+ )
138
139 def test_POST_deploy_passes_bridge_settings(self):
140 self.patch(node_module.Node, "_start")
141@@ -740,10 +746,16 @@ class TestMachineAPI(APITestCase.ForUser):
142 }
143 self.client.post(self.get_machine_uri(machine), request)
144 self.assertThat(
145- machine_method, MockCalledOnceWith(
146- ANY, ANY, agent_name=ANY,
147- bridge_all=True, bridge_fd=7,
148- bridge_stp=True, comment=None))
149+ machine_method,
150+ MockCalledOnceWith(
151+ ANY,
152+ agent_name=ANY,
153+ bridge_all=True,
154+ bridge_fd=7,
155+ bridge_stp=True,
156+ comment=None,
157+ ),
158+ )
159
160 def test_POST_release_releases_owned_machine(self):
161 self.patch(node_module.Machine, '_stop')
162@@ -898,10 +910,16 @@ class TestMachineAPI(APITestCase.ForUser):
163 reverse('machines_handler'),
164 {'op': 'allocate', 'comment': comment})
165 self.assertThat(
166- machine_method, MockCalledOnceWith(
167- ANY, ANY, agent_name=ANY,
168- bridge_all=False, bridge_fd=False,
169- bridge_stp=False, comment=comment))
170+ machine_method,
171+ MockCalledOnceWith(
172+ ANY,
173+ agent_name=ANY,
174+ bridge_all=False,
175+ bridge_fd=False,
176+ bridge_stp=False,
177+ comment=comment,
178+ ),
179+ )
180
181 def test_POST_allocate_handles_missing_comment(self):
182 factory.make_Node(
183@@ -911,10 +929,16 @@ class TestMachineAPI(APITestCase.ForUser):
184 self.client.post(
185 reverse('machines_handler'), {'op': 'allocate'})
186 self.assertThat(
187- machine_method, MockCalledOnceWith(
188- ANY, ANY, agent_name=ANY,
189- bridge_all=False, bridge_fd=False,
190- bridge_stp=False, comment=None))
191+ machine_method,
192+ MockCalledOnceWith(
193+ ANY,
194+ agent_name=ANY,
195+ bridge_all=False,
196+ bridge_fd=0,
197+ bridge_stp=False,
198+ comment=None,
199+ ),
200+ )
201
202 def test_POST_release_frees_hwe_kernel(self):
203 self.patch(node_module.Machine, '_stop')
204diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py
205index b58f737..474ecaf 100644
206--- a/src/maasserver/api/tests/test_machines.py
207+++ b/src/maasserver/api/tests/test_machines.py
208@@ -1,4 +1,4 @@
209-# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
210+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
211 # GNU Affero General Public License version 3 (see the file LICENSE).
212
213 """Tests for the machines API."""
214@@ -33,10 +33,6 @@ from maasserver.models import (
215 node as node_module,
216 )
217 from maasserver.models.node import RELEASABLE_STATUSES
218-from maasserver.models.user import (
219- create_auth_token,
220- get_auth_tokens,
221-)
222 from maasserver.node_constraint_filter_forms import AcquireNodeForm
223 from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
224 from maasserver.testing.api import (
225@@ -546,29 +542,11 @@ class TestMachinesAPI(APITestCase.ForUser):
226 [machine.system_id for machine in machines],
227 extract_system_ids(parsed_result))
228
229- def test_GET_list_allocated_returns_only_allocated_with_user_token(self):
230- # If the user's allocated machines have different session tokens,
231- # list_allocated should only return the machines that have the
232- # current request's token on them.
233- machine_1 = factory.make_Node(
234- status=NODE_STATUS.ALLOCATED, owner=self.user,
235- token=get_auth_tokens(self.user)[0])
236- second_token = create_auth_token(self.user)
237- factory.make_Node(
238- owner=self.user, status=NODE_STATUS.ALLOCATED,
239- token=second_token)
240-
241- user_2 = factory.make_User()
242- create_auth_token(user_2)
243- factory.make_Node(
244- owner=self.user, status=NODE_STATUS.ALLOCATED,
245- token=second_token)
246-
247- # At this point we have two machines owned by the same user but
248- # allocated with different tokens, and a third machine allocated to
249- # someone else entirely. We expect list_allocated to
250- # return the machine with the same token as the one used in
251- # self.client, which is the one we set on machine_1 above.
252+ def test_GET_list_allocated_returns_only_allocated_with_user(self):
253+ machine = factory.make_Node(
254+ status=NODE_STATUS.ALLOCATED, owner=self.user
255+ )
256+ factory.make_Node(status=NODE_STATUS.ALLOCATED)
257
258 response = self.client.get(reverse('machines_handler'), {
259 'op': 'list_allocated'})
260@@ -576,17 +554,17 @@ class TestMachinesAPI(APITestCase.ForUser):
261 parsed_result = json.loads(
262 response.content.decode(settings.DEFAULT_CHARSET))
263 self.assertItemsEqual(
264- [machine_1.system_id], extract_system_ids(parsed_result))
265+ [machine.system_id], extract_system_ids(parsed_result)
266+ )
267
268 def test_GET_list_allocated_filters_by_id(self):
269- # list_allocated takes an optional list of 'id' parameters to
270- # filter returned results.
271- current_token = get_auth_tokens(self.user)[0]
272 machines = []
273 for _ in range(3):
274- machines.append(factory.make_Node(
275- status=NODE_STATUS.ALLOCATED,
276- owner=self.user, token=current_token))
277+ machines.append(
278+ factory.make_Node(
279+ status=NODE_STATUS.ALLOCATED, owner=self.user
280+ )
281+ )
282
283 required_machine_ids = [machines[0].system_id, machines[1].system_id]
284 response = self.client.get(reverse('machines_handler'), {
285@@ -1450,19 +1428,6 @@ class TestMachinesAPI(APITestCase.ForUser):
286 response.content.decode(settings.DEFAULT_CHARSET))['system_id']
287 self.assertEqual(eligible_machine.system_id, system_id)
288
289- def test_POST_allocate_sets_a_token(self):
290- # "acquire" should set the Token being used in the request on
291- # the Machine that is allocated.
292- available_status = NODE_STATUS.READY
293- machine = factory.make_Node(
294- status=available_status, owner=None, with_boot_disk=True)
295- response = self.client.post(
296- reverse('machines_handler'), {'op': 'allocate'})
297- self.assertThat(response, HasStatusCode(http.client.OK))
298- machine = Machine.objects.get(system_id=machine.system_id)
299- oauth_key = self.client.token.key
300- self.assertEqual(oauth_key, machine.token.key)
301-
302 def test_POST_accept_gets_machine_out_of_declared_state(self):
303 # This will change when we add provisioning. Until then,
304 # acceptance gets a machine straight to Ready state.
305diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
306index 1eb14d9..cb49543 100644
307--- a/src/maasserver/models/node.py
308+++ b/src/maasserver/models/node.py
309@@ -2551,11 +2551,16 @@ class Node(CleanSave, TimestampedModel):
310 "s" if len(missing_packages) > 1 else ""))
311
312 def acquire(
313- self, user, token=None, agent_name='', comment=None,
314- bridge_all=False, bridge_stp=None, bridge_fd=None):
315- """Mark commissioned node as acquired by the given user and token."""
316+ self,
317+ user,
318+ agent_name="",
319+ comment=None,
320+ bridge_all=False,
321+ bridge_stp=None,
322+ bridge_fd=None,
323+ ):
324+ """Mark commissioned node as acquired by the given user."""
325 assert self.owner is None or self.owner == user
326- assert token is None or token.user == user
327
328 self._create_acquired_filesystems()
329 self._register_request_event(
330@@ -2564,7 +2569,6 @@ class Node(CleanSave, TimestampedModel):
331 self.status = NODE_STATUS.ALLOCATED
332 self.owner = user
333 self.agent_name = agent_name
334- self.token = token
335 if bridge_all:
336 self._create_acquired_bridges(
337 bridge_stp=bridge_stp, bridge_fd=bridge_fd)
338@@ -2823,8 +2827,7 @@ class Node(CleanSave, TimestampedModel):
339 finalize_release = True
340
341 self.status = NODE_STATUS.RELEASING
342- self.token = None
343- self.agent_name = ''
344+ self.agent_name = ""
345 self.set_netboot()
346 self.osystem = ''
347 self.distro_series = ''
348diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
349index a47ee5d..193296e 100644
350--- a/src/maasserver/models/tests/test_node.py
351+++ b/src/maasserver/models/tests/test_node.py
352@@ -1,4 +1,4 @@
353-# Copyright 2012-2017 Canonical Ltd. This software is licensed under the
354+# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
355 # GNU Affero General Public License version 3 (see the file LICENSE).
356
357 """Test maasserver models."""
358@@ -108,7 +108,6 @@ from maasserver.models.node import (
359 )
360 from maasserver.models.signals import power as node_query
361 from maasserver.models.timestampedmodel import now
362-from maasserver.models.user import create_auth_token
363 from maasserver.node_status import (
364 COMMISSIONING_LIKE_STATUSES,
365 NODE_FAILURE_MONITORED_STATUS_TRANSITIONS,
366@@ -1113,12 +1112,6 @@ class TestNode(MAASServerTestCase):
367 node = factory.make_Node(bios_boot_method=factory.make_name("boot"))
368 self.assertEqual("pxe", node.get_bios_boot_method())
369
370- def test_add_node_with_token(self):
371- user = factory.make_User()
372- token = create_auth_token(user)
373- node = factory.make_Node(token=token)
374- self.assertEqual(token, node.token)
375-
376 def test_add_physical_interface(self):
377 mac = factory.make_mac_address()
378 node = factory.make_Node()
379@@ -1560,9 +1553,8 @@ class TestNode(MAASServerTestCase):
380 def test_acquire(self):
381 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
382 user = factory.make_User()
383- token = create_auth_token(user)
384- agent_name = factory.make_name('agent-name')
385- node.acquire(user, token, agent_name)
386+ agent_name = factory.make_name("agent-name")
387+ node.acquire(user, agent_name)
388 self.assertEqual(
389 (user, NODE_STATUS.ALLOCATED, agent_name),
390 (node.owner, node.status, node.agent_name))
391@@ -1570,36 +1562,44 @@ class TestNode(MAASServerTestCase):
392 def test_acquire_calls__create_acquired_filesystems(self):
393 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
394 user = factory.make_User()
395- token = create_auth_token(user)
396- agent_name = factory.make_name('agent-name')
397+ agent_name = factory.make_name("agent-name")
398 mock_create_acquired_filesystems = self.patch_autospec(
399- node, "_create_acquired_filesystems")
400- node.acquire(user, token, agent_name)
401+ node, "_create_acquired_filesystems"
402+ )
403+ node.acquire(user, agent_name)
404 self.assertThat(mock_create_acquired_filesystems, MockCalledOnceWith())
405
406 def test_acquire_logs_user_request(self):
407 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
408 user = factory.make_User()
409- token = create_auth_token(user)
410- agent_name = factory.make_name('agent-name')
411- register_event = self.patch(node, '_register_request_event')
412- node.acquire(user, token, agent_name)
413- self.assertThat(register_event, MockCalledOnceWith(
414- user, EVENT_TYPES.REQUEST_NODE_ACQUIRE, action='acquire',
415- comment=None))
416+ agent_name = factory.make_name("agent-name")
417+ register_event = self.patch(node, "_register_request_event")
418+ node.acquire(user, agent_name)
419+ self.assertThat(
420+ register_event,
421+ MockCalledOnceWith(
422+ user,
423+ EVENT_TYPES.REQUEST_NODE_ACQUIRE,
424+ action="acquire",
425+ comment=None,
426+ ),
427+ )
428
429 def test_acquire_calls__create_acquired_bridges(self):
430 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
431 user = factory.make_User()
432- token = create_auth_token(user)
433- agent_name = factory.make_name('agent-name')
434+ agent_name = factory.make_name("agent-name")
435 mock_create_acquired_bridges = self.patch_autospec(
436 node, "_create_acquired_bridges")
437 bridge_stp = factory.pick_bool()
438 bridge_fd = random.randint(0, 500)
439 node.acquire(
440- user, token, agent_name,
441- bridge_all=True, bridge_stp=bridge_stp, bridge_fd=bridge_fd)
442+ user,
443+ agent_name,
444+ bridge_all=True,
445+ bridge_stp=bridge_stp,
446+ bridge_fd=bridge_fd,
447+ )
448 self.assertThat(
449 mock_create_acquired_bridges,
450 MockCalledOnceWith(bridge_stp=bridge_stp, bridge_fd=bridge_fd))
451@@ -2102,8 +2102,7 @@ class TestNode(MAASServerTestCase):
452 MockCalledOnceWith(node.system_id, node.get_releasing_time()))
453 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
454 self.expectThat(node.owner, Equals(owner))
455- self.expectThat(node.agent_name, Equals(''))
456- self.expectThat(node.token, Is(None))
457+ self.expectThat(node.agent_name, Equals(""))
458 self.expectThat(node.netboot, Is(True))
459 self.expectThat(node.osystem, Equals(''))
460 self.expectThat(node.distro_series, Equals(''))
461@@ -2141,8 +2140,7 @@ class TestNode(MAASServerTestCase):
462 self.expectThat(Node._set_status_expires, MockNotCalled())
463 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
464 self.expectThat(node.owner, Equals(owner))
465- self.expectThat(node.agent_name, Equals(''))
466- self.expectThat(node.token, Is(None))
467+ self.expectThat(node.agent_name, Equals(""))
468 self.expectThat(node.netboot, Is(True))
469 self.expectThat(node.osystem, Equals(''))
470 self.expectThat(node.distro_series, Equals(''))
471@@ -2169,8 +2167,7 @@ class TestNode(MAASServerTestCase):
472 self.expectThat(Node._set_status_expires, MockNotCalled())
473 self.expectThat(node.status, Equals(NODE_STATUS.READY))
474 self.expectThat(node.owner, Equals(None))
475- self.expectThat(node.agent_name, Equals(''))
476- self.expectThat(node.token, Is(None))
477+ self.expectThat(node.agent_name, Equals(""))
478 self.expectThat(node.netboot, Is(True))
479 self.expectThat(node.osystem, Equals(''))
480 self.expectThat(node.distro_series, Equals(''))
481diff --git a/src/maasserver/models/tests/test_userprofile.py b/src/maasserver/models/tests/test_userprofile.py
482index 9fa2ba1..1a9bdc3 100644
483--- a/src/maasserver/models/tests/test_userprofile.py
484+++ b/src/maasserver/models/tests/test_userprofile.py
485@@ -1,4 +1,4 @@
486-# Copyright 2012-2016 Canonical Ltd. This software is licensed under the
487+# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
488 # GNU Affero General Public License version 3 (see the file LICENSE).
489
490 """Tests for the UserProfile model."""
491@@ -85,6 +85,14 @@ class UserProfileTest(MAASServerTestCase):
492 self.assertFalse(Consumer.objects.filter(id__in=consumer_ids).exists())
493 self.assertFalse(Token.objects.filter(id__in=token_ids).exists())
494
495+ def test_delete_doesnt_delete_node(self):
496+ user = factory.make_User()
497+ profile = user.userprofile
498+ _, token = profile.create_authorisation_token()
499+ node = factory.make_Node(owner=user, token=token)
500+ profile.delete_authorisation_token(token.key)
501+ self.assertIsNotNone(reload_object(node))
502+
503 def test_delete_deletes_related_filestorage_objects(self):
504 # Deleting a profile deletes the related filestorage objects.
505 profile = factory.make_User().userprofile
506diff --git a/src/maasserver/models/userprofile.py b/src/maasserver/models/userprofile.py
507index fcffb8d..aaa302f 100644
508--- a/src/maasserver/models/userprofile.py
509+++ b/src/maasserver/models/userprofile.py
510@@ -1,4 +1,4 @@
511-# Copyright 2012-2016 Canonical Ltd. This software is licensed under the
512+# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
513 # GNU Affero General Public License version 3 (see the file LICENSE).
514
515 """UserProfile model."""
516@@ -19,6 +19,7 @@ from django.db.models import (
517 from django.shortcuts import get_object_or_404
518 from maasserver import DefaultMeta
519 from maasserver.exceptions import CannotDeleteUserException
520+from maasserver.models import Node
521 from maasserver.models.cleansave import CleanSave
522 from piston3.models import Token
523
524@@ -139,7 +140,11 @@ class UserProfile(CleanSave, Model):
525
526 """
527 token = get_object_or_404(
528- Token, user=self.user, token_type=Token.ACCESS, key=token_key)
529+ Token, user=self.user, token_type=Token.ACCESS, key=token_key,
530+ )
531+ # LP:1870171 - Make sure the key being deleted isn't assoicated with
532+ # any node. In MAAS 2.8+ Node.token has been removed.
533+ Node.objects.filter(token=token).update(token=None)
534 token.consumer.delete()
535 token.delete()
536
537diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py
538index 74c8df7..f034453 100644
539--- a/src/maasserver/node_action.py
540+++ b/src/maasserver/node_action.py
541@@ -311,7 +311,10 @@ class Acquire(NodeAction):
542 def execute(self):
543 """See `NodeAction.execute`."""
544 with locks.node_acquire:
545- self.node.acquire(self.user, token=None)
546+ try:
547+ self.node.acquire(self.user)
548+ except ValidationError as e:
549+ raise NodeActionError(e)
550
551
552 class Deploy(NodeAction):
553@@ -327,7 +330,10 @@ class Deploy(NodeAction):
554 """See `NodeAction.execute`."""
555 if self.node.owner is None:
556 with locks.node_acquire:
557- self.node.acquire(self.user, token=None)
558+ try:
559+ self.node.acquire(self.user)
560+ except ValidationError as e:
561+ raise NodeActionError(e)
562
563 if osystem and distro_series:
564 try:

Subscribers

People subscribed via source and target branches