Merge lp:~rackspace-titan/nova/osapi-server-ips-lp751560 into lp:~hudson-openstack/nova/trunk

Proposed by Naveed Massjouni
Status: Merged
Approved by: Devin Carlen
Approved revision: 947
Merged at revision: 959
Proposed branch: lp:~rackspace-titan/nova/osapi-server-ips-lp751560
Merge into: lp:~hudson-openstack/nova/trunk
Prerequisite: lp:~rackspace-titan/nova/osapi-xml-serialization
Diff against target: 232 lines (+164/-11)
5 files modified
nova/api/openstack/__init__.py (+6/-0)
nova/api/openstack/ips.py (+72/-0)
nova/api/openstack/servers.py (+0/-9)
nova/api/openstack/views/addresses.py (+8/-2)
nova/tests/api/openstack/test_servers.py (+78/-0)
To merge this branch: bzr merge lp:~rackspace-titan/nova/osapi-server-ips-lp751560
Reviewer Review Type Date Requested Status
Devin Carlen (community) Approve
Rick Harris (community) Approve
Brian Waldon (community) Approve
Jay Pipes (community) Approve
Review via email: mp+56645@code.launchpad.net

Description of the change

Added support for listing addresses of a server in the openstack api.
Now you can GET
 * /servers/1/ips
 * /servers/1/ips/public
 * /servers/1/ips/private
Supports v1.0 json and xml.
Added corresponding tests.

To post a comment you must log in.
Revision history for this message
Jay Pipes (jaypipes) wrote :

lgtm. nice work.

review: Approve
Revision history for this message
Brian Waldon (bcwaldon) wrote :

Should build_private/public_parts be private?

review: Needs Information
Revision history for this message
Brian Waldon (bcwaldon) wrote :

Do you have any plans for v1.1 support?

Revision history for this message
Naveed Massjouni (ironcamel) wrote :

@Brian:

> Should build_private/public_parts be private?

Nope. I abstracted that logic out so I could call it from the nova/api/openstack/ips.py controller.

> Do you have any plans for v1.1 support?

Not for cactus.

Revision history for this message
Brian Waldon (bcwaldon) wrote :

> @Brian:
>
> > Should build_private/public_parts be private?
>
> Nope. I abstracted that logic out so I could call it from the
> nova/api/openstack/ips.py controller.
>
> > Do you have any plans for v1.1 support?
>
> Not for cactus.

Gotcha.

review: Approve
946. By Naveed Massjouni

Merge from trunk

947. By Naveed Massjouni

Controllers now inherit from nova.api.openstack.common.OpenstackController.

Revision history for this message
Rick Harris (rconradharris) wrote :

lgtm

review: Approve
Revision history for this message
Devin Carlen (devcamcar) wrote :

