Merge lp:~rackspace-titan/nova/server-addresses-lp761652 into lp:~hudson-openstack/nova/trunk
- server-addresses-lp761652
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Dan Prince | ||||
Approved revision: | 1251 | ||||
Merged at revision: | 1277 | ||||
Proposed branch: | lp:~rackspace-titan/nova/server-addresses-lp761652 | ||||
Merge into: | lp:~hudson-openstack/nova/trunk | ||||
Diff against target: |
648 lines (+307/-71) 7 files modified
nova/api/openstack/__init__.py (+4/-8) nova/api/openstack/ips.py (+68/-19) nova/api/openstack/servers.py (+13/-13) nova/api/openstack/views/addresses.py (+30/-9) nova/api/openstack/views/servers.py (+12/-1) nova/db/sqlalchemy/api.py (+16/-8) nova/tests/api/openstack/test_servers.py (+164/-13) |
||||
To merge this branch: | bzr merge lp:~rackspace-titan/nova/server-addresses-lp761652 | ||||
Related bugs: |
|
||||
Related blueprints: |
Openstack API 1.1 Finalization
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dan Prince (community) | Approve | ||
Matt Dietz (community) | Approve | ||
Review via email: mp+66830@code.launchpad.net |
Commit message
Description of the change
- Present ip addresses in their actual networks, not just a static public/private
- Floating ip addresses are grouped into the networks with their associated fixed ips
- Add addresses attribute to server entities
Brian Waldon (bcwaldon) wrote : | # |
Matt Dietz (cerberus) wrote : | # |
366 +def require_
I'm a little bit concerned about this decorator, not because it isn'ta good idea (it is) but because I worry that people will get lazy with
enforcing an instance actually exists. I realize that it merged in a previous patch,
but regardless, I personally think the responsibility
of it should fall to the code interested in performing something with an instance,
not to the database to enforce.
Beyond that, it seems like it would introduce a bit of unnecessary overhead when someone
is being a good Nova citizen?
I realize you're only using it in a few places currently, but it would only be a matter of time
before it was used everywhere.
What do you think?
At the very least, wouldn't you want to set the name of the wrapper method ala:
399 - new_func.__name__ = func.__name__
With all that said, I won't hold up the review. I'm just genuinely curious
Brian Waldon (bcwaldon) wrote : | # |
> 366 +def require_
>
> I'm a little bit concerned about this decorator, not because it isn'ta good
> idea (it is) but because I worry that people will get lazy with
> enforcing an instance actually exists. I realize that it merged in a previous
> patch,
> but regardless, I personally think the responsibility
> of it should fall to the code interested in performing something with an
> instance,
> not to the database to enforce.
>
> Beyond that, it seems like it would introduce a bit of unnecessary overhead
> when someone
> is being a good Nova citizen?
I can definitely agree that it could duplicate the existence check in some cases. However, I feel whomever is adding code to Nova should first see what is available to them. I originally wrote this decorator because I was duplicating code all over the place in the OSAPI. I felt this was a natural place to do this check, but if you think there is a better solution, I would love to hear your thoughts.
> At the very least, wouldn't you want to set the name of the wrapper method
> ala:
>
> 399 - new_func.__name__ = func.__name__
I removed that because none of the other decorators had it, but I think you're right, it should be added back.
Dan Prince (dan-prince) wrote : | # |
I would side with Matt on this in that the require_
The require_
In any case I'm not sure nits on this issue belong in this merge prop. Just wanted to chime in on the conversation since we are having it.
Matt Dietz (cerberus) wrote : | # |
"In any case I'm not sure nits on this issue belong in this merge prop. Just wanted to chime in on the conversation since we are having it."
You're right. It's not a good enough reason to hold this patch up, but I would like to consider alternatives in the future.
Preview Diff
1 | === modified file 'nova/api/openstack/__init__.py' |
2 | --- nova/api/openstack/__init__.py 2011-06-24 14:37:43 +0000 |
3 | +++ nova/api/openstack/__init__.py 2011-07-14 18:50:41 +0000 |
4 | @@ -125,6 +125,10 @@ |
5 | collection={'detail': 'GET'}, |
6 | member=self.server_members) |
7 | |
8 | + mapper.resource("ip", "ips", controller=ips.create_resource(version), |
9 | + parent_resource=dict(member_name='server', |
10 | + collection_name='servers')) |
11 | + |
12 | mapper.resource("image", "images", |
13 | controller=images.create_resource(version), |
14 | collection={'detail': 'GET'}) |
15 | @@ -144,9 +148,6 @@ |
16 | |
17 | def _setup_routes(self, mapper): |
18 | super(APIRouterV10, self)._setup_routes(mapper, '1.0') |
19 | - mapper.resource("image", "images", |
20 | - controller=images.create_resource('1.0'), |
21 | - collection={'detail': 'GET'}) |
22 | |
23 | mapper.resource("shared_ip_group", "shared_ip_groups", |
24 | collection={'detail': 'GET'}, |
25 | @@ -157,11 +158,6 @@ |
26 | parent_resource=dict(member_name='server', |
27 | collection_name='servers')) |
28 | |
29 | - mapper.resource("ip", "ips", controller=ips.create_resource(), |
30 | - collection=dict(public='GET', private='GET'), |
31 | - parent_resource=dict(member_name='server', |
32 | - collection_name='servers')) |
33 | - |
34 | |
35 | class APIRouterV11(APIRouter): |
36 | """Define routes specific to OpenStack API V1.1.""" |
37 | |
38 | === modified file 'nova/api/openstack/ips.py' |
39 | --- nova/api/openstack/ips.py 2011-07-06 20:28:10 +0000 |
40 | +++ nova/api/openstack/ips.py 2011-07-14 18:50:41 +0000 |
41 | @@ -23,6 +23,7 @@ |
42 | from nova.api.openstack import faults |
43 | import nova.api.openstack.views.addresses |
44 | from nova.api.openstack import wsgi |
45 | +from nova import db |
46 | |
47 | |
48 | class Controller(object): |
49 | @@ -30,7 +31,6 @@ |
50 | |
51 | def __init__(self): |
52 | self.compute_api = nova.compute.API() |
53 | - self.builder = nova.api.openstack.views.addresses.ViewBuilderV10() |
54 | |
55 | def _get_instance(self, req, server_id): |
56 | try: |
57 | @@ -40,21 +40,6 @@ |
58 | return faults.Fault(exc.HTTPNotFound()) |
59 | return instance |
60 | |
61 | - def index(self, req, server_id): |
62 | - instance = self._get_instance(req, server_id) |
63 | - return {'addresses': self.builder.build(instance)} |
64 | - |
65 | - def public(self, req, server_id): |
66 | - instance = self._get_instance(req, server_id) |
67 | - return {'public': self.builder.build_public_parts(instance)} |
68 | - |
69 | - def private(self, req, server_id): |
70 | - instance = self._get_instance(req, server_id) |
71 | - return {'private': self.builder.build_private_parts(instance)} |
72 | - |
73 | - def show(self, req, server_id, id): |
74 | - return faults.Fault(exc.HTTPNotImplemented()) |
75 | - |
76 | def create(self, req, server_id, body): |
77 | return faults.Fault(exc.HTTPNotImplemented()) |
78 | |
79 | @@ -62,7 +47,71 @@ |
80 | return faults.Fault(exc.HTTPNotImplemented()) |
81 | |
82 | |
83 | -def create_resource(): |
84 | +class ControllerV10(Controller): |
85 | + |
86 | + def index(self, req, server_id): |
87 | + instance = self._get_instance(req, server_id) |
88 | + builder = nova.api.openstack.views.addresses.ViewBuilderV10() |
89 | + return {'addresses': builder.build(instance)} |
90 | + |
91 | + def show(self, req, server_id, id): |
92 | + instance = self._get_instance(req, server_id) |
93 | + builder = self._get_view_builder(req) |
94 | + if id == 'private': |
95 | + view = builder.build_private_parts(instance) |
96 | + elif id == 'public': |
97 | + view = builder.build_public_parts(instance) |
98 | + else: |
99 | + msg = _("Only private and public networks available") |
100 | + return faults.Fault(exc.HTTPNotFound(explanation=msg)) |
101 | + |
102 | + return {id: view} |
103 | + |
104 | + def _get_view_builder(self, req): |
105 | + return nova.api.openstack.views.addresses.ViewBuilderV10() |
106 | + |
107 | + |
108 | +class ControllerV11(Controller): |
109 | + |
110 | + def index(self, req, server_id): |
111 | + context = req.environ['nova.context'] |
112 | + interfaces = self._get_virtual_interfaces(context, server_id) |
113 | + networks = self._get_view_builder(req).build(interfaces) |
114 | + return {'addresses': networks} |
115 | + |
116 | + def show(self, req, server_id, id): |
117 | + context = req.environ['nova.context'] |
118 | + interfaces = self._get_virtual_interfaces(context, server_id) |
119 | + network = self._get_view_builder(req).build_network(interfaces, id) |
120 | + |
121 | + if network is None: |
122 | + msg = _("Instance is not a member of specified network") |
123 | + return faults.Fault(exc.HTTPNotFound(explanation=msg)) |
124 | + |
125 | + return network |
126 | + |
127 | + def _get_virtual_interfaces(self, context, server_id): |
128 | + try: |
129 | + return db.api.virtual_interface_get_by_instance(context, server_id) |
130 | + except nova.exception.InstanceNotFound: |
131 | + msg = _("Instance does not exist") |
132 | + raise exc.HTTPNotFound(explanation=msg) |
133 | + |
134 | + def _get_view_builder(self, req): |
135 | + return nova.api.openstack.views.addresses.ViewBuilderV11() |
136 | + |
137 | + |
138 | +def create_resource(version): |
139 | + controller = { |
140 | + '1.0': ControllerV10, |
141 | + '1.1': ControllerV11, |
142 | + }[version]() |
143 | + |
144 | + xmlns = { |
145 | + '1.0': wsgi.XMLNS_V10, |
146 | + '1.1': wsgi.XMLNS_V11, |
147 | + }[version] |
148 | + |
149 | metadata = { |
150 | 'list_collections': { |
151 | 'public': {'item_name': 'ip', 'item_key': 'addr'}, |
152 | @@ -72,8 +121,8 @@ |
153 | |
154 | body_serializers = { |
155 | 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, |
156 | - xmlns=wsgi.XMLNS_V10), |
157 | + xmlns=xmlns), |
158 | } |
159 | serializer = wsgi.ResponseSerializer(body_serializers) |
160 | |
161 | - return wsgi.Resource(Controller(), serializer=serializer) |
162 | + return wsgi.Resource(controller, serializer=serializer) |
163 | |
164 | === modified file 'nova/api/openstack/servers.py' |
165 | --- nova/api/openstack/servers.py 2011-07-12 16:13:02 +0000 |
166 | +++ nova/api/openstack/servers.py 2011-07-14 18:50:41 +0000 |
167 | @@ -19,6 +19,7 @@ |
168 | from webob import exc |
169 | |
170 | from nova import compute |
171 | +from nova import db |
172 | from nova import exception |
173 | from nova import flags |
174 | from nova import log as logging |
175 | @@ -62,7 +63,7 @@ |
176 | return exc.HTTPBadRequest(explanation=str(err)) |
177 | return servers |
178 | |
179 | - def _get_view_builder(self, req): |
180 | + def _build_view(self, req, instance, is_detail=False): |
181 | raise NotImplementedError() |
182 | |
183 | def _limit_items(self, items, req): |
184 | @@ -88,8 +89,7 @@ |
185 | fixed_ip=fixed_ip, |
186 | recurse_zones=recurse_zones) |
187 | limited_list = self._limit_items(instance_list, req) |
188 | - builder = self._get_view_builder(req) |
189 | - servers = [builder.build(inst, is_detail)['server'] |
190 | + servers = [self._build_view(req, inst, is_detail)['server'] |
191 | for inst in limited_list] |
192 | return dict(servers=servers) |
193 | |
194 | @@ -99,8 +99,7 @@ |
195 | try: |
196 | instance = self.compute_api.routing_get( |
197 | req.environ['nova.context'], id) |
198 | - builder = self._get_view_builder(req) |
199 | - return builder.build(instance, is_detail=True) |
200 | + return self._build_view(req, instance, is_detail=True) |
201 | except exception.NotFound: |
202 | return faults.Fault(exc.HTTPNotFound()) |
203 | |
204 | @@ -121,8 +120,7 @@ |
205 | for key in ['instance_type', 'image_ref']: |
206 | inst[key] = extra_values[key] |
207 | |
208 | - builder = self._get_view_builder(req) |
209 | - server = builder.build(inst, is_detail=True) |
210 | + server = self._build_view(req, inst, is_detail=True) |
211 | server['server']['adminPass'] = extra_values['password'] |
212 | return server |
213 | |
214 | @@ -426,10 +424,10 @@ |
215 | def _flavor_id_from_req_data(self, data): |
216 | return data['server']['flavorId'] |
217 | |
218 | - def _get_view_builder(self, req): |
219 | - addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV10() |
220 | - return nova.api.openstack.views.servers.ViewBuilderV10( |
221 | - addresses_builder) |
222 | + def _build_view(self, req, instance, is_detail=False): |
223 | + addresses = nova.api.openstack.views.addresses.ViewBuilderV10() |
224 | + builder = nova.api.openstack.views.servers.ViewBuilderV10(addresses) |
225 | + return builder.build(instance, is_detail=is_detail) |
226 | |
227 | def _limit_items(self, items, req): |
228 | return common.limited(items, req) |
229 | @@ -498,16 +496,18 @@ |
230 | href = data['server']['flavorRef'] |
231 | return common.get_id_from_href(href) |
232 | |
233 | - def _get_view_builder(self, req): |
234 | + def _build_view(self, req, instance, is_detail=False): |
235 | base_url = req.application_url |
236 | flavor_builder = nova.api.openstack.views.flavors.ViewBuilderV11( |
237 | base_url) |
238 | image_builder = nova.api.openstack.views.images.ViewBuilderV11( |
239 | base_url) |
240 | addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV11() |
241 | - return nova.api.openstack.views.servers.ViewBuilderV11( |
242 | + builder = nova.api.openstack.views.servers.ViewBuilderV11( |
243 | addresses_builder, flavor_builder, image_builder, base_url) |
244 | |
245 | + return builder.build(instance, is_detail=is_detail) |
246 | + |
247 | def _action_change_password(self, input_dict, req, id): |
248 | context = req.environ['nova.context'] |
249 | if (not 'changePassword' in input_dict |
250 | |
251 | === modified file 'nova/api/openstack/views/addresses.py' |
252 | --- nova/api/openstack/views/addresses.py 2011-06-30 19:42:51 +0000 |
253 | +++ nova/api/openstack/views/addresses.py 2011-07-14 18:50:41 +0000 |
254 | @@ -20,13 +20,14 @@ |
255 | |
256 | |
257 | class ViewBuilder(object): |
258 | - ''' Models a server addresses response as a python dictionary.''' |
259 | + """Models a server addresses response as a python dictionary.""" |
260 | |
261 | def build(self, inst): |
262 | raise NotImplementedError() |
263 | |
264 | |
265 | class ViewBuilderV10(ViewBuilder): |
266 | + |
267 | def build(self, inst): |
268 | private_ips = self.build_private_parts(inst) |
269 | public_ips = self.build_public_parts(inst) |
270 | @@ -40,11 +41,31 @@ |
271 | |
272 | |
273 | class ViewBuilderV11(ViewBuilder): |
274 | - def build(self, inst): |
275 | - # TODO(tr3buchet) - this shouldn't be hard coded to 4... |
276 | - private_ips = utils.get_from_path(inst, 'fixed_ips/address') |
277 | - private_ips = [dict(version=4, addr=a) for a in private_ips] |
278 | - public_ips = utils.get_from_path(inst, |
279 | - 'fixed_ips/floating_ips/address') |
280 | - public_ips = [dict(version=4, addr=a) for a in public_ips] |
281 | - return dict(public=public_ips, private=private_ips) |
282 | + |
283 | + def build(self, interfaces): |
284 | + networks = {} |
285 | + for interface in interfaces: |
286 | + network_label = interface['network']['label'] |
287 | + |
288 | + if network_label not in networks: |
289 | + networks[network_label] = [] |
290 | + |
291 | + networks[network_label].extend(self._extract_ipv4(interface)) |
292 | + |
293 | + return networks |
294 | + |
295 | + def build_network(self, interfaces, network_label): |
296 | + for interface in interfaces: |
297 | + if interface['network']['label'] == network_label: |
298 | + ips = self._extract_ipv4(interface) |
299 | + return {network_label: list(ips)} |
300 | + return None |
301 | + |
302 | + def _extract_ipv4(self, interface): |
303 | + for fixed_ip in interface['fixed_ips']: |
304 | + yield self._build_ip_entity(fixed_ip['address'], 4) |
305 | + for floating_ip in fixed_ip.get('floating_ips', []): |
306 | + yield self._build_ip_entity(floating_ip['address'], 4) |
307 | + |
308 | + def _build_ip_entity(self, address, version): |
309 | + return {'addr': address, 'version': version} |
310 | |
311 | === modified file 'nova/api/openstack/views/servers.py' |
312 | --- nova/api/openstack/views/servers.py 2011-07-07 13:42:13 +0000 |
313 | +++ nova/api/openstack/views/servers.py 2011-07-14 18:50:41 +0000 |
314 | @@ -77,7 +77,6 @@ |
315 | inst_dict = { |
316 | 'id': inst['id'], |
317 | 'name': inst['display_name'], |
318 | - 'addresses': self.addresses_builder.build(inst), |
319 | 'status': power_mapping[inst.get('state')]} |
320 | |
321 | ctxt = nova.context.get_admin_context() |
322 | @@ -98,10 +97,15 @@ |
323 | |
324 | self._build_image(inst_dict, inst) |
325 | self._build_flavor(inst_dict, inst) |
326 | + self._build_addresses(inst_dict, inst) |
327 | |
328 | inst_dict['uuid'] = inst['uuid'] |
329 | return dict(server=inst_dict) |
330 | |
331 | + def _build_addresses(self, response, inst): |
332 | + """Return the addresses sub-resource of a server.""" |
333 | + raise NotImplementedError() |
334 | + |
335 | def _build_image(self, response, inst): |
336 | """Return the image sub-resource of a server.""" |
337 | raise NotImplementedError() |
338 | @@ -128,6 +132,9 @@ |
339 | if 'instance_type' in dict(inst): |
340 | response['flavorId'] = inst['instance_type']['flavorid'] |
341 | |
342 | + def _build_addresses(self, response, inst): |
343 | + response['addresses'] = self.addresses_builder.build(inst) |
344 | + |
345 | |
346 | class ViewBuilderV11(ViewBuilder): |
347 | """Model an Openstack API V1.0 server response.""" |
348 | @@ -151,6 +158,10 @@ |
349 | flavor_ref = self.flavor_builder.generate_href(flavor_id) |
350 | response["flavorRef"] = flavor_ref |
351 | |
352 | + def _build_addresses(self, response, inst): |
353 | + interfaces = inst.get('virtual_interfaces', []) |
354 | + response['addresses'] = self.addresses_builder.build(interfaces) |
355 | + |
356 | def _build_extra(self, response, inst): |
357 | self._build_links(response, inst) |
358 | |
359 | |
360 | === modified file 'nova/db/sqlalchemy/api.py' |
361 | --- nova/db/sqlalchemy/api.py 2011-07-01 15:07:08 +0000 |
362 | +++ nova/db/sqlalchemy/api.py 2011-07-14 18:50:41 +0000 |
363 | @@ -118,8 +118,23 @@ |
364 | return wrapper |
365 | |
366 | |
367 | +def require_instance_exists(f): |
368 | + """Decorator to require the specified instance to exist. |
369 | + |
370 | + Requres the wrapped function to use context and instance_id as |
371 | + their first two arguments. |
372 | + """ |
373 | + |
374 | + def wrapper(context, instance_id, *args, **kwargs): |
375 | + db.api.instance_get(context, instance_id) |
376 | + return f(context, instance_id, *args, **kwargs) |
377 | + wrapper.__name__ = f.__name__ |
378 | + return wrapper |
379 | + |
380 | + |
381 | ################### |
382 | |
383 | + |
384 | @require_admin_context |
385 | def service_destroy(context, service_id): |
386 | session = get_session() |
387 | @@ -921,6 +936,7 @@ |
388 | |
389 | |
390 | @require_context |
391 | +@require_instance_exists |
392 | def virtual_interface_get_by_instance(context, instance_id): |
393 | """Gets all virtual interfaces for instance. |
394 | |
395 | @@ -3071,14 +3087,6 @@ |
396 | #################### |
397 | |
398 | |
399 | -def require_instance_exists(func): |
400 | - def new_func(context, instance_id, *args, **kwargs): |
401 | - db.api.instance_get(context, instance_id) |
402 | - return func(context, instance_id, *args, **kwargs) |
403 | - new_func.__name__ = func.__name__ |
404 | - return new_func |
405 | - |
406 | - |
407 | @require_context |
408 | @require_instance_exists |
409 | def instance_metadata_get(context, instance_id): |
410 | |
411 | === modified file 'nova/tests/api/openstack/test_servers.py' |
412 | --- nova/tests/api/openstack/test_servers.py 2011-07-11 21:26:46 +0000 |
413 | +++ nova/tests/api/openstack/test_servers.py 2011-07-14 18:50:41 +0000 |
414 | @@ -65,6 +65,18 @@ |
415 | return stub_instance(id, uuid=uuid) |
416 | |
417 | |
418 | +def return_virtual_interface_by_instance(interfaces): |
419 | + def _return_virtual_interface_by_instance(context, instance_id): |
420 | + return interfaces |
421 | + return _return_virtual_interface_by_instance |
422 | + |
423 | + |
424 | +def return_virtual_interface_instance_nonexistant(interfaces): |
425 | + def _return_virtual_interface_by_instance(context, instance_id): |
426 | + raise exception.InstanceNotFound(instance_id=instance_id) |
427 | + return _return_virtual_interface_by_instance |
428 | + |
429 | + |
430 | def return_server_with_addresses(private, public): |
431 | def _return_server(context, id): |
432 | return stub_instance(id, private_address=private, |
433 | @@ -72,6 +84,12 @@ |
434 | return _return_server |
435 | |
436 | |
437 | +def return_server_with_interfaces(interfaces): |
438 | + def _return_server(context, id): |
439 | + return stub_instance(id, interfaces=interfaces) |
440 | + return _return_server |
441 | + |
442 | + |
443 | def return_server_with_power_state(power_state): |
444 | def _return_server(context, id): |
445 | return stub_instance(id, power_state=power_state) |
446 | @@ -124,10 +142,13 @@ |
447 | |
448 | def stub_instance(id, user_id=1, private_address=None, public_addresses=None, |
449 | host=None, power_state=0, reservation_id="", |
450 | - uuid=FAKE_UUID): |
451 | + uuid=FAKE_UUID, interfaces=None): |
452 | metadata = [] |
453 | metadata.append(InstanceMetadata(key='seq', value=id)) |
454 | |
455 | + if interfaces is None: |
456 | + interfaces = [] |
457 | + |
458 | inst_type = instance_types.get_instance_type_by_flavor_id(1) |
459 | |
460 | if public_addresses is None: |
461 | @@ -171,7 +192,8 @@ |
462 | "display_description": "", |
463 | "locked": False, |
464 | "metadata": metadata, |
465 | - "uuid": uuid} |
466 | + "uuid": uuid, |
467 | + "virtual_interfaces": interfaces} |
468 | |
469 | instance["fixed_ips"] = { |
470 | "address": private_address, |
471 | @@ -411,23 +433,152 @@ |
472 | self.assertEquals(ip.getAttribute('addr'), private) |
473 | |
474 | def test_get_server_by_id_with_addresses_v1_1(self): |
475 | - private = "192.168.0.3" |
476 | - public = ["1.2.3.4"] |
477 | - new_return_server = return_server_with_addresses(private, public) |
478 | + interfaces = [ |
479 | + { |
480 | + 'network': {'label': 'network_1'}, |
481 | + 'fixed_ips': [ |
482 | + {'address': '192.168.0.3'}, |
483 | + {'address': '192.168.0.4'}, |
484 | + ], |
485 | + }, |
486 | + { |
487 | + 'network': {'label': 'network_2'}, |
488 | + 'fixed_ips': [ |
489 | + {'address': '172.19.0.1'}, |
490 | + {'address': '172.19.0.2'}, |
491 | + ], |
492 | + }, |
493 | + ] |
494 | + new_return_server = return_server_with_interfaces(interfaces) |
495 | self.stubs.Set(nova.db.api, 'instance_get', new_return_server) |
496 | + |
497 | req = webob.Request.blank('/v1.1/servers/1') |
498 | res = req.get_response(fakes.wsgi_app()) |
499 | + |
500 | res_dict = json.loads(res.body) |
501 | self.assertEqual(res_dict['server']['id'], 1) |
502 | self.assertEqual(res_dict['server']['name'], 'server1') |
503 | addresses = res_dict['server']['addresses'] |
504 | - # RM(4047): Figure otu what is up with the 1.1 api and multi-nic |
505 | - #self.assertEqual(len(addresses["public"]), len(public)) |
506 | - #self.assertEqual(addresses["public"][0], |
507 | - # {"version": 4, "addr": public[0]}) |
508 | - #self.assertEqual(len(addresses["private"]), 1) |
509 | - #self.assertEqual(addresses["private"][0], |
510 | - # {"version": 4, "addr": private}) |
511 | + expected = { |
512 | + 'network_1': [ |
513 | + {'addr': '192.168.0.3', 'version': 4}, |
514 | + {'addr': '192.168.0.4', 'version': 4}, |
515 | + ], |
516 | + 'network_2': [ |
517 | + {'addr': '172.19.0.1', 'version': 4}, |
518 | + {'addr': '172.19.0.2', 'version': 4}, |
519 | + ], |
520 | + } |
521 | + |
522 | + self.assertEqual(addresses, expected) |
523 | + |
524 | + def test_get_server_addresses_v1_1(self): |
525 | + interfaces = [ |
526 | + { |
527 | + 'network': {'label': 'network_1'}, |
528 | + 'fixed_ips': [ |
529 | + {'address': '192.168.0.3'}, |
530 | + {'address': '192.168.0.4'}, |
531 | + ], |
532 | + }, |
533 | + { |
534 | + 'network': {'label': 'network_2'}, |
535 | + 'fixed_ips': [ |
536 | + { |
537 | + 'address': '172.19.0.1', |
538 | + 'floating_ips': [ |
539 | + {'address': '1.2.3.4'}, |
540 | + ], |
541 | + }, |
542 | + {'address': '172.19.0.2'}, |
543 | + ], |
544 | + }, |
545 | + ] |
546 | + |
547 | + _return_vifs = return_virtual_interface_by_instance(interfaces) |
548 | + self.stubs.Set(nova.db.api, |
549 | + 'virtual_interface_get_by_instance', |
550 | + _return_vifs) |
551 | + |
552 | + req = webob.Request.blank('/v1.1/servers/1/ips') |
553 | + res = req.get_response(fakes.wsgi_app()) |
554 | + res_dict = json.loads(res.body) |
555 | + |
556 | + expected = { |
557 | + 'addresses': { |
558 | + 'network_1': [ |
559 | + {'version': 4, 'addr': '192.168.0.3'}, |
560 | + {'version': 4, 'addr': '192.168.0.4'}, |
561 | + ], |
562 | + 'network_2': [ |
563 | + {'version': 4, 'addr': '172.19.0.1'}, |
564 | + {'version': 4, 'addr': '1.2.3.4'}, |
565 | + {'version': 4, 'addr': '172.19.0.2'}, |
566 | + ], |
567 | + }, |
568 | + } |
569 | + |
570 | + self.assertEqual(res_dict, expected) |
571 | + |
572 | + def test_get_server_addresses_single_network_v1_1(self): |
573 | + interfaces = [ |
574 | + { |
575 | + 'network': {'label': 'network_1'}, |
576 | + 'fixed_ips': [ |
577 | + {'address': '192.168.0.3'}, |
578 | + {'address': '192.168.0.4'}, |
579 | + ], |
580 | + }, |
581 | + { |
582 | + 'network': {'label': 'network_2'}, |
583 | + 'fixed_ips': [ |
584 | + { |
585 | + 'address': '172.19.0.1', |
586 | + 'floating_ips': [ |
587 | + {'address': '1.2.3.4'}, |
588 | + ], |
589 | + }, |
590 | + {'address': '172.19.0.2'}, |
591 | + ], |
592 | + }, |
593 | + ] |
594 | + _return_vifs = return_virtual_interface_by_instance(interfaces) |
595 | + self.stubs.Set(nova.db.api, |
596 | + 'virtual_interface_get_by_instance', |
597 | + _return_vifs) |
598 | + |
599 | + req = webob.Request.blank('/v1.1/servers/1/ips/network_2') |
600 | + res = req.get_response(fakes.wsgi_app()) |
601 | + self.assertEqual(res.status_int, 200) |
602 | + res_dict = json.loads(res.body) |
603 | + expected = { |
604 | + 'network_2': [ |
605 | + {'version': 4, 'addr': '172.19.0.1'}, |
606 | + {'version': 4, 'addr': '1.2.3.4'}, |
607 | + {'version': 4, 'addr': '172.19.0.2'}, |
608 | + ], |
609 | + } |
610 | + self.assertEqual(res_dict, expected) |
611 | + |
612 | + def test_get_server_addresses_nonexistant_network_v1_1(self): |
613 | + _return_vifs = return_virtual_interface_by_instance([]) |
614 | + self.stubs.Set(nova.db.api, |
615 | + 'virtual_interface_get_by_instance', |
616 | + _return_vifs) |
617 | + |
618 | + req = webob.Request.blank('/v1.1/servers/1/ips/network_0') |
619 | + res = req.get_response(fakes.wsgi_app()) |
620 | + self.assertEqual(res.status_int, 404) |
621 | + |
622 | + def test_get_server_addresses_nonexistant_server_v1_1(self): |
623 | + _return_vifs = return_virtual_interface_instance_nonexistant([]) |
624 | + self.stubs.Set(nova.db.api, |
625 | + 'virtual_interface_get_by_instance', |
626 | + _return_vifs) |
627 | + |
628 | + req = webob.Request.blank('/v1.1/servers/600/ips') |
629 | + res = req.get_response(fakes.wsgi_app()) |
630 | + self.assertEqual(res.status_int, 404) |
631 | |
632 | def test_get_server_list(self): |
633 | req = webob.Request.blank('/v1.0/servers') |
634 | @@ -787,13 +938,13 @@ |
635 | |
636 | res = req.get_response(fakes.wsgi_app()) |
637 | |
638 | + self.assertEqual(res.status_int, 200) |
639 | server = json.loads(res.body)['server'] |
640 | self.assertEqual(16, len(server['adminPass'])) |
641 | self.assertEqual('server_test', server['name']) |
642 | self.assertEqual(1, server['id']) |
643 | self.assertEqual(flavor_ref, server['flavorRef']) |
644 | self.assertEqual(image_href, server['imageRef']) |
645 | - self.assertEqual(res.status_int, 200) |
646 | |
647 | def test_create_instance_v1_1_bad_href(self): |
648 | self._setup_for_create_instance() |
Just a heads up, the images mapper was duplicated in the v1.0 api router. This branch removes the extra one.