Merge ~andreserl/maas:lp1785721 into maas:master

Proposed by Andres Rodriguez
Status: Merged
Approved by: Andres Rodriguez
Approved revision: 15374587d3a4efa98297fe5ce15daa696f22efe3
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~andreserl/maas:lp1785721
Merge into: maas:master
Diff against target: 281 lines (+152/-10)
5 files modified
src/maasserver/api/resourcepools.py (+29/-3)
src/maasserver/api/tests/test_resourcepool.py (+23/-4)
src/maasserver/models/__init__.py (+1/-0)
src/maasserver/models/resourcepool.py (+43/-2)
src/maasserver/models/tests/test_resourcepool.py (+56/-1)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Blake Rouse (community) Approve
MAAS Lander Approve
Review via email: mp+352526@code.launchpad.net

Commit message

LP: #1785721 - Allow read, update and delete a resource pool with its name

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Nice job implementing specifiers for the ResourcePool, and thanks for the docstring cleanup!

Overall, the code looks good to me. But you'll need to add tests in src/maasserver/api/tests/test_resourcepool.py to cover the additional functionality added in this branch.

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

UNIT TESTS
-b lp1785721 lp:~andreserl/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/3536/console
COMMIT: fe8373ae96ea19926f6660743adc0cf7224b0b14

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

UNIT TESTS
-b lp1785721 lp:~andreserl/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: e919f44bfcea73ad8df9a28d44428ab9c8e08b33

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) :
review: Needs Fixing
Revision history for this message
Andres Rodriguez (andreserl) wrote :

FWIW, CI has passed: http://162.213.35.104:8080/job/maas-git-manual/341/

