From 98851c82fd9fab193e034e8fe3b13e249f3b53bd Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 23 Dec 2011 09:22:38 -0500 Subject: Converting accounts resource to admin extension Relates to blueprint separate-nova-adminapi Change-Id: I672c14df30eea4807930a0975916237542fe9d68 --- nova/api/openstack/v2/__init__.py | 5 - nova/api/openstack/v2/accounts.py | 103 ------------- nova/api/openstack/v2/contrib/accounts.py | 116 +++++++++++++++ .../api/openstack/v2/contrib/test_accounts.py | 162 +++++++++++++++++++++ nova/tests/api/openstack/v2/test_accounts.py | 162 --------------------- nova/tests/api/openstack/v2/test_extensions.py | 1 + 6 files changed, 279 insertions(+), 270 deletions(-) delete mode 100644 nova/api/openstack/v2/accounts.py create mode 100644 nova/api/openstack/v2/contrib/accounts.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_accounts.py delete mode 100644 nova/tests/api/openstack/v2/test_accounts.py diff --git a/nova/api/openstack/v2/__init__.py b/nova/api/openstack/v2/__init__.py index 82a8764a0..1d3830b05 100644 --- a/nova/api/openstack/v2/__init__.py +++ b/nova/api/openstack/v2/__init__.py @@ -24,7 +24,6 @@ import routes import webob.dec import webob.exc -from nova.api.openstack.v2 import accounts from nova.api.openstack.v2 import consoles from nova.api.openstack.v2 import extensions from nova.api.openstack.v2 import flavors @@ -139,10 +138,6 @@ class APIRouter(base_wsgi.Router): controller=users.create_resource(), collection={'detail': 'GET'}) - mapper.resource("account", "accounts", - controller=accounts.create_resource(), - collection={'detail': 'GET'}) - mapper.resource("zone", "zones", controller=zones.create_resource(), collection={'detail': 'GET', diff --git a/nova/api/openstack/v2/accounts.py b/nova/api/openstack/v2/accounts.py deleted file mode 100644 index 0f1584261..000000000 --- a/nova/api/openstack/v2/accounts.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import webob.exc - -from nova.api.openstack import wsgi -from nova.api.openstack import xmlutil -from nova.auth import manager -from nova import exception -from nova import flags -from nova import log as logging - - -FLAGS = flags.FLAGS -LOG = logging.getLogger('nova.api.openstack.v2.accounts') - - -def _translate_keys(account): - return dict(id=account.id, - name=account.name, - description=account.description, - manager=account.project_manager_id) - - -class Controller(object): - - def __init__(self): - self.manager = manager.AuthManager() - - def _check_admin(self, context): - """We cannot depend on the db layer to check for admin access - for the auth manager, so we do it here""" - if not context.is_admin: - raise exception.AdminRequired() - - def index(self, req): - raise webob.exc.HTTPNotImplemented() - - def detail(self, req): - raise webob.exc.HTTPNotImplemented() - - def show(self, req, id): - """Return data about the given account id""" - account = self.manager.get_project(id) - return dict(account=_translate_keys(account)) - - def delete(self, req, id): - self._check_admin(req.environ['nova.context']) - self.manager.delete_project(id) - return {} - - def create(self, req, body): - """We use update with create-or-update semantics - because the id comes from an external source""" - raise webob.exc.HTTPNotImplemented() - - def update(self, req, id, body): - """This is really create or update.""" - self._check_admin(req.environ['nova.context']) - description = body['account'].get('description') - manager = body['account'].get('manager') - try: - account = self.manager.get_project(id) - self.manager.modify_project(id, manager, description) - except exception.NotFound: - account = self.manager.create_project(id, manager, description) - return dict(account=_translate_keys(account)) - - -class AccountTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('account', selector='account') - root.set('id', 'id') - root.set('name', 'name') - root.set('description', 'description') - root.set('manager', 'manager') - - return xmlutil.MasterTemplate(root, 1) - - -class AccountXMLSerializer(xmlutil.XMLTemplateSerializer): - def default(self): - return AccountTemplate() - - -def create_resource(): - body_serializers = { - 'application/xml': AccountXMLSerializer(), - } - serializer = wsgi.ResponseSerializer(body_serializers) - return wsgi.Resource(Controller(), serializer=serializer) diff --git a/nova/api/openstack/v2/contrib/accounts.py b/nova/api/openstack/v2/contrib/accounts.py new file mode 100644 index 000000000..263eda640 --- /dev/null +++ b/nova/api/openstack/v2/contrib/accounts.py @@ -0,0 +1,116 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import webob.exc + +from nova.api.openstack.v2 import extensions +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil +from nova.auth import manager +from nova import exception +from nova import flags +from nova import log as logging + + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.api.openstack.v2.contrib.accounts') + + +def _translate_keys(account): + return dict(id=account.id, + name=account.name, + description=account.description, + manager=account.project_manager_id) + + +class Controller(object): + + def __init__(self): + self.manager = manager.AuthManager() + + def _check_admin(self, context): + """We cannot depend on the db layer to check for admin access + for the auth manager, so we do it here""" + if not context.is_admin: + raise exception.AdminRequired() + + def index(self, req): + raise webob.exc.HTTPNotImplemented() + + def show(self, req, id): + """Return data about the given account id""" + account = self.manager.get_project(id) + return dict(account=_translate_keys(account)) + + def delete(self, req, id): + self._check_admin(req.environ['nova.context']) + self.manager.delete_project(id) + return {} + + def create(self, req, body): + """We use update with create-or-update semantics + because the id comes from an external source""" + raise webob.exc.HTTPNotImplemented() + + def update(self, req, id, body): + """This is really create or update.""" + self._check_admin(req.environ['nova.context']) + description = body['account'].get('description') + manager = body['account'].get('manager') + try: + account = self.manager.get_project(id) + self.manager.modify_project(id, manager, description) + except exception.NotFound: + account = self.manager.create_project(id, manager, description) + return dict(account=_translate_keys(account)) + + +class AccountTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('account', selector='account') + root.set('id', 'id') + root.set('name', 'name') + root.set('description', 'description') + root.set('manager', 'manager') + + return xmlutil.MasterTemplate(root, 1) + + +class AccountXMLSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return AccountTemplate() + + +class Accounts(extensions.ExtensionDescriptor): + """Admin-only access to accounts""" + + name = "Accounts" + alias = "os-accounts" + namespace = "http://docs.openstack.org/compute/ext/accounts/api/v1.1" + updated = "2011-12-23T00:00:00+00:00" + admin_only = True + + def get_resources(self): + body_serializers = { + 'application/xml': AccountXMLSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + + #TODO(bcwaldon): This should be prefixed with 'os-' + res = extensions.ResourceExtension('accounts', + Controller(), + serializer=serializer) + + return [res] diff --git a/nova/tests/api/openstack/v2/contrib/test_accounts.py b/nova/tests/api/openstack/v2/contrib/test_accounts.py new file mode 100644 index 000000000..f799853dc --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_accounts.py @@ -0,0 +1,162 @@ +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import json + +from lxml import etree +import webob + +from nova import test +from nova.api.openstack.v2.contrib import accounts +from nova.auth.manager import User +from nova.tests.api.openstack import fakes + + +def fake_init(self): + self.manager = fakes.FakeAuthManager() + + +def fake_admin_check(self, req): + return True + + +class AccountsTest(test.TestCase): + def setUp(self): + super(AccountsTest, self).setUp() + self.flags(verbose=True, allow_admin_api=True) + self.stubs.Set(accounts.Controller, '__init__', + fake_init) + self.stubs.Set(accounts.Controller, '_check_admin', + fake_admin_check) + fakes.FakeAuthManager.clear_fakes() + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + + fakemgr = fakes.FakeAuthManager() + joeuser = User('id1', 'guy1', 'acc1', 'secret1', False) + superuser = User('id2', 'guy2', 'acc2', 'secret2', True) + fakemgr.add_user(joeuser) + fakemgr.add_user(superuser) + fakemgr.create_project('test1', joeuser) + fakemgr.create_project('test2', superuser) + + def test_get_account(self): + req = webob.Request.blank('/v2/fake/accounts/test1') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'test1') + self.assertEqual(res_dict['account']['name'], 'test1') + self.assertEqual(res_dict['account']['manager'], 'id1') + + def test_get_account_xml(self): + req = webob.Request.blank('/v2/fake/accounts/test1.xml') + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual('account', res_tree.tag) + self.assertEqual('test1', res_tree.get('id')) + self.assertEqual('test1', res_tree.get('name')) + self.assertEqual('id1', res_tree.get('manager')) + + def test_account_delete(self): + req = webob.Request.blank('/v2/fake/accounts/test1') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertTrue('test1' not in fakes.FakeAuthManager.projects) + self.assertEqual(res.status_int, 200) + + def test_account_create(self): + body = dict(account=dict(description='test account', + manager='id1')) + req = webob.Request.blank('/v2/fake/accounts/newacct') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'newacct') + self.assertEqual(res_dict['account']['name'], 'newacct') + self.assertEqual(res_dict['account']['description'], 'test account') + self.assertEqual(res_dict['account']['manager'], 'id1') + self.assertTrue('newacct' in + fakes.FakeAuthManager.projects) + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) + + def test_account_create_xml(self): + body = dict(account=dict(description='test account', + manager='id1')) + req = webob.Request.blank('/v2/fake/accounts/newacct.xml') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_tree.tag, 'account') + self.assertEqual(res_tree.get('id'), 'newacct') + self.assertEqual(res_tree.get('name'), 'newacct') + self.assertEqual(res_tree.get('description'), 'test account') + self.assertEqual(res_tree.get('manager'), 'id1') + self.assertTrue('newacct' in + fakes.FakeAuthManager.projects) + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) + + def test_account_update(self): + body = dict(account=dict(description='test account', + manager='id2')) + req = webob.Request.blank('/v2/fake/accounts/test1') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'test1') + self.assertEqual(res_dict['account']['name'], 'test1') + self.assertEqual(res_dict['account']['description'], 'test account') + self.assertEqual(res_dict['account']['manager'], 'id2') + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) + + def test_account_update_xml(self): + body = dict(account=dict(description='test account', + manager='id2')) + req = webob.Request.blank('/v2/fake/accounts/test1.xml') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_tree.tag, 'account') + self.assertEqual(res_tree.get('id'), 'test1') + self.assertEqual(res_tree.get('name'), 'test1') + self.assertEqual(res_tree.get('description'), 'test account') + self.assertEqual(res_tree.get('manager'), 'id2') + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) diff --git a/nova/tests/api/openstack/v2/test_accounts.py b/nova/tests/api/openstack/v2/test_accounts.py deleted file mode 100644 index bf97c0f98..000000000 --- a/nova/tests/api/openstack/v2/test_accounts.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -import json - -from lxml import etree -import webob - -from nova import test -from nova.api.openstack.v2 import accounts -from nova.auth.manager import User -from nova.tests.api.openstack import fakes - - -def fake_init(self): - self.manager = fakes.FakeAuthManager() - - -def fake_admin_check(self, req): - return True - - -class AccountsTest(test.TestCase): - def setUp(self): - super(AccountsTest, self).setUp() - self.flags(verbose=True, allow_admin_api=True) - self.stubs.Set(accounts.Controller, '__init__', - fake_init) - self.stubs.Set(accounts.Controller, '_check_admin', - fake_admin_check) - fakes.FakeAuthManager.clear_fakes() - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - - fakemgr = fakes.FakeAuthManager() - joeuser = User('id1', 'guy1', 'acc1', 'secret1', False) - superuser = User('id2', 'guy2', 'acc2', 'secret2', True) - fakemgr.add_user(joeuser) - fakemgr.add_user(superuser) - fakemgr.create_project('test1', joeuser) - fakemgr.create_project('test2', superuser) - - def test_get_account(self): - req = webob.Request.blank('/v2/fake/accounts/test1') - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'test1') - self.assertEqual(res_dict['account']['name'], 'test1') - self.assertEqual(res_dict['account']['manager'], 'id1') - - def test_get_account_xml(self): - req = webob.Request.blank('/v2/fake/accounts/test1.xml') - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual('account', res_tree.tag) - self.assertEqual('test1', res_tree.get('id')) - self.assertEqual('test1', res_tree.get('name')) - self.assertEqual('id1', res_tree.get('manager')) - - def test_account_delete(self): - req = webob.Request.blank('/v2/fake/accounts/test1') - req.method = 'DELETE' - res = req.get_response(fakes.wsgi_app()) - self.assertTrue('test1' not in fakes.FakeAuthManager.projects) - self.assertEqual(res.status_int, 200) - - def test_account_create(self): - body = dict(account=dict(description='test account', - manager='id1')) - req = webob.Request.blank('/v2/fake/accounts/newacct') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'newacct') - self.assertEqual(res_dict['account']['name'], 'newacct') - self.assertEqual(res_dict['account']['description'], 'test account') - self.assertEqual(res_dict['account']['manager'], 'id1') - self.assertTrue('newacct' in - fakes.FakeAuthManager.projects) - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) - - def test_account_create_xml(self): - body = dict(account=dict(description='test account', - manager='id1')) - req = webob.Request.blank('/v2/fake/accounts/newacct.xml') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_tree.tag, 'account') - self.assertEqual(res_tree.get('id'), 'newacct') - self.assertEqual(res_tree.get('name'), 'newacct') - self.assertEqual(res_tree.get('description'), 'test account') - self.assertEqual(res_tree.get('manager'), 'id1') - self.assertTrue('newacct' in - fakes.FakeAuthManager.projects) - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) - - def test_account_update(self): - body = dict(account=dict(description='test account', - manager='id2')) - req = webob.Request.blank('/v2/fake/accounts/test1') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'test1') - self.assertEqual(res_dict['account']['name'], 'test1') - self.assertEqual(res_dict['account']['description'], 'test account') - self.assertEqual(res_dict['account']['manager'], 'id2') - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) - - def test_account_update_xml(self): - body = dict(account=dict(description='test account', - manager='id2')) - req = webob.Request.blank('/v2/fake/accounts/test1.xml') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_tree.tag, 'account') - self.assertEqual(res_tree.get('id'), 'test1') - self.assertEqual(res_tree.get('name'), 'test1') - self.assertEqual(res_tree.get('description'), 'test account') - self.assertEqual(res_tree.get('manager'), 'id2') - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/v2/test_extensions.py index 2063e6e2d..7b9a46a93 100644 --- a/nova/tests/api/openstack/v2/test_extensions.py +++ b/nova/tests/api/openstack/v2/test_extensions.py @@ -98,6 +98,7 @@ class ExtensionControllerTest(ExtensionTestCase): super(ExtensionControllerTest, self).setUp() self.flags(allow_admin_api=True) self.ext_list = [ + "Accounts", "AdminActions", "Console_output", "Createserverext", -- cgit