# 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 oslo_utils import timeutils from webob import exc from nova.api.openstack.compute import cells as cells_ext_v21 from nova.api.openstack.compute.legacy_v2.contrib import cells as cells_ext_v2 from nova.api.openstack import extensions from nova.cells import rpcapi as cells_rpcapi from nova import context from nova import exception from nova import rpc from nova import test from nova.tests.unit.api.openstack import fakes class BaseCellsTest(test.NoDBTestCase): def setUp(self): super(BaseCellsTest, self).setUp() self.fake_cells = [ dict(id=1, name='cell1', is_parent=True, weight_scale=1.0, weight_offset=0.0, transport_url='rabbit://bob:xxxx@r1.example.org/'), dict(id=2, name='cell2', is_parent=False, weight_scale=1.0, weight_offset=0.0, transport_url='rabbit://alice:qwerty@r2.example.org/')] self.fake_capabilities = [ {'cap1': '0,1', 'cap2': '2,3'}, {'cap3': '4,5', 'cap4': '5,6'}] def fake_cell_get(_self, context, cell_name): for cell in self.fake_cells: if cell_name == cell['name']: return cell else: raise exception.CellNotFound(cell_name=cell_name) def fake_cell_create(_self, context, values): cell = dict(id=1) cell.update(values) return cell def fake_cell_update(_self, context, cell_id, values): cell = fake_cell_get(_self, context, cell_id) cell.update(values) return cell def fake_cells_api_get_all_cell_info(*args): return self._get_all_cell_info(*args) self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_get', fake_cell_get) self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_update', fake_cell_update) self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_create', fake_cell_create) self.stubs.Set(cells_rpcapi.CellsAPI, 'get_cell_info_for_neighbors', fake_cells_api_get_all_cell_info) def _get_all_cell_info(self, *args): def insecure_transport_url(url): transport_url = rpc.get_transport_url(url) transport_url.hosts[0].password = None return str(transport_url) cells = copy.deepcopy(self.fake_cells) cells[0]['transport_url'] = insecure_transport_url( cells[0]['transport_url']) cells[1]['transport_url'] = insecure_transport_url( cells[1]['transport_url']) for i, cell in enumerate(cells): cell['capabilities'] = self.fake_capabilities[i] return cells class CellsTestV21(BaseCellsTest): cell_extension = 'os_compute_api:os-cells' bad_request = exception.ValidationError def _get_cell_controller(self, ext_mgr): return cells_ext_v21.CellsController() def _get_request(self, resource): return fakes.HTTPRequest.blank('/v2/fake/' + resource) def setUp(self): super(CellsTestV21, self).setUp() self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager) self.controller = self._get_cell_controller(self.ext_mgr) self.context = context.get_admin_context() self.flags(enable=True, group='cells') 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'], self.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'], self.fake_cells[i]['name']) self.assertEqual(cell['capabilities'], self.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 _cell_delete(self): call_info = {'delete_called': 0} def fake_cell_delete(inst, context, cell_name): self.assertEqual(cell_name, 'cell999') call_info['delete_called'] += 1 self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete) req = self._get_request("cells/cell999") req.environ['nova.context'] = self.context self.controller.delete(req, 'cell999') self.assertEqual(call_info['delete_called'], 1) def test_cell_delete(self): # Test cell delete with just cell policy rules = {"default": "is_admin:true", self.cell_extension: "is_admin:true"} self.policy.set_rules(rules) self._cell_delete() def test_cell_delete_with_delete_policy(self): self._cell_delete() def test_delete_bogus_cell_raises(self): def fake_cell_delete(inst, context, cell_name): return 0 self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete) req = self._get_request("cells/cell999") req.environ['nova.context'] = self.context self.assertRaises(exc.HTTPNotFound, self.controller.delete, req, 'cell999') def test_cell_delete_fails_for_invalid_policy(self): def fake_cell_delete(inst, context, cell_name): pass self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete) req = self._get_request("cells/cell999") req.environ['nova.context'] = self.context req.environ["nova.context"].is_admin = False self.assertRaises(exception.PolicyNotAuthorized, self.controller.delete, req, 'cell999') def _cell_create_parent(self): body = {'cell': {'name': 'meow', 'username': 'fred', 'password': 'fubar', 'rpc_host': 'r3.example.org', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context res_dict = self.controller.create(req, body=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_parent(self): # Test create with just cells policy rules = {"default": "is_admin:true", self.cell_extension: "is_admin:true"} self.policy.set_rules(rules) self._cell_create_parent() def test_cell_create_parent_with_create_policy(self): self._cell_create_parent() def _cell_create_child(self): body = {'cell': {'name': 'meow', 'username': 'fred', 'password': 'fubar', 'rpc_host': 'r3.example.org', 'type': 'child'}} req = self._get_request("cells") req.environ['nova.context'] = self.context res_dict = self.controller.create(req, body=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_child(self): # Test create with just cells policy rules = {"default": "is_admin:true", self.cell_extension: "is_admin:true"} self.policy.set_rules(rules) self._cell_create_child() def test_cell_create_child_with_create_policy(self): self._cell_create_child() 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") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=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") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def test_cell_create_name_with_invalid_character_raises(self): body = {'cell': {'name': 'moo\x00cow', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=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") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def test_cell_create_name_with_exclamation_point_raises(self): body = {'cell': {'name': 'moo!cow', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def test_cell_create_name_with_at_raises(self): body = {'cell': {'name': 'moo@cow', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=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") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def test_cell_create_fails_for_invalid_policy(self): body = {'cell': {'name': 'fake'}} req = self._get_request("cells") req.environ['nova.context'] = self.context req.environ['nova.context'].is_admin = False self.assertRaises(exception.PolicyNotAuthorized, self.controller.create, req, body=body) def test_cell_create_rpc_port_with_string(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': '123', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.controller.create(req, body=body) def test_cell_create_rpc_port_with_null(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': None, 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def test_cell_create_rpc_port_empty_string_raises(self): body = {'cell': {'name': 'moocow', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': '', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.create, req, body=body) def _cell_update(self): body = {'cell': {'username': 'zeb', 'password': 'sneaky'}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context res_dict = self.controller.update(req, 'cell1', body=body) cell = res_dict['cell'] self.assertEqual(cell['name'], 'cell1') self.assertEqual(cell['rpc_host'], 'r1.example.org') self.assertEqual(cell['username'], 'zeb') self.assertNotIn('password', cell) def test_cell_update(self): # Test cell update with just cell policy rules = {"default": "is_admin:true", self.cell_extension: "is_admin:true"} self.policy.set_rules(rules) self._cell_update() def test_cell_update_with_update_policy(self): self._cell_update() def test_cell_update_fails_for_invalid_policy(self): body = {'cell': {'name': 'got_changed'}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context req.environ['nova.context'].is_admin = False self.assertRaises(exception.PolicyNotAuthorized, self.controller.create, req, body=body) def test_cell_update_empty_name_raises(self): body = {'cell': {'name': '', 'username': 'zeb', 'password': 'sneaky'}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.update, req, 'cell1', body=body) def test_cell_update_empty_rpc_port_raises(self): body = {'cell': {'name': 'fake', 'username': 'zeb', 'password': 'sneaky', 'rpc_port': ''}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.update, req, 'cell1', body=body) def test_cell_update_invalid_type_raises(self): body = {'cell': {'username': 'zeb', 'type': 'invalid', 'password': 'sneaky'}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.update, req, 'cell1', body=body) def test_cell_update_without_type_specified(self): body = {'cell': {'username': 'wingwj'}} req = self._get_request("cells/cell1") req.environ['nova.context'] = self.context res_dict = self.controller.update(req, 'cell1', body=body) cell = res_dict['cell'] self.assertEqual(cell['name'], 'cell1') self.assertEqual(cell['rpc_host'], 'r1.example.org') self.assertEqual(cell['username'], 'wingwj') self.assertEqual(cell['type'], 'parent') def test_cell_update_with_type_specified(self): body1 = {'cell': {'username': 'wingwj', 'type': 'child'}} body2 = {'cell': {'username': 'wingwj', 'type': 'parent'}} req1 = self._get_request("cells/cell1") req1.environ['nova.context'] = self.context res_dict1 = self.controller.update(req1, 'cell1', body=body1) cell1 = res_dict1['cell'] req2 = self._get_request("cells/cell2") req2.environ['nova.context'] = self.context res_dict2 = self.controller.update(req2, 'cell2', body=body2) cell2 = res_dict2['cell'] self.assertEqual(cell1['name'], 'cell1') self.assertEqual(cell1['rpc_host'], 'r1.example.org') self.assertEqual(cell1['username'], 'wingwj') self.assertEqual(cell1['type'], 'child') self.assertEqual(cell2['name'], 'cell2') self.assertEqual(cell2['rpc_host'], 'r2.example.org') self.assertEqual(cell2['username'], 'wingwj') self.assertEqual(cell2['type'], 'parent') def test_cell_update_rpc_port_with_string(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': '123', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.controller.update(req, 'cell1', body=body) def test_cell_update_rpc_port_with_null(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': None, 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.update, req, 'cell1', body=body) def test_cell_update_rpc_port_empty_string_raises(self): body = {'cell': {'name': 'moocow', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': '', 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.assertRaises(self.bad_request, self.controller.update, req, 'cell1', body=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): if (self.cell_extension == 'compute_extension:cells'): 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): if (self.cell_extension == 'compute_extension:cells'): self.ext_mgr.is_loaded('os-cell-capacities').AndReturn(True) rules = {self.cell_extension: "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): if (self.cell_extension == 'compute_extension:cells'): 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): if (self.cell_extension == 'compute_extension:cells'): 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') call_info['deleted'] = kwargs.get('deleted') self.stubs.Set(cells_rpcapi.CellsAPI, 'sync_instances', sync_instances) req = self._get_request("cells/sync_instances") req.environ['nova.context'] = self.context body = {} self.controller.sync_instances(req, body=body) self.assertIsNone(call_info['project_id']) self.assertIsNone(call_info['updated_since']) body = {'project_id': 'test-project'} self.controller.sync_instances(req, body=body) self.assertEqual(call_info['project_id'], 'test-project') self.assertIsNone(call_info['updated_since']) 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.assertIsNone(call_info['project_id']) self.assertEqual(call_info['updated_since'], expected) body = {'updated_since': 'skjdfkjsdkf'} self.assertRaises(self.bad_request, self.controller.sync_instances, req, body=body) body = {'deleted': False} self.controller.sync_instances(req, body=body) self.assertIsNone(call_info['project_id']) self.assertIsNone(call_info['updated_since']) self.assertEqual(call_info['deleted'], False) body = {'deleted': 'False'} self.controller.sync_instances(req, body=body) self.assertIsNone(call_info['project_id']) self.assertIsNone(call_info['updated_since']) self.assertEqual(call_info['deleted'], False) body = {'deleted': 'True'} self.controller.sync_instances(req, body=body) self.assertIsNone(call_info['project_id']) self.assertIsNone(call_info['updated_since']) self.assertEqual(call_info['deleted'], True) body = {'deleted': 'foo'} self.assertRaises(self.bad_request, self.controller.sync_instances, req, body=body) body = {'foo': 'meow'} self.assertRaises(self.bad_request, self.controller.sync_instances, req, body=body) def test_sync_instances_fails_for_invalid_policy(self): def sync_instances(self, context, **kwargs): pass self.stubs.Set(cells_rpcapi.CellsAPI, 'sync_instances', sync_instances) req = self._get_request("cells/sync_instances") req.environ['nova.context'] = self.context req.environ['nova.context'].is_admin = False body = {} self.assertRaises(exception.PolicyNotAuthorized, self.controller.sync_instances, req, body=body) def test_cells_disabled(self): self.flags(enable=False, group='cells') req = self._get_request("cells") self.assertRaises(exc.HTTPNotImplemented, self.controller.index, req) req = self._get_request("cells/detail") self.assertRaises(exc.HTTPNotImplemented, self.controller.detail, req) req = self._get_request("cells/cell1") self.assertRaises(exc.HTTPNotImplemented, self.controller.show, req) self.assertRaises(exc.HTTPNotImplemented, self.controller.delete, req, 'cell999') req = self._get_request("cells/cells") self.assertRaises(exc.HTTPNotImplemented, self.controller.create, req, {}) req = self._get_request("cells/capacities") self.assertRaises(exc.HTTPNotImplemented, self.controller.capacities, req) req = self._get_request("cells/sync_instances") self.assertRaises(exc.HTTPNotImplemented, self.controller.sync_instances, req, {}) class CellsTestV2(CellsTestV21): cell_extension = 'compute_extension:cells' bad_request = exc.HTTPBadRequest def _get_cell_controller(self, ext_mgr): return cells_ext_v2.Controller(ext_mgr) def test_cell_create_name_with_invalid_character_raises(self): pass def test_cell_create_rpc_port_with_null(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': None, 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.controller.create(req, body=body) def test_cell_update_rpc_port_with_null(self): body = {'cell': {'name': 'fake', 'username': 'fred', 'password': 'secret', 'rpc_host': 'r3.example.org', 'rpc_port': None, 'type': 'parent'}} req = self._get_request("cells") req.environ['nova.context'] = self.context self.controller.update(req, 'cell1', body=body)