I'll add the test.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good. Thanks for adding the test.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Thanks for adding the tests!

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/resourcepools.py b/src/maasserver/api/resourcepools.py
2index cb232d9..495b640 100644
3--- a/src/maasserver/api/resourcepools.py
4+++ b/src/maasserver/api/resourcepools.py
5@@ -13,8 +13,11 @@ from maasserver.api.support import (
6 ModelCollectionOperationsHandler,
7 ModelOperationsHandler,
8 )
9+from maasserver.enum import NODE_PERMISSION
10+from maasserver.exceptions import MAASAPIValidationError
11 from maasserver.forms import ResourcePoolForm
12 from maasserver.models import ResourcePool
13+from piston3.utils import rc
14
15
16 DISPLAYED_RESOURCEPOOL_FIELDS = (
17@@ -45,14 +48,34 @@ class ResourcePoolHandler(ModelOperationsHandler):
18
19 Returns 404 if the resource pool is not found.
20 """
21- return super().read(request, id=id)
22+ return ResourcePool.objects.get_resource_pool_or_404(
23+ id, request.user, NODE_PERMISSION.VIEW)
24
25 def update(self, request, id):
26 """PUT request. Update resource pool.
27
28+ Please see the documentation for the 'create' operation for detailed
29+ descriptions of each parameter.
30+
31+ Optional parameters
32+ -------------------
33+
34+ name
35+ Name of the resource pool.
36+
37+ description
38+ Description of the resource pool.
39+
40 Returns 404 if the resource pool is not found.
41 """
42- return super().update(request, id=id)
43+
44+ pool = ResourcePool.objects.get_resource_pool_or_404(
45+ id, request.user, NODE_PERMISSION.ADMIN)
46+ form = ResourcePoolForm(instance=pool, data=request.data)
47+ if form.is_valid():
48+ return form.save()
49+ else:
50+ raise MAASAPIValidationError(form.errors)
51
52 def delete(self, request, id):
53 """DELETE request. Delete resource pool.
54@@ -60,7 +83,10 @@ class ResourcePoolHandler(ModelOperationsHandler):
55 Returns 404 if the resource pool is not found.
56 Returns 204 if the resource pool is successfully deleted.
57 """
58- return super().delete(request, id=id)
59+ pool = ResourcePool.objects.get_resource_pool_or_404(
60+ id, request.user, NODE_PERMISSION.ADMIN)
61+ pool.delete()
62+ return rc.DELETED
63
64
65 class ResourcePoolsHandler(ModelCollectionOperationsHandler):
66diff --git a/src/maasserver/api/tests/test_resourcepool.py b/src/maasserver/api/tests/test_resourcepool.py
67index 7e93e43..4e3d2df 100644
68--- a/src/maasserver/api/tests/test_resourcepool.py
69+++ b/src/maasserver/api/tests/test_resourcepool.py
70@@ -57,6 +57,19 @@ class TestResourcePoolAPI(APITestCase.ForUser):
71 self.assertEqual(pool.name, new_name)
72 self.assertEqual(pool.description, new_description)
73
74+ def test_PUT_updates_pool_by_name(self):
75+ self.become_admin()
76+ pool = factory.make_ResourcePool()
77+ new_name = factory.make_name('name')
78+ new_description = factory.make_name('description')
79+ response = self.client.put(
80+ reverse('resourcepool_handler', args=[pool.name]),
81+ {'name': new_name, 'description': new_description})
82+ self.assertEqual(response.status_code, http.client.OK)
83+ pool = reload_object(pool)
84+ self.assertEqual(pool.name, new_name)
85+ self.assertEqual(pool.description, new_description)
86+
87 def test_PUT_missing(self):
88 self.become_admin()
89 description = factory.make_string()
90@@ -72,7 +85,7 @@ class TestResourcePoolAPI(APITestCase.ForUser):
91 {'description': factory.make_string()})
92 self.assertEqual(response.status_code, http.client.FORBIDDEN)
93
94- def test_DELETE_removes_pool(self):
95+ def test_DELETE_removes_pool_by_id(self):
96 self.become_admin()
97 pool = factory.make_ResourcePool()
98 response = self.client.delete(
99@@ -80,6 +93,14 @@ class TestResourcePoolAPI(APITestCase.ForUser):
100 self.assertEqual(response.status_code, http.client.NO_CONTENT)
101 self.assertIsNone(reload_object(pool))
102
103+ def test_DELETE_removes_pool_by_name(self):
104+ self.become_admin()
105+ pool = factory.make_ResourcePool()
106+ response = self.client.delete(
107+ reverse('resourcepool_handler', args=[pool.name]), {})
108+ self.assertEqual(response.status_code, http.client.NO_CONTENT)
109+ self.assertIsNone(reload_object(pool))
110+
111 def test_DELETE_default_pool_denied(self):
112 self.become_admin()
113 response = self.client.delete(
114@@ -100,6 +121,4 @@ class TestResourcePoolAPI(APITestCase.ForUser):
115 response = self.client.delete(
116 reverse('resourcepool_handler', args=[pool.id]))
117 self.assertEqual(response.status_code, http.client.NO_CONTENT)
118- response = self.client.delete(
119- reverse('resourcepool_handler', args=[pool.id]))
120- self.assertEqual(response.status_code, http.client.NO_CONTENT)
121+ self.assertIsNone(reload_object(pool))
122diff --git a/src/maasserver/models/__init__.py b/src/maasserver/models/__init__.py
123index d58ef1e..9177321 100644
124--- a/src/maasserver/models/__init__.py
125+++ b/src/maasserver/models/__init__.py
126@@ -280,6 +280,7 @@ UNRESTRICTED_READ_MODELS = (
127 Domain,
128 Fabric,
129 FanNetwork,
130+ ResourcePool,
131 Space,
132 Subnet,
133 StaticRoute,
134diff --git a/src/maasserver/models/resourcepool.py b/src/maasserver/models/resourcepool.py
135index 377db23..d1651c3 100644
136--- a/src/maasserver/models/resourcepool.py
137+++ b/src/maasserver/models/resourcepool.py
138@@ -11,7 +11,10 @@ __all__ = [
139
140 from datetime import datetime
141
142-from django.core.exceptions import ValidationError
143+from django.core.exceptions import (
144+ PermissionDenied,
145+ ValidationError,
146+)
147 from django.db.models import (
148 CharField,
149 Manager,
150@@ -21,13 +24,27 @@ from maasserver import DefaultMeta
151 from maasserver.fields import MODEL_NAME_VALIDATOR
152 from maasserver.models.cleansave import CleanSave
153 from maasserver.models.timestampedmodel import TimestampedModel
154+from maasserver.utils.orm import MAASQueriesMixin
155
156
157 DEFAULT_RESOURCEPOOL_NAME = 'default'
158 DEFAULT_RESOURCEPOOL_DESCRIPTION = 'Default pool'
159
160
161-class ResourcePoolManager(Manager):
162+class ResourcePoolQueriesMixin(MAASQueriesMixin):
163+
164+ def get_specifiers_q(self, specifiers, separator=':', **kwargs):
165+ specifier_types = {
166+ None: self._add_default_query,
167+ 'name': "__name",
168+ 'id': "__id",
169+ }
170+ return super(ResourcePoolQueriesMixin, self).get_specifiers_q(
171+ specifiers, specifier_types=specifier_types, separator=separator,
172+ **kwargs)
173+
174+
175+class ResourcePoolManager(Manager, ResourcePoolQueriesMixin):
176 """Manager for the :class:`ResourcePool` model."""
177
178 def get_default_resource_pool(self):
179@@ -43,6 +60,30 @@ class ResourcePoolManager(Manager):
180 'updated': now})
181 return pool
182
183+ def get_resource_pool_or_404(self, specifiers, user, perm):
184+ """Fetch a `ResourcePool` by its id. Raise exceptions if no
185+ `ResourcePool` with this id exists or if the provided user has not
186+ the required permission to access this `ResourcePool`.
187+
188+ :param specifiers: A specifier to uniquely locate the ResourcePool.
189+ :type specifiers: unicode
190+ :param user: The user that should be used in the permission check.
191+ :type user: django.contrib.auth.models.User
192+ :param perm: The permission to assert that the user has on the node.
193+ :type perm: unicode
194+ :raises: django.http.Http404_,
195+ :class:`maasserver.exceptions.PermissionDenied`.
196+
197+ .. _django.http.Http404: https://
198+ docs.djangoproject.com/en/dev/topics/http/views/
199+ #the-http404-exception
200+ """
201+ pool = self.get_object_by_specifiers_or_raise(specifiers)
202+ if user.has_perm(perm, pool):
203+ return pool
204+ else:
205+ raise PermissionDenied()
206+
207
208 class ResourcePool(CleanSave, TimestampedModel):
209 """A resource pool."""
210diff --git a/src/maasserver/models/tests/test_resourcepool.py b/src/maasserver/models/tests/test_resourcepool.py
211index 8984826..9875084 100644
212--- a/src/maasserver/models/tests/test_resourcepool.py
213+++ b/src/maasserver/models/tests/test_resourcepool.py
214@@ -3,7 +3,11 @@
215
216 """Test ResourcePool objects."""
217
218-from django.core.exceptions import ValidationError
219+from django.core.exceptions import (
220+ PermissionDenied,
221+ ValidationError,
222+)
223+from maasserver.enum import NODE_PERMISSION
224 from maasserver.models.resourcepool import (
225 DEFAULT_RESOURCEPOOL_DESCRIPTION,
226 DEFAULT_RESOURCEPOOL_NAME,
227@@ -63,3 +67,54 @@ class TestResourcePool(MAASServerTestCase):
228 pool = ResourcePool.objects.get_default_resource_pool()
229 factory.make_Node(pool=pool)
230 self.assertRaises(ValidationError, pool.delete)
231+
232+
233+class TestResourcePoolManagerGetResourcePoolOr404(MAASServerTestCase):
234+
235+ def test__user_view_returns_resource_pool(self):
236+ user = factory.make_User()
237+ pool = factory.make_ResourcePool()
238+ self.assertEqual(
239+ pool,
240+ ResourcePool.objects.get_resource_pool_or_404(
241+ pool.id, user, NODE_PERMISSION.VIEW))
242+
243+ def test__user_edit_raises_PermissionError(self):
244+ user = factory.make_User()
245+ pool = factory.make_ResourcePool()
246+ self.assertRaises(
247+ PermissionDenied,
248+ ResourcePool.objects.get_resource_pool_or_404,
249+ pool.id, user, NODE_PERMISSION.EDIT)
250+
251+ def test__user_admin_raises_PermissionError(self):
252+ user = factory.make_User()
253+ pool = factory.make_ResourcePool()
254+ self.assertRaises(
255+ PermissionDenied,
256+ ResourcePool.objects.get_resource_pool_or_404,
257+ pool.id, user, NODE_PERMISSION.ADMIN)
258+
259+ def test__admin_view_returns_resource_pool(self):
260+ admin = factory.make_admin()
261+ pool = factory.make_ResourcePool()
262+ self.assertEqual(
263+ pool,
264+ ResourcePool.objects.get_resource_pool_or_404(
265+ pool.id, admin, NODE_PERMISSION.VIEW))
266+
267+ def test__admin_edit_returns_resource_pool(self):
268+ admin = factory.make_admin()
269+ pool = factory.make_ResourcePool()
270+ self.assertEqual(
271+ pool,
272+ ResourcePool.objects.get_resource_pool_or_404(
273+ pool.id, admin, NODE_PERMISSION.EDIT))
274+
275+ def test__admin_admin_returns_pool(self):
276+ admin = factory.make_admin()
277+ pool = factory.make_ResourcePool()
278+ self.assertEqual(
279+ pool,
280+ ResourcePool.objects.get_resource_pool_or_404(
281+ pool.id, admin, NODE_PERMISSION.ADMIN))

Subscribers

People subscribed via source and target branches