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
diff --git a/src/maasserver/api/account.py b/src/maasserver/api/account.py
index adc8722..ba6ed54 100644
--- a/src/maasserver/api/account.py
+++ b/src/maasserver/api/account.py
@@ -1,4 +1,4 @@
1# Copyright 2014-2016 Canonical Ltd. This software is licensed under the1# Copyright 2014-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""API handler: `Account`."""4"""API handler: `Account`."""
diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
index e8b671b..9482631 100644
--- a/src/maasserver/api/machines.py
+++ b/src/maasserver/api/machines.py
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__all__ = [4__all__ = [
@@ -41,7 +41,6 @@ from maasserver.api.support import (
41)41)
42from maasserver.api.utils import (42from maasserver.api.utils import (
43 get_mandatory_param,43 get_mandatory_param,
44 get_oauth_token,
45 get_optional_list,44 get_optional_list,
46 get_optional_param,45 get_optional_param,
47)46)
@@ -525,7 +524,7 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin):
525 "Request from user %s to acquire machine: %s (%s)",524 "Request from user %s to acquire machine: %s (%s)",
526 request.user.username, machine.fqdn, machine.system_id)525 request.user.username, machine.fqdn, machine.system_id)
527 machine.acquire(526 machine.acquire(
528 request.user, get_oauth_token(request),527 request.user,
529 agent_name=agent_name, comment=comment,528 agent_name=agent_name, comment=comment,
530 bridge_all=bridge_all, bridge_stp=bridge_stp,529 bridge_all=bridge_all, bridge_stp=bridge_stp,
531 bridge_fd=bridge_fd)530 bridge_fd=bridge_fd)
@@ -1314,11 +1313,24 @@ class MachinesHandler(NodesHandler, PowersMixin):
13141313
1315 @operation(idempotent=True)1314 @operation(idempotent=True)
1316 def list_allocated(self, request):1315 def list_allocated(self, request):
1317 """Fetch Machines that were allocated to the User/oauth token."""1316 """@description-title List allocated
1318 token = get_oauth_token(request)1317 @description List machines that were allocated to the User.
1319 match_ids = get_optional_list(request.GET, 'id')1318
1320 machines = Machine.objects.get_allocated_visible_machines(1319 @success (http-status-code) "200" 200
1321 token, match_ids)1320 @success (json) "success-json" A JSON object containing a list of
1321 allocated machines.
1322 @success-example "success-json" [exkey=machines-placeholder]
1323 placeholder text
1324 """
1325 # limit to machines that the user can view
1326 machines = Machine.objects.get_nodes(
1327 request.user, NODE_PERMISSION.VIEW)
1328 machines = machines.filter(
1329 owner=request.user, status=NODE_STATUS.ALLOCATED
1330 )
1331 system_ids = get_optional_list(request.GET, "id")
1332 if system_ids:
1333 machines = machines.filter(system_id__in=system_ids)
1322 return machines.order_by('id')1334 return machines.order_by('id')
13231335
1324 @operation(idempotent=False)1336 @operation(idempotent=False)
@@ -1606,7 +1618,7 @@ class MachinesHandler(NodesHandler, PowersMixin):
1606 raise NodesNotAvailable(message)1618 raise NodesNotAvailable(message)
1607 if not dry_run:1619 if not dry_run:
1608 machine.acquire(1620 machine.acquire(
1609 request.user, get_oauth_token(request),1621 request.user,
1610 agent_name=agent_name, comment=comment,1622 agent_name=agent_name, comment=comment,
1611 bridge_all=bridge_all, bridge_stp=bridge_stp,1623 bridge_all=bridge_all, bridge_stp=bridge_stp,
1612 bridge_fd=bridge_fd)1624 bridge_fd=bridge_fd)
diff --git a/src/maasserver/api/tests/test_api.py b/src/maasserver/api/tests/test_api.py
index 08892d9..4e8b9e0 100644
--- a/src/maasserver/api/tests/test_api.py
+++ b/src/maasserver/api/tests/test_api.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2016 Canonical Ltd. This software is licensed under the1# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test maasserver API."""4"""Test maasserver API."""
@@ -45,7 +45,10 @@ from maasserver.testing.testclient import MAASSensibleOAuthClient
45from maasserver.utils.converters import json_load_bytes45from maasserver.utils.converters import json_load_bytes
46from maasserver.utils.django_urls import reverse46from maasserver.utils.django_urls import reverse
47from maasserver.utils.keys import ImportSSHKeysError47from maasserver.utils.keys import ImportSSHKeysError
48from maasserver.utils.orm import get_one48from maasserver.utils.orm import (
49 get_one,
50 reload_object,
51)
49from maastesting.matchers import MockCalledOnceWith52from maastesting.matchers import MockCalledOnceWith
50from maastesting.testcase import MAASTestCase53from maastesting.testcase import MAASTestCase
51from piston3.doc import generate_doc54from piston3.doc import generate_doc
@@ -288,6 +291,16 @@ class AccountAPITest(APITestCase.ForUser):
288291
289 self.assertEqual(http.client.BAD_REQUEST, response.status_code)292 self.assertEqual(http.client.BAD_REQUEST, response.status_code)
290293
294 def test_delete_authorisation_token_doesnt_delete_node(self):
295 token = self.user.tokens.first()
296 node = factory.make_Node(owner=self.user, token=token)
297 response = self.client.post(
298 reverse("account_handler"),
299 {"op": "delete_authorisation_token", "token_key": token.key},
300 )
301 self.assertEqual(http.client.NO_CONTENT, response.status_code)
302 self.assertIsNotNone(reload_object(node))
303
291 def test_update_authorisation_token(self):304 def test_update_authorisation_token(self):
292 token_name_orig = 'Test_Token'305 token_name_orig = 'Test_Token'
293 token_name_updated = 'Test_Token update'306 token_name_updated = 'Test_Token update'
diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py
index 5b55eaf..e0b45ab 100644
--- a/src/maasserver/api/tests/test_machine.py
+++ b/src/maasserver/api/tests/test_machine.py
@@ -716,10 +716,16 @@ class TestMachineAPI(APITestCase.ForUser):
716 }716 }
717 self.client.post(self.get_machine_uri(machine), request)717 self.client.post(self.get_machine_uri(machine), request)
718 self.assertThat(718 self.assertThat(
719 machine_method, MockCalledOnceWith(719 machine_method,
720 ANY, ANY, agent_name=ANY,720 MockCalledOnceWith(
721 bridge_all=False, bridge_fd=False,721 ANY,
722 bridge_stp=False, comment=request['comment']))722 agent_name=ANY,
723 bridge_all=False,
724 bridge_fd=0,
725 bridge_stp=False,
726 comment=request["comment"],
727 ),
728 )
723729
724 def test_POST_deploy_passes_bridge_settings(self):730 def test_POST_deploy_passes_bridge_settings(self):
725 self.patch(node_module.Node, "_start")731 self.patch(node_module.Node, "_start")
@@ -740,10 +746,16 @@ class TestMachineAPI(APITestCase.ForUser):
740 }746 }
741 self.client.post(self.get_machine_uri(machine), request)747 self.client.post(self.get_machine_uri(machine), request)
742 self.assertThat(748 self.assertThat(
743 machine_method, MockCalledOnceWith(749 machine_method,
744 ANY, ANY, agent_name=ANY,750 MockCalledOnceWith(
745 bridge_all=True, bridge_fd=7,751 ANY,
746 bridge_stp=True, comment=None))752 agent_name=ANY,
753 bridge_all=True,
754 bridge_fd=7,
755 bridge_stp=True,
756 comment=None,
757 ),
758 )
747759
748 def test_POST_release_releases_owned_machine(self):760 def test_POST_release_releases_owned_machine(self):
749 self.patch(node_module.Machine, '_stop')761 self.patch(node_module.Machine, '_stop')
@@ -898,10 +910,16 @@ class TestMachineAPI(APITestCase.ForUser):
898 reverse('machines_handler'),910 reverse('machines_handler'),
899 {'op': 'allocate', 'comment': comment})911 {'op': 'allocate', 'comment': comment})
900 self.assertThat(912 self.assertThat(
901 machine_method, MockCalledOnceWith(913 machine_method,
902 ANY, ANY, agent_name=ANY,914 MockCalledOnceWith(
903 bridge_all=False, bridge_fd=False,915 ANY,
904 bridge_stp=False, comment=comment))916 agent_name=ANY,
917 bridge_all=False,
918 bridge_fd=False,
919 bridge_stp=False,
920 comment=comment,
921 ),
922 )
905923
906 def test_POST_allocate_handles_missing_comment(self):924 def test_POST_allocate_handles_missing_comment(self):
907 factory.make_Node(925 factory.make_Node(
@@ -911,10 +929,16 @@ class TestMachineAPI(APITestCase.ForUser):
911 self.client.post(929 self.client.post(
912 reverse('machines_handler'), {'op': 'allocate'})930 reverse('machines_handler'), {'op': 'allocate'})
913 self.assertThat(931 self.assertThat(
914 machine_method, MockCalledOnceWith(932 machine_method,
915 ANY, ANY, agent_name=ANY,933 MockCalledOnceWith(
916 bridge_all=False, bridge_fd=False,934 ANY,
917 bridge_stp=False, comment=None))935 agent_name=ANY,
936 bridge_all=False,
937 bridge_fd=0,
938 bridge_stp=False,
939 comment=None,
940 ),
941 )
918942
919 def test_POST_release_frees_hwe_kernel(self):943 def test_POST_release_frees_hwe_kernel(self):
920 self.patch(node_module.Machine, '_stop')944 self.patch(node_module.Machine, '_stop')
diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py
index b58f737..474ecaf 100644
--- a/src/maasserver/api/tests/test_machines.py
+++ b/src/maasserver/api/tests/test_machines.py
@@ -1,4 +1,4 @@
1# Copyright 2015-2016 Canonical Ltd. This software is licensed under the1# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for the machines API."""4"""Tests for the machines API."""
@@ -33,10 +33,6 @@ from maasserver.models import (
33 node as node_module,33 node as node_module,
34)34)
35from maasserver.models.node import RELEASABLE_STATUSES35from maasserver.models.node import RELEASABLE_STATUSES
36from maasserver.models.user import (
37 create_auth_token,
38 get_auth_tokens,
39)
40from maasserver.node_constraint_filter_forms import AcquireNodeForm36from maasserver.node_constraint_filter_forms import AcquireNodeForm
41from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture37from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
42from maasserver.testing.api import (38from maasserver.testing.api import (
@@ -546,29 +542,11 @@ class TestMachinesAPI(APITestCase.ForUser):
546 [machine.system_id for machine in machines],542 [machine.system_id for machine in machines],
547 extract_system_ids(parsed_result))543 extract_system_ids(parsed_result))
548544
549 def test_GET_list_allocated_returns_only_allocated_with_user_token(self):545 def test_GET_list_allocated_returns_only_allocated_with_user(self):
550 # If the user's allocated machines have different session tokens,546 machine = factory.make_Node(
551 # list_allocated should only return the machines that have the547 status=NODE_STATUS.ALLOCATED, owner=self.user
552 # current request's token on them.548 )
553 machine_1 = factory.make_Node(549 factory.make_Node(status=NODE_STATUS.ALLOCATED)
554 status=NODE_STATUS.ALLOCATED, owner=self.user,
555 token=get_auth_tokens(self.user)[0])
556 second_token = create_auth_token(self.user)
557 factory.make_Node(
558 owner=self.user, status=NODE_STATUS.ALLOCATED,
559 token=second_token)
560
561 user_2 = factory.make_User()
562 create_auth_token(user_2)
563 factory.make_Node(
564 owner=self.user, status=NODE_STATUS.ALLOCATED,
565 token=second_token)
566
567 # At this point we have two machines owned by the same user but
568 # allocated with different tokens, and a third machine allocated to
569 # someone else entirely. We expect list_allocated to
570 # return the machine with the same token as the one used in
571 # self.client, which is the one we set on machine_1 above.
572550
573 response = self.client.get(reverse('machines_handler'), {551 response = self.client.get(reverse('machines_handler'), {
574 'op': 'list_allocated'})552 'op': 'list_allocated'})
@@ -576,17 +554,17 @@ class TestMachinesAPI(APITestCase.ForUser):
576 parsed_result = json.loads(554 parsed_result = json.loads(
577 response.content.decode(settings.DEFAULT_CHARSET))555 response.content.decode(settings.DEFAULT_CHARSET))
578 self.assertItemsEqual(556 self.assertItemsEqual(
579 [machine_1.system_id], extract_system_ids(parsed_result))557 [machine.system_id], extract_system_ids(parsed_result)
558 )
580559
581 def test_GET_list_allocated_filters_by_id(self):560 def test_GET_list_allocated_filters_by_id(self):
582 # list_allocated takes an optional list of 'id' parameters to
583 # filter returned results.
584 current_token = get_auth_tokens(self.user)[0]
585 machines = []561 machines = []
586 for _ in range(3):562 for _ in range(3):
587 machines.append(factory.make_Node(563 machines.append(
588 status=NODE_STATUS.ALLOCATED,564 factory.make_Node(
589 owner=self.user, token=current_token))565 status=NODE_STATUS.ALLOCATED, owner=self.user
566 )
567 )
590568
591 required_machine_ids = [machines[0].system_id, machines[1].system_id]569 required_machine_ids = [machines[0].system_id, machines[1].system_id]
592 response = self.client.get(reverse('machines_handler'), {570 response = self.client.get(reverse('machines_handler'), {
@@ -1450,19 +1428,6 @@ class TestMachinesAPI(APITestCase.ForUser):
1450 response.content.decode(settings.DEFAULT_CHARSET))['system_id']1428 response.content.decode(settings.DEFAULT_CHARSET))['system_id']
1451 self.assertEqual(eligible_machine.system_id, system_id)1429 self.assertEqual(eligible_machine.system_id, system_id)
14521430
1453 def test_POST_allocate_sets_a_token(self):
1454 # "acquire" should set the Token being used in the request on
1455 # the Machine that is allocated.
1456 available_status = NODE_STATUS.READY
1457 machine = factory.make_Node(
1458 status=available_status, owner=None, with_boot_disk=True)
1459 response = self.client.post(
1460 reverse('machines_handler'), {'op': 'allocate'})
1461 self.assertThat(response, HasStatusCode(http.client.OK))
1462 machine = Machine.objects.get(system_id=machine.system_id)
1463 oauth_key = self.client.token.key
1464 self.assertEqual(oauth_key, machine.token.key)
1465
1466 def test_POST_accept_gets_machine_out_of_declared_state(self):1431 def test_POST_accept_gets_machine_out_of_declared_state(self):
1467 # This will change when we add provisioning. Until then,1432 # This will change when we add provisioning. Until then,
1468 # acceptance gets a machine straight to Ready state.1433 # acceptance gets a machine straight to Ready state.
diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
index 1eb14d9..cb49543 100644
--- a/src/maasserver/models/node.py
+++ b/src/maasserver/models/node.py
@@ -2551,11 +2551,16 @@ class Node(CleanSave, TimestampedModel):
2551 "s" if len(missing_packages) > 1 else ""))2551 "s" if len(missing_packages) > 1 else ""))
25522552
2553 def acquire(2553 def acquire(
2554 self, user, token=None, agent_name='', comment=None,2554 self,
2555 bridge_all=False, bridge_stp=None, bridge_fd=None):2555 user,
2556 """Mark commissioned node as acquired by the given user and token."""2556 agent_name="",
2557 comment=None,
2558 bridge_all=False,
2559 bridge_stp=None,
2560 bridge_fd=None,
2561 ):
2562 """Mark commissioned node as acquired by the given user."""
2557 assert self.owner is None or self.owner == user2563 assert self.owner is None or self.owner == user
2558 assert token is None or token.user == user
25592564
2560 self._create_acquired_filesystems()2565 self._create_acquired_filesystems()
2561 self._register_request_event(2566 self._register_request_event(
@@ -2564,7 +2569,6 @@ class Node(CleanSave, TimestampedModel):
2564 self.status = NODE_STATUS.ALLOCATED2569 self.status = NODE_STATUS.ALLOCATED
2565 self.owner = user2570 self.owner = user
2566 self.agent_name = agent_name2571 self.agent_name = agent_name
2567 self.token = token
2568 if bridge_all:2572 if bridge_all:
2569 self._create_acquired_bridges(2573 self._create_acquired_bridges(
2570 bridge_stp=bridge_stp, bridge_fd=bridge_fd)2574 bridge_stp=bridge_stp, bridge_fd=bridge_fd)
@@ -2823,8 +2827,7 @@ class Node(CleanSave, TimestampedModel):
2823 finalize_release = True2827 finalize_release = True
28242828
2825 self.status = NODE_STATUS.RELEASING2829 self.status = NODE_STATUS.RELEASING
2826 self.token = None2830 self.agent_name = ""
2827 self.agent_name = ''
2828 self.set_netboot()2831 self.set_netboot()
2829 self.osystem = ''2832 self.osystem = ''
2830 self.distro_series = ''2833 self.distro_series = ''
diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
index a47ee5d..193296e 100644
--- a/src/maasserver/models/tests/test_node.py
+++ b/src/maasserver/models/tests/test_node.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2017 Canonical Ltd. This software is licensed under the1# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test maasserver models."""4"""Test maasserver models."""
@@ -108,7 +108,6 @@ from maasserver.models.node import (
108)108)
109from maasserver.models.signals import power as node_query109from maasserver.models.signals import power as node_query
110from maasserver.models.timestampedmodel import now110from maasserver.models.timestampedmodel import now
111from maasserver.models.user import create_auth_token
112from maasserver.node_status import (111from maasserver.node_status import (
113 COMMISSIONING_LIKE_STATUSES,112 COMMISSIONING_LIKE_STATUSES,
114 NODE_FAILURE_MONITORED_STATUS_TRANSITIONS,113 NODE_FAILURE_MONITORED_STATUS_TRANSITIONS,
@@ -1113,12 +1112,6 @@ class TestNode(MAASServerTestCase):
1113 node = factory.make_Node(bios_boot_method=factory.make_name("boot"))1112 node = factory.make_Node(bios_boot_method=factory.make_name("boot"))
1114 self.assertEqual("pxe", node.get_bios_boot_method())1113 self.assertEqual("pxe", node.get_bios_boot_method())
11151114
1116 def test_add_node_with_token(self):
1117 user = factory.make_User()
1118 token = create_auth_token(user)
1119 node = factory.make_Node(token=token)
1120 self.assertEqual(token, node.token)
1121
1122 def test_add_physical_interface(self):1115 def test_add_physical_interface(self):
1123 mac = factory.make_mac_address()1116 mac = factory.make_mac_address()
1124 node = factory.make_Node()1117 node = factory.make_Node()
@@ -1560,9 +1553,8 @@ class TestNode(MAASServerTestCase):
1560 def test_acquire(self):1553 def test_acquire(self):
1561 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)1554 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
1562 user = factory.make_User()1555 user = factory.make_User()
1563 token = create_auth_token(user)1556 agent_name = factory.make_name("agent-name")
1564 agent_name = factory.make_name('agent-name')1557 node.acquire(user, agent_name)
1565 node.acquire(user, token, agent_name)
1566 self.assertEqual(1558 self.assertEqual(
1567 (user, NODE_STATUS.ALLOCATED, agent_name),1559 (user, NODE_STATUS.ALLOCATED, agent_name),
1568 (node.owner, node.status, node.agent_name))1560 (node.owner, node.status, node.agent_name))
@@ -1570,36 +1562,44 @@ class TestNode(MAASServerTestCase):
1570 def test_acquire_calls__create_acquired_filesystems(self):1562 def test_acquire_calls__create_acquired_filesystems(self):
1571 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)1563 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
1572 user = factory.make_User()1564 user = factory.make_User()
1573 token = create_auth_token(user)1565 agent_name = factory.make_name("agent-name")
1574 agent_name = factory.make_name('agent-name')
1575 mock_create_acquired_filesystems = self.patch_autospec(1566 mock_create_acquired_filesystems = self.patch_autospec(
1576 node, "_create_acquired_filesystems")1567 node, "_create_acquired_filesystems"
1577 node.acquire(user, token, agent_name)1568 )
1569 node.acquire(user, agent_name)
1578 self.assertThat(mock_create_acquired_filesystems, MockCalledOnceWith())1570 self.assertThat(mock_create_acquired_filesystems, MockCalledOnceWith())
15791571
1580 def test_acquire_logs_user_request(self):1572 def test_acquire_logs_user_request(self):
1581 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)1573 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
1582 user = factory.make_User()1574 user = factory.make_User()
1583 token = create_auth_token(user)1575 agent_name = factory.make_name("agent-name")
1584 agent_name = factory.make_name('agent-name')1576 register_event = self.patch(node, "_register_request_event")
1585 register_event = self.patch(node, '_register_request_event')1577 node.acquire(user, agent_name)
1586 node.acquire(user, token, agent_name)1578 self.assertThat(
1587 self.assertThat(register_event, MockCalledOnceWith(1579 register_event,
1588 user, EVENT_TYPES.REQUEST_NODE_ACQUIRE, action='acquire',1580 MockCalledOnceWith(
1589 comment=None))1581 user,
1582 EVENT_TYPES.REQUEST_NODE_ACQUIRE,
1583 action="acquire",
1584 comment=None,
1585 ),
1586 )
15901587
1591 def test_acquire_calls__create_acquired_bridges(self):1588 def test_acquire_calls__create_acquired_bridges(self):
1592 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)1589 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
1593 user = factory.make_User()1590 user = factory.make_User()
1594 token = create_auth_token(user)1591 agent_name = factory.make_name("agent-name")
1595 agent_name = factory.make_name('agent-name')
1596 mock_create_acquired_bridges = self.patch_autospec(1592 mock_create_acquired_bridges = self.patch_autospec(
1597 node, "_create_acquired_bridges")1593 node, "_create_acquired_bridges")
1598 bridge_stp = factory.pick_bool()1594 bridge_stp = factory.pick_bool()
1599 bridge_fd = random.randint(0, 500)1595 bridge_fd = random.randint(0, 500)
1600 node.acquire(1596 node.acquire(
1601 user, token, agent_name,1597 user,
1602 bridge_all=True, bridge_stp=bridge_stp, bridge_fd=bridge_fd)1598 agent_name,
1599 bridge_all=True,
1600 bridge_stp=bridge_stp,
1601 bridge_fd=bridge_fd,
1602 )
1603 self.assertThat(1603 self.assertThat(
1604 mock_create_acquired_bridges,1604 mock_create_acquired_bridges,
1605 MockCalledOnceWith(bridge_stp=bridge_stp, bridge_fd=bridge_fd))1605 MockCalledOnceWith(bridge_stp=bridge_stp, bridge_fd=bridge_fd))
@@ -2102,8 +2102,7 @@ class TestNode(MAASServerTestCase):
2102 MockCalledOnceWith(node.system_id, node.get_releasing_time()))2102 MockCalledOnceWith(node.system_id, node.get_releasing_time()))
2103 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))2103 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
2104 self.expectThat(node.owner, Equals(owner))2104 self.expectThat(node.owner, Equals(owner))
2105 self.expectThat(node.agent_name, Equals(''))2105 self.expectThat(node.agent_name, Equals(""))
2106 self.expectThat(node.token, Is(None))
2107 self.expectThat(node.netboot, Is(True))2106 self.expectThat(node.netboot, Is(True))
2108 self.expectThat(node.osystem, Equals(''))2107 self.expectThat(node.osystem, Equals(''))
2109 self.expectThat(node.distro_series, Equals(''))2108 self.expectThat(node.distro_series, Equals(''))
@@ -2141,8 +2140,7 @@ class TestNode(MAASServerTestCase):
2141 self.expectThat(Node._set_status_expires, MockNotCalled())2140 self.expectThat(Node._set_status_expires, MockNotCalled())
2142 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))2141 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
2143 self.expectThat(node.owner, Equals(owner))2142 self.expectThat(node.owner, Equals(owner))
2144 self.expectThat(node.agent_name, Equals(''))2143 self.expectThat(node.agent_name, Equals(""))
2145 self.expectThat(node.token, Is(None))
2146 self.expectThat(node.netboot, Is(True))2144 self.expectThat(node.netboot, Is(True))
2147 self.expectThat(node.osystem, Equals(''))2145 self.expectThat(node.osystem, Equals(''))
2148 self.expectThat(node.distro_series, Equals(''))2146 self.expectThat(node.distro_series, Equals(''))
@@ -2169,8 +2167,7 @@ class TestNode(MAASServerTestCase):
2169 self.expectThat(Node._set_status_expires, MockNotCalled())2167 self.expectThat(Node._set_status_expires, MockNotCalled())
2170 self.expectThat(node.status, Equals(NODE_STATUS.READY))2168 self.expectThat(node.status, Equals(NODE_STATUS.READY))
2171 self.expectThat(node.owner, Equals(None))2169 self.expectThat(node.owner, Equals(None))
2172 self.expectThat(node.agent_name, Equals(''))2170 self.expectThat(node.agent_name, Equals(""))
2173 self.expectThat(node.token, Is(None))
2174 self.expectThat(node.netboot, Is(True))2171 self.expectThat(node.netboot, Is(True))
2175 self.expectThat(node.osystem, Equals(''))2172 self.expectThat(node.osystem, Equals(''))
2176 self.expectThat(node.distro_series, Equals(''))2173 self.expectThat(node.distro_series, Equals(''))
diff --git a/src/maasserver/models/tests/test_userprofile.py b/src/maasserver/models/tests/test_userprofile.py
index 9fa2ba1..1a9bdc3 100644
--- a/src/maasserver/models/tests/test_userprofile.py
+++ b/src/maasserver/models/tests/test_userprofile.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2016 Canonical Ltd. This software is licensed under the1# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for the UserProfile model."""4"""Tests for the UserProfile model."""
@@ -85,6 +85,14 @@ class UserProfileTest(MAASServerTestCase):
85 self.assertFalse(Consumer.objects.filter(id__in=consumer_ids).exists())85 self.assertFalse(Consumer.objects.filter(id__in=consumer_ids).exists())
86 self.assertFalse(Token.objects.filter(id__in=token_ids).exists())86 self.assertFalse(Token.objects.filter(id__in=token_ids).exists())
8787
88 def test_delete_doesnt_delete_node(self):
89 user = factory.make_User()
90 profile = user.userprofile
91 _, token = profile.create_authorisation_token()
92 node = factory.make_Node(owner=user, token=token)
93 profile.delete_authorisation_token(token.key)
94 self.assertIsNotNone(reload_object(node))
95
88 def test_delete_deletes_related_filestorage_objects(self):96 def test_delete_deletes_related_filestorage_objects(self):
89 # Deleting a profile deletes the related filestorage objects.97 # Deleting a profile deletes the related filestorage objects.
90 profile = factory.make_User().userprofile98 profile = factory.make_User().userprofile
diff --git a/src/maasserver/models/userprofile.py b/src/maasserver/models/userprofile.py
index fcffb8d..aaa302f 100644
--- a/src/maasserver/models/userprofile.py
+++ b/src/maasserver/models/userprofile.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2016 Canonical Ltd. This software is licensed under the1# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""UserProfile model."""4"""UserProfile model."""
@@ -19,6 +19,7 @@ from django.db.models import (
19from django.shortcuts import get_object_or_40419from django.shortcuts import get_object_or_404
20from maasserver import DefaultMeta20from maasserver import DefaultMeta
21from maasserver.exceptions import CannotDeleteUserException21from maasserver.exceptions import CannotDeleteUserException
22from maasserver.models import Node
22from maasserver.models.cleansave import CleanSave23from maasserver.models.cleansave import CleanSave
23from piston3.models import Token24from piston3.models import Token
2425
@@ -139,7 +140,11 @@ class UserProfile(CleanSave, Model):
139140
140 """141 """
141 token = get_object_or_404(142 token = get_object_or_404(
142 Token, user=self.user, token_type=Token.ACCESS, key=token_key)143 Token, user=self.user, token_type=Token.ACCESS, key=token_key,
144 )
145 # LP:1870171 - Make sure the key being deleted isn't assoicated with
146 # any node. In MAAS 2.8+ Node.token has been removed.
147 Node.objects.filter(token=token).update(token=None)
143 token.consumer.delete()148 token.consumer.delete()
144 token.delete()149 token.delete()
145150
diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py
index 74c8df7..f034453 100644
--- a/src/maasserver/node_action.py
+++ b/src/maasserver/node_action.py
@@ -311,7 +311,10 @@ class Acquire(NodeAction):
311 def execute(self):311 def execute(self):
312 """See `NodeAction.execute`."""312 """See `NodeAction.execute`."""
313 with locks.node_acquire:313 with locks.node_acquire:
314 self.node.acquire(self.user, token=None)314 try:
315 self.node.acquire(self.user)
316 except ValidationError as e:
317 raise NodeActionError(e)
315318
316319
317class Deploy(NodeAction):320class Deploy(NodeAction):
@@ -327,7 +330,10 @@ class Deploy(NodeAction):
327 """See `NodeAction.execute`."""330 """See `NodeAction.execute`."""
328 if self.node.owner is None:331 if self.node.owner is None:
329 with locks.node_acquire:332 with locks.node_acquire:
330 self.node.acquire(self.user, token=None)333 try:
334 self.node.acquire(self.user)
335 except ValidationError as e:
336 raise NodeActionError(e)
331337
332 if osystem and distro_series:338 if osystem and distro_series:
333 try:339 try:

Subscribers

People subscribed via source and target branches