Merge lp:~julian-edwards/maas/api-doc-bug-1391193 into lp:~maas-committers/maas/trunk
- api-doc-bug-1391193
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Julian Edwards | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 3385 | ||||
Proposed branch: | lp:~julian-edwards/maas/api-doc-bug-1391193 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
712 lines (+225/-30) 11 files modified
src/maasserver/api/files.py (+5/-0) src/maasserver/api/ip_addresses.py (+5/-0) src/maasserver/api/node_group_interfaces.py (+18/-2) src/maasserver/api/node_groups.py (+38/-5) src/maasserver/api/node_macs.py (+17/-4) src/maasserver/api/nodes.py (+85/-9) src/maasserver/api/ssh_keys.py (+9/-2) src/maasserver/api/ssl_keys.py (+10/-2) src/maasserver/api/tags.py (+23/-3) src/maasserver/api/users.py (+2/-0) src/maasserver/api/zones.py (+13/-3) |
||||
To merge this branch: | bzr merge lp:~julian-edwards/maas/api-doc-bug-1391193 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Newell Jensen (community) | Approve | ||
Review via email: mp+242543@code.launchpad.net |
Commit message
Clarify return codes used in all the API docstrings.
Description of the change
I've not covered ValidationError yet, I want to make sure it always returns a 400.
Julian Edwards (julian-edwards) wrote : | # |
Gosh darn I thought I had fixed those, thanks for spotting!
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~julian-edwards/maas/api-doc-bug-1391193 into lp:maas failed. Below is the output from the failed tests.
Ign http://
Hit http://
Hit http://
Ign http://
Ign http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Ign http://
Ign http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === modified file 'src/maasserver/api/files.py' | |||
2 | --- src/maasserver/api/files.py 2014-08-16 10:34:55 +0000 | |||
3 | +++ src/maasserver/api/files.py 2014-11-24 18:23:46 +0000 | |||
4 | @@ -184,6 +184,11 @@ | |||
5 | 184 | :type filename: string | 184 | :type filename: string |
6 | 185 | :param file: Actual file data with content type | 185 | :param file: Actual file data with content type |
7 | 186 | application/octet-stream | 186 | application/octet-stream |
8 | 187 | |||
9 | 188 | Returns 400 if any of these conditions apply: | ||
10 | 189 | - The filename is missing from the parameters | ||
11 | 190 | - The file data is missing | ||
12 | 191 | - More than one file is supplied | ||
13 | 187 | """ | 192 | """ |
14 | 188 | filename = request.data.get("filename", None) | 193 | filename = request.data.get("filename", None) |
15 | 189 | if not filename: | 194 | if not filename: |
16 | 190 | 195 | ||
17 | === modified file 'src/maasserver/api/ip_addresses.py' | |||
18 | --- src/maasserver/api/ip_addresses.py 2014-10-22 10:35:09 +0000 | |||
19 | +++ src/maasserver/api/ip_addresses.py 2014-11-24 18:23:46 +0000 | |||
20 | @@ -79,6 +79,9 @@ | |||
21 | 79 | :param network: CIDR representation of the network on which the IP | 79 | :param network: CIDR representation of the network on which the IP |
22 | 80 | reservation is required. e.g. 10.1.2.0/24 | 80 | reservation is required. e.g. 10.1.2.0/24 |
23 | 81 | :type network: unicode | 81 | :type network: unicode |
24 | 82 | |||
25 | 83 | Returns 400 if there is no network in MAAS matching the provided one. | ||
26 | 84 | Returns 503 if there are no more IP addresses available. | ||
27 | 82 | """ | 85 | """ |
28 | 83 | network = get_mandatory_param(request.POST, "network") | 86 | network = get_mandatory_param(request.POST, "network") |
29 | 84 | requested_address = get_optional_param( | 87 | requested_address = get_optional_param( |
30 | @@ -111,6 +114,8 @@ | |||
31 | 111 | 114 | ||
32 | 112 | :param ip: The IP address to release. | 115 | :param ip: The IP address to release. |
33 | 113 | :type ip: unicode | 116 | :type ip: unicode |
34 | 117 | |||
35 | 118 | Returns 404 if the provided IP address is not found. | ||
36 | 114 | """ | 119 | """ |
37 | 115 | ip = get_mandatory_param(request.POST, "ip") | 120 | ip = get_mandatory_param(request.POST, "ip") |
38 | 116 | staticaddress = get_object_or_404( | 121 | staticaddress = get_object_or_404( |
39 | 117 | 122 | ||
40 | === modified file 'src/maasserver/api/node_group_interfaces.py' | |||
41 | --- src/maasserver/api/node_group_interfaces.py 2014-09-10 16:20:31 +0000 | |||
42 | +++ src/maasserver/api/node_group_interfaces.py 2014-11-24 18:23:46 +0000 | |||
43 | @@ -94,6 +94,10 @@ | |||
44 | 94 | :param static_ip_range_high: Highest static IP address to assign to | 94 | :param static_ip_range_high: Highest static IP address to assign to |
45 | 95 | clients. | 95 | clients. |
46 | 96 | :type static_ip_range_high: unicode (IP Address) | 96 | :type static_ip_range_high: unicode (IP Address) |
47 | 97 | |||
48 | 98 | Returns 404 if the node group (cluster) is not found. | ||
49 | 99 | Returns 403 if the user does not have permission to access the | ||
50 | 100 | interface. | ||
51 | 97 | """ | 101 | """ |
52 | 98 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 102 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
53 | 99 | if not request.user.is_superuser: | 103 | if not request.user.is_superuser: |
54 | @@ -139,7 +143,10 @@ | |||
55 | 139 | return nodegroupinterface | 143 | return nodegroupinterface |
56 | 140 | 144 | ||
57 | 141 | def read(self, request, uuid, name): | 145 | def read(self, request, uuid, name): |
59 | 142 | """List of NodeGroupInterfaces of a NodeGroup.""" | 146 | """List of NodeGroupInterfaces of a NodeGroup. |
60 | 147 | |||
61 | 148 | Returns 404 if the nodegroup (cluster) is not found. | ||
62 | 149 | """ | ||
63 | 143 | # Read-only access is allowed to any user. | 150 | # Read-only access is allowed to any user. |
64 | 144 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 151 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
65 | 145 | return get_object_or_404( | 152 | return get_object_or_404( |
66 | @@ -171,6 +178,10 @@ | |||
67 | 171 | :param static_ip_range_high: Highest static IP address to assign to | 178 | :param static_ip_range_high: Highest static IP address to assign to |
68 | 172 | clients. | 179 | clients. |
69 | 173 | :type static_ip_range_high: unicode (IP Address) | 180 | :type static_ip_range_high: unicode (IP Address) |
70 | 181 | |||
71 | 182 | Returns 404 if the nodegroup (cluster) is not found. | ||
72 | 183 | Returns 403 if the user does not have permission to access the | ||
73 | 184 | interface. | ||
74 | 174 | """ | 185 | """ |
75 | 175 | nodegroupinterface = self.get_interface(request, uuid, name) | 186 | nodegroupinterface = self.get_interface(request, uuid, name) |
76 | 176 | form = NodeGroupInterfaceForm( | 187 | form = NodeGroupInterfaceForm( |
77 | @@ -181,7 +192,12 @@ | |||
78 | 181 | raise ValidationError(form.errors) | 192 | raise ValidationError(form.errors) |
79 | 182 | 193 | ||
80 | 183 | def delete(self, request, uuid, name): | 194 | def delete(self, request, uuid, name): |
82 | 184 | """Delete a specific NodeGroupInterface.""" | 195 | """Delete a specific NodeGroupInterface. |
83 | 196 | |||
84 | 197 | Returns 404 if the nodegroup (cluster) is not found. | ||
85 | 198 | Returns 403 if the user does not have permission to access the | ||
86 | 199 | interface. | ||
87 | 200 | """ | ||
88 | 185 | nodegroupinterface = self.get_interface(request, uuid, name) | 201 | nodegroupinterface = self.get_interface(request, uuid, name) |
89 | 186 | nodegroupinterface.delete() | 202 | nodegroupinterface.delete() |
90 | 187 | return rc.DELETED | 203 | return rc.DELETED |
91 | 188 | 204 | ||
92 | === modified file 'src/maasserver/api/node_groups.py' | |||
93 | --- src/maasserver/api/node_groups.py 2014-10-09 20:24:04 +0000 | |||
94 | +++ src/maasserver/api/node_groups.py 2014-11-24 18:23:46 +0000 | |||
95 | @@ -92,7 +92,10 @@ | |||
96 | 92 | :param uuid: The UUID (or list of UUIDs) of the nodegroup(s) to accept. | 92 | :param uuid: The UUID (or list of UUIDs) of the nodegroup(s) to accept. |
97 | 93 | :type name: unicode (or list of unicodes) | 93 | :type name: unicode (or list of unicodes) |
98 | 94 | 94 | ||
100 | 95 | This method is reserved to admin users. | 95 | This method is reserved to admin users and returns 403 if the |
101 | 96 | user is not an admin. | ||
102 | 97 | |||
103 | 98 | Returns 404 if the nodegroup (cluster) is not found. | ||
104 | 96 | """ | 99 | """ |
105 | 97 | uuids = request.data.getlist('uuid') | 100 | uuids = request.data.getlist('uuid') |
106 | 98 | for uuid in uuids: | 101 | for uuid in uuids: |
107 | @@ -125,7 +128,10 @@ | |||
108 | 125 | :param uuid: The UUID (or list of UUIDs) of the nodegroup(s) to reject. | 128 | :param uuid: The UUID (or list of UUIDs) of the nodegroup(s) to reject. |
109 | 126 | :type name: unicode (or list of unicodes) | 129 | :type name: unicode (or list of unicodes) |
110 | 127 | 130 | ||
112 | 128 | This method is reserved to admin users. | 131 | This method is reserved to admin users and returns 403 if the |
113 | 132 | user is not an admin. | ||
114 | 133 | |||
115 | 134 | Returns 404 if the nodegroup (cluster) is not found. | ||
116 | 129 | """ | 135 | """ |
117 | 130 | uuids = request.data.getlist('uuid') | 136 | uuids = request.data.getlist('uuid') |
118 | 131 | for uuid in uuids: | 137 | for uuid in uuids: |
119 | @@ -173,7 +179,10 @@ | |||
120 | 173 | fields = DISPLAYED_NODEGROUP_FIELDS | 179 | fields = DISPLAYED_NODEGROUP_FIELDS |
121 | 174 | 180 | ||
122 | 175 | def read(self, request, uuid): | 181 | def read(self, request, uuid): |
124 | 176 | """GET a node group.""" | 182 | """GET a node group. |
125 | 183 | |||
126 | 184 | Returns 404 if the nodegroup (cluster) is not found. | ||
127 | 185 | """ | ||
128 | 177 | return get_object_or_404(NodeGroup, uuid=uuid) | 186 | return get_object_or_404(NodeGroup, uuid=uuid) |
129 | 178 | 187 | ||
130 | 179 | @classmethod | 188 | @classmethod |
131 | @@ -195,6 +204,8 @@ | |||
132 | 195 | :param status: The new status for this cluster (see | 204 | :param status: The new status for this cluster (see |
133 | 196 | vocabulary `NODEGROUP_STATUS`). | 205 | vocabulary `NODEGROUP_STATUS`). |
134 | 197 | :type status: int | 206 | :type status: int |
135 | 207 | |||
136 | 208 | Returns 404 if the nodegroup (cluster) is not found. | ||
137 | 198 | """ | 209 | """ |
138 | 199 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 210 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
139 | 200 | form = NodeGroupEdit(instance=nodegroup, data=request.data) | 211 | form = NodeGroupEdit(instance=nodegroup, data=request.data) |
140 | @@ -206,7 +217,10 @@ | |||
141 | 206 | @admin_method | 217 | @admin_method |
142 | 207 | @operation(idempotent=False) | 218 | @operation(idempotent=False) |
143 | 208 | def import_boot_images(self, request, uuid): | 219 | def import_boot_images(self, request, uuid): |
145 | 209 | """Import the pxe files on this cluster controller.""" | 220 | """Import the pxe files on this cluster controller. |
146 | 221 | |||
147 | 222 | Returns 404 if the nodegroup (cluster) is not found. | ||
148 | 223 | """ | ||
149 | 210 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 224 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
150 | 211 | nodegroup.import_boot_images() | 225 | nodegroup.import_boot_images() |
151 | 212 | return HttpResponse( | 226 | return HttpResponse( |
152 | @@ -215,7 +229,10 @@ | |||
153 | 215 | 229 | ||
154 | 216 | @operation(idempotent=True) | 230 | @operation(idempotent=True) |
155 | 217 | def list_nodes(self, request, uuid): | 231 | def list_nodes(self, request, uuid): |
157 | 218 | """Get the list of node ids that are part of this group.""" | 232 | """Get the list of node ids that are part of this group. |
158 | 233 | |||
159 | 234 | Returns 404 if the nodegroup (cluster) is not found. | ||
160 | 235 | """ | ||
161 | 219 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 236 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
162 | 220 | if not request.user.is_superuser: | 237 | if not request.user.is_superuser: |
163 | 221 | check_nodegroup_access(request, nodegroup) | 238 | check_nodegroup_access(request, nodegroup) |
164 | @@ -250,6 +267,8 @@ | |||
165 | 250 | b) Requests for nodes that are not part of the nodegroup are | 267 | b) Requests for nodes that are not part of the nodegroup are |
166 | 251 | just ignored. | 268 | just ignored. |
167 | 252 | 269 | ||
168 | 270 | Returns 404 if the nodegroup (cluster) is not found. | ||
169 | 271 | Returns 403 if the user does not have access to the nodegroup. | ||
170 | 253 | """ | 272 | """ |
171 | 254 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 273 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
172 | 255 | if not request.user.is_superuser: | 274 | if not request.user.is_superuser: |
173 | @@ -318,6 +337,10 @@ | |||
174 | 318 | :param error: Optional error string. A download that has submitted an | 337 | :param error: Optional error string. A download that has submitted an |
175 | 319 | error with its last progress report is considered to have failed. | 338 | error with its last progress report is considered to have failed. |
176 | 320 | :type error: unicode | 339 | :type error: unicode |
177 | 340 | |||
178 | 341 | Returns 404 if the nodegroup (cluster) is not found. | ||
179 | 342 | Returns 403 if the user does not have access to the nodegroup. | ||
180 | 343 | Returns 400 if the required parameters were not passed. | ||
181 | 321 | """ | 344 | """ |
182 | 322 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 345 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
183 | 323 | check_nodegroup_access(request, nodegroup) | 346 | check_nodegroup_access(request, nodegroup) |
184 | @@ -372,6 +395,10 @@ | |||
185 | 372 | :param power_pass: The password to use, when qemu+ssh is given as a | 395 | :param power_pass: The password to use, when qemu+ssh is given as a |
186 | 373 | connection string and ssh key authentication is not being used. | 396 | connection string and ssh key authentication is not being used. |
187 | 374 | :type power_pass: unicode | 397 | :type power_pass: unicode |
188 | 398 | |||
189 | 399 | Returns 404 if the nodegroup (cluster) is not found. | ||
190 | 400 | Returns 403 if the user does not have access to the nodegroup. | ||
191 | 401 | Returns 400 if the required parameters were not passed. | ||
192 | 375 | """ | 402 | """ |
193 | 376 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 403 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
194 | 377 | 404 | ||
195 | @@ -409,6 +436,9 @@ | |||
196 | 409 | :param password: The password for the API. | 436 | :param password: The password for the API. |
197 | 410 | :type password: unicode | 437 | :type password: unicode |
198 | 411 | 438 | ||
199 | 439 | Returns 404 if the nodegroup (cluster) is not found. | ||
200 | 440 | Returns 403 if the user does not have access to the nodegroup. | ||
201 | 441 | Returns 400 if the required parameters were not passed. | ||
202 | 412 | """ | 442 | """ |
203 | 413 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 443 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
204 | 414 | 444 | ||
205 | @@ -432,6 +462,9 @@ | |||
206 | 432 | :param password: The password for the MSCM. | 462 | :param password: The password for the MSCM. |
207 | 433 | :type password: unicode | 463 | :type password: unicode |
208 | 434 | 464 | ||
209 | 465 | Returns 404 if the nodegroup (cluster) is not found. | ||
210 | 466 | Returns 403 if the user does not have access to the nodegroup. | ||
211 | 467 | Returns 400 if the required parameters were not passed. | ||
212 | 435 | """ | 468 | """ |
213 | 436 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) | 469 | nodegroup = get_object_or_404(NodeGroup, uuid=uuid) |
214 | 437 | 470 | ||
215 | 438 | 471 | ||
216 | === modified file 'src/maasserver/api/node_macs.py' | |||
217 | --- src/maasserver/api/node_macs.py 2014-08-18 13:27:34 +0000 | |||
218 | +++ src/maasserver/api/node_macs.py 2014-11-24 18:23:46 +0000 | |||
219 | @@ -41,14 +41,20 @@ | |||
220 | 41 | update = delete = None | 41 | update = delete = None |
221 | 42 | 42 | ||
222 | 43 | def read(self, request, system_id): | 43 | def read(self, request, system_id): |
224 | 44 | """Read all MAC addresses related to a Node.""" | 44 | """Read all MAC addresses related to a Node. |
225 | 45 | |||
226 | 46 | Returns 404 if the node is not found. | ||
227 | 47 | """ | ||
228 | 45 | node = Node.objects.get_node_or_404( | 48 | node = Node.objects.get_node_or_404( |
229 | 46 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.VIEW) | 49 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.VIEW) |
230 | 47 | 50 | ||
231 | 48 | return MACAddress.objects.filter(node=node).order_by('id') | 51 | return MACAddress.objects.filter(node=node).order_by('id') |
232 | 49 | 52 | ||
233 | 50 | def create(self, request, system_id): | 53 | def create(self, request, system_id): |
235 | 51 | """Create a MAC address for a specified Node.""" | 54 | """Create a MAC address for a specified Node. |
236 | 55 | |||
237 | 56 | Returns 404 if the node is not found. | ||
238 | 57 | """ | ||
239 | 52 | node = Node.objects.get_node_or_404( | 58 | node = Node.objects.get_node_or_404( |
240 | 53 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) | 59 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) |
241 | 54 | mac = node.add_mac_address(request.data.get('mac_address', None)) | 60 | mac = node.add_mac_address(request.data.get('mac_address', None)) |
242 | @@ -71,7 +77,10 @@ | |||
243 | 71 | model = MACAddress | 77 | model = MACAddress |
244 | 72 | 78 | ||
245 | 73 | def read(self, request, system_id, mac_address): | 79 | def read(self, request, system_id, mac_address): |
247 | 74 | """Read a MAC address related to a Node.""" | 80 | """Read a MAC address related to a Node. |
248 | 81 | |||
249 | 82 | Returns 404 if the node or the MAC address is not found. | ||
250 | 83 | """ | ||
251 | 75 | node = Node.objects.get_node_or_404( | 84 | node = Node.objects.get_node_or_404( |
252 | 76 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.VIEW) | 85 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.VIEW) |
253 | 77 | 86 | ||
254 | @@ -80,7 +89,11 @@ | |||
255 | 80 | MACAddress, node=node, mac_address=mac_address) | 89 | MACAddress, node=node, mac_address=mac_address) |
256 | 81 | 90 | ||
257 | 82 | def delete(self, request, system_id, mac_address): | 91 | def delete(self, request, system_id, mac_address): |
259 | 83 | """Delete a specific MAC address for the specified Node.""" | 92 | """Delete a specific MAC address for the specified Node. |
260 | 93 | |||
261 | 94 | Returns 404 if the node or the MAC address is not found. | ||
262 | 95 | Returns 204 after the MAC address is successfully deleted. | ||
263 | 96 | """ | ||
264 | 84 | validate_mac(mac_address) | 97 | validate_mac(mac_address) |
265 | 85 | node = Node.objects.get_node_or_404( | 98 | node = Node.objects.get_node_or_404( |
266 | 86 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) | 99 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) |
267 | 87 | 100 | ||
268 | === modified file 'src/maasserver/api/nodes.py' | |||
269 | --- src/maasserver/api/nodes.py 2014-11-05 15:10:12 +0000 | |||
270 | +++ src/maasserver/api/nodes.py 2014-11-24 18:23:46 +0000 | |||
271 | @@ -197,7 +197,10 @@ | |||
272 | 197 | return node.owner.username | 197 | return node.owner.username |
273 | 198 | 198 | ||
274 | 199 | def read(self, request, system_id): | 199 | def read(self, request, system_id): |
276 | 200 | """Read a specific Node.""" | 200 | """Read a specific Node. |
277 | 201 | |||
278 | 202 | Returns 404 if the node is not found. | ||
279 | 203 | """ | ||
280 | 201 | return Node.objects.get_node_or_404( | 204 | return Node.objects.get_node_or_404( |
281 | 202 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.VIEW) | 205 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.VIEW) |
282 | 203 | 206 | ||
283 | @@ -231,6 +234,9 @@ | |||
284 | 231 | :type power_parameters_skip_check: unicode | 234 | :type power_parameters_skip_check: unicode |
285 | 232 | :param zone: Name of a valid physical zone in which to place this node | 235 | :param zone: Name of a valid physical zone in which to place this node |
286 | 233 | :type zone: unicode | 236 | :type zone: unicode |
287 | 237 | |||
288 | 238 | Returns 404 if the node is node found. | ||
289 | 239 | Returns 403 if the user does not have permission to update the node. | ||
290 | 234 | """ | 240 | """ |
291 | 235 | node = Node.objects.get_node_or_404( | 241 | node = Node.objects.get_node_or_404( |
292 | 236 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) | 242 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) |
293 | @@ -243,7 +249,12 @@ | |||
294 | 243 | raise ValidationError(form.errors) | 249 | raise ValidationError(form.errors) |
295 | 244 | 250 | ||
296 | 245 | def delete(self, request, system_id): | 251 | def delete(self, request, system_id): |
298 | 246 | """Delete a specific Node.""" | 252 | """Delete a specific Node. |
299 | 253 | |||
300 | 254 | Returns 404 if the node is not found. | ||
301 | 255 | Returns 403 if the user does not have permission to delete the node. | ||
302 | 256 | Returns 204 if the node is successfully deleted. | ||
303 | 257 | """ | ||
304 | 247 | node = Node.objects.get_node_or_404( | 258 | node = Node.objects.get_node_or_404( |
305 | 248 | system_id=system_id, user=request.user, | 259 | system_id=system_id, user=request.user, |
306 | 249 | perm=NODE_PERMISSION.ADMIN) | 260 | perm=NODE_PERMISSION.ADMIN) |
307 | @@ -274,6 +285,9 @@ | |||
308 | 274 | gracefully before powering off, while a hard power off | 285 | gracefully before powering off, while a hard power off |
309 | 275 | occurs immediately without any warning to the OS. | 286 | occurs immediately without any warning to the OS. |
310 | 276 | :type stop_mode: unicode | 287 | :type stop_mode: unicode |
311 | 288 | |||
312 | 289 | Returns 404 if the node is not found. | ||
313 | 290 | Returns 403 if the user does not have permission to stop the node. | ||
314 | 277 | """ | 291 | """ |
315 | 278 | stop_mode = request.POST.get('stop_mode', 'hard') | 292 | stop_mode = request.POST.get('stop_mode', 'hard') |
316 | 279 | node = Node.objects.get_node_or_404( | 293 | node = Node.objects.get_node_or_404( |
317 | @@ -300,6 +314,12 @@ | |||
318 | 300 | deal with the encapsulation of binary data, but couldn't make it work | 314 | deal with the encapsulation of binary data, but couldn't make it work |
319 | 301 | with the framework in reasonable time so went for a dumb, manual | 315 | with the framework in reasonable time so went for a dumb, manual |
320 | 302 | encoding instead. | 316 | encoding instead. |
321 | 317 | |||
322 | 318 | Returns 404 if the node is not found. | ||
323 | 319 | Returns 403 if the user does not have permission to stop the node. | ||
324 | 320 | Returns 503 if the start-up attempted to allocate an IP address, | ||
325 | 321 | and there were no IP addresses available on the relevant cluster | ||
326 | 322 | interface. | ||
327 | 303 | """ | 323 | """ |
328 | 304 | user_data = request.POST.get('user_data', None) | 324 | user_data = request.POST.get('user_data', None) |
329 | 305 | series = request.POST.get('distro_series', None) | 325 | series = request.POST.get('distro_series', None) |
330 | @@ -335,7 +355,12 @@ | |||
331 | 335 | 355 | ||
332 | 336 | @operation(idempotent=False) | 356 | @operation(idempotent=False) |
333 | 337 | def release(self, request, system_id): | 357 | def release(self, request, system_id): |
335 | 338 | """Release a node. Opposite of `NodesHandler.acquire`.""" | 358 | """Release a node. Opposite of `NodesHandler.acquire`. |
336 | 359 | |||
337 | 360 | Returns 404 if the node is not found. | ||
338 | 361 | Returns 403 if the user does not have permission to release the node. | ||
339 | 362 | Returns 409 if the node is in a state where it may not be released. | ||
340 | 363 | """ | ||
341 | 339 | node = Node.objects.get_node_or_404( | 364 | node = Node.objects.get_node_or_404( |
342 | 340 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) | 365 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) |
343 | 341 | if node.status == NODE_STATUS.READY: | 366 | if node.status == NODE_STATUS.READY: |
344 | @@ -360,6 +385,8 @@ | |||
345 | 360 | already in the 'ready' state this is considered a re-commissioning | 385 | already in the 'ready' state this is considered a re-commissioning |
346 | 361 | process which is useful if commissioning tests were changed after | 386 | process which is useful if commissioning tests were changed after |
347 | 362 | it previously commissioned. | 387 | it previously commissioned. |
348 | 388 | |||
349 | 389 | Returns 404 if the node is not found. | ||
350 | 363 | """ | 390 | """ |
351 | 364 | node = get_object_or_404(Node, system_id=system_id) | 391 | node = get_object_or_404(Node, system_id=system_id) |
352 | 365 | form_class = get_action_form(user=request.user) | 392 | form_class = get_action_form(user=request.user) |
353 | @@ -383,6 +410,8 @@ | |||
354 | 383 | Note that this is returned as BSON and not JSON. This is for | 410 | Note that this is returned as BSON and not JSON. This is for |
355 | 384 | efficiency, but mainly because JSON can't do binary content | 411 | efficiency, but mainly because JSON can't do binary content |
356 | 385 | without applying additional encoding like base-64. | 412 | without applying additional encoding like base-64. |
357 | 413 | |||
358 | 414 | Returns 404 if the node is not found. | ||
359 | 386 | """ | 415 | """ |
360 | 387 | node = get_object_or_404(Node, system_id=system_id) | 416 | node = get_object_or_404(Node, system_id=system_id) |
361 | 388 | probe_details = get_single_probed_details(node.system_id) | 417 | probe_details = get_single_probed_details(node.system_id) |
362 | @@ -415,6 +444,12 @@ | |||
363 | 415 | an admin to give a node a stable IP, since normally an automatic | 444 | an admin to give a node a stable IP, since normally an automatic |
364 | 416 | IP is allocated to a node only during the time a user has | 445 | IP is allocated to a node only during the time a user has |
365 | 417 | acquired and started a node. | 446 | acquired and started a node. |
366 | 447 | |||
367 | 448 | Returns 404 if the node is not found. | ||
368 | 449 | Returns 409 if the node is in an allocated state. | ||
369 | 450 | Returns 400 if the mac_address is not found on the node. | ||
370 | 451 | Returns 503 if there are not enough IPs left on the cluster interface | ||
371 | 452 | to which the mac_address is linked. | ||
372 | 418 | """ | 453 | """ |
373 | 419 | node = get_object_or_404(Node, system_id=system_id) | 454 | node = get_object_or_404(Node, system_id=system_id) |
374 | 420 | if node.status == NODE_STATUS.ALLOCATED: | 455 | if node.status == NODE_STATUS.ALLOCATED: |
375 | @@ -449,6 +484,10 @@ | |||
376 | 449 | :param error_description: An optional description of the reason the | 484 | :param error_description: An optional description of the reason the |
377 | 450 | node is being marked broken. | 485 | node is being marked broken. |
378 | 451 | :type error_description: unicode | 486 | :type error_description: unicode |
379 | 487 | |||
380 | 488 | Returns 404 if the node is not found. | ||
381 | 489 | Returns 403 if the user does not have permission to mark the node | ||
382 | 490 | broken. | ||
383 | 452 | """ | 491 | """ |
384 | 453 | node = Node.objects.get_node_or_404( | 492 | node = Node.objects.get_node_or_404( |
385 | 454 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) | 493 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT) |
386 | @@ -459,7 +498,12 @@ | |||
387 | 459 | 498 | ||
388 | 460 | @operation(idempotent=False) | 499 | @operation(idempotent=False) |
389 | 461 | def mark_fixed(self, request, system_id): | 500 | def mark_fixed(self, request, system_id): |
391 | 462 | """Mark a broken node as fixed and set its status as 'ready'.""" | 501 | """Mark a broken node as fixed and set its status as 'ready'. |
392 | 502 | |||
393 | 503 | Returns 404 if the node is not found. | ||
394 | 504 | Returns 403 if the user does not have permission to mark the node | ||
395 | 505 | broken. | ||
396 | 506 | """ | ||
397 | 463 | node = Node.objects.get_node_or_404( | 507 | node = Node.objects.get_node_or_404( |
398 | 464 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.ADMIN) | 508 | user=request.user, system_id=system_id, perm=NODE_PERMISSION.ADMIN) |
399 | 465 | node.mark_fixed() | 509 | node.mark_fixed() |
400 | @@ -473,11 +517,14 @@ | |||
401 | 473 | def power_parameters(self, request, system_id): | 517 | def power_parameters(self, request, system_id): |
402 | 474 | """Obtain power parameters. | 518 | """Obtain power parameters. |
403 | 475 | 519 | ||
405 | 476 | This method is reserved for admin users. | 520 | This method is reserved for admin users and returns a 403 if the |
406 | 521 | user is not one. | ||
407 | 477 | 522 | ||
408 | 478 | This returns the power parameters, if any, configured for a | 523 | This returns the power parameters, if any, configured for a |
409 | 479 | node. For some types of power control this will include private | 524 | node. For some types of power control this will include private |
410 | 480 | information such as passwords and secret keys. | 525 | information such as passwords and secret keys. |
411 | 526 | |||
412 | 527 | Returns 404 if the node is not found. | ||
413 | 481 | """ | 528 | """ |
414 | 482 | node = get_object_or_404(Node, system_id=system_id) | 529 | node = get_object_or_404(Node, system_id=system_id) |
415 | 483 | return node.power_parameters | 530 | return node.power_parameters |
416 | @@ -492,12 +539,13 @@ | |||
417 | 492 | Use this method sparingly as it ties up an appserver thread | 539 | Use this method sparingly as it ties up an appserver thread |
418 | 493 | while waiting. | 540 | while waiting. |
419 | 494 | 541 | ||
420 | 495 | If there is a problem, SERVICE_UNAVAILABLE is returned with text | ||
421 | 496 | explaining why. | ||
422 | 497 | |||
423 | 498 | :param system_id: The node to query. | 542 | :param system_id: The node to query. |
424 | 499 | :return: a dict whose key is "state" with a value of one of | 543 | :return: a dict whose key is "state" with a value of one of |
425 | 500 | 'on' or 'off'. | 544 | 'on' or 'off'. |
426 | 545 | |||
427 | 546 | Returns 404 if the node is not found. | ||
428 | 547 | Returns 503 (with explanatory text) if the power state could not | ||
429 | 548 | be queried. | ||
430 | 501 | """ | 549 | """ |
431 | 502 | node = get_object_or_404(Node, system_id=system_id) | 550 | node = get_object_or_404(Node, system_id=system_id) |
432 | 503 | ng = node.nodegroup | 551 | ng = node.nodegroup |
433 | @@ -540,6 +588,10 @@ | |||
434 | 540 | """Abort a node's current operation. | 588 | """Abort a node's current operation. |
435 | 541 | 589 | ||
436 | 542 | This currently only supports aborting of the 'Disk Erasing' operation. | 590 | This currently only supports aborting of the 'Disk Erasing' operation. |
437 | 591 | |||
438 | 592 | Returns 404 if the node could not be found. | ||
439 | 593 | Returns 403 if the user does not have permission to abort the | ||
440 | 594 | current operation. | ||
441 | 543 | """ | 595 | """ |
442 | 544 | node = Node.objects.get_node_or_404( | 596 | node = Node.objects.get_node_or_404( |
443 | 545 | system_id=system_id, user=request.user, | 597 | system_id=system_id, user=request.user, |
444 | @@ -648,6 +700,8 @@ | |||
445 | 648 | :type mac_address: unicode | 700 | :type mac_address: unicode |
446 | 649 | :return: 'true' or 'false'. | 701 | :return: 'true' or 'false'. |
447 | 650 | :rtype: unicode | 702 | :rtype: unicode |
448 | 703 | |||
449 | 704 | Returns 400 if any mandatory parameters are missing. | ||
450 | 651 | """ | 705 | """ |
451 | 652 | mac_address = get_mandatory_param(request.GET, 'mac_address') | 706 | mac_address = get_mandatory_param(request.GET, 'mac_address') |
452 | 653 | mac_addresses = MACAddress.objects.filter(mac_address=mac_address) | 707 | mac_addresses = MACAddress.objects.filter(mac_address=mac_address) |
453 | @@ -656,7 +710,10 @@ | |||
454 | 656 | 710 | ||
455 | 657 | @operation(idempotent=False) | 711 | @operation(idempotent=False) |
456 | 658 | def accept(self, request): | 712 | def accept(self, request): |
458 | 659 | """Accept a node's enlistment: not allowed to anonymous users.""" | 713 | """Accept a node's enlistment: not allowed to anonymous users. |
459 | 714 | |||
460 | 715 | Always returns 401. | ||
461 | 716 | """ | ||
462 | 660 | raise Unauthorized("You must be logged in to accept nodes.") | 717 | raise Unauthorized("You must be logged in to accept nodes.") |
463 | 661 | 718 | ||
464 | 662 | @classmethod | 719 | @classmethod |
465 | @@ -726,6 +783,9 @@ | |||
466 | 726 | :return: The system_ids of any nodes that have their status changed | 783 | :return: The system_ids of any nodes that have their status changed |
467 | 727 | by this call. Thus, nodes that were already accepted are | 784 | by this call. Thus, nodes that were already accepted are |
468 | 728 | excluded from the result. | 785 | excluded from the result. |
469 | 786 | |||
470 | 787 | Returns 400 if any of the nodes do not exist. | ||
471 | 788 | Returns 403 if the user is not an admin. | ||
472 | 729 | """ | 789 | """ |
473 | 730 | system_ids = set(request.POST.getlist('nodes')) | 790 | system_ids = set(request.POST.getlist('nodes')) |
474 | 731 | # Check the existence of these nodes first. | 791 | # Check the existence of these nodes first. |
475 | @@ -813,6 +873,12 @@ | |||
476 | 813 | :return: The system_ids of any nodes that have their status | 873 | :return: The system_ids of any nodes that have their status |
477 | 814 | changed by this call. Thus, nodes that were already released | 874 | changed by this call. Thus, nodes that were already released |
478 | 815 | are excluded from the result. | 875 | are excluded from the result. |
479 | 876 | |||
480 | 877 | Returns 400 if any of the nodes cannot be found. | ||
481 | 878 | Returns 403 if the user does not have permission to release any of | ||
482 | 879 | the nodes. | ||
483 | 880 | Returns a 409 if any of the nodes could not be released due to their | ||
484 | 881 | current state. | ||
485 | 816 | """ | 882 | """ |
486 | 817 | system_ids = set(request.POST.getlist('nodes')) | 883 | system_ids = set(request.POST.getlist('nodes')) |
487 | 818 | # Check the existence of these nodes first. | 884 | # Check the existence of these nodes first. |
488 | @@ -964,6 +1030,9 @@ | |||
489 | 964 | :param agent_name: An optional agent name to attach to the | 1030 | :param agent_name: An optional agent name to attach to the |
490 | 965 | acquired node. | 1031 | acquired node. |
491 | 966 | :type agent_name: unicode | 1032 | :type agent_name: unicode |
492 | 1033 | |||
493 | 1034 | Returns 409 if a suitable node matching the constraints could not be | ||
494 | 1035 | found. | ||
495 | 967 | """ | 1036 | """ |
496 | 968 | form = AcquireNodeForm(data=request.data) | 1037 | form = AcquireNodeForm(data=request.data) |
497 | 969 | maaslog.info( | 1038 | maaslog.info( |
498 | @@ -1006,6 +1075,8 @@ | |||
499 | 1006 | will be taken out of their physical zones. | 1075 | will be taken out of their physical zones. |
500 | 1007 | :param nodes: system_ids of the nodes whose zones are to be set. | 1076 | :param nodes: system_ids of the nodes whose zones are to be set. |
501 | 1008 | (An empty list is acceptable). | 1077 | (An empty list is acceptable). |
502 | 1078 | |||
503 | 1079 | Raises 403 if the user is not an admin. | ||
504 | 1009 | """ | 1080 | """ |
505 | 1010 | data = { | 1081 | data = { |
506 | 1011 | 'action': 'set_zone', | 1082 | 'action': 'set_zone', |
507 | @@ -1027,6 +1098,8 @@ | |||
508 | 1027 | :type id: iterable | 1098 | :type id: iterable |
509 | 1028 | 1099 | ||
510 | 1029 | :return: A dictionary of power parameters, keyed by node system_id. | 1100 | :return: A dictionary of power parameters, keyed by node system_id. |
511 | 1101 | |||
512 | 1102 | Raises 403 if the user is not an admin. | ||
513 | 1030 | """ | 1103 | """ |
514 | 1031 | match_ids = get_optional_list(request.GET, 'id') | 1104 | match_ids = get_optional_list(request.GET, 'id') |
515 | 1032 | 1105 | ||
516 | @@ -1043,6 +1116,9 @@ | |||
517 | 1043 | 1116 | ||
518 | 1044 | :param nodes: Mandatory list of system IDs for nodes whose status | 1117 | :param nodes: Mandatory list of system IDs for nodes whose status |
519 | 1045 | you wish to check. | 1118 | you wish to check. |
520 | 1119 | |||
521 | 1120 | Returns 400 if mandatory parameters are missing. | ||
522 | 1121 | Returns 403 if the user has no permission to view any of the nodes. | ||
523 | 1046 | """ | 1122 | """ |
524 | 1047 | system_ids = set(request.GET.getlist('nodes')) | 1123 | system_ids = set(request.GET.getlist('nodes')) |
525 | 1048 | # Check the existence of these nodes first. | 1124 | # Check the existence of these nodes first. |
526 | 1049 | 1125 | ||
527 | === modified file 'src/maasserver/api/ssh_keys.py' | |||
528 | --- src/maasserver/api/ssh_keys.py 2014-08-16 15:05:54 +0000 | |||
529 | +++ src/maasserver/api/ssh_keys.py 2014-11-24 18:23:46 +0000 | |||
530 | @@ -83,13 +83,20 @@ | |||
531 | 83 | create = update = None | 83 | create = update = None |
532 | 84 | 84 | ||
533 | 85 | def read(self, request, keyid): | 85 | def read(self, request, keyid): |
535 | 86 | """GET an SSH key.""" | 86 | """GET an SSH key. |
536 | 87 | |||
537 | 88 | Returns 404 if the key does not exist. | ||
538 | 89 | """ | ||
539 | 87 | key = get_object_or_404(SSHKey, id=keyid) | 90 | key = get_object_or_404(SSHKey, id=keyid) |
540 | 88 | return key | 91 | return key |
541 | 89 | 92 | ||
542 | 90 | @operation(idempotent=False) | 93 | @operation(idempotent=False) |
543 | 91 | def delete(self, request, keyid): | 94 | def delete(self, request, keyid): |
545 | 92 | """DELETE an SSH key.""" | 95 | """DELETE an SSH key. |
546 | 96 | |||
547 | 97 | Returns 404 if the key does not exist. | ||
548 | 98 | Returns 401 if the key does not belong to the calling user. | ||
549 | 99 | """ | ||
550 | 93 | key = get_object_or_404(SSHKey, id=keyid) | 100 | key = get_object_or_404(SSHKey, id=keyid) |
551 | 94 | if key.user != request.user: | 101 | if key.user != request.user: |
552 | 95 | return HttpResponse( | 102 | return HttpResponse( |
553 | 96 | 103 | ||
554 | === modified file 'src/maasserver/api/ssl_keys.py' | |||
555 | --- src/maasserver/api/ssl_keys.py 2014-08-17 01:01:12 +0000 | |||
556 | +++ src/maasserver/api/ssl_keys.py 2014-11-24 18:23:46 +0000 | |||
557 | @@ -83,7 +83,11 @@ | |||
558 | 83 | create = update = None | 83 | create = update = None |
559 | 84 | 84 | ||
560 | 85 | def read(self, request, keyid): | 85 | def read(self, request, keyid): |
562 | 86 | """GET an SSL key.""" | 86 | """GET an SSL key. |
563 | 87 | |||
564 | 88 | Returns 404 if the keyid is not found. | ||
565 | 89 | Returns 401 if the key does not belong to the requesting user. | ||
566 | 90 | """ | ||
567 | 87 | key = get_object_or_404(SSLKey, id=keyid) | 91 | key = get_object_or_404(SSLKey, id=keyid) |
568 | 88 | if key.user != request.user: | 92 | if key.user != request.user: |
569 | 89 | return HttpResponse( | 93 | return HttpResponse( |
570 | @@ -92,7 +96,11 @@ | |||
571 | 92 | 96 | ||
572 | 93 | @operation(idempotent=True) | 97 | @operation(idempotent=True) |
573 | 94 | def delete(self, request, keyid): | 98 | def delete(self, request, keyid): |
575 | 95 | """DELETE an SSL key.""" | 99 | """DELETE an SSL key. |
576 | 100 | |||
577 | 101 | Returns 401 if the key does not belong to the requesting user. | ||
578 | 102 | Returns 204 if the key is successfully deleted. | ||
579 | 103 | """ | ||
580 | 96 | key = get_object_or_404(SSLKey, id=keyid) | 104 | key = get_object_or_404(SSLKey, id=keyid) |
581 | 97 | if key.user != request.user: | 105 | if key.user != request.user: |
582 | 98 | return HttpResponse( | 106 | return HttpResponse( |
583 | 99 | 107 | ||
584 | === modified file 'src/maasserver/api/tags.py' | |||
585 | --- src/maasserver/api/tags.py 2014-08-18 11:43:18 +0000 | |||
586 | +++ src/maasserver/api/tags.py 2014-11-24 18:23:46 +0000 | |||
587 | @@ -62,7 +62,10 @@ | |||
588 | 62 | ) | 62 | ) |
589 | 63 | 63 | ||
590 | 64 | def read(self, request, name): | 64 | def read(self, request, name): |
592 | 65 | """Read a specific Tag""" | 65 | """Read a specific Tag. |
593 | 66 | |||
594 | 67 | Returns 404 if the tag is not found. | ||
595 | 68 | """ | ||
596 | 66 | return Tag.objects.get_tag_or_404(name=name, user=request.user) | 69 | return Tag.objects.get_tag_or_404(name=name, user=request.user) |
597 | 67 | 70 | ||
598 | 68 | def update(self, request, name): | 71 | def update(self, request, name): |
599 | @@ -74,6 +77,8 @@ | |||
600 | 74 | It is meant as a human readable description of the tag. | 77 | It is meant as a human readable description of the tag. |
601 | 75 | :param definition: An XPATH query that will be evaluated against the | 78 | :param definition: An XPATH query that will be evaluated against the |
602 | 76 | hardware_details stored for all nodes (output of `lshw -xml`). | 79 | hardware_details stored for all nodes (output of `lshw -xml`). |
603 | 80 | |||
604 | 81 | Returns 404 if the tag is not found. | ||
605 | 77 | """ | 82 | """ |
606 | 78 | tag = Tag.objects.get_tag_or_404( | 83 | tag = Tag.objects.get_tag_or_404( |
607 | 79 | name=name, user=request.user, to_edit=True) | 84 | name=name, user=request.user, to_edit=True) |
608 | @@ -90,7 +95,11 @@ | |||
609 | 90 | raise ValidationError(form.errors) | 95 | raise ValidationError(form.errors) |
610 | 91 | 96 | ||
611 | 92 | def delete(self, request, name): | 97 | def delete(self, request, name): |
613 | 93 | """Delete a specific Tag.""" | 98 | """Delete a specific Tag. |
614 | 99 | |||
615 | 100 | Returns 404 if the tag is not found. | ||
616 | 101 | Returns 204 if the tag is successfully deleted. | ||
617 | 102 | """ | ||
618 | 94 | tag = Tag.objects.get_tag_or_404( | 103 | tag = Tag.objects.get_tag_or_404( |
619 | 95 | name=name, user=request.user, to_edit=True) | 104 | name=name, user=request.user, to_edit=True) |
620 | 96 | tag.delete() | 105 | tag.delete() |
621 | @@ -98,7 +107,10 @@ | |||
622 | 98 | 107 | ||
623 | 99 | @operation(idempotent=True) | 108 | @operation(idempotent=True) |
624 | 100 | def nodes(self, request, name): | 109 | def nodes(self, request, name): |
626 | 101 | """Get the list of nodes that have this tag.""" | 110 | """Get the list of nodes that have this tag. |
627 | 111 | |||
628 | 112 | Returns 404 if the tag is not found. | ||
629 | 113 | """ | ||
630 | 102 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user) | 114 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user) |
631 | 103 | return Node.objects.get_nodes( | 115 | return Node.objects.get_nodes( |
632 | 104 | request.user, NODE_PERMISSION.VIEW, from_nodes=tag.node_set.all()) | 116 | request.user, NODE_PERMISSION.VIEW, from_nodes=tag.node_set.all()) |
633 | @@ -120,6 +132,8 @@ | |||
634 | 120 | This is considered a maintenance operation, which should normally not | 132 | This is considered a maintenance operation, which should normally not |
635 | 121 | be necessary. Adding nodes or updating a tag's definition should | 133 | be necessary. Adding nodes or updating a tag's definition should |
636 | 122 | automatically trigger the appropriate changes. | 134 | automatically trigger the appropriate changes. |
637 | 135 | |||
638 | 136 | Returns 404 if the tag is not found. | ||
639 | 123 | """ | 137 | """ |
640 | 124 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user, | 138 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user, |
641 | 125 | to_edit=True) | 139 | to_edit=True) |
642 | @@ -141,6 +155,10 @@ | |||
643 | 141 | supplied, then the requester must be the worker associated with | 155 | supplied, then the requester must be the worker associated with |
644 | 142 | that nodegroup, and only nodes that are part of that nodegroup can | 156 | that nodegroup, and only nodes that are part of that nodegroup can |
645 | 143 | be updated. | 157 | be updated. |
646 | 158 | |||
647 | 159 | Returns 404 if the tag is not found. | ||
648 | 160 | Returns 401 if the user does not have permission to update the nodes. | ||
649 | 161 | Returns 409 if 'definition' doesn't match the current definition. | ||
650 | 144 | """ | 162 | """ |
651 | 145 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user) | 163 | tag = Tag.objects.get_tag_or_404(name=name, user=request.user) |
652 | 146 | nodegroup = None | 164 | nodegroup = None |
653 | @@ -196,6 +214,8 @@ | |||
654 | 196 | value overrides the global 'kernel_opts' setting. If more than one | 214 | value overrides the global 'kernel_opts' setting. If more than one |
655 | 197 | tag is associated with a node, the one with the lowest alphabetical | 215 | tag is associated with a node, the one with the lowest alphabetical |
656 | 198 | name will be picked (eg 01-my-tag will be taken over 99-tag-name). | 216 | name will be picked (eg 01-my-tag will be taken over 99-tag-name). |
657 | 217 | |||
658 | 218 | Returns 401 if the user is not an admin. | ||
659 | 199 | """ | 219 | """ |
660 | 200 | if not request.user.is_superuser: | 220 | if not request.user.is_superuser: |
661 | 201 | raise PermissionDenied() | 221 | raise PermissionDenied() |
662 | 202 | 222 | ||
663 | === modified file 'src/maasserver/api/users.py' | |||
664 | --- src/maasserver/api/users.py 2014-08-16 15:14:46 +0000 | |||
665 | +++ src/maasserver/api/users.py 2014-11-24 18:23:46 +0000 | |||
666 | @@ -59,6 +59,8 @@ | |||
667 | 59 | :type password: unicode | 59 | :type password: unicode |
668 | 60 | :param is_superuser: Whether the new user is to be an administrator. | 60 | :param is_superuser: Whether the new user is to be an administrator. |
669 | 61 | :type is_superuser: bool ('0' for False, '1' for True) | 61 | :type is_superuser: bool ('0' for False, '1' for True) |
670 | 62 | |||
671 | 63 | Returns 400 if any mandatory parameters are missing. | ||
672 | 62 | """ | 64 | """ |
673 | 63 | username = get_mandatory_param(request.data, 'username') | 65 | username = get_mandatory_param(request.data, 'username') |
674 | 64 | email = get_mandatory_param(request.data, 'email') | 66 | email = get_mandatory_param(request.data, 'email') |
675 | 65 | 67 | ||
676 | === modified file 'src/maasserver/api/zones.py' | |||
677 | --- src/maasserver/api/zones.py 2014-08-16 14:54:27 +0000 | |||
678 | +++ src/maasserver/api/zones.py 2014-11-24 18:23:46 +0000 | |||
679 | @@ -48,12 +48,18 @@ | |||
680 | 48 | create = None | 48 | create = None |
681 | 49 | 49 | ||
682 | 50 | def read(self, request, name): | 50 | def read(self, request, name): |
684 | 51 | """GET request. Return zone.""" | 51 | """GET request. Return zone. |
685 | 52 | |||
686 | 53 | Returns 404 if the zone is not found. | ||
687 | 54 | """ | ||
688 | 52 | return get_object_or_404(Zone, name=name) | 55 | return get_object_or_404(Zone, name=name) |
689 | 53 | 56 | ||
690 | 54 | @admin_method | 57 | @admin_method |
691 | 55 | def update(self, request, name): | 58 | def update(self, request, name): |
693 | 56 | """PUT request. Update zone.""" | 59 | """PUT request. Update zone. |
694 | 60 | |||
695 | 61 | Returns 404 if the zone is not found. | ||
696 | 62 | """ | ||
697 | 57 | zone = get_object_or_404(Zone, name=name) | 63 | zone = get_object_or_404(Zone, name=name) |
698 | 58 | form = ZoneForm(instance=zone, data=request.data) | 64 | form = ZoneForm(instance=zone, data=request.data) |
699 | 59 | if not form.is_valid(): | 65 | if not form.is_valid(): |
700 | @@ -62,7 +68,11 @@ | |||
701 | 62 | 68 | ||
702 | 63 | @admin_method | 69 | @admin_method |
703 | 64 | def delete(self, request, name): | 70 | def delete(self, request, name): |
705 | 65 | """DELETE request. Delete zone.""" | 71 | """DELETE request. Delete zone. |
706 | 72 | |||
707 | 73 | Returns 404 if the zone is not found. | ||
708 | 74 | Returns 204 if the zone is successfully deleted. | ||
709 | 75 | """ | ||
710 | 66 | zone = get_one(Zone.objects.filter(name=name)) | 76 | zone = get_one(Zone.objects.filter(name=name)) |
711 | 67 | if zone is not None: | 77 | if zone is not None: |
712 | 68 | zone.delete() | 78 | zone.delete() |
Approve. See inline comments.