Merge lp:~chemikadze/nova/contrib-extention-networks into lp:~hudson-openstack/nova/trunk
- contrib-extention-networks
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~chemikadze/nova/contrib-extention-networks |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
398 lines (+378/-0) 3 files modified
nova/api/openstack/contrib/networks.py (+148/-0) nova/tests/api/openstack/contrib/test_networks.py (+229/-0) nova/tests/api/openstack/test_extensions.py (+1/-0) |
To merge this branch: | bzr merge lp:~chemikadze/nova/contrib-extention-networks |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brian Lamar (community) | Needs Fixing | ||
Thierry Carrez (community) | ffe | Abstain | |
Matt Dietz (community) | Needs Fixing | ||
Review via email: mp+72204@code.launchpad.net |
Commit message
Description of the change
This contrib extention adds some admin-level API into Openstack Nova.
In that extention implemented non-single-use methods of nova-manage-
Delete networks:
DELETE /admin/
DELETE /admin/
Disassociate networks:
DELETE /admin/
DELETE /admin/
List all networks:
GET /admin/networks/
Get specifiq network:
GET /admin/
GET /admin/
- 1456. By Nikolay Sokolov
-
Fixed log formatting and extention tests.
Nikolay Sokolov (chemikadze) wrote : | # |
I didn't expect that adding an extention may cause other test failures, I'll be more careful in future. Fixed that problems.
Brian Lamar (blamar) wrote : | # |
I haven't done a full review yet, but I was curious how/if this is currently work for you. It seems like you're setting up self.network_api, but never using it?
69 + self.network_api = network.API()
Does this not need to be used anywhere?
- 1457. By Nikolay Sokolov
-
Removed dead code
Nikolay Sokolov (chemikadze) wrote : | # |
Thanks again, Brian. Yes, you are right, I mistakely got this code from FloatingIPs extention, what I have used as pattern during porting my code from openstackx to nova. Now I understand extention mechanics in OS a little bit more, so that can be deleted.
- 1458. By Nikolay Sokolov
-
Consistent get_updated
Thierry Carrez (ttx) wrote : | # |
This should wait for Essex.
Matt Dietz (cerberus) wrote : | # |
I'm not really a fan of this being an admin route, exactly. Currently there are no other extensions that use the "admin" route or any standard spec controllers that do. I would actually suggest we simply remove "admin" from the route, because I think it could be confusing to the end user trying to discover functional
ity about the API. Specifically, they might wonder why other "admin" looking actions are routed under existing controllers, and what about "networks" makes it specifically admin related.
7 +# Copyright 2011 OpenStack LLC.
8 +# Copyright 2011 Grid Dynamics
9 +# Copyright 2011 Nikolay Sokolov
If you're adding a new file, you only need whichever copyright is most relevant to the work you're doing. I'm guessing this would primarily be Grid Dynamics in this case?
I'm concerned about the unit tests in this patch, too. While I endorse the idea that we don't use the database for unit tests, I'm afraid that, given your overriding every single database API method, if the networks tables were to change in any way, your tests would continue to pass. What do you think?
- 1459. By Nikolay Sokolov
-
Changed endpoint to os-networks
- 1460. By Nikolay Sokolov
-
Updated copyright
Nikolay Sokolov (chemikadze) wrote : | # |
Matt, thanks for your comments.
I checked admin api bluprints and now see that unlike openstackx, core nova extentions doesn't use special admin route. So I changed it to common-style 'os-networks'.
Copyrights were made like in floating ips extention, and I thought that if it is in trunk, then it is ok. I'm master in legal issues, but reviewing other sources that my patch is outstanding in that case too, and I fixed that.
But the last one is not clear to me: I agree with thought, that tests passing on mock underlaying code and broken with real code is not good (by the way, just today i've read Sandy's article about that). I tried to find sample of how to deal with that, but I didn't find it neither in extention tests, nor in core OS API tests. In that particular case, I think there is possible to write separate test case, just checking that "fake net" object fits to database. But in general, I'm confused that other tests doesn't fit that requirement. From the other side, isn't verifying backward compatability a problem of database tests?
Thierry Carrez (ttx) wrote : | # |
Essex is open !
Brian Lamar (blamar) wrote : | # |
Sorry for the delay in review,
66 + def _network_
I don't love the need for this function, but I suppose it's a design decision to allow for multiple types of network identifiers to be passed to show/delete/
122 + except exception.
123 + query_params = urlparse.
124 + cidrs = query_params.
125 + if cidrs:
126 + for cidr in cidrs:
127 + if id == 'disassociate':
128 + self.disassocia
129 + elif id == 'delete':
130 + self.delete(req, cidr)
131 + return exc.HTTPAccepted()
At first this exception handling was a bit confusing to me, then I realized it was because this method is handling multiple cases:
DELETE /v1.1/os-
DELETE /v1.1/os-
DELETE /v1.1/os-
You're using special identifiers 'delete' and 'disassociate' to allow for the deletion or disassociation of multiple networks at the same time. IMO this is something we should avoid and I'd rather have one way to do things instead of 2. Also there aren't any prior API calls that I'm aware of that do this (multiple deletions at the same time) so it would be more 'standard' to remove this feature and only allow deletions and disassociations by integer identifier. It's quite confusing to call DELETE on /v1.1/os-
447 + "NetworkAdmin",
Can you replace these tabs with spaces?
Nikolay Sokolov (chemikadze) wrote : | # |
When I wrote that extention, I was confused about API too: from one side, it is nice to have possibility to manipulate networks by CIDR, like using nova-manage, from another -- resulting API was conceptually unclear. "Feature creature" won at that time, but now
after serveral weeks passed I totally agree with you (especially after viewing OS API -- it seemed fairy nice and simple to me). What do you think about changing resulting IP to something like that:
Delete networks:
DELETE /os-networks/{id}
Disassociate networks:
DELETE /os-networks/
List all networks:
GET /os-networks/
Get specific network:
GET /os-networks/{id}
- 1461. By Nikolay Sokolov
-
Fixed issue with tabs
Brian Lamar (blamar) wrote : | # |
Not sure if you're in IRC, but I'm curious what:
DELETE /os-networks/
does still. It looks like it just calls DB's network_
POST /os-networks/
with a body of:
{
"disassociate": null
}
might be the most compliant. It seems like this lines up with our server "actions" (http://
That being said this is a pretty big switch from what you have so I'm open for talking more about the best approach! Other than that I think:
Delete networks:
DELETE /os-networks/{id}
List all networks:
GET /os-networks/
Get specific network:
GET /os-networks/{id}
Looks great.
- 1462. By Nikolay Sokolov
-
More openstack-style IP
- 1463. By Nikolay Sokolov
-
pep8 fix
Nikolay Sokolov (chemikadze) wrote : | # |
It is not hard to me make this extention more close to nova api. You can check it out just now.
Matt Dietz (cerberus) wrote : | # |
Thanks for the fixes, but noticed something else
In the test methods where you use the global keyword, you actually don't need it. Python is a little opaque on how it handles scoping, but in the methods where you don't actually reassign fake_nets, you don't need global.
See http://
300 + global fake_nets
301 + fake_nets = init_fake_nets()
You *would* need it here, because you're assigning it.
Nikolay Sokolov (chemikadze) wrote : | # |
Yes, I know. Wrote those extra lines just to emphasize with what these functions actually work. If you don't like such redurancy, I can fix but don't see real reason for that.
Matt Dietz (cerberus) wrote : | # |
I'd argue the code speaks for itself, and doesn't need global constructs
to highlight it. I'd prefer you fix it for brevity's sake.
On 9/22/11 1:21 AM, "Nikolay Sokolov" <email address hidden> wrote:
>Yes, I know. Wrote those extra lines just to emphasize with what these
>functions actually work. If you don't like such redurancy, I can fix but
>don't see real reason for that.
>--
>https:/
>rge/72204
>You are reviewing the proposed merge of
>lp:~chemikadze/nova/contrib-extention-networks into lp:nova.
This email may include confidential information. If you received it in error, please delete it.
Unmerged revisions
- 1463. By Nikolay Sokolov
-
pep8 fix
- 1462. By Nikolay Sokolov
-
More openstack-style IP
- 1461. By Nikolay Sokolov
-
Fixed issue with tabs
- 1460. By Nikolay Sokolov
-
Updated copyright
- 1459. By Nikolay Sokolov
-
Changed endpoint to os-networks
- 1458. By Nikolay Sokolov
-
Consistent get_updated
- 1457. By Nikolay Sokolov
-
Removed dead code
- 1456. By Nikolay Sokolov
-
Fixed log formatting and extention tests.
- 1455. By Nikolay Sokolov
-
pep8
- 1454. By Nikolay Sokolov
-
Deleting by CIDR
Preview Diff
1 | === added file 'nova/api/openstack/contrib/networks.py' | |||
2 | --- nova/api/openstack/contrib/networks.py 1970-01-01 00:00:00 +0000 | |||
3 | +++ nova/api/openstack/contrib/networks.py 2011-09-20 09:08:24 +0000 | |||
4 | @@ -0,0 +1,148 @@ | |||
5 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
6 | 2 | |||
7 | 3 | # Copyright 2011 Grid Dynamics | ||
8 | 4 | # All Rights Reserved. | ||
9 | 5 | # | ||
10 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
11 | 7 | # not use this file except in compliance with the License. You may obtain | ||
12 | 8 | # a copy of the License at | ||
13 | 9 | # | ||
14 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
15 | 11 | # | ||
16 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
17 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
18 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
19 | 15 | # License for the specific language governing permissions and limitations | ||
20 | 16 | # under the License. | ||
21 | 17 | |||
22 | 18 | import urlparse | ||
23 | 19 | |||
24 | 20 | from webob import exc | ||
25 | 21 | |||
26 | 22 | from nova import db | ||
27 | 23 | from nova import exception | ||
28 | 24 | from nova import flags | ||
29 | 25 | from nova import log as logging | ||
30 | 26 | |||
31 | 27 | from nova.api.openstack import extensions | ||
32 | 28 | from nova.api.openstack.contrib.admin_only import admin_only | ||
33 | 29 | |||
34 | 30 | FLAGS = flags.FLAGS | ||
35 | 31 | |||
36 | 32 | LOG = logging.getLogger('nova.api.contrib.networks') | ||
37 | 33 | |||
38 | 34 | |||
39 | 35 | def network_dict(network): | ||
40 | 36 | if network: | ||
41 | 37 | fields = ( | ||
42 | 38 | 'bridge', 'vpn_public_port', 'dhcp_start', 'bridge_interface', | ||
43 | 39 | 'updated_at', 'id', 'cidr_v6', 'deleted_at', | ||
44 | 40 | 'gateway', 'label', 'project_id', 'vpn_private_address', | ||
45 | 41 | 'deleted', | ||
46 | 42 | 'vlan', 'broadcast', 'netmask', 'injected', | ||
47 | 43 | 'cidr', 'vpn_public_address', 'multi_host', 'dns1', 'host', | ||
48 | 44 | 'gateway_v6', 'netmask_v6', 'created_at') | ||
49 | 45 | return dict((field, getattr(network, field)) for field in fields) | ||
50 | 46 | else: | ||
51 | 47 | return {} | ||
52 | 48 | |||
53 | 49 | |||
54 | 50 | def require_admin(f): | ||
55 | 51 | def wraps(self, req, *args, **kwargs): | ||
56 | 52 | if 'nova.context' in req.environ and\ | ||
57 | 53 | req.environ['nova.context'].is_admin: | ||
58 | 54 | return f(self, req, *args, **kwargs) | ||
59 | 55 | else: | ||
60 | 56 | raise exception.AdminRequired() | ||
61 | 57 | return wraps | ||
62 | 58 | |||
63 | 59 | |||
64 | 60 | class NetworkController(object): | ||
65 | 61 | |||
66 | 62 | @require_admin | ||
67 | 63 | def action(self, req, id, body): | ||
68 | 64 | actions = {'disassociate': self._disassociate} | ||
69 | 65 | for action, data in body.iteritems(): | ||
70 | 66 | try: | ||
71 | 67 | return actions[action](req, id, body) | ||
72 | 68 | except KeyError: | ||
73 | 69 | msg = _("Network does not have %s action") % action | ||
74 | 70 | raise exc.HTTPBadRequest(explanation=msg) | ||
75 | 71 | msg = _("Invalid request body") | ||
76 | 72 | raise exc.HTTPBadRequest(explanation=msg) | ||
77 | 73 | |||
78 | 74 | def _disassociate(self, req, id, body): | ||
79 | 75 | context = req.environ['nova.context'] | ||
80 | 76 | LOG.debug(_("Disassociating network with id %{query}s, " | ||
81 | 77 | "context %{context}s"), | ||
82 | 78 | {"query": id, "context": context}) | ||
83 | 79 | try: | ||
84 | 80 | net = db.network_get(context, int(id)) | ||
85 | 81 | except exception.NetworkNotFound: | ||
86 | 82 | raise exc.HTTPNotFound() | ||
87 | 83 | db.network_disassociate(context, net.id) | ||
88 | 84 | return {'disassociated': int(id)} | ||
89 | 85 | |||
90 | 86 | @require_admin | ||
91 | 87 | def index(self, req): | ||
92 | 88 | """Can filter projects """ | ||
93 | 89 | context = req.environ['nova.context'] | ||
94 | 90 | LOG.info(_("Getting networks with context %{ctxt}s"), | ||
95 | 91 | {"ctxt": context}) | ||
96 | 92 | networks = db.network_get_all(context) | ||
97 | 93 | result = [network_dict(net_ref) for net_ref in networks] | ||
98 | 94 | return {'networks': result} | ||
99 | 95 | |||
100 | 96 | @require_admin | ||
101 | 97 | def show(self, req, id): | ||
102 | 98 | context = req.environ['nova.context'] | ||
103 | 99 | LOG.info(_("Showing network with id %{query}s, context %{ctxt}s"), | ||
104 | 100 | {"query": id, "context": context}) | ||
105 | 101 | try: | ||
106 | 102 | net = db.network_get(context, int(id)) | ||
107 | 103 | except exception.NetworkNotFound: | ||
108 | 104 | raise exc.HTTPNotFound() | ||
109 | 105 | return {'network': network_dict(net)} | ||
110 | 106 | |||
111 | 107 | @require_admin | ||
112 | 108 | def delete(self, req, id): | ||
113 | 109 | context = req.environ['nova.context'] | ||
114 | 110 | LOG.audit(_("Deleting network with id %{query}s, context %{ctxt}s"), | ||
115 | 111 | {"query": context, "ctxt": context}) | ||
116 | 112 | try: | ||
117 | 113 | net = db.network_get(context, int(id)) | ||
118 | 114 | except exception.NetworkNotFound: | ||
119 | 115 | raise exc.HTTPNotFound() | ||
120 | 116 | db.network_delete_safe(context, net.id) | ||
121 | 117 | return exc.HTTPAccepted() | ||
122 | 118 | |||
123 | 119 | # TODO(nsokolov): implement full CRUD, not done right in nova too | ||
124 | 120 | |||
125 | 121 | |||
126 | 122 | class Networks(extensions.ExtensionDescriptor): | ||
127 | 123 | def __init__(self): | ||
128 | 124 | pass | ||
129 | 125 | |||
130 | 126 | def get_name(self): | ||
131 | 127 | return "NetworkAdmin" | ||
132 | 128 | |||
133 | 129 | def get_alias(self): | ||
134 | 130 | return "NETWORK" | ||
135 | 131 | |||
136 | 132 | def get_description(self): | ||
137 | 133 | return "The Network API Extension" | ||
138 | 134 | |||
139 | 135 | def get_namespace(self): | ||
140 | 136 | return "http://docs.openstack.org/ext/os-networks/api/v1.1" | ||
141 | 137 | |||
142 | 138 | def get_updated(self): | ||
143 | 139 | return "2011-08-23 07:44:50.888131" | ||
144 | 140 | |||
145 | 141 | @admin_only | ||
146 | 142 | def get_resources(self): | ||
147 | 143 | resources = [] | ||
148 | 144 | resources.append(extensions.ResourceExtension('os-networks', | ||
149 | 145 | NetworkController(), | ||
150 | 146 | member_actions={ | ||
151 | 147 | 'action': 'POST'})) | ||
152 | 148 | return resources | ||
153 | 0 | 149 | ||
154 | === added file 'nova/tests/api/openstack/contrib/test_networks.py' | |||
155 | --- nova/tests/api/openstack/contrib/test_networks.py 1970-01-01 00:00:00 +0000 | |||
156 | +++ nova/tests/api/openstack/contrib/test_networks.py 2011-09-20 09:08:24 +0000 | |||
157 | @@ -0,0 +1,229 @@ | |||
158 | 1 | # Copyright 2011 Grid Dynamics | ||
159 | 2 | # All Rights Reserved. | ||
160 | 3 | # | ||
161 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
162 | 5 | # not use this file except in compliance with the License. You may obtain | ||
163 | 6 | # a copy of the License at | ||
164 | 7 | # | ||
165 | 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
166 | 9 | # | ||
167 | 10 | # Unless required by applicable law or agreed to in writing, software | ||
168 | 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
169 | 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
170 | 13 | # License for the specific language governing permissions and limitations | ||
171 | 14 | # under the License. | ||
172 | 15 | |||
173 | 16 | import json | ||
174 | 17 | from urllib import urlencode | ||
175 | 18 | import webob | ||
176 | 19 | |||
177 | 20 | from nova import context | ||
178 | 21 | from nova import db | ||
179 | 22 | from nova.db.sqlalchemy.api import require_admin_context, require_context | ||
180 | 23 | from nova import exception | ||
181 | 24 | from nova import test | ||
182 | 25 | from nova.tests.api.openstack import fakes | ||
183 | 26 | |||
184 | 27 | from nova.flags import FLAGS | ||
185 | 28 | |||
186 | 29 | from nova.api.openstack.contrib.networks import NetworkController | ||
187 | 30 | |||
188 | 31 | |||
189 | 32 | class fake_ref(dict): | ||
190 | 33 | def __getattr__(self, item): | ||
191 | 34 | return self[item] | ||
192 | 35 | |||
193 | 36 | |||
194 | 37 | def init_fake_nets(): | ||
195 | 38 | return [fake_ref({'bridge': 'br100', 'vpn_public_port': 1000, | ||
196 | 39 | 'dhcp_start': '10.0.0.3', 'bridge_interface': 'eth0', | ||
197 | 40 | 'updated_at': '2011-08-16 09:26:13.048257', 'id': 1, | ||
198 | 41 | 'cidr_v6': None, 'deleted_at': None, | ||
199 | 42 | 'gateway': '10.0.0.1', 'label': 'mynet_0', | ||
200 | 43 | 'project_id': 'admin', | ||
201 | 44 | 'vpn_private_address': '10.0.0.2', 'deleted': False, | ||
202 | 45 | 'vlan': 100, 'broadcast': '10.0.0.7', | ||
203 | 46 | 'netmask': '255.255.255.248', 'injected': False, | ||
204 | 47 | 'cidr': '10.0.0.0/29', | ||
205 | 48 | 'vpn_public_address': '127.0.0.1', 'multi_host': False, | ||
206 | 49 | 'dns1': None, 'host': 'nsokolov-desktop', | ||
207 | 50 | 'gateway_v6': None, 'netmask_v6': None, | ||
208 | 51 | 'created_at': '2011-08-15 06:19:19.387525'}), | ||
209 | 52 | fake_ref({'bridge': 'br101', 'vpn_public_port': 1001, | ||
210 | 53 | 'dhcp_start': '10.0.0.11', 'bridge_interface': 'eth0', | ||
211 | 54 | 'updated_at': None, 'id': 2, 'cidr_v6': None, | ||
212 | 55 | 'deleted_at': None, 'gateway': '10.0.0.9', | ||
213 | 56 | 'label': 'mynet_1', 'project_id': None, | ||
214 | 57 | 'vpn_private_address': '10.0.0.10', 'deleted': False, | ||
215 | 58 | 'vlan': 101, 'broadcast': '10.0.0.15', | ||
216 | 59 | 'netmask': '255.255.255.248', 'injected': False, | ||
217 | 60 | 'cidr': '10.0.0.10/29', 'vpn_public_address': None, | ||
218 | 61 | 'multi_host': False, 'dns1': None, 'host': None, | ||
219 | 62 | 'gateway_v6': None, 'netmask_v6': None, | ||
220 | 63 | 'created_at': '2011-08-15 06:19:19.885495'})] | ||
221 | 64 | |||
222 | 65 | fake_nets = init_fake_nets() | ||
223 | 66 | |||
224 | 67 | |||
225 | 68 | @require_admin_context | ||
226 | 69 | def db_network_disassociate(context, id): | ||
227 | 70 | global fake_nets | ||
228 | 71 | for net in fake_nets: | ||
229 | 72 | if net['id'] == id: | ||
230 | 73 | net['project_id'] = None | ||
231 | 74 | return net | ||
232 | 75 | raise exception.NetworkNotFound() | ||
233 | 76 | |||
234 | 77 | |||
235 | 78 | @require_admin_context | ||
236 | 79 | def db_network_get_all(context): | ||
237 | 80 | global fake_nets | ||
238 | 81 | return fake_nets | ||
239 | 82 | |||
240 | 83 | |||
241 | 84 | @require_context | ||
242 | 85 | def db_project_get_networks(context, tenant_id, associate=False): | ||
243 | 86 | global fake_nets | ||
244 | 87 | tenant_nets = [] | ||
245 | 88 | for net in fake_nets: | ||
246 | 89 | if net['project_id'] == tenant_id: | ||
247 | 90 | tenant_nets.append(net) | ||
248 | 91 | return tenant_nets | ||
249 | 92 | |||
250 | 93 | |||
251 | 94 | @require_context | ||
252 | 95 | def db_network_get(context, network_id): | ||
253 | 96 | global fake_nets | ||
254 | 97 | for net in fake_nets: | ||
255 | 98 | if net['id'] == network_id: | ||
256 | 99 | return net | ||
257 | 100 | raise exception.NetworkNotFound() | ||
258 | 101 | |||
259 | 102 | |||
260 | 103 | @require_context | ||
261 | 104 | def db_network_get_by_cidr(context, cidr): | ||
262 | 105 | global fake_nets | ||
263 | 106 | for net in fake_nets: | ||
264 | 107 | if net['cidr'] == cidr: | ||
265 | 108 | return net | ||
266 | 109 | raise exception.NetworkNotFound() | ||
267 | 110 | |||
268 | 111 | |||
269 | 112 | @require_admin_context | ||
270 | 113 | def db_network_delete(context, network_id): | ||
271 | 114 | global fake_nets | ||
272 | 115 | fake_nets.remove(db.network_get(context, network_id)) | ||
273 | 116 | |||
274 | 117 | |||
275 | 118 | class NetworkExtentionTest(test.TestCase): | ||
276 | 119 | def setUp(self): | ||
277 | 120 | super(NetworkExtentionTest, self).setUp() | ||
278 | 121 | FLAGS.allow_admin_api = True | ||
279 | 122 | self.controller = NetworkController() | ||
280 | 123 | fakes.stub_out_networking(self.stubs) | ||
281 | 124 | fakes.stub_out_rate_limiting(self.stubs) | ||
282 | 125 | self.stubs.Set(db, "network_disassociate", | ||
283 | 126 | db_network_disassociate) | ||
284 | 127 | self.stubs.Set(db, "network_get_all", | ||
285 | 128 | db_network_get_all) | ||
286 | 129 | self.stubs.Set(db, "project_get_networks", | ||
287 | 130 | db_project_get_networks) | ||
288 | 131 | self.stubs.Set(db, "network_get", | ||
289 | 132 | db_network_get) | ||
290 | 133 | self.stubs.Set(db, "network_get_by_cidr", | ||
291 | 134 | db_network_get_by_cidr) | ||
292 | 135 | self.stubs.Set(db, "network_delete_safe", | ||
293 | 136 | db_network_delete) | ||
294 | 137 | self.user = 'user' | ||
295 | 138 | self.project = 'project' | ||
296 | 139 | self.user_context = context.RequestContext(self.user, self.project, | ||
297 | 140 | is_admin=False) | ||
298 | 141 | self.admin_context = context.RequestContext(self.user, self.project, | ||
299 | 142 | is_admin=True) | ||
300 | 143 | global fake_nets | ||
301 | 144 | fake_nets = init_fake_nets() | ||
302 | 145 | |||
303 | 146 | def test_network_list_all(self): | ||
304 | 147 | req = webob.Request.blank('/v1.1/os-networks') | ||
305 | 148 | req.method = 'GET' | ||
306 | 149 | req.headers['Content-Type'] = 'application/json' | ||
307 | 150 | |||
308 | 151 | res = req.get_response(fakes.wsgi_app( | ||
309 | 152 | fake_auth_context=self.admin_context)) | ||
310 | 153 | self.assertEqual(res.status_int, 200) | ||
311 | 154 | res_dict = json.loads(res.body) | ||
312 | 155 | self.assertEquals(res_dict, {'networks': fake_nets}) | ||
313 | 156 | |||
314 | 157 | req = webob.Request.blank('/v1.1/os-networks') | ||
315 | 158 | req.method = 'GET' | ||
316 | 159 | req.headers['Content-Type'] = 'application/json' | ||
317 | 160 | |||
318 | 161 | with self.assertRaises(exception.AdminRequired): | ||
319 | 162 | res = req.get_response(fakes.wsgi_app( | ||
320 | 163 | fake_auth_context=self.user_context)) | ||
321 | 164 | self.assertEqual(res.status_int, 500) # admin required | ||
322 | 165 | res_dict = json.loads(res.body) | ||
323 | 166 | |||
324 | 167 | right_ans = {'networks': db_network_get_all(self.user_context)} | ||
325 | 168 | |||
326 | 169 | def test_network_disassociate(self): | ||
327 | 170 | req = webob.Request.blank('/v1.1/os-networks/1/action') | ||
328 | 171 | req.method = 'POST' | ||
329 | 172 | req.body = json.dumps({'disassociate': None}) | ||
330 | 173 | req.headers['Content-Type'] = 'application/json' | ||
331 | 174 | |||
332 | 175 | res = req.get_response(fakes.wsgi_app( | ||
333 | 176 | fake_auth_context=self.admin_context)) | ||
334 | 177 | self.assertEqual(res.status_int, 200) | ||
335 | 178 | self.assertEqual(json.loads(res.body), {'disassociated': 1}) | ||
336 | 179 | self.assertEqual(db.network_get(self.admin_context, 1)['project_id'], | ||
337 | 180 | None) | ||
338 | 181 | |||
339 | 182 | req = webob.Request.blank( | ||
340 | 183 | '/v1.1/os-networks/12345/action') # not present | ||
341 | 184 | req.method = 'POST' | ||
342 | 185 | req.body = json.dumps({'disassociate': None}) | ||
343 | 186 | req.headers['Content-Type'] = 'application/json' | ||
344 | 187 | |||
345 | 188 | res = req.get_response(fakes.wsgi_app( | ||
346 | 189 | fake_auth_context=self.admin_context)) | ||
347 | 190 | self.assertEqual(res.status_int, 404) | ||
348 | 191 | |||
349 | 192 | def test_network_get(self): | ||
350 | 193 | req = webob.Request.blank('/v1.1/os-networks/1') | ||
351 | 194 | req.method = 'GET' | ||
352 | 195 | req.headers['Content-Type'] = 'application/json' | ||
353 | 196 | res = req.get_response(fakes.wsgi_app( | ||
354 | 197 | fake_auth_context=self.admin_context)) | ||
355 | 198 | self.assertEqual(res.status_int, 200) | ||
356 | 199 | res_dict = json.loads(res.body) | ||
357 | 200 | waited = {'network': db_network_get(self.admin_context, 1)} | ||
358 | 201 | self.assertEquals(res_dict, waited) | ||
359 | 202 | |||
360 | 203 | req = webob.Request.blank('/v1.1/os-networks/1') | ||
361 | 204 | req.headers['Content-Type'] = 'application/json' | ||
362 | 205 | res = req.get_response(fakes.wsgi_app( | ||
363 | 206 | fake_auth_context=self.user_context)) | ||
364 | 207 | self.assertEqual(res.status_int, 500) | ||
365 | 208 | |||
366 | 209 | def test_network_delete(self): | ||
367 | 210 | req = webob.Request.blank('/v1.1/os-networks/1') | ||
368 | 211 | req.method = 'DELETE' | ||
369 | 212 | req.headers['Content-Type'] = 'application/json' | ||
370 | 213 | res = req.get_response(fakes.wsgi_app( | ||
371 | 214 | fake_auth_context=self.admin_context)) | ||
372 | 215 | self.assertEqual(res.status_int, 202) | ||
373 | 216 | # check it was really deleted | ||
374 | 217 | req = webob.Request.blank('/v1.1/os-networks/1') | ||
375 | 218 | req.method = 'GET' | ||
376 | 219 | req.headers['Content-Type'] = 'application/json' | ||
377 | 220 | res = req.get_response(fakes.wsgi_app( | ||
378 | 221 | fake_auth_context=self.admin_context)) | ||
379 | 222 | self.assertEqual(res.status_int, 404) | ||
380 | 223 | |||
381 | 224 | req = webob.Request.blank('/v1.1/os-networks/12345') # not present | ||
382 | 225 | req.method = 'DELETE' | ||
383 | 226 | req.headers['Content-Type'] = 'application/json' | ||
384 | 227 | res = req.get_response(fakes.wsgi_app( | ||
385 | 228 | fake_auth_context=self.admin_context)) | ||
386 | 229 | self.assertEqual(res.status_int, 404) | ||
387 | 0 | 230 | ||
388 | === modified file 'nova/tests/api/openstack/test_extensions.py' | |||
389 | --- nova/tests/api/openstack/test_extensions.py 2011-09-14 15:54:56 +0000 | |||
390 | +++ nova/tests/api/openstack/test_extensions.py 2011-09-20 09:08:24 +0000 | |||
391 | @@ -93,6 +93,7 @@ | |||
392 | 93 | "Hosts", | 93 | "Hosts", |
393 | 94 | "Keypairs", | 94 | "Keypairs", |
394 | 95 | "Multinic", | 95 | "Multinic", |
395 | 96 | "NetworkAdmin", | ||
396 | 96 | "Quotas", | 97 | "Quotas", |
397 | 97 | "Rescue", | 98 | "Rescue", |
398 | 98 | "SecurityGroups", | 99 | "SecurityGroups", |
I got errors when I run unittest. paste.openstack .org/show/ 2228/
Plz check it
http://