summaryrefslogtreecommitdiffstats
path: root/nova/tests
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-06-28 19:49:05 +0000
committerGerrit Code Review <review@openstack.org>2013-06-28 19:49:05 +0000
commit12c97dc44ab0d8941aff2c54ed730a322e69e5fe (patch)
tree03ee7261158286e625754ee9b3ccf15d034fa0f0 /nova/tests
parentc012bd44dea93c63e122befe9574a095f22f5a3c (diff)
parent6515e01f13920dba94d25eef5e4c33900c1736b4 (diff)
Merge "Port cells extension to v3 API Part 1"
Diffstat (limited to 'nova/tests')
-rw-r--r--nova/tests/api/openstack/compute/plugins/v3/test_cells.py474
1 files changed, 474 insertions, 0 deletions
diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_cells.py b/nova/tests/api/openstack/compute/plugins/v3/test_cells.py
new file mode 100644
index 000000000..a9e77693e
--- /dev/null
+++ b/nova/tests/api/openstack/compute/plugins/v3/test_cells.py
@@ -0,0 +1,474 @@
+# Copyright 2011-2012 OpenStack Foundation
+# 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 copy
+
+from lxml import etree
+from webob import exc
+
+from nova.api.openstack.compute.contrib import cells as cells_ext
+from nova.api.openstack import extensions
+from nova.api.openstack import xmlutil
+from nova.cells import rpcapi as cells_rpcapi
+from nova import context
+from nova import db
+from nova import exception
+from nova.openstack.common import timeutils
+from nova import test
+from nova.tests.api.openstack import fakes
+from nova.tests import utils
+
+
+FAKE_CELLS = [
+ dict(id=1, name='cell1', username='bob', is_parent=True,
+ weight_scale=1.0, weight_offset=0.0,
+ rpc_host='r1.example.org', password='xxxx'),
+ dict(id=2, name='cell2', username='alice', is_parent=False,
+ weight_scale=1.0, weight_offset=0.0,
+ rpc_host='r2.example.org', password='qwerty')]
+
+
+FAKE_CAPABILITIES = [
+ {'cap1': '0,1', 'cap2': '2,3'},
+ {'cap3': '4,5', 'cap4': '5,6'}]
+
+
+def fake_db_cell_get(context, cell_name):
+ for cell in FAKE_CELLS:
+ if cell_name == cell['name']:
+ return cell
+ else:
+ raise exception.CellNotFound(cell_name=cell_name)
+
+
+def fake_db_cell_create(context, values):
+ cell = dict(id=1)
+ cell.update(values)
+ return cell
+
+
+def fake_db_cell_update(context, cell_id, values):
+ cell = fake_db_cell_get(context, cell_id)
+ cell.update(values)
+ return cell
+
+
+def fake_cells_api_get_all_cell_info(*args):
+ cells = copy.deepcopy(FAKE_CELLS)
+ del cells[0]['password']
+ del cells[1]['password']
+ for i, cell in enumerate(cells):
+ cell['capabilities'] = FAKE_CAPABILITIES[i]
+ return cells
+
+
+def fake_db_cell_get_all(context):
+ return FAKE_CELLS
+
+
+class CellsTest(test.TestCase):
+ def setUp(self):
+ super(CellsTest, self).setUp()
+ self.stubs.Set(db, 'cell_get', fake_db_cell_get)
+ self.stubs.Set(db, 'cell_get_all', fake_db_cell_get_all)
+ self.stubs.Set(db, 'cell_update', fake_db_cell_update)
+ self.stubs.Set(db, 'cell_create', fake_db_cell_create)
+ self.stubs.Set(cells_rpcapi.CellsAPI, 'get_cell_info_for_neighbors',
+ fake_cells_api_get_all_cell_info)
+
+ self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager)
+ self.controller = cells_ext.Controller(self.ext_mgr)
+ self.context = context.get_admin_context()
+
+ def _get_request(self, resource):
+ return fakes.HTTPRequest.blank('/v2/fake/' + resource)
+
+ def test_index(self):
+ req = self._get_request("cells")
+ res_dict = self.controller.index(req)
+
+ self.assertEqual(len(res_dict['cells']), 2)
+ for i, cell in enumerate(res_dict['cells']):
+ self.assertEqual(cell['name'], FAKE_CELLS[i]['name'])
+ self.assertNotIn('capabilitiles', cell)
+ self.assertNotIn('password', cell)
+
+ def test_detail(self):
+ req = self._get_request("cells/detail")
+ res_dict = self.controller.detail(req)
+
+ self.assertEqual(len(res_dict['cells']), 2)
+ for i, cell in enumerate(res_dict['cells']):
+ self.assertEqual(cell['name'], FAKE_CELLS[i]['name'])
+ self.assertEqual(cell['capabilities'], FAKE_CAPABILITIES[i])
+ self.assertNotIn('password', cell)
+
+ def test_show_bogus_cell_raises(self):
+ req = self._get_request("cells/bogus")
+ self.assertRaises(exc.HTTPNotFound, self.controller.show, req, 'bogus')
+
+ def test_get_cell_by_name(self):
+ req = self._get_request("cells/cell1")
+ res_dict = self.controller.show(req, 'cell1')
+ cell = res_dict['cell']
+
+ self.assertEqual(cell['name'], 'cell1')
+ self.assertEqual(cell['rpc_host'], 'r1.example.org')
+ self.assertNotIn('password', cell)
+
+ def test_cell_delete(self):
+ call_info = {'delete_called': 0}
+
+ def fake_db_cell_delete(context, cell_name):
+ self.assertEqual(cell_name, 'cell999')
+ call_info['delete_called'] += 1
+
+ self.stubs.Set(db, 'cell_delete', fake_db_cell_delete)
+
+ req = self._get_request("cells/cell999")
+ self.controller.delete(req, 'cell999')
+ self.assertEqual(call_info['delete_called'], 1)
+
+ def test_delete_bogus_cell_raises(self):
+ req = self._get_request("cells/cell999")
+ req.environ['nova.context'] = self.context
+ self.assertRaises(exc.HTTPNotFound, self.controller.delete, req,
+ 'cell999')
+
+ def test_cell_create_parent(self):
+ body = {'cell': {'name': 'meow',
+ 'username': 'fred',
+ 'password': 'fubar',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'parent',
+ # Also test this is ignored/stripped
+ 'is_parent': False}}
+
+ req = self._get_request("cells")
+ res_dict = self.controller.create(req, body)
+ cell = res_dict['cell']
+
+ self.assertEqual(cell['name'], 'meow')
+ self.assertEqual(cell['username'], 'fred')
+ self.assertEqual(cell['rpc_host'], 'r3.example.org')
+ self.assertEqual(cell['type'], 'parent')
+ self.assertNotIn('password', cell)
+ self.assertNotIn('is_parent', cell)
+
+ def test_cell_create_child(self):
+ body = {'cell': {'name': 'meow',
+ 'username': 'fred',
+ 'password': 'fubar',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'child'}}
+
+ req = self._get_request("cells")
+ res_dict = self.controller.create(req, body)
+ cell = res_dict['cell']
+
+ self.assertEqual(cell['name'], 'meow')
+ self.assertEqual(cell['username'], 'fred')
+ self.assertEqual(cell['rpc_host'], 'r3.example.org')
+ self.assertEqual(cell['type'], 'child')
+ self.assertNotIn('password', cell)
+ self.assertNotIn('is_parent', cell)
+
+ def test_cell_create_no_name_raises(self):
+ body = {'cell': {'username': 'moocow',
+ 'password': 'secret',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'parent'}}
+
+ req = self._get_request("cells")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.create, req, body)
+
+ def test_cell_create_name_empty_string_raises(self):
+ body = {'cell': {'name': '',
+ 'username': 'fred',
+ 'password': 'secret',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'parent'}}
+
+ req = self._get_request("cells")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.create, req, body)
+
+ def test_cell_create_name_with_bang_raises(self):
+ body = {'cell': {'name': 'moo!cow',
+ 'username': 'fred',
+ 'password': 'secret',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'parent'}}
+
+ req = self._get_request("cells")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.create, req, body)
+
+ def test_cell_create_name_with_dot_raises(self):
+ body = {'cell': {'name': 'moo.cow',
+ 'username': 'fred',
+ 'password': 'secret',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'parent'}}
+
+ req = self._get_request("cells")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.create, req, body)
+
+ def test_cell_create_name_with_invalid_type_raises(self):
+ body = {'cell': {'name': 'moocow',
+ 'username': 'fred',
+ 'password': 'secret',
+ 'rpc_host': 'r3.example.org',
+ 'type': 'invalid'}}
+
+ req = self._get_request("cells")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.create, req, body)
+
+ def test_cell_update(self):
+ body = {'cell': {'username': 'zeb',
+ 'password': 'sneaky'}}
+
+ req = self._get_request("cells/cell1")
+ res_dict = self.controller.update(req, 'cell1', body)
+ cell = res_dict['cell']
+
+ self.assertEqual(cell['name'], 'cell1')
+ self.assertEqual(cell['rpc_host'], FAKE_CELLS[0]['rpc_host'])
+ self.assertEqual(cell['username'], 'zeb')
+ self.assertNotIn('password', cell)
+
+ def test_cell_update_empty_name_raises(self):
+ body = {'cell': {'name': '',
+ 'username': 'zeb',
+ 'password': 'sneaky'}}
+
+ req = self._get_request("cells/cell1")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.update, req, 'cell1', body)
+
+ def test_cell_update_invalid_type_raises(self):
+ body = {'cell': {'username': 'zeb',
+ 'type': 'invalid',
+ 'password': 'sneaky'}}
+
+ req = self._get_request("cells/cell1")
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.update, req, 'cell1', body)
+
+ def test_cell_info(self):
+ caps = ['cap1=a;b', 'cap2=c;d']
+ self.flags(name='darksecret', capabilities=caps, group='cells')
+
+ req = self._get_request("cells/info")
+ res_dict = self.controller.info(req)
+ cell = res_dict['cell']
+ cell_caps = cell['capabilities']
+
+ self.assertEqual(cell['name'], 'darksecret')
+ self.assertEqual(cell_caps['cap1'], 'a;b')
+ self.assertEqual(cell_caps['cap2'], 'c;d')
+
+ def test_show_capacities(self):
+ self.ext_mgr.is_loaded('os-cell-capacities').AndReturn(True)
+ self.mox.StubOutWithMock(self.controller.cells_rpcapi,
+ 'get_capacities')
+ response = {"ram_free":
+ {"units_by_mb": {"8192": 0, "512": 13,
+ "4096": 1, "2048": 3, "16384": 0},
+ "total_mb": 7680},
+ "disk_free":
+ {"units_by_mb": {"81920": 11, "20480": 46,
+ "40960": 23, "163840": 5, "0": 0},
+ "total_mb": 1052672}
+ }
+ self.controller.cells_rpcapi.\
+ get_capacities(self.context, cell_name=None).AndReturn(response)
+ self.mox.ReplayAll()
+ req = self._get_request("cells/capacities")
+ req.environ["nova.context"] = self.context
+ res_dict = self.controller.capacities(req)
+ self.assertEqual(response, res_dict['cell']['capacities'])
+
+ def test_show_capacity_fails_with_non_admin_context(self):
+ self.ext_mgr.is_loaded('os-cell-capacities').AndReturn(True)
+ rules = {"compute_extension:cells": "is_admin:true"}
+ self.policy.set_rules(rules)
+
+ self.mox.ReplayAll()
+ req = self._get_request("cells/capacities")
+ req.environ["nova.context"] = self.context
+ req.environ["nova.context"].is_admin = False
+ self.assertRaises(exception.PolicyNotAuthorized,
+ self.controller.capacities, req)
+
+ def test_show_capacities_for_invalid_cell(self):
+ self.ext_mgr.is_loaded('os-cell-capacities').AndReturn(True)
+ self.mox.StubOutWithMock(self.controller.cells_rpcapi,
+ 'get_capacities')
+ self.controller.cells_rpcapi. \
+ get_capacities(self.context, cell_name="invalid_cell").AndRaise(
+ exception.CellNotFound(cell_name="invalid_cell"))
+ self.mox.ReplayAll()
+ req = self._get_request("cells/invalid_cell/capacities")
+ req.environ["nova.context"] = self.context
+ self.assertRaises(exc.HTTPNotFound,
+ self.controller.capacities, req, "invalid_cell")
+
+ def test_show_capacities_for_cell(self):
+ self.ext_mgr.is_loaded('os-cell-capacities').AndReturn(True)
+ self.mox.StubOutWithMock(self.controller.cells_rpcapi,
+ 'get_capacities')
+ response = {"ram_free":
+ {"units_by_mb": {"8192": 0, "512": 13,
+ "4096": 1, "2048": 3, "16384": 0},
+ "total_mb": 7680},
+ "disk_free":
+ {"units_by_mb": {"81920": 11, "20480": 46,
+ "40960": 23, "163840": 5, "0": 0},
+ "total_mb": 1052672}
+ }
+ self.controller.cells_rpcapi.\
+ get_capacities(self.context, cell_name='cell_name').\
+ AndReturn(response)
+ self.mox.ReplayAll()
+ req = self._get_request("cells/capacities")
+ req.environ["nova.context"] = self.context
+ res_dict = self.controller.capacities(req, 'cell_name')
+ self.assertEqual(response, res_dict['cell']['capacities'])
+
+ def test_sync_instances(self):
+ call_info = {}
+
+ def sync_instances(self, context, **kwargs):
+ call_info['project_id'] = kwargs.get('project_id')
+ call_info['updated_since'] = kwargs.get('updated_since')
+
+ self.stubs.Set(cells_rpcapi.CellsAPI, 'sync_instances', sync_instances)
+
+ req = self._get_request("cells/sync_instances")
+ body = {}
+ self.controller.sync_instances(req, body=body)
+ self.assertEqual(call_info['project_id'], None)
+ self.assertEqual(call_info['updated_since'], None)
+
+ body = {'project_id': 'test-project'}
+ self.controller.sync_instances(req, body=body)
+ self.assertEqual(call_info['project_id'], 'test-project')
+ self.assertEqual(call_info['updated_since'], None)
+
+ expected = timeutils.utcnow().isoformat()
+ if not expected.endswith("+00:00"):
+ expected += "+00:00"
+
+ body = {'updated_since': expected}
+ self.controller.sync_instances(req, body=body)
+ self.assertEqual(call_info['project_id'], None)
+ self.assertEqual(call_info['updated_since'], expected)
+
+ body = {'updated_since': 'skjdfkjsdkf'}
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.sync_instances, req, body=body)
+
+ body = {'foo': 'meow'}
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller.sync_instances, req, body=body)
+
+
+class TestCellsXMLSerializer(test.TestCase):
+ def test_multiple_cells(self):
+ fixture = {'cells': fake_cells_api_get_all_cell_info()}
+
+ serializer = cells_ext.CellsTemplate()
+ output = serializer.serialize(fixture)
+ res_tree = etree.XML(output)
+
+ self.assertEqual(res_tree.tag, '{%s}cells' % xmlutil.XMLNS_V10)
+ self.assertEqual(len(res_tree), 2)
+ self.assertEqual(res_tree[0].tag, '{%s}cell' % xmlutil.XMLNS_V10)
+ self.assertEqual(res_tree[1].tag, '{%s}cell' % xmlutil.XMLNS_V10)
+
+ def test_single_cell_with_caps(self):
+ cell = {'id': 1,
+ 'name': 'darksecret',
+ 'username': 'meow',
+ 'capabilities': {'cap1': 'a;b',
+ 'cap2': 'c;d'}}
+ fixture = {'cell': cell}
+
+ serializer = cells_ext.CellTemplate()
+ output = serializer.serialize(fixture)
+ res_tree = etree.XML(output)
+
+ self.assertEqual(res_tree.tag, '{%s}cell' % xmlutil.XMLNS_V10)
+ self.assertEqual(res_tree.get('name'), 'darksecret')
+ self.assertEqual(res_tree.get('username'), 'meow')
+ self.assertEqual(res_tree.get('password'), None)
+ self.assertEqual(len(res_tree), 1)
+
+ child = res_tree[0]
+ self.assertEqual(child.tag,
+ '{%s}capabilities' % xmlutil.XMLNS_V10)
+ for elem in child:
+ self.assertIn(elem.tag, ('{%s}cap1' % xmlutil.XMLNS_V10,
+ '{%s}cap2' % xmlutil.XMLNS_V10))
+ if elem.tag == '{%s}cap1' % xmlutil.XMLNS_V10:
+ self.assertEqual(elem.text, 'a;b')
+ elif elem.tag == '{%s}cap2' % xmlutil.XMLNS_V10:
+ self.assertEqual(elem.text, 'c;d')
+
+ def test_single_cell_without_caps(self):
+ cell = {'id': 1,
+ 'username': 'woof',
+ 'name': 'darksecret'}
+ fixture = {'cell': cell}
+
+ serializer = cells_ext.CellTemplate()
+ output = serializer.serialize(fixture)
+ res_tree = etree.XML(output)
+
+ self.assertEqual(res_tree.tag, '{%s}cell' % xmlutil.XMLNS_V10)
+ self.assertEqual(res_tree.get('name'), 'darksecret')
+ self.assertEqual(res_tree.get('username'), 'woof')
+ self.assertEqual(res_tree.get('password'), None)
+ self.assertEqual(len(res_tree), 0)
+
+
+class TestCellsXMLDeserializer(test.TestCase):
+ def test_cell_deserializer(self):
+ caps_dict = {'cap1': 'a;b',
+ 'cap2': 'c;d'}
+ caps_xml = ("<capabilities><cap1>a;b</cap1>"
+ "<cap2>c;d</cap2></capabilities>")
+ expected = {'cell': {'name': 'testcell1',
+ 'type': 'child',
+ 'rpc_host': 'localhost',
+ 'capabilities': caps_dict}}
+ intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<cell><name>testcell1</name><type>child</type>"
+ "<rpc_host>localhost</rpc_host>"
+ "%s</cell>") % caps_xml
+ deserializer = cells_ext.CellDeserializer()
+ result = deserializer.deserialize(intext)
+ self.assertEqual(dict(body=expected), result)
+
+ def test_with_corrupt_xml(self):
+ deserializer = cells_ext.CellDeserializer()
+ self.assertRaises(
+ exception.MalformedRequestBody,
+ deserializer.deserialize,
+ utils.killer_xml_body())