Merge lp:~cloudbuilders/nova/os-admin-flavors into lp:~hudson-openstack/nova/trunk

Proposed by Jake Dahn
Status: Work in progress
Proposed branch: lp:~cloudbuilders/nova/os-admin-flavors
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 225 lines (+205/-0)
3 files modified
nova/api/openstack/contrib/admin_flavors.py (+103/-0)
nova/tests/api/openstack/contrib/test_admin_flavors.py (+101/-0)
nova/tests/api/openstack/test_extensions.py (+1/-0)
To merge this branch: bzr merge lp:~cloudbuilders/nova/os-admin-flavors
Reviewer Review Type Date Requested Status
Nova Core security contacts Pending
Review via email: mp+72483@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

1473. By Jake Dahn

removing the ability to purge (hard delete) flavors, it is really not necessary.

1472. By Jake Dahn

adding admin flavors to extension test

1471. By Anthony Young

make tests pass

1470. By Jake Dahn

adding another test

1469. By Jake Dahn

pep8 fixes

1468. By Jake Dahn

adding admin api extension for adding and creating flavors, and WIP tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'nova/api/openstack/contrib/admin_flavors.py'
2--- nova/api/openstack/contrib/admin_flavors.py 1970-01-01 00:00:00 +0000
3+++ nova/api/openstack/contrib/admin_flavors.py 2011-08-22 18:39:25 +0000
4@@ -0,0 +1,103 @@
5+# vim: tabstop=4 shiftwidth=4 softtabstop=4
6+
7+# Copyright 2011 OpenStack LLC.
8+# All Rights Reserved.
9+#
10+# Licensed under the Apache License, Version 2.0 (the "License"); you may
11+# not use this file except in compliance with the License. You may obtain
12+# a copy of the License at
13+#
14+# http://www.apache.org/licenses/LICENSE-2.0
15+#
16+# Unless required by applicable law or agreed to in writing, software
17+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19+# License for the specific language governing permissions and limitations
20+# under the License.
21+
22+import urlparse
23+import webob
24+
25+from nova import exception
26+from nova.compute import instance_types
27+from nova.api.openstack import extensions
28+from nova.api.openstack import views
29+from webob import exc
30+
31+
32+class AdminFlavorsController(object):
33+
34+ def create(self, req, body):
35+ """ Create Flavor """
36+
37+ name = body['flavor'].get('name')
38+ memory_mb = body['flavor'].get('memory_mb')
39+ vcpus = body['flavor'].get('vcpus')
40+ local_gb = body['flavor'].get('local_gb')
41+ flavorid = body['flavor'].get('flavorid')
42+ swap = body['flavor'].get('swap')
43+ rxtx_quota = body['flavor'].get('rxtx_quota')
44+ rxtx_cap = body['flavor'].get('rxtx_cap')
45+
46+ try:
47+ flavor = instance_types.create(name, memory_mb, vcpus,
48+ local_gb, flavorid,
49+ swap, rxtx_quota, rxtx_cap)
50+ builder = self._get_view_builder(req)
51+ values = builder.build(body['flavor'], is_detail=True)
52+ return dict(flavor=values)
53+ except exception.AdminRequired:
54+ return webob.Response(status_int=403)
55+
56+ def delete(self, req, id):
57+ flavor = instance_types.get_instance_type_by_flavor_id(id)
58+ instasnce_types.destroy(flavor['name'])
59+
60+ return exc.HTTPAccepted()
61+
62+ def _get_view_builder(self, req):
63+
64+ class ViewBuilder(views.flavors.ViewBuilderV11):
65+
66+ def __init__(self, base_url):
67+ self.base_url = base_url
68+
69+ def _build_simple(self, flavor_obj):
70+ simple = {
71+ "id": flavor_obj["flavorid"],
72+ "name": flavor_obj["name"],
73+ "ram": flavor_obj["memory_mb"],
74+ "disk": flavor_obj["local_gb"],
75+ "vcpus": flavor_obj["vcpus"],
76+ }
77+ return simple
78+
79+ base_url = req.application_url
80+ return ViewBuilder(base_url)
81+
82+
83+class Admin_flavors(extensions.ExtensionDescriptor):
84+
85+ def get_name(self):
86+ return "Admin_flavors"
87+
88+ def get_alias(self):
89+ return "os-admin-flavors"
90+
91+ def get_description(self):
92+ return "Flavors management for admins"
93+
94+ def get_namespace(self):
95+ return "http://docs.openstack.org/ext/os-flavors-admin/api/v1.1"
96+
97+ def get_updated(self):
98+ return "2011-08-08T00:00:00+00:00"
99+
100+ def get_resources(self):
101+ resources = []
102+
103+ res = extensions.ResourceExtension('os-admin-flavors',
104+ AdminFlavorsController())
105+ resources.append(res)
106+
107+ return resources
108
109=== added file 'nova/tests/api/openstack/contrib/test_admin_flavors.py'
110--- nova/tests/api/openstack/contrib/test_admin_flavors.py 1970-01-01 00:00:00 +0000
111+++ nova/tests/api/openstack/contrib/test_admin_flavors.py 2011-08-22 18:39:25 +0000
112@@ -0,0 +1,101 @@
113+# vim: tabstop=4 shiftwidth=4 softtabstop=4
114+
115+# Copyright 2011 OpenStack LLC.
116+# All Rights Reserved.
117+#
118+# Licensed under the Apache License, Version 2.0 (the "License"); you may
119+# not use this file except in compliance with the License. You may obtain
120+# a copy of the License at
121+#
122+# http://www.apache.org/licenses/LICENSE-2.0
123+#
124+# Unless required by applicable law or agreed to in writing, software
125+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
126+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
127+# License for the specific language governing permissions and limitations
128+# under the License.
129+
130+import json
131+import webob
132+
133+from nova import context
134+from nova import test
135+from nova.compute import instance_types
136+from nova.tests.api.openstack import fakes
137+from webob import exc
138+
139+from nova.api.openstack.contrib.admin_flavors import AdminFlavorsController
140+
141+
142+def stub_flavor(flavor_id):
143+ return {'flavor': {'name': flavor_id, 'memory_mb': 512, 'vcpus': 2,
144+ 'local_gb': 1, 'flavorid': 23, 'swap': 0,
145+ 'rxtx_quota': 0, 'rxtx_cap': 0}}
146+
147+
148+def get_instance_type_by_flavor_id(instance_type_id):
149+ return {'rxtx_quota': 0, 'local_gb': 20, 'name': 'm1.small',
150+ 'deleted': False, 'created_at': None, 'updated_at': None,
151+ 'memory_mb': 2048, 'vcpus': 1, 'rxtx_cap': 0,
152+ 'flavorid': 23, 'swap': 0, 'extra_specs': {},
153+ 'deleted_at': None, 'id': 5}
154+
155+
156+def purge_instance_type(instance_type_id):
157+ pass
158+
159+
160+def destroy_instance_type(instance_type_id):
161+ pass
162+
163+
164+class AdminFlavorTest(test.TestCase):
165+
166+ def setUp(self):
167+ super(AdminFlavorTest, self).setUp()
168+ self.controller = AdminFlavorsController()
169+ self.admin_context = context.RequestContext('admin',
170+ 'admin',
171+ is_admin=True)
172+ self.stubs.Set(instance_types, "destroy", destroy_instance_type)
173+ self.stubs.Set(instance_types, "purge", purge_instance_type)
174+ self.stubs.Set(instance_types, "get_instance_type_by_flavor_id",
175+ get_instance_type_by_flavor_id)
176+
177+ def test_create_flavor(self):
178+ expected = {'flavor': {'name': 'test_flavor',
179+ 'links': [
180+ {'href': 'http://localhost/v1.1/flavors/23',
181+ 'rel': 'self'},
182+ {'href': 'http://localhost/flavors/23',
183+ 'rel': 'bookmark'}],
184+ 'ram': 512,
185+ 'vcpus': 2,
186+ 'disk': 1,
187+ 'id': 23}}
188+
189+ req = webob.Request.blank('/v1.1/os-admin-flavors')
190+ req.method = 'POST'
191+ req.body = json.dumps(stub_flavor('test_flavor'))
192+ req.headers['Content-Type'] = 'application/json'
193+ res = req.get_response(fakes.wsgi_app(
194+ fake_auth_context=self.admin_context))
195+
196+ self.assertEqual(res.status_int, 200)
197+ self.assertEqual(json.loads(res.body), expected)
198+
199+ def test_delete_flavor_by_purge(self):
200+ req = webob.Request.blank('/v1.1/os-admin-flavors/23?purge=True')
201+ req.method = 'DELETE'
202+ req.headers['Content-Type'] = 'application/json'
203+ res = req.get_response(fakes.wsgi_app(
204+ fake_auth_context=self.admin_context))
205+ self.assertEqual(res.status_int, 202)
206+
207+ def test_delete_flavor_by_destroy(self):
208+ req = webob.Request.blank('/v1.1/os-admin-flavors/23')
209+ req.method = 'DELETE'
210+ req.headers['Content-Type'] = 'application/json'
211+ res = req.get_response(fakes.wsgi_app(
212+ fake_auth_context=self.admin_context))
213+ self.assertEqual(res.status_int, 202)
214
215=== modified file 'nova/tests/api/openstack/test_extensions.py'
216--- nova/tests/api/openstack/test_extensions.py 2011-08-19 01:22:24 +0000
217+++ nova/tests/api/openstack/test_extensions.py 2011-08-22 18:39:25 +0000
218@@ -85,6 +85,7 @@
219 ext_path = os.path.join(os.path.dirname(__file__), "extensions")
220 self.flags(osapi_extensions_path=ext_path)
221 self.ext_list = [
222+ "Admin_flavors",
223 "FlavorExtraSpecs",
224 "Floating_ips",
225 "Fox In Socks",