great unit tests, approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/openstack/__init__.py'
2--- nova/api/openstack/__init__.py 2011-03-30 02:09:42 +0000
3+++ nova/api/openstack/__init__.py 2011-04-06 21:51:35 +0000
4@@ -34,6 +34,7 @@
5 from nova.api.openstack import flavors
6 from nova.api.openstack import images
7 from nova.api.openstack import image_metadata
8+from nova.api.openstack import ips
9 from nova.api.openstack import limits
10 from nova.api.openstack import servers
11 from nova.api.openstack import server_metadata
12@@ -144,6 +145,11 @@
13 parent_resource=dict(member_name='server',
14 collection_name='servers'))
15
16+ mapper.resource("ip", "ips", controller=ips.Controller(),
17+ collection=dict(public='GET', private='GET'),
18+ parent_resource=dict(member_name='server',
19+ collection_name='servers'))
20+
21
22 class APIRouterV11(APIRouter):
23 """Define routes specific to OpenStack API V1.1."""
24
25=== added file 'nova/api/openstack/ips.py'
26--- nova/api/openstack/ips.py 1970-01-01 00:00:00 +0000
27+++ nova/api/openstack/ips.py 2011-04-06 21:51:35 +0000
28@@ -0,0 +1,72 @@
29+# vim: tabstop=4 shiftwidth=4 softtabstop=4
30+
31+# Copyright 2011 OpenStack LLC.
32+# All Rights Reserved.
33+#
34+# Licensed under the Apache License, Version 2.0 (the "License"); you may
35+# not use this file except in compliance with the License. You may obtain
36+# a copy of the License at
37+#
38+# http://www.apache.org/licenses/LICENSE-2.0
39+#
40+# Unless required by applicable law or agreed to in writing, software
41+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
42+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
43+# License for the specific language governing permissions and limitations
44+# under the License.
45+
46+import time
47+
48+from webob import exc
49+
50+import nova
51+import nova.api.openstack.views.addresses
52+from nova.api.openstack import common
53+from nova.api.openstack import faults
54+
55+
56+class Controller(common.OpenstackController):
57+ """The servers addresses API controller for the Openstack API."""
58+
59+ _serialization_metadata = {
60+ 'application/xml': {
61+ 'list_collections': {
62+ 'public': {'item_name': 'ip', 'item_key': 'addr'},
63+ 'private': {'item_name': 'ip', 'item_key': 'addr'},
64+ },
65+ },
66+ }
67+
68+ def __init__(self):
69+ self.compute_api = nova.compute.API()
70+ self.builder = nova.api.openstack.views.addresses.ViewBuilderV10()
71+
72+ def index(self, req, server_id):
73+ try:
74+ instance = self.compute_api.get(req.environ['nova.context'], id)
75+ except nova.exception.NotFound:
76+ return faults.Fault(exc.HTTPNotFound())
77+ return {'addresses': self.builder.build(instance)}
78+
79+ def public(self, req, server_id):
80+ try:
81+ instance = self.compute_api.get(req.environ['nova.context'], id)
82+ except nova.exception.NotFound:
83+ return faults.Fault(exc.HTTPNotFound())
84+ return {'public': self.builder.build_public_parts(instance)}
85+
86+ def private(self, req, server_id):
87+ try:
88+ instance = self.compute_api.get(req.environ['nova.context'], id)
89+ except nova.exception.NotFound:
90+ return faults.Fault(exc.HTTPNotFound())
91+ return {'private': self.builder.build_private_parts(instance)}
92+
93+ def show(self, req, server_id, id):
94+ return faults.Fault(exc.HTTPNotImplemented())
95+
96+ def create(self, req, server_id):
97+ return faults.Fault(exc.HTTPNotImplemented())
98+
99+ def delete(self, req, server_id, id):
100+ return faults.Fault(exc.HTTPNotImplemented())
101
102=== modified file 'nova/api/openstack/servers.py'
103--- nova/api/openstack/servers.py 2011-04-06 21:51:35 +0000
104+++ nova/api/openstack/servers.py 2011-04-06 21:51:35 +0000
105@@ -70,15 +70,6 @@
106 self._image_service = utils.import_object(FLAGS.image_service)
107 super(Controller, self).__init__()
108
109- def ips(self, req, id):
110- try:
111- instance = self.compute_api.get(req.environ['nova.context'], id)
112- except exception.NotFound:
113- return faults.Fault(exc.HTTPNotFound())
114-
115- builder = self._get_addresses_view_builder(req)
116- return builder.build(instance)
117-
118 def index(self, req):
119 """ Returns a list of server names and ids for a given user """
120 return self._items(req, is_detail=False)
121
122=== modified file 'nova/api/openstack/views/addresses.py'
123--- nova/api/openstack/views/addresses.py 2011-03-17 07:21:09 +0000
124+++ nova/api/openstack/views/addresses.py 2011-04-06 21:51:35 +0000
125@@ -28,10 +28,16 @@
126
127 class ViewBuilderV10(ViewBuilder):
128 def build(self, inst):
129- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
130- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
131+ private_ips = self.build_private_parts(inst)
132+ public_ips = self.build_public_parts(inst)
133 return dict(public=public_ips, private=private_ips)
134
135+ def build_public_parts(self, inst):
136+ return utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
137+
138+ def build_private_parts(self, inst):
139+ return utils.get_from_path(inst, 'fixed_ip/address')
140+
141
142 class ViewBuilderV11(ViewBuilder):
143 def build(self, inst):
144
145=== modified file 'nova/tests/api/openstack/test_servers.py'
146--- nova/tests/api/openstack/test_servers.py 2011-04-06 21:51:35 +0000
147+++ nova/tests/api/openstack/test_servers.py 2011-04-06 21:51:35 +0000
148@@ -228,6 +228,84 @@
149 self.assertEqual(len(addresses["private"]), 1)
150 self.assertEqual(addresses["private"][0], private)
151
152+ def test_get_server_addresses_V10(self):
153+ private = '192.168.0.3'
154+ public = ['1.2.3.4']
155+ new_return_server = return_server_with_addresses(private, public)
156+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
157+ req = webob.Request.blank('/v1.0/servers/1/ips')
158+ res = req.get_response(fakes.wsgi_app())
159+ res_dict = json.loads(res.body)
160+ self.assertEqual(res_dict, {
161+ 'addresses': {'public': public, 'private': [private]}})
162+
163+ def test_get_server_addresses_xml_V10(self):
164+ private_expected = "192.168.0.3"
165+ public_expected = ["1.2.3.4"]
166+ new_return_server = return_server_with_addresses(private_expected,
167+ public_expected)
168+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
169+ req = webob.Request.blank('/v1.0/servers/1/ips')
170+ req.headers['Accept'] = 'application/xml'
171+ res = req.get_response(fakes.wsgi_app())
172+ dom = minidom.parseString(res.body)
173+ (addresses,) = dom.childNodes
174+ self.assertEquals(addresses.nodeName, 'addresses')
175+ (public,) = addresses.getElementsByTagName('public')
176+ (ip,) = public.getElementsByTagName('ip')
177+ self.assertEquals(ip.getAttribute('addr'), public_expected[0])
178+ (private,) = addresses.getElementsByTagName('private')
179+ (ip,) = private.getElementsByTagName('ip')
180+ self.assertEquals(ip.getAttribute('addr'), private_expected)
181+
182+ def test_get_server_addresses_public_V10(self):
183+ private = "192.168.0.3"
184+ public = ["1.2.3.4"]
185+ new_return_server = return_server_with_addresses(private, public)
186+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
187+ req = webob.Request.blank('/v1.0/servers/1/ips/public')
188+ res = req.get_response(fakes.wsgi_app())
189+ res_dict = json.loads(res.body)
190+ self.assertEqual(res_dict, {'public': public})
191+
192+ def test_get_server_addresses_private_V10(self):
193+ private = "192.168.0.3"
194+ public = ["1.2.3.4"]
195+ new_return_server = return_server_with_addresses(private, public)
196+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
197+ req = webob.Request.blank('/v1.0/servers/1/ips/private')
198+ res = req.get_response(fakes.wsgi_app())
199+ res_dict = json.loads(res.body)
200+ self.assertEqual(res_dict, {'private': [private]})
201+
202+ def test_get_server_addresses_public_xml_V10(self):
203+ private = "192.168.0.3"
204+ public = ["1.2.3.4"]
205+ new_return_server = return_server_with_addresses(private, public)
206+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
207+ req = webob.Request.blank('/v1.0/servers/1/ips/public')
208+ req.headers['Accept'] = 'application/xml'
209+ res = req.get_response(fakes.wsgi_app())
210+ dom = minidom.parseString(res.body)
211+ (public_node,) = dom.childNodes
212+ self.assertEquals(public_node.nodeName, 'public')
213+ (ip,) = public_node.getElementsByTagName('ip')
214+ self.assertEquals(ip.getAttribute('addr'), public[0])
215+
216+ def test_get_server_addresses_private_xml_V10(self):
217+ private = "192.168.0.3"
218+ public = ["1.2.3.4"]
219+ new_return_server = return_server_with_addresses(private, public)
220+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
221+ req = webob.Request.blank('/v1.0/servers/1/ips/private')
222+ req.headers['Accept'] = 'application/xml'
223+ res = req.get_response(fakes.wsgi_app())
224+ dom = minidom.parseString(res.body)
225+ (private_node,) = dom.childNodes
226+ self.assertEquals(private_node.nodeName, 'private')
227+ (ip,) = private_node.getElementsByTagName('ip')
228+ self.assertEquals(ip.getAttribute('addr'), private)
229+
230 def test_get_server_by_id_with_addresses_v11(self):
231 private = "192.168.0.3"
232 public = ["1.2.3.4"]