diff options
Diffstat (limited to 'nova/tests')
79 files changed, 7747 insertions, 1816 deletions
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 543bf4a62..c9e8ff42f 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -46,6 +46,7 @@ from nova.network import api as network_api from nova.network import quantumv2 from nova.openstack.common import log as logging from nova.openstack.common import rpc +from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack.compute.contrib import ( test_quantum_security_groups as test_quantum) @@ -880,6 +881,7 @@ class CloudTestCase(test.TestCase): 'instance_type_id': 1, 'host': 'host1', 'vm_state': 'active', + 'launched_at': timeutils.utcnow(), 'hostname': 'server-1111', 'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1), 'system_metadata': sys_meta @@ -891,6 +893,7 @@ class CloudTestCase(test.TestCase): 'instance_type_id': 1, 'host': 'host2', 'vm_state': 'active', + 'launched_at': timeutils.utcnow(), 'hostname': 'server-1112', 'created_at': datetime.datetime(2012, 5, 1, 1, 1, 2), 'system_metadata': sys_meta @@ -2121,6 +2124,7 @@ class CloudTestCase(test.TestCase): return [dict(id=1, source_type='snapshot', destination_type='volume', + instance_uuid=inst_id, snapshot_id=snapshots[0], volume_id=volumes[0], volume_size=1, @@ -2134,7 +2138,8 @@ class CloudTestCase(test.TestCase): virt_driver = {} - def fake_power_on(self, instance): + def fake_power_on(self, context, instance, network_info, + block_device_info): virt_driver['powered_on'] = True self.stubs.Set(fake_virt.FakeDriver, 'power_on', fake_power_on) @@ -2442,6 +2447,7 @@ class CloudTestCase(test.TestCase): 'image_ref': image_uuid, 'instance_type_id': 1, 'vm_state': 'active', + 'launched_at': timeutils.utcnow(), 'hostname': 'server-1111', 'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1) } @@ -2492,6 +2498,7 @@ class CloudTestCase(test.TestCase): 'image_ref': image_uuid, 'instance_type_id': 1, 'vm_state': 'active', + 'launched_at': timeutils.utcnow(), 'hostname': 'server-1111', 'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1) } @@ -2501,6 +2508,7 @@ class CloudTestCase(test.TestCase): 'image_ref': image_uuid, 'instance_type_id': 1, 'vm_state': 'active', + 'launched_at': timeutils.utcnow(), 'hostname': 'server-1112', 'created_at': datetime.datetime(2012, 5, 1, 1, 1, 2) } diff --git a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py index 5e64bdf94..39eebbcd1 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -26,6 +26,7 @@ from nova.conductor import api as conductor_api from nova import context from nova import exception from nova.openstack.common import jsonutils +from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack import fakes @@ -41,6 +42,7 @@ INSTANCE = { "tenant_id": 'fake_tenant_id', "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "launched_at": datetime.datetime(2010, 11, 11, 11, 0, 0), "security_groups": [{"id": 1, "name": "test"}], "progress": 0, "image_ref": 'http://foo.com/123', @@ -61,7 +63,7 @@ def fake_compute_api_raises_invalid_state(*args, **kwargs): def fake_compute_api_get(self, context, instance_id): return {'id': 1, 'uuid': instance_id, 'vm_state': vm_states.ACTIVE, - 'task_state': None} + 'task_state': None, 'launched_at': timeutils.utcnow()} class AdminActionsTest(test.TestCase): diff --git a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py index 2ccb9fa31..0b63960ce 100644 --- a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py +++ b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py @@ -76,6 +76,7 @@ def fake_set_availability_zones(context, services): class AvailabilityZoneApiTest(test.TestCase): def setUp(self): super(AvailabilityZoneApiTest, self).setUp() + availability_zones._reset_cache() self.stubs.Set(db, 'service_get_all', fake_service_get_all) self.stubs.Set(availability_zones, 'set_availability_zones', fake_set_availability_zones) diff --git a/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py b/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py index 957625b34..93a623bf6 100644 --- a/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py +++ b/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py @@ -12,7 +12,7 @@ # 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 +# under the License. import telnetlib diff --git a/nova/tests/api/openstack/compute/contrib/test_evacuate.py b/nova/tests/api/openstack/compute/contrib/test_evacuate.py index 816bac565..f2ebf9d78 100644 --- a/nova/tests/api/openstack/compute/contrib/test_evacuate.py +++ b/nova/tests/api/openstack/compute/contrib/test_evacuate.py @@ -76,8 +76,8 @@ class EvacuateTest(test.TestCase): ctxt.project_id = 'fake' ctxt.is_admin = True app = fakes.wsgi_app(fake_auth_context=ctxt) - uuid = self.UUID - req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid) + uuid1 = self.UUID + req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid1) req.method = 'POST' req.body = jsonutils.dumps({ 'evacuate': { @@ -105,8 +105,8 @@ class EvacuateTest(test.TestCase): ctxt.project_id = 'fake' ctxt.is_admin = True app = fakes.wsgi_app(fake_auth_context=ctxt) - uuid = self.UUID - req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid) + uuid1 = self.UUID + req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid1) req.method = 'POST' req.body = jsonutils.dumps({ 'evacuate': { @@ -132,8 +132,8 @@ class EvacuateTest(test.TestCase): ctxt.project_id = 'fake' ctxt.is_admin = True app = fakes.wsgi_app(fake_auth_context=ctxt) - uuid = self.UUID - req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid) + uuid1 = self.UUID + req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid1) req.method = 'POST' req.body = jsonutils.dumps({ 'evacuate': { @@ -161,8 +161,8 @@ class EvacuateTest(test.TestCase): ctxt.project_id = 'fake' ctxt.is_admin = True app = fakes.wsgi_app(fake_auth_context=ctxt) - uuid = self.UUID - req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid) + uuid1 = self.UUID + req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid1) req.method = 'POST' req.body = jsonutils.dumps({ 'evacuate': { @@ -184,8 +184,8 @@ class EvacuateTest(test.TestCase): def test_not_admin(self): ctxt = context.RequestContext('fake', 'fake', is_admin=False) app = fakes.wsgi_app(fake_auth_context=ctxt) - uuid = self.UUID - req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid) + uuid1 = self.UUID + req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid1) req.method = 'POST' req.body = jsonutils.dumps({ 'evacuate': { diff --git a/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py index 814c0fff4..59d60acf2 100644 --- a/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py +++ b/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py @@ -19,6 +19,7 @@ import webob from nova.api.openstack.compute.contrib import extended_availability_zone from nova import availability_zones from nova import compute +from nova.compute import vm_states from nova import exception from nova.openstack.common import jsonutils from nova import test @@ -29,14 +30,31 @@ UUID2 = '00000000-0000-0000-0000-000000000002' UUID3 = '00000000-0000-0000-0000-000000000003' +def fake_compute_get_az(*args, **kwargs): + inst = fakes.stub_instance(1, uuid=UUID3, host="get-host", + vm_state=vm_states.ACTIVE, + availability_zone='fakeaz') + return inst + + +def fake_compute_get_empty(*args, **kwargs): + inst = fakes.stub_instance(1, uuid=UUID3, host="", + vm_state=vm_states.ACTIVE, + availability_zone='fakeaz') + return inst + + def fake_compute_get(*args, **kwargs): - inst = fakes.stub_instance(1, uuid=UUID3, host="get-host") + inst = fakes.stub_instance(1, uuid=UUID3, host="get-host", + vm_state=vm_states.ACTIVE) return inst def fake_compute_get_all(*args, **kwargs): - inst1 = fakes.stub_instance(1, uuid=UUID1, host="all-host") - inst2 = fakes.stub_instance(2, uuid=UUID2, host="all-host") + inst1 = fakes.stub_instance(1, uuid=UUID1, host="all-host", + vm_state=vm_states.ACTIVE) + inst2 = fakes.stub_instance(2, uuid=UUID2, host="all-host", + vm_state=vm_states.ACTIVE) return [inst1, inst2] @@ -44,12 +62,17 @@ def fake_get_host_availability_zone(context, host): return host +def fake_get_no_host_availability_zone(context, host): + return None + + class ExtendedServerAttributesTest(test.TestCase): content_type = 'application/json' prefix = 'OS-EXT-AZ:' def setUp(self): super(ExtendedServerAttributesTest, self).setUp() + availability_zones._reset_cache() fakes.stub_out_nw_api(self.stubs) self.stubs.Set(compute.api.API, 'get', fake_compute_get) self.stubs.Set(compute.api.API, 'get_all', fake_compute_get_all) @@ -77,6 +100,28 @@ class ExtendedServerAttributesTest(test.TestCase): self.assertEqual(server.get('%savailability_zone' % self.prefix), az) + def test_show_no_host_az(self): + self.stubs.Set(compute.api.API, 'get', fake_compute_get_az) + self.stubs.Set(availability_zones, 'get_host_availability_zone', + fake_get_no_host_availability_zone) + + url = '/v2/fake/servers/%s' % UUID3 + res = self._make_request(url) + + self.assertEqual(res.status_int, 200) + self.assertServerAttributes(self._get_server(res.body), 'fakeaz') + + def test_show_empty_host_az(self): + self.stubs.Set(compute.api.API, 'get', fake_compute_get_empty) + self.stubs.Set(availability_zones, 'get_host_availability_zone', + fake_get_no_host_availability_zone) + + url = '/v2/fake/servers/%s' % UUID3 + res = self._make_request(url) + + self.assertEqual(res.status_int, 200) + self.assertServerAttributes(self._get_server(res.body), 'fakeaz') + def test_show(self): url = '/v2/fake/servers/%s' % UUID3 res = self._make_request(url) diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py index df2c3d392..459dae932 100644 --- a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py +++ b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py @@ -218,3 +218,18 @@ class FlavorManageTest(test.TestCase): req.body = jsonutils.dumps(expected) res = req.get_response(self.app) self.assertEqual(res.status_int, 409) + + def test_invalid_memory_mb(self): + """Check negative and decimal number can't be accepted.""" + + self.stubs.UnsetAll() + self.assertRaises(exception.InvalidInput, flavors.create, "abc", + -512, 2, 1, 1, 1234, 512, 1, True) + self.assertRaises(exception.InvalidInput, flavors.create, "abcd", + 512.2, 2, 1, 1, 1234, 512, 1, True) + self.assertRaises(exception.InvalidInput, flavors.create, "abcde", + None, 2, 1, 1, 1234, 512, 1, True) + self.assertRaises(exception.InvalidInput, flavors.create, "abcdef", + 512, 2, None, 1, 1234, 512, 1, True) + self.assertRaises(exception.InvalidInput, flavors.create, "abcdef", + "test_memory_mb", 2, None, 1, 1234, 512, 1, True) diff --git a/nova/tests/api/openstack/compute/contrib/test_security_groups.py b/nova/tests/api/openstack/compute/contrib/test_security_groups.py index f1433bd0a..ac3e8885d 100644 --- a/nova/tests/api/openstack/compute/contrib/test_security_groups.py +++ b/nova/tests/api/openstack/compute/contrib/test_security_groups.py @@ -716,7 +716,7 @@ class TestSecurityGroupRules(test.TestCase): db1 = security_group_db(self.sg1) db2 = security_group_db(self.sg2) - def return_security_group(context, group_id): + def return_security_group(context, group_id, columns_to_join=None): if group_id == db1['id']: return db1 if group_id == db2['id']: diff --git a/nova/tests/api/openstack/compute/extensions/test_plugin_api_extensions.py b/nova/tests/api/openstack/compute/extensions/test_plugin_api_extensions.py deleted file mode 100644 index 3aac638c6..000000000 --- a/nova/tests/api/openstack/compute/extensions/test_plugin_api_extensions.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2011 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 pkg_resources - -from nova.api.openstack.compute import extensions as computeextensions -from nova.api.openstack import extensions -from nova.openstack.common.plugin import plugin -from nova import test - - -class StubController(object): - - def i_am_the_stub(self): - pass - - -class StubControllerExtension(extensions.ExtensionDescriptor): - """This is a docstring. We need it.""" - name = 'stubextension' - alias = 'stubby' - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension('testme', - StubController()) - resources.append(res) - return resources - - -service_list = [] - - -class TestPluginClass(plugin.Plugin): - - def __init__(self, service_name): - super(TestPluginClass, self).__init__(service_name) - self._add_api_extension_descriptor(StubControllerExtension) - service_list.append(service_name) - - -class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): - return TestPluginClass - - -class APITestCase(test.TestCase): - """Test case for the plugin api extension interface.""" - def test_add_extension(self): - def mock_load(_s): - return TestPluginClass() - - def mock_iter_entry_points(_t): - return [MockEntrypoint("fake", "fake", ["fake"])] - - self.stubs.Set(pkg_resources, 'iter_entry_points', - mock_iter_entry_points) - global service_list - service_list = [] - - # Marking out the default extension paths makes this test MUCH faster. - self.flags(osapi_compute_extension=[]) - - found = False - mgr = computeextensions.ExtensionManager() - for res in mgr.get_resources(): - # We have to use this weird 'dir' check because - # the plugin framework muddies up the classname - # such that 'isinstance' doesn't work right. - if 'i_am_the_stub' in dir(res.controller): - found = True - - self.assertTrue(found) - self.assertEqual(len(service_list), 1) - self.assertEqual(service_list[0], 'compute-extensions') diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_agents.py b/nova/tests/api/openstack/compute/plugins/v3/test_agents.py new file mode 100644 index 000000000..0154ada9e --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_agents.py @@ -0,0 +1,181 @@ +# Copyright 2012 IBM Corp. +# +# 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. + + +from nova.api.openstack.compute.contrib import agents +from nova import context +from nova import db +from nova.db.sqlalchemy import models +from nova import test + +fake_agents_list = [{'hypervisor': 'kvm', 'os': 'win', + 'architecture': 'x86', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545', + 'id': 1}, + {'hypervisor': 'kvm', 'os': 'linux', + 'architecture': 'x86', + 'version': '16.0', + 'url': 'xxx://xxxx/xxx/xxx1', + 'md5hash': 'add6bb58e139be103324d04d82d8f546', + 'id': 2}, + {'hypervisor': 'xen', 'os': 'linux', + 'architecture': 'x86', + 'version': '16.0', + 'url': 'xxx://xxxx/xxx/xxx2', + 'md5hash': 'add6bb58e139be103324d04d82d8f547', + 'id': 3}, + {'hypervisor': 'xen', 'os': 'win', + 'architecture': 'power', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx3', + 'md5hash': 'add6bb58e139be103324d04d82d8f548', + 'id': 4}, + ] + + +def fake_agent_build_get_all(context, hypervisor): + agent_build_all = [] + for agent in fake_agents_list: + if hypervisor and hypervisor != agent['hypervisor']: + continue + agent_build_ref = models.AgentBuild() + agent_build_ref.update(agent) + agent_build_all.append(agent_build_ref) + return agent_build_all + + +def fake_agent_build_update(context, agent_build_id, values): + pass + + +def fake_agent_build_destroy(context, agent_update_id): + pass + + +def fake_agent_build_create(context, values): + values['id'] = 1 + agent_build_ref = models.AgentBuild() + agent_build_ref.update(values) + return agent_build_ref + + +class FakeRequest(object): + environ = {"nova.context": context.get_admin_context()} + GET = {} + + +class FakeRequestWithHypervisor(object): + environ = {"nova.context": context.get_admin_context()} + GET = {'hypervisor': 'kvm'} + + +class AgentsTest(test.TestCase): + + def setUp(self): + super(AgentsTest, self).setUp() + + self.stubs.Set(db, "agent_build_get_all", + fake_agent_build_get_all) + self.stubs.Set(db, "agent_build_update", + fake_agent_build_update) + self.stubs.Set(db, "agent_build_destroy", + fake_agent_build_destroy) + self.stubs.Set(db, "agent_build_create", + fake_agent_build_create) + self.context = context.get_admin_context() + self.controller = agents.AgentController() + + def test_agents_create(self): + req = FakeRequest() + body = {'agent': {'hypervisor': 'kvm', + 'os': 'win', + 'architecture': 'x86', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545'}} + response = {'agent': {'hypervisor': 'kvm', + 'os': 'win', + 'architecture': 'x86', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545', + 'agent_id': 1}} + res_dict = self.controller.create(req, body) + self.assertEqual(res_dict, response) + + def test_agents_delete(self): + req = FakeRequest() + self.controller.delete(req, 1) + + def test_agents_list(self): + req = FakeRequest() + res_dict = self.controller.index(req) + agents_list = [{'hypervisor': 'kvm', 'os': 'win', + 'architecture': 'x86', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545', + 'agent_id': 1}, + {'hypervisor': 'kvm', 'os': 'linux', + 'architecture': 'x86', + 'version': '16.0', + 'url': 'xxx://xxxx/xxx/xxx1', + 'md5hash': 'add6bb58e139be103324d04d82d8f546', + 'agent_id': 2}, + {'hypervisor': 'xen', 'os': 'linux', + 'architecture': 'x86', + 'version': '16.0', + 'url': 'xxx://xxxx/xxx/xxx2', + 'md5hash': 'add6bb58e139be103324d04d82d8f547', + 'agent_id': 3}, + {'hypervisor': 'xen', 'os': 'win', + 'architecture': 'power', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx3', + 'md5hash': 'add6bb58e139be103324d04d82d8f548', + 'agent_id': 4}, + ] + self.assertEqual(res_dict, {'agents': agents_list}) + + def test_agents_list_with_hypervisor(self): + req = FakeRequestWithHypervisor() + res_dict = self.controller.index(req) + response = [{'hypervisor': 'kvm', 'os': 'win', + 'architecture': 'x86', + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545', + 'agent_id': 1}, + {'hypervisor': 'kvm', 'os': 'linux', + 'architecture': 'x86', + 'version': '16.0', + 'url': 'xxx://xxxx/xxx/xxx1', + 'md5hash': 'add6bb58e139be103324d04d82d8f546', + 'agent_id': 2}, + ] + self.assertEqual(res_dict, {'agents': response}) + + def test_agents_update(self): + req = FakeRequest() + body = {'para': {'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545'}} + response = {'agent': {'agent_id': 1, + 'version': '7.0', + 'url': 'xxx://xxxx/xxx/xxx', + 'md5hash': 'add6bb58e139be103324d04d82d8f545'}} + res_dict = self.controller.update(req, 1, body) + self.assertEqual(res_dict, response) 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..f369c06e3 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_cells.py @@ -0,0 +1,469 @@ +# 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.plugins.v3 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.controller = cells_ext.CellsController() + self.context = context.get_admin_context() + + def _get_request(self, resource): + return fakes.HTTPRequestV3.blank('/' + 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.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): + 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.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.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()) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py b/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py new file mode 100644 index 000000000..222087872 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py @@ -0,0 +1,77 @@ +# Copyright (c) 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. + +from lxml import etree + +from nova.api.openstack.compute.plugins.v3 import certificates +from nova import context +from nova.openstack.common import rpc +from nova import test +from nova.tests.api.openstack import fakes + + +def fake_get_root_cert(context, *args, **kwargs): + return 'fakeroot' + + +def fake_create_cert(context, *args, **kwargs): + return 'fakepk', 'fakecert' + + +class CertificatesTest(test.TestCase): + def setUp(self): + super(CertificatesTest, self).setUp() + self.context = context.RequestContext('fake', 'fake') + self.controller = certificates.CertificatesController() + + def test_translate_certificate_view(self): + pk, cert = fake_create_cert(self.context) + view = certificates._translate_certificate_view(cert, pk) + self.assertEqual(view['data'], cert) + self.assertEqual(view['private_key'], pk) + + def test_certificates_show_root(self): + self.stubs.Set(rpc, 'call', fake_get_root_cert) + req = fakes.HTTPRequestV3.blank('/os-certificates/root') + res_dict = self.controller.show(req, 'root') + + cert = fake_get_root_cert(self.context) + response = {'certificate': {'data': cert, 'private_key': None}} + self.assertEqual(res_dict, response) + + def test_certificates_create_certificate(self): + self.stubs.Set(rpc, 'call', fake_create_cert) + req = fakes.HTTPRequestV3.blank('/os-certificates/') + res_dict = self.controller.create(req) + + pk, cert = fake_create_cert(self.context) + response = {'certificate': {'data': cert, 'private_key': pk}} + self.assertEqual(res_dict, response) + + +class CertificatesSerializerTest(test.TestCase): + def test_index_serializer(self): + serializer = certificates.CertificateTemplate() + text = serializer.serialize(dict( + certificate=dict( + data='fakecert', + private_key='fakepk'), + )) + + tree = etree.fromstring(text) + + self.assertEqual('certificate', tree.tag) + self.assertEqual('fakepk', tree.get('private_key')) + self.assertEqual('fakecert', tree.get('data')) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_evacuate.py b/nova/tests/api/openstack/compute/plugins/v3/test_evacuate.py new file mode 100644 index 000000000..72a531277 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_evacuate.py @@ -0,0 +1,198 @@ +# Copyright 2013 OpenStack Foundation +# +# 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 uuid + +from oslo.config import cfg +import webob + +from nova.compute import api as compute_api +from nova.compute import vm_states +from nova import context +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + +CONF = cfg.CONF +CONF.import_opt('password_length', 'nova.utils') + + +def fake_compute_api(*args, **kwargs): + return True + + +def fake_compute_api_get(self, context, instance_id): + return { + 'id': 1, + 'uuid': instance_id, + 'vm_state': vm_states.ACTIVE, + 'task_state': None, 'host': 'host1' + } + + +class EvacuateTest(test.TestCase): + + _methods = ('resize', 'evacuate') + + def setUp(self): + super(EvacuateTest, self).setUp() + self.stubs.Set(compute_api.API, 'get', fake_compute_api_get) + self.UUID = uuid.uuid4() + for _method in self._methods: + self.stubs.Set(compute_api.API, _method, fake_compute_api) + + def test_evacuate_instance_with_no_target(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + req = webob.Request.blank('/v3/servers/%s/action' % self.UUID) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'onSharedStorage': 'False', + 'adminPass': 'MyNewPass' + } + }) + req.content_type = 'application/json' + res = req.get_response(app) + self.assertEqual(res.status_int, 400) + + def test_evacuate_instance_with_target(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + uuid1 = self.UUID + req = webob.Request.blank('/v3/servers/%s/action' % uuid1) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'host': 'my_host', + 'onSharedStorage': 'false', + 'adminPass': 'MyNewPass' + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + self.stubs.Set(compute_api.API, 'update', fake_update) + + resp = req.get_response(app) + self.assertEqual(resp.status_int, 200) + resp_json = jsonutils.loads(resp.body) + self.assertEqual("MyNewPass", resp_json['adminPass']) + + def test_evacuate_shared_and_pass(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + uuid1 = self.UUID + req = webob.Request.blank('/v3/servers/%s/action' % uuid1) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'host': 'my_host', + 'onSharedStorage': 'True', + 'adminPass': 'MyNewPass' + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + self.stubs.Set(compute_api.API, 'update', fake_update) + + res = req.get_response(app) + self.assertEqual(res.status_int, 400) + + def test_evacuate_not_shared_pass_generated(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + uuid1 = self.UUID + req = webob.Request.blank('/v3/servers/%s/action' % uuid1) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'host': 'my_host', + 'onSharedStorage': 'False', + } + }) + + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + self.stubs.Set(compute_api.API, 'update', fake_update) + + resp = req.get_response(app) + self.assertEqual(resp.status_int, 200) + resp_json = jsonutils.loads(resp.body) + self.assertEqual(CONF.password_length, len(resp_json['adminPass'])) + + def test_evacuate_shared(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + uuid1 = self.UUID + req = webob.Request.blank('/v3/servers/%s/action' % uuid1) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'host': 'my_host', + 'onSharedStorage': 'True', + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + self.stubs.Set(compute_api.API, 'update', fake_update) + + res = req.get_response(app) + self.assertEqual(res.status_int, 200) + + def test_not_admin(self): + ctxt = context.RequestContext('fake', 'fake', is_admin=False) + app = fakes.wsgi_app_v3(fake_auth_context=ctxt) + uuid1 = self.UUID + req = webob.Request.blank('/v3/servers/%s/action' % uuid1) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'evacuate': { + 'host': 'my_host', + 'onSharedStorage': 'True', + } + }) + req.content_type = 'application/json' + res = req.get_response(app) + self.assertEqual(res.status_int, 403) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py b/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py new file mode 100644 index 000000000..a19d28064 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py @@ -0,0 +1,108 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 IBM Corp. +# +# 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. + +from nova.api.openstack.compute import plugins +from nova.api.openstack.compute.plugins.v3 import extension_info +from nova import exception +from nova import policy +from nova import test +from nova.tests.api.openstack import fakes + + +class fake_extension(object): + def __init__(self, name, alias, description, namespace, version): + self.name = name + self.alias = alias + self.__doc__ = description + self.namespace = namespace + self.version = version + + +fake_extensions = { + 'ext1-alias': fake_extension('ext1', 'ext1-alias', 'ext1 description', + 'ext1 namespace', 1), + 'ext2-alias': fake_extension('ext2', 'ext2-alias', 'ext2 description', + 'ext2 namespace', 2), + 'ext3-alias': fake_extension('ext3', 'ext3-alias', 'ext3 description', + 'ext3 namespace', 1) +} + +def fake_policy_enforce(context, action, target, do_raise=True): + return True + +def fake_policy_enforce_selective(context, action, target, do_raise=True): + if action == 'compute_extension:v3:ext1-alias:discoverable': + raise exception.NotAuthorized + else: + return True + + +class ExtensionInfoTest(test.TestCase): + + def setUp(self): + super(ExtensionInfoTest, self).setUp() + ext_info = plugins.LoadedExtensionInfo() + ext_info.extensions = fake_extensions + self.controller = extension_info.ExtensionInfoController(ext_info) + + def test_extension_info_list(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce) + req = fakes.HTTPRequestV3.blank('/extensions') + res_dict = self.controller.index(req) + self.assertEqual(3, len(res_dict['extensions'])) + for e in res_dict['extensions']: + self.assertIn(e['alias'], fake_extensions) + self.assertEqual(e['name'], fake_extensions[e['alias']].name) + self.assertEqual(e['alias'], fake_extensions[e['alias']].alias) + self.assertEqual(e['description'], + fake_extensions[e['alias']].__doc__) + self.assertEqual(e['namespace'], + fake_extensions[e['alias']].namespace) + self.assertEqual(e['version'], + fake_extensions[e['alias']].version) + + def test_extension_info_show(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce) + req = fakes.HTTPRequestV3.blank('/extensions/ext1-alias') + res_dict = self.controller.show(req, 'ext1-alias') + self.assertEqual(1, len(res_dict)) + self.assertEqual(res_dict['extension']['name'], + fake_extensions['ext1-alias'].name) + self.assertEqual(res_dict['extension']['alias'], + fake_extensions['ext1-alias'].alias) + self.assertEqual(res_dict['extension']['description'], + fake_extensions['ext1-alias'].__doc__) + self.assertEqual(res_dict['extension']['namespace'], + fake_extensions['ext1-alias'].namespace) + self.assertEqual(res_dict['extension']['version'], + fake_extensions['ext1-alias'].version) + + def test_extension_info_list_not_all_discoverable(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce_selective) + req = fakes.HTTPRequestV3.blank('/extensions') + res_dict = self.controller.index(req) + self.assertEqual(2, len(res_dict['extensions'])) + for e in res_dict['extensions']: + self.assertNotEqual('ext1-alias', e['alias']) + self.assertIn(e['alias'], fake_extensions) + self.assertEqual(e['name'], fake_extensions[e['alias']].name) + self.assertEqual(e['alias'], fake_extensions[e['alias']].alias) + self.assertEqual(e['description'], + fake_extensions[e['alias']].__doc__) + self.assertEqual(e['namespace'], + fake_extensions[e['alias']].namespace) + self.assertEqual(e['version'], + fake_extensions[e['alias']].version) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_flavor_access.py b/nova/tests/api/openstack/compute/plugins/v3/test_flavor_access.py new file mode 100644 index 000000000..7d9ec93df --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_flavor_access.py @@ -0,0 +1,308 @@ +# Copyright 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 datetime + +from lxml import etree +from webob import exc + +from nova.api.openstack.compute.plugins.v3 import flavor_access +from nova.api.openstack.compute import flavors as flavors_api +from nova.compute import flavors +from nova import context +from nova import exception +from nova import test +from nova.tests.api.openstack import fakes + + +def generate_flavor(flavorid, ispublic): + return { + 'id': flavorid, + 'flavorid': str(flavorid), + 'root_gb': 1, + 'ephemeral_gb': 1, + 'name': u'test', + 'deleted': False, + 'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1), + 'updated_at': None, + 'memory_mb': 512, + 'vcpus': 1, + 'swap': 512, + 'rxtx_factor': 1.0, + 'extra_specs': {}, + 'deleted_at': None, + 'vcpu_weight': None, + 'is_public': bool(ispublic) + } + + +INSTANCE_TYPES = { + '0': generate_flavor(0, True), + '1': generate_flavor(1, True), + '2': generate_flavor(2, False), + '3': generate_flavor(3, False)} + + +ACCESS_LIST = [{'flavor_id': '2', 'project_id': 'proj2'}, + {'flavor_id': '2', 'project_id': 'proj3'}, + {'flavor_id': '3', 'project_id': 'proj3'}] + + +def fake_get_flavor_access_by_flavor_id(flavorid): + res = [] + for access in ACCESS_LIST: + if access['flavor_id'] == flavorid: + res.append(access) + return res + + +def fake_get_flavor_by_flavor_id(flavorid): + return INSTANCE_TYPES[flavorid] + + +def _has_flavor_access(flavorid, projectid): + for access in ACCESS_LIST: + if access['flavor_id'] == flavorid and \ + access['project_id'] == projectid: + return True + return False + + +def fake_get_all_flavors(context, inactive=0, filters=None): + if filters == None or filters['is_public'] == None: + return INSTANCE_TYPES + + res = {} + for k, v in INSTANCE_TYPES.iteritems(): + if filters['is_public'] and _has_flavor_access(k, context.project_id): + res.update({k: v}) + continue + if v['is_public'] == filters['is_public']: + res.update({k: v}) + + return res + + +class FakeRequest(object): + environ = {"nova.context": context.get_admin_context()} + + def get_db_flavor(self, flavor_id): + return INSTANCE_TYPES[flavor_id] + + +class FakeResponse(object): + obj = {'flavor': {'id': '0'}, + 'flavors': [ + {'id': '0'}, + {'id': '2'}] + } + + def attach(self, **kwargs): + pass + + +class FlavorAccessTest(test.TestCase): + def setUp(self): + super(FlavorAccessTest, self).setUp() + self.flavor_controller = flavors_api.Controller() + self.flavor_access_controller = flavor_access.FlavorAccessController() + self.flavor_action_controller = flavor_access.FlavorActionController() + self.req = FakeRequest() + self.context = self.req.environ['nova.context'] + self.stubs.Set(flavors, 'get_flavor_by_flavor_id', + fake_get_flavor_by_flavor_id) + self.stubs.Set(flavors, 'get_all_flavors', fake_get_all_flavors) + self.stubs.Set(flavors, 'get_flavor_access_by_flavor_id', + fake_get_flavor_access_by_flavor_id) + + def _verify_flavor_list(self, result, expected): + # result already sorted by flavor_id + self.assertEqual(len(result), len(expected)) + + for d1, d2 in zip(result, expected): + self.assertEqual(d1['id'], d2['id']) + + def test_list_flavor_access_public(self): + # query os-flavor-access on public flavor should return 404 + req = fakes.HTTPRequest.blank('/v3/fake/flavors/os-flavor-access', + use_admin_context=True) + self.assertRaises(exc.HTTPNotFound, + self.flavor_access_controller.index, + self.req, '1') + + def test_list_flavor_access_private(self): + expected = {'flavor_access': [ + {'flavor_id': '2', 'tenant_id': 'proj2'}, + {'flavor_id': '2', 'tenant_id': 'proj3'}]} + result = self.flavor_access_controller.index(self.req, '2') + self.assertEqual(result, expected) + + def test_list_flavor_with_admin_default_proj1(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors', + use_admin_context=True) + req.environ['nova.context'].project_id = 'proj1' + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_admin_default_proj2(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}, {'id': '2'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors', + use_admin_context=True) + req.environ['nova.context'].project_id = 'proj2' + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_admin_ispublic_true(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=true', + use_admin_context=True) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_admin_ispublic_false(self): + expected = {'flavors': [{'id': '2'}, {'id': '3'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=false', + use_admin_context=True) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_admin_ispublic_false_proj2(self): + expected = {'flavors': [{'id': '2'}, {'id': '3'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=false', + use_admin_context=True) + req.environ['nova.context'].project_id = 'proj2' + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_admin_ispublic_none(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}, {'id': '2'}, + {'id': '3'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=none', + use_admin_context=True) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_no_admin_default(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors', + use_admin_context=False) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_no_admin_ispublic_true(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=true', + use_admin_context=False) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_no_admin_ispublic_false(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=false', + use_admin_context=False) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_list_flavor_with_no_admin_ispublic_none(self): + expected = {'flavors': [{'id': '0'}, {'id': '1'}]} + req = fakes.HTTPRequest.blank('/v3/fake/flavors?is_public=none', + use_admin_context=False) + result = self.flavor_controller.index(req) + self._verify_flavor_list(result['flavors'], expected['flavors']) + + def test_show(self): + resp = FakeResponse() + self.flavor_action_controller.show(self.req, resp, '0') + self.assertEqual({'id': '0', 'os-flavor-access:is_public': True}, + resp.obj['flavor']) + self.flavor_action_controller.show(self.req, resp, '2') + self.assertEqual({'id': '0', 'os-flavor-access:is_public': False}, + resp.obj['flavor']) + + def test_detail(self): + resp = FakeResponse() + self.flavor_action_controller.detail(self.req, resp) + self.assertEqual([{'id': '0', 'os-flavor-access:is_public': True}, + {'id': '2', 'os-flavor-access:is_public': False}], + resp.obj['flavors']) + + def test_create(self): + resp = FakeResponse() + self.flavor_action_controller.create(self.req, {}, resp) + self.assertEqual({'id': '0', 'os-flavor-access:is_public': True}, + resp.obj['flavor']) + + def test_add_tenant_access(self): + def stub_add_flavor_access(flavorid, projectid, ctxt=None): + self.assertEqual('3', flavorid, "flavorid") + self.assertEqual("proj2", projectid, "projectid") + self.stubs.Set(flavors, 'add_flavor_access', + stub_add_flavor_access) + expected = {'flavor_access': + [{'flavor_id': '3', 'tenant_id': 'proj3'}]} + body = {'addTenantAccess': {'tenant': 'proj2'}} + req = fakes.HTTPRequest.blank('/v3/fake/flavors/2/action', + use_admin_context=True) + result = self.flavor_action_controller.\ + _addTenantAccess(req, '3', body) + self.assertEqual(result, expected) + + def test_add_tenant_access_with_already_added_access(self): + def stub_add_flavor_access(flavorid, projectid, ctxt=None): + raise exception.FlavorAccessExists(flavor_id=flavorid, + project_id=projectid) + self.stubs.Set(flavors, 'add_flavor_access', + stub_add_flavor_access) + body = {'addTenantAccess': {'tenant': 'proj2'}} + req = fakes.HTTPRequest.blank('/v3/fake/flavors/2/action', + use_admin_context=True) + self.assertRaises(exc.HTTPConflict, + self.flavor_action_controller._addTenantAccess, + self.req, '3', body) + + def test_remove_tenant_access_with_bad_access(self): + def stub_remove_flavor_access(flavorid, projectid, ctxt=None): + raise exception.FlavorAccessNotFound(flavor_id=flavorid, + project_id=projectid) + self.stubs.Set(flavors, 'remove_flavor_access', + stub_remove_flavor_access) + body = {'removeTenantAccess': {'tenant': 'proj2'}} + req = fakes.HTTPRequest.blank('/v3/fake/flavors/2/action', + use_admin_context=True) + self.assertRaises(exc.HTTPNotFound, + self.flavor_action_controller._removeTenantAccess, + self.req, '3', body) + + +class FlavorAccessSerializerTest(test.TestCase): + def test_serializer_empty(self): + serializer = flavor_access.FlavorAccessTemplate() + text = serializer.serialize(dict(flavor_access=[])) + tree = etree.fromstring(text) + self.assertEqual(len(tree), 0) + + def test_serializer(self): + expected = ("<?xml version='1.0' encoding='UTF-8'?>\n" + '<flavor_access>' + '<access tenant_id="proj2" flavor_id="2"/>' + '<access tenant_id="proj3" flavor_id="2"/>' + '</flavor_access>') + access_list = [{'flavor_id': '2', 'tenant_id': 'proj2'}, + {'flavor_id': '2', 'tenant_id': 'proj3'}] + + serializer = flavor_access.FlavorAccessTemplate() + text = serializer.serialize(dict(flavor_access=access_list)) + self.assertEqual(text, expected) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_flavor_disabled.py b/nova/tests/api/openstack/compute/plugins/v3/test_flavor_disabled.py new file mode 100644 index 000000000..5ff7f4035 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_flavor_disabled.py @@ -0,0 +1,101 @@ +# Copyright 2012 Nebula, Inc. +# +# 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. + +from lxml import etree +import webob + +from nova.api.openstack.compute.plugins.v3 import flavor_disabled +from nova.compute import flavors +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + +FAKE_FLAVORS = { + 'flavor 1': { + "flavorid": '1', + "name": 'flavor 1', + "memory_mb": '256', + "root_gb": '10', + "disabled": False, + }, + 'flavor 2': { + "flavorid": '2', + "name": 'flavor 2', + "memory_mb": '512', + "root_gb": '20', + "disabled": True, + }, +} + + +def fake_flavor_get_by_flavor_id(flavorid): + return FAKE_FLAVORS['flavor %s' % flavorid] + + +def fake_flavor_get_all(*args, **kwargs): + return FAKE_FLAVORS + + +class FlavorDisabledTest(test.TestCase): + content_type = 'application/json' + prefix = '%s:' % flavor_disabled.FlavorDisabled.alias + + def setUp(self): + super(FlavorDisabledTest, self).setUp() + fakes.stub_out_nw_api(self.stubs) + self.stubs.Set(flavors, "get_all_flavors", + fake_flavor_get_all) + self.stubs.Set(flavors, + "get_flavor_by_flavor_id", + fake_flavor_get_by_flavor_id) + + def _make_request(self, url): + req = webob.Request.blank(url) + req.headers['Accept'] = self.content_type + app = fakes.wsgi_app_v3(init_only=('servers', 'flavors', + 'os-flavor-disabled')) + return req.get_response(app) + + def _get_flavor(self, body): + return jsonutils.loads(body).get('flavor') + + def _get_flavors(self, body): + return jsonutils.loads(body).get('flavors') + + def assertFlavorDisabled(self, flavor, disabled): + self.assertEqual(str(flavor.get('%sdisabled' % self.prefix)), disabled) + + def test_show(self): + res = self._make_request('/v3/flavors/1') + self.assertEqual(res.status_int, 200, res.body) + self.assertFlavorDisabled(self._get_flavor(res.body), 'False') + + def test_detail(self): + res = self._make_request('/v3/flavors/detail') + + self.assertEqual(res.status_int, 200, res.body) + flavors = self._get_flavors(res.body) + self.assertFlavorDisabled(flavors[0], 'False') + self.assertFlavorDisabled(flavors[1], 'True') + + +class FlavorDisabledXmlTest(FlavorDisabledTest): + content_type = 'application/xml' + prefix = '{%s}' % flavor_disabled.FlavorDisabled.namespace + + def _get_flavor(self, body): + return etree.XML(body) + + def _get_flavors(self, body): + return etree.XML(body).getchildren() diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_flavors.py b/nova/tests/api/openstack/compute/plugins/v3/test_flavors.py new file mode 100644 index 000000000..d204af11f --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_flavors.py @@ -0,0 +1,696 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 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. + +from lxml import etree +import webob + +import urlparse + +from nova.api.openstack.compute.plugins.v3 import flavors +from nova.api.openstack import xmlutil +import nova.compute.flavors +from nova import context +from nova import db +from nova import exception +from nova import test +from nova.tests.api.openstack import fakes +from nova.tests import matchers + +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" + + +FAKE_FLAVORS = { + 'flavor 1': { + "flavorid": '1', + "name": 'flavor 1', + "memory_mb": '256', + "root_gb": '10', + }, + 'flavor 2': { + "flavorid": '2', + "name": 'flavor 2', + "memory_mb": '512', + "root_gb": '20', + }, +} + + +def fake_flavor_get_by_flavor_id(flavorid): + return FAKE_FLAVORS['flavor %s' % flavorid] + + +def fake_flavor_get_all(inactive=False, filters=None): + def reject_min(db_attr, filter_attr): + return (filter_attr in filters and + int(flavor[db_attr]) < int(filters[filter_attr])) + + filters = filters or {} + output = {} + for (flavor_name, flavor) in FAKE_FLAVORS.items(): + if reject_min('memory_mb', 'min_memory_mb'): + continue + elif reject_min('root_gb', 'min_root_gb'): + continue + + output[flavor_name] = flavor + + return output + + +def empty_flavor_get_all(inactive=False, filters=None): + return {} + + +def return_flavor_not_found(flavor_id): + raise exception.InstanceTypeNotFound(instance_type_id=flavor_id) + + +class FlavorsTest(test.TestCase): + def setUp(self): + super(FlavorsTest, self).setUp() + self.flags(osapi_compute_extension=[]) + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + self.stubs.Set(nova.compute.flavors, "get_all_flavors", + fake_flavor_get_all) + self.stubs.Set(nova.compute.flavors, + "get_flavor_by_flavor_id", + fake_flavor_get_by_flavor_id) + + self.controller = flavors.FlavorsController() + + def test_get_flavor_by_invalid_id(self): + self.stubs.Set(nova.compute.flavors, + "get_flavor_by_flavor_id", + return_flavor_not_found) + req = fakes.HTTPRequestV3.blank('/flavors/asdf') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, 'asdf') + + def test_get_flavor_by_id(self): + req = fakes.HTTPRequestV3.blank('/flavors/1') + flavor = self.controller.show(req, '1') + expected = { + "flavor": { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/1", + }, + ], + }, + } + self.assertEqual(flavor, expected) + + def test_get_flavor_with_custom_link_prefix(self): + self.flags(osapi_compute_link_prefix='http://zoo.com:42', + osapi_glance_link_prefix='http://circus.com:34') + req = fakes.HTTPRequestV3.blank('/flavors/1') + flavor = self.controller.show(req, '1') + expected = { + "flavor": { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://zoo.com:42/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://zoo.com:42/flavors/1", + }, + ], + }, + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list(self): + req = fakes.HTTPRequestV3.blank('/flavors') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_with_marker(self): + self.maxDiff = None + req = fakes.HTTPRequestV3.blank('/flavors?limit=1&marker=1') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + 'flavors_links': [ + {'href': 'http://localhost/v3/flavors?limit=1&marker=2', + 'rel': 'next'} + ] + } + self.assertThat(flavor, matchers.DictMatches(expected)) + + def test_get_flavor_detail_with_limit(self): + req = fakes.HTTPRequestV3.blank('/flavors/detail?limit=1') + response = self.controller.index(req) + response_list = response["flavors"] + response_links = response["flavors_links"] + + expected_flavors = [ + { + "id": "1", + "name": "flavor 1", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/1", + }, + ], + }, + ] + self.assertEqual(response_list, expected_flavors) + self.assertEqual(response_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v3/flavors', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + self.assertThat({'limit': ['1'], 'marker': ['1']}, + matchers.DictMatches(params)) + + def test_get_flavor_with_limit(self): + req = fakes.HTTPRequestV3.blank('/flavors?limit=2') + response = self.controller.index(req) + response_list = response["flavors"] + response_links = response["flavors_links"] + + expected_flavors = [ + { + "id": "1", + "name": "flavor 1", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + } + ] + self.assertEqual(response_list, expected_flavors) + self.assertEqual(response_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v3/flavors', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + self.assertThat({'limit': ['2'], 'marker': ['2']}, + matchers.DictMatches(params)) + + def test_get_flavor_list_detail(self): + req = fakes.HTTPRequestV3.blank('/flavors/detail') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_empty_flavor_list(self): + self.stubs.Set(nova.compute.flavors, "get_all_flavors", + empty_flavor_get_all) + + req = fakes.HTTPRequestV3.blank('/flavors') + flavors = self.controller.index(req) + expected = {'flavors': []} + self.assertEqual(flavors, expected) + + def test_get_flavor_list_filter_min_ram(self): + # Flavor lists may be filtered by minRam. + req = fakes.HTTPRequestV3.blank('/flavors?minRam=512') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_filter_invalid_min_ram(self): + # Ensure you cannot list flavors with invalid minRam param. + req = fakes.HTTPRequestV3.blank('/flavors?minRam=NaN') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + def test_get_flavor_list_filter_min_disk(self): + # Flavor lists may be filtered by minDisk. + req = fakes.HTTPRequestV3.blank('/flavors?minDisk=20') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_filter_invalid_min_disk(self): + # Ensure you cannot list flavors with invalid minDisk param. + req = fakes.HTTPRequestV3.blank('/flavors?minDisk=NaN') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + def test_get_flavor_list_detail_min_ram_and_min_disk(self): + """Tests that filtering work on flavor details and that minRam and + minDisk filters can be combined + """ + req = fakes.HTTPRequestV3.blank('/flavors/detail' + '?minRam=256&minDisk=20') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + +class FlavorsXMLSerializationTest(test.TestCase): + def _create_flavor(self): + id = 0 + while True: + id += 1 + yield { + "id": str(id), + "name": "asdf", + "ram": "256", + "disk": "10", + "vcpus": "", + "swap": "512", + "links": [ + { + "rel": "self", + "href": "http://localhost/v3/flavors/%s" % id, + }, + { + "rel": "bookmark", + "href": "http://localhost/flavors/%s" % id, + }, + ], + } + + def setUp(self): + super(FlavorsXMLSerializationTest, self).setUp() + self.flavors = self._create_flavor() + + def test_xml_declaration(self): + serializer = flavors.FlavorTemplate() + fixture = {'flavor': next(self.flavors)} + output = serializer.serialize(fixture) + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") + self.assertTrue(has_dec) + + def test_show(self): + serializer = flavors.FlavorTemplate() + + fixture = {'flavor': next(self.flavors)} + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavor', version='v3') + flavor_dict = fixture['flavor'] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(root.get(key), str(flavor_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_handles_integers(self): + serializer = flavors.FlavorTemplate() + + fixture = {'flavor': next(self.flavors)} + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavor', version='v3') + flavor_dict = fixture['flavor'] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(root.get(key), str(flavor_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_detail(self): + serializer = flavors.FlavorsTemplate() + + fixture = { + "flavors": [ + next(self.flavors), + next(self.flavors), + ], + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors', version='v3') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 2) + for i, flavor_elem in enumerate(flavor_elems): + flavor_dict = fixture['flavors'][i] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) + + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index(self): + serializer = flavors.MinimalFlavorsTemplate() + + fixture = { + "flavors": [ + next(self.flavors), + next(self.flavors), + ], + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors_index') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 2) + for i, flavor_elem in enumerate(flavor_elems): + flavor_dict = fixture['flavors'][i] + + for key in ['name', 'id']: + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) + + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index_empty(self): + serializer = flavors.MinimalFlavorsTemplate() + + fixture = { + "flavors": [], + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors_index') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 0) + + +class DisabledFlavorsWithRealDBTest(test.TestCase): + """ + Tests that disabled flavors should not be shown nor listed. + """ + def setUp(self): + super(DisabledFlavorsWithRealDBTest, self).setUp() + self.controller = flavors.FlavorsController() + + # Add a new disabled type to the list of flavors + self.req = fakes.HTTPRequestV3.blank('/flavors') + self.context = self.req.environ['nova.context'] + self.admin_context = context.get_admin_context() + + self.disabled_type = self._create_disabled_instance_type() + self.inst_types = db.api.instance_type_get_all(self.admin_context) + + def tearDown(self): + db.api.instance_type_destroy(self.admin_context, + self.disabled_type['name']) + + super(DisabledFlavorsWithRealDBTest, self).tearDown() + + def _create_disabled_instance_type(self): + inst_types = db.api.instance_type_get_all(self.admin_context) + + inst_type = inst_types[0] + + del inst_type['id'] + inst_type['name'] += '.disabled' + inst_type['flavorid'] = unicode(max( + [int(flavor['flavorid']) for flavor in inst_types]) + 1) + inst_type['disabled'] = True + + disabled_type = db.api.instance_type_create(self.admin_context, + inst_type) + + return disabled_type + + def test_index_should_not_list_disabled_flavors_to_user(self): + self.context.is_admin = False + + flavor_list = self.controller.index(self.req)['flavors'] + api_flavorids = set(f['id'] for f in flavor_list) + + db_flavorids = set(i['flavorid'] for i in self.inst_types) + disabled_flavorid = str(self.disabled_type['flavorid']) + + self.assert_(disabled_flavorid in db_flavorids) + self.assertEqual(db_flavorids - set([disabled_flavorid]), + api_flavorids) + + def test_index_should_list_disabled_flavors_to_admin(self): + self.context.is_admin = True + + flavor_list = self.controller.index(self.req)['flavors'] + api_flavorids = set(f['id'] for f in flavor_list) + + db_flavorids = set(i['flavorid'] for i in self.inst_types) + disabled_flavorid = str(self.disabled_type['flavorid']) + + self.assert_(disabled_flavorid in db_flavorids) + self.assertEqual(db_flavorids, api_flavorids) + + def test_show_should_include_disabled_flavor_for_user(self): + """ + Counterintuitively we should show disabled flavors to all users and not + just admins. The reason is that, when a user performs a server-show + request, we want to be able to display the pretty flavor name ('512 MB + Instance') and not just the flavor-id even if the flavor id has been + marked disabled. + """ + self.context.is_admin = False + + flavor = self.controller.show( + self.req, self.disabled_type['flavorid'])['flavor'] + + self.assertEqual(flavor['name'], self.disabled_type['name']) + + def test_show_should_include_disabled_flavor_for_admin(self): + self.context.is_admin = True + + flavor = self.controller.show( + self.req, self.disabled_type['flavorid'])['flavor'] + + self.assertEqual(flavor['name'], self.disabled_type['name']) + + +class ParseIsPublicTest(test.TestCase): + def setUp(self): + super(ParseIsPublicTest, self).setUp() + self.controller = flavors.FlavorsController() + + def assertPublic(self, expected, is_public): + self.assertIs(expected, self.controller._parse_is_public(is_public), + '%s did not return %s' % (is_public, expected)) + + def test_None(self): + self.assertPublic(True, None) + + def test_truthy(self): + self.assertPublic(True, True) + self.assertPublic(True, 't') + self.assertPublic(True, 'true') + self.assertPublic(True, 'yes') + self.assertPublic(True, '1') + + def test_falsey(self): + self.assertPublic(False, False) + self.assertPublic(False, 'f') + self.assertPublic(False, 'false') + self.assertPublic(False, 'no') + self.assertPublic(False, '0') + + def test_string_none(self): + self.assertPublic(None, 'none') + + def test_other(self): + self.assertRaises( + webob.exc.HTTPBadRequest, self.assertPublic, None, 'other') diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_images.py b/nova/tests/api/openstack/compute/plugins/v3/test_images.py new file mode 100644 index 000000000..712e3c8a5 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_images.py @@ -0,0 +1,1336 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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. + +""" +Tests of the new image services, both as a service layer, +and as a WSGI layer +""" + +import urlparse + +from lxml import etree +import webob + +from nova.api.openstack.compute.plugins.v3 import images +from nova.api.openstack.compute.views import images as images_view +from nova.api.openstack import xmlutil +from nova import exception +from nova.image import glance +from nova import test +from nova.tests.api.openstack import fakes +from nova.tests import matchers + +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" +NOW_API_FORMAT = "2010-10-11T10:30:22Z" + + +class ImagesControllerTest(test.TestCase): + """ + Test of the OpenStack API /images application controller w/Glance. + """ + + def setUp(self): + """Run before each test.""" + super(ImagesControllerTest, self).setUp() + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + fakes.stub_out_compute_api_snapshot(self.stubs) + fakes.stub_out_compute_api_backup(self.stubs) + fakes.stub_out_glance(self.stubs) + + self.controller = images.ImagesController() + + def test_get_image(self): + fake_req = fakes.HTTPRequestV3.blank('/os-images/123') + actual_image = self.controller.show(fake_req, '124') + + href = "http://localhost/v3/images/124" + bookmark = "http://localhost/images/124" + alternate = "%s/fake/images/124" % glance.generate_glance_url() + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v3/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + + expected_image = { + "image": { + "id": "124", + "name": "queued snapshot", + "updated": NOW_API_FORMAT, + "created": NOW_API_FORMAT, + "status": "SAVING", + "progress": 25, + "minDisk": 0, + "minRam": 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "metadata": { + "instance_uuid": server_uuid, + "user_id": "fake", + }, + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "href": bookmark, + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate + }], + }, + } + + self.assertThat(actual_image, matchers.DictMatches(expected_image)) + + def test_get_image_with_custom_prefix(self): + self.flags(osapi_compute_link_prefix='https://zoo.com:42', + osapi_glance_link_prefix='http://circus.com:34') + fake_req = fakes.HTTPRequestV3.blank('/v3/os-images/124') + actual_image = self.controller.show(fake_req, '124') + href = "https://zoo.com:42/v3/images/124" + bookmark = "https://zoo.com:42/images/124" + alternate = "http://circus.com:34/fake/images/124" + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "https://zoo.com:42/v3/servers/" + server_uuid + server_bookmark = "https://zoo.com:42/servers/" + server_uuid + + expected_image = { + "image": { + "id": "124", + "name": "queued snapshot", + "updated": NOW_API_FORMAT, + "created": NOW_API_FORMAT, + "status": "SAVING", + "progress": 25, + "minDisk": 0, + "minRam": 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "metadata": { + "instance_uuid": server_uuid, + "user_id": "fake", + }, + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "href": bookmark, + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate + }], + }, + } + self.assertThat(actual_image, matchers.DictMatches(expected_image)) + + def test_get_image_404(self): + fake_req = fakes.HTTPRequestV3.blank('/os-images/unknown') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, fake_req, 'unknown') + + def test_get_image_details(self): + request = fakes.HTTPRequestV3.blank('/os-images/detail') + response = self.controller.detail(request) + response_list = response["images"] + + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v3/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + alternate = "%s/fake/images/%s" + + expected = [{ + 'id': '123', + 'name': 'public image', + 'metadata': {'key1': 'value1'}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 10, + 'minRam': 128, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (glance.generate_glance_url(), 123), + }], + }, + { + 'id': '124', + 'name': 'queued snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'progress': 25, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (glance.generate_glance_url(), 124), + }], + }, + { + 'id': '125', + 'name': 'saving snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'progress': 50, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/125", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/125", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/125" % glance.generate_glance_url() + }], + }, + { + 'id': '126', + 'name': 'active snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/126", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/126", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/126" % glance.generate_glance_url() + }], + }, + { + 'id': '127', + 'name': 'killed snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ERROR', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/127", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/127", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/127" % glance.generate_glance_url() + }], + }, + { + 'id': '128', + 'name': 'deleted snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'DELETED', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/128", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/128", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/128" % glance.generate_glance_url() + }], + }, + { + 'id': '129', + 'name': 'pending_delete snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'DELETED', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/129", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/129", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/129" % glance.generate_glance_url() + }], + }, + { + 'id': '130', + 'name': None, + 'metadata': {}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 0, + 'minRam': 0, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/130", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/130", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/130" % glance.generate_glance_url() + }], + }, + ] + + self.assertThat(expected, matchers.DictListMatches(response_list)) + + def test_get_image_details_with_limit(self): + request = fakes.HTTPRequestV3.blank('/os-images/detail?limit=2') + response = self.controller.detail(request) + response_list = response["images"] + response_links = response["images_links"] + + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v3/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + alternate = "%s/fake/images/%s" + + expected = [{ + 'id': '123', + 'name': 'public image', + 'metadata': {'key1': 'value1'}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'minDisk': 10, + 'progress': 100, + 'minRam': 128, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (glance.generate_glance_url(), 123), + }], + }, + { + 'id': '124', + 'name': 'queued snapshot', + 'metadata': { + u'instance_uuid': server_uuid, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'minDisk': 0, + 'progress': 25, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v3/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (glance.generate_glance_url(), 124), + }], + }] + + self.assertThat(expected, matchers.DictListMatches(response_list)) + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v3/images', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + + self.assertThat({'limit': ['2'], 'marker': ['124']}, + matchers.DictMatches(params)) + + def test_image_detail_filter_with_name(self): + image_service = self.mox.CreateMockAnything() + filters = {'name': 'testname'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail' + '?name=testname') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_with_status(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'active'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail' + '?status=ACTIVE') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_with_property(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-test': '3'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail' + '?property-test=3') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_server_href(self): + image_service = self.mox.CreateMockAnything() + uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' + ref = 'http://localhost:8774/servers/' + uuid + url = '/v3/os-images/detail?server=' + ref + filters = {'property-instance_uuid': uuid} + request = fakes.HTTPRequestV3.blank(url) + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_server_uuid(self): + image_service = self.mox.CreateMockAnything() + uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' + url = '/v2/fake/images/detail?server=' + uuid + filters = {'property-instance_uuid': uuid} + request = fakes.HTTPRequestV3.blank(url) + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_changes_since(self): + image_service = self.mox.CreateMockAnything() + filters = {'changes-since': '2011-01-24T17:08Z'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail' + '?changes-since=2011-01-24T17:08Z') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_with_type(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-image_type': 'BASE'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail?type=BASE') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_filter_not_supported(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'active'} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail?status=' + 'ACTIVE&UNSUPPORTEDFILTER=testname') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_no_filters(self): + image_service = self.mox.CreateMockAnything() + filters = {} + request = fakes.HTTPRequestV3.blank('/v3/os-images/detail') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.ImagesController(image_service=image_service) + controller.detail(request) + + def test_image_detail_invalid_marker(self): + class InvalidImageService(object): + + def detail(self, *args, **kwargs): + raise exception.Invalid('meow') + + request = fakes.HTTPRequestV3.blank('/v3/os-images?marker=invalid') + controller = images.ImagesController(image_service=InvalidImageService()) + self.assertRaises(webob.exc.HTTPBadRequest, controller.detail, request) + + def test_generate_alternate_link(self): + view = images_view.ViewBuilder() + request = fakes.HTTPRequestV3.blank('/v3/os-images/1') + generated_url = view._get_alternate_link(request, 1) + actual_url = "%s/fake/images/1" % glance.generate_glance_url() + self.assertEqual(generated_url, actual_url) + + def test_delete_image(self): + request = fakes.HTTPRequestV3.blank('/v3/os-images/124') + request.method = 'DELETE' + response = self.controller.delete(request, '124') + self.assertEqual(response.status_int, 204) + + def test_delete_deleted_image(self): + """If you try to delete a deleted image, you get back 403 Forbidden.""" + + deleted_image_id = 128 + # see nova.tests.api.openstack.fakes:_make_image_fixtures + + request = fakes.HTTPRequestV3.blank( + '/v3/os-images/%s' % deleted_image_id) + request.method = 'DELETE' + self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete, + request, '%s' % deleted_image_id) + + def test_delete_image_not_found(self): + request = fakes.HTTPRequestV3.blank('/v3/os-images/300') + request.method = 'DELETE' + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, request, '300') + + +class ImageXMLSerializationTest(test.TestCase): + + TIMESTAMP = "2010-10-11T10:30:22Z" + SERVER_UUID = 'aa640691-d1a7-4a67-9d3c-d35ee6b3cc74' + SERVER_HREF = 'http://localhost/v3/servers/' + SERVER_UUID + SERVER_BOOKMARK = 'http://localhost/servers/' + SERVER_UUID + IMAGE_HREF = 'http://localhost/v3/os-images/%s' + IMAGE_NEXT = 'http://localhost/v3/os-images?limit=%s&marker=%s' + IMAGE_BOOKMARK = 'http://localhost/os-images/%s' + + def test_xml_declaration(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") + self.assertTrue(has_dec) + + def test_show(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minRam': 10, + 'minDisk': 100, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_zero_metadata(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': {}, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) + self.assertEqual(len(meta_nodes), 0) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_image_no_metadata_key(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) + self.assertEqual(len(meta_nodes), 0) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_no_server(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root, None) + + def test_show_with_min_ram(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minRam': 256, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress', + 'minRam']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_with_min_disk(self): + serializer = images.ImageTemplate() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minDisk': 5, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress', + 'minDisk']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index(self): + serializer = images.MinimalImagesTemplate() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': 2, + 'name': 'Image2', + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ] + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index_with_links(self): + serializer = images.MinimalImagesTemplate() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': 2, + 'name': 'Image2', + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ], + 'images_links': [ + { + 'rel': 'next', + 'href': self.IMAGE_NEXT % (2, 2), + } + ], + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + # Check images_links + images_links = root.findall('{0}link'.format(ATOMNS)) + for i, link in enumerate(fixture['images_links']): + for key, value in link.items(): + self.assertEqual(images_links[i].get(key), value) + + def test_index_zero_images(self): + serializer = images.MinimalImagesTemplate() + + fixtures = { + 'images': [], + } + + output = serializer.serialize(fixtures) + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 0) + + def test_detail(self): + serializer = images.ImagesTemplate() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': '2', + 'name': 'Image2', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'SAVING', + 'progress': 80, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ] + } + + output = serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'images') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py b/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py new file mode 100644 index 000000000..c4f79118e --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_quota_classes.py @@ -0,0 +1,188 @@ +# Copyright 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. + +from lxml import etree +import webob + +from nova.api.openstack.compute.contrib import quota_classes +from nova.api.openstack import wsgi +from nova import test +from nova.tests.api.openstack import fakes + + +def quota_set(class_name): + return {'quota_class_set': {'id': class_name, 'metadata_items': 128, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'instances': 10, + 'injected_files': 5, 'cores': 20, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, 'key_pairs': 100, + 'injected_file_path_bytes': 255}} + + +class QuotaClassSetsTest(test.TestCase): + + def setUp(self): + super(QuotaClassSetsTest, self).setUp() + self.controller = quota_classes.QuotaClassSetsController() + + def test_format_quota_set(self): + raw_quota_set = { + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'floating_ips': 10, + 'fixed_ips': -1, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_path_bytes': 255, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100, + } + + quota_set = self.controller._format_quota_set('test_class', + raw_quota_set) + qs = quota_set['quota_class_set'] + + self.assertEqual(qs['id'], 'test_class') + self.assertEqual(qs['instances'], 10) + self.assertEqual(qs['cores'], 20) + self.assertEqual(qs['ram'], 51200) + self.assertEqual(qs['floating_ips'], 10) + self.assertEqual(qs['fixed_ips'], -1) + self.assertEqual(qs['metadata_items'], 128) + self.assertEqual(qs['injected_files'], 5) + self.assertEqual(qs['injected_file_path_bytes'], 255) + self.assertEqual(qs['injected_file_content_bytes'], 10240) + self.assertEqual(qs['security_groups'], 10) + self.assertEqual(qs['security_group_rules'], 20) + self.assertEqual(qs['key_pairs'], 100) + + def test_quotas_show_as_admin(self): + req = fakes.HTTPRequest.blank( + '/v2/fake4/os-quota-class-sets/test_class', + use_admin_context=True) + res_dict = self.controller.show(req, 'test_class') + + self.assertEqual(res_dict, quota_set('test_class')) + + def test_quotas_show_as_unauthorized_user(self): + req = fakes.HTTPRequest.blank( + '/v2/fake4/os-quota-class-sets/test_class') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.show, + req, 'test_class') + + def test_quotas_update_as_admin(self): + body = {'quota_class_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + + req = fakes.HTTPRequest.blank( + '/v2/fake4/os-quota-class-sets/test_class', + use_admin_context=True) + res_dict = self.controller.update(req, 'test_class', body) + + self.assertEqual(res_dict, body) + + def test_quotas_update_as_user(self): + body = {'quota_class_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100, + }} + + req = fakes.HTTPRequest.blank( + '/v2/fake4/os-quota-class-sets/test_class') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, + req, 'test_class', body) + + +class QuotaTemplateXMLSerializerTest(test.TestCase): + def setUp(self): + super(QuotaTemplateXMLSerializerTest, self).setUp() + self.serializer = quota_classes.QuotaClassTemplate() + self.deserializer = wsgi.XMLDeserializer() + + def test_serializer(self): + exemplar = dict(quota_class_set=dict( + id='test_class', + metadata_items=10, + injected_file_path_bytes=255, + injected_file_content_bytes=20, + ram=50, + floating_ips=60, + fixed_ips=-1, + instances=70, + injected_files=80, + security_groups=10, + security_group_rules=20, + key_pairs=100, + cores=90)) + text = self.serializer.serialize(exemplar) + + tree = etree.fromstring(text) + + self.assertEqual('quota_class_set', tree.tag) + self.assertEqual('test_class', tree.get('id')) + self.assertEqual(len(exemplar['quota_class_set']) - 1, len(tree)) + for child in tree: + self.assertTrue(child.tag in exemplar['quota_class_set']) + self.assertEqual(int(child.text), + exemplar['quota_class_set'][child.tag]) + + def test_deserializer(self): + exemplar = dict(quota_class_set=dict( + metadata_items='10', + injected_file_content_bytes='20', + ram='50', + floating_ips='60', + fixed_ips='-1', + instances='70', + injected_files='80', + security_groups='10', + security_group_rules='20', + key_pairs='100', + cores='90')) + intext = ("<?xml version='1.0' encoding='UTF-8'?>\n" + '<quota_class_set>' + '<metadata_items>10</metadata_items>' + '<injected_file_content_bytes>20' + '</injected_file_content_bytes>' + '<ram>50</ram>' + '<floating_ips>60</floating_ips>' + '<fixed_ips>-1</fixed_ips>' + '<instances>70</instances>' + '<injected_files>80</injected_files>' + '<cores>90</cores>' + '<security_groups>10</security_groups>' + '<security_group_rules>20</security_group_rules>' + '<key_pairs>100</key_pairs>' + '</quota_class_set>') + + result = self.deserializer.deserialize(intext)['body'] + self.assertEqual(result, exemplar) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_quota_sets.py b/nova/tests/api/openstack/compute/plugins/v3/test_quota_sets.py new file mode 100644 index 000000000..6eea01cff --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_quota_sets.py @@ -0,0 +1,399 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation +# Copyright 2013 IBM Corp. +# 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. + +from lxml import etree +import webob + +from nova.api.openstack.compute.plugins.v3 import quota_sets as quotas +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova import context as context_maker +from nova import quota +from nova import test +from nova.tests.api.openstack import fakes + + +def quota_set(id): + return {'quota_set': {'id': id, 'metadata_items': 128, + 'ram': 51200, 'floating_ips': 10, 'fixed_ips': -1, + 'instances': 10, 'injected_files': 5, 'cores': 20, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, 'security_group_rules': 20, + 'key_pairs': 100, 'injected_file_path_bytes': 255}} + + +class QuotaSetsTest(test.TestCase): + + def setUp(self): + super(QuotaSetsTest, self).setUp() + self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager) + self.controller = quotas.QuotaSetsController(self.ext_mgr) + + def test_format_quota_set(self): + raw_quota_set = { + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'floating_ips': 10, + 'fixed_ips': -1, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_path_bytes': 255, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100} + + quota_set = self.controller._format_quota_set('1234', raw_quota_set) + qs = quota_set['quota_set'] + + self.assertEqual(qs['id'], '1234') + self.assertEqual(qs['instances'], 10) + self.assertEqual(qs['cores'], 20) + self.assertEqual(qs['ram'], 51200) + self.assertEqual(qs['floating_ips'], 10) + self.assertEqual(qs['fixed_ips'], -1) + self.assertEqual(qs['metadata_items'], 128) + self.assertEqual(qs['injected_files'], 5) + self.assertEqual(qs['injected_file_path_bytes'], 255) + self.assertEqual(qs['injected_file_content_bytes'], 10240) + self.assertEqual(qs['security_groups'], 10) + self.assertEqual(qs['security_group_rules'], 20) + self.assertEqual(qs['key_pairs'], 100) + + def test_quotas_defaults(self): + uri = '/os-quota-sets/fake_tenant/defaults' + + req = fakes.HTTPRequestV3.blank(uri) + res_dict = self.controller.defaults(req, 'fake_tenant') + + expected = {'quota_set': { + 'id': 'fake_tenant', + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'floating_ips': 10, + 'fixed_ips': -1, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_path_bytes': 255, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + + self.assertEqual(res_dict, expected) + + def test_quotas_show_as_admin(self): + req = fakes.HTTPRequestV3.blank('/os-quota-sets/1234', + use_admin_context=True) + res_dict = self.controller.show(req, 1234) + + self.assertEqual(res_dict, quota_set('1234')) + + def test_quotas_show_as_unauthorized_user(self): + req = fakes.HTTPRequestV3.blank('/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.show, + req, 1234) + + def test_quotas_update_as_admin(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100, 'fixed_ips': -1}} + + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + res_dict = self.controller.update(req, 'update_me', body) + + self.assertEqual(res_dict, body) + + def test_quotas_update_as_user(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, + req, 'update_me', body) + + def test_quotas_update_invalid_key(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + body = {'quota_set': {'instances2': -2, 'cores': -2, + 'ram': -2, 'floating_ips': -2, + 'metadata_items': -2, 'injected_files': -2, + 'injected_file_content_bytes': -2}} + + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 'update_me', body) + + def test_quotas_update_invalid_limit(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + body = {'quota_set': {'instances': -2, 'cores': -2, + 'ram': -2, 'floating_ips': -2, 'fixed_ips': -2, + 'metadata_items': -2, 'injected_files': -2, + 'injected_file_content_bytes': -2}} + + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 'update_me', body) + + def test_quotas_update_invalid_value_json_fromat_empty_string(self): + expected_resp = {'quota_set': { + 'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + + # when PUT JSON format with empty string for quota + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': '', 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + res_dict = self.controller.update(req, 'update_me', body) + self.assertEqual(res_dict, expected_resp) + + def test_quotas_update_invalid_value_xml_fromat_empty_string(self): + expected_resp = {'quota_set': { + 'instances': 50, 'cores': 50, + 'ram': 51200, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + # when PUT XML format with empty string for quota + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': {}, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + res_dict = self.controller.update(req, 'update_me', body) + self.assertEqual(res_dict, expected_resp) + + def test_quotas_update_invalid_value_non_int(self): + # when PUT non integer value + body = {'quota_set': {'instances': test, 'cores': 50, + 'ram': {}, 'floating_ips': 10, + 'fixed_ips': -1, 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'security_groups': 10, + 'security_group_rules': 20, + 'key_pairs': 100}} + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 'update_me', body) + + def test_delete_quotas_when_extension_not_loaded(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(False) + self.mox.ReplayAll() + req = fakes.HTTPRequestV3.blank('/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, 1234) + + def test_quotas_delete_as_unauthorized_user(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + req = fakes.HTTPRequestV3.blank('/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete, + req, 1234) + + def test_quotas_delete_as_admin(self): + context = context_maker.get_admin_context() + self.req = fakes.HTTPRequestV3.blank('/os-quota-sets/1234') + self.req.environ['nova.context'] = context + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.StubOutWithMock(quota.QUOTAS, + "destroy_all_by_project") + quota.QUOTAS.destroy_all_by_project(context, 1234) + self.mox.ReplayAll() + res = self.controller.delete(self.req, 1234) + self.mox.VerifyAll() + self.assertEqual(res.status_int, 202) + + +class QuotaXMLSerializerTest(test.TestCase): + def setUp(self): + super(QuotaXMLSerializerTest, self).setUp() + self.serializer = quotas.QuotaTemplate() + self.deserializer = wsgi.XMLDeserializer() + + def test_serializer(self): + exemplar = dict(quota_set=dict( + id='project_id', + metadata_items=10, + injected_file_path_bytes=255, + injected_file_content_bytes=20, + ram=50, + floating_ips=60, + fixed_ips=-1, + instances=70, + injected_files=80, + security_groups=10, + security_group_rules=20, + key_pairs=100, + cores=90)) + text = self.serializer.serialize(exemplar) + + tree = etree.fromstring(text) + + self.assertEqual('quota_set', tree.tag) + self.assertEqual('project_id', tree.get('id')) + self.assertEqual(len(exemplar['quota_set']) - 1, len(tree)) + for child in tree: + self.assertTrue(child.tag in exemplar['quota_set']) + self.assertEqual(int(child.text), exemplar['quota_set'][child.tag]) + + def test_deserializer(self): + exemplar = dict(quota_set=dict( + metadata_items='10', + injected_file_content_bytes='20', + ram='50', + floating_ips='60', + fixed_ips='-1', + instances='70', + injected_files='80', + security_groups='10', + security_group_rules='20', + key_pairs='100', + cores='90')) + intext = ("<?xml version='1.0' encoding='UTF-8'?>\n" + '<quota_set>' + '<metadata_items>10</metadata_items>' + '<injected_file_content_bytes>20' + '</injected_file_content_bytes>' + '<ram>50</ram>' + '<floating_ips>60</floating_ips>' + '<fixed_ips>-1</fixed_ips>' + '<instances>70</instances>' + '<injected_files>80</injected_files>' + '<security_groups>10</security_groups>' + '<security_group_rules>20</security_group_rules>' + '<key_pairs>100</key_pairs>' + '<cores>90</cores>' + '</quota_set>') + + result = self.deserializer.deserialize(intext)['body'] + self.assertEqual(result, exemplar) + + +fake_quotas = {'ram': {'limit': 51200, + 'in_use': 12800, + 'reserved': 12800}, + 'cores': {'limit': 20, + 'in_use': 10, + 'reserved': 5}, + 'instances': {'limit': 100, + 'in_use': 0, + 'reserved': 0}} + + +def fake_get_quotas(self, context, id, usages=False): + if usages: + return fake_quotas + else: + return dict((k, v['limit']) for k, v in fake_quotas.items()) + + +class ExtendedQuotasTest(test.TestCase): + + def setUp(self): + super(ExtendedQuotasTest, self).setUp() + self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager) + self.controller = quotas.QuotaSetsController(self.ext_mgr) + + def test_quotas_update_exceed_in_used(self): + + body = {'quota_set': {'cores': 10}} + + self.stubs.Set(quotas.QuotaSetsController, '_get_quotas', + fake_get_quotas) + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 'update_me', body) + + def test_quotas_force_update_exceed_in_used(self): + self.stubs.Set(quotas.QuotaSetsController, '_get_quotas', + fake_get_quotas) + req = fakes.HTTPRequestV3.blank('/os-quota-sets/update_me', + use_admin_context=True) + expected = {'quota_set': {'ram': 25600, 'instances': 200, 'cores': 10}} + body = {'quota_set': {'ram': 25600, + 'instances': 200, + 'cores': 10, + 'force': 'True'}} + fake_quotas.get('ram')['limit'] = 25600 + fake_quotas.get('cores')['limit'] = 10 + fake_quotas.get('instances')['limit'] = 200 + + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + res_dict = self.controller.update(req, 'update_me', body) + self.assertEqual(res_dict, expected) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py b/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py new file mode 100644 index 000000000..75733e50f --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py @@ -0,0 +1,126 @@ +# Copyright 2011 OpenStack Foundation +# +# 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. + +from oslo.config import cfg +import webob + +from nova import compute +from nova import exception +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + +CONF = cfg.CONF +CONF.import_opt('password_length', 'nova.utils') + + +def rescue(self, context, instance, rescue_password=None): + pass + + +def unrescue(self, context, instance): + pass + + +class RescueTest(test.TestCase): + def setUp(self): + super(RescueTest, self).setUp() + + def fake_compute_get(*args, **kwargs): + uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' + return {'id': 1, 'uuid': uuid} + + self.stubs.Set(compute.api.API, "get", fake_compute_get) + self.stubs.Set(compute.api.API, "rescue", rescue) + self.stubs.Set(compute.api.API, "unrescue", unrescue) + self.app = fakes.wsgi_app_v3(init_only=('servers', 'os-rescue')) + + def test_rescue_with_preset_password(self): + body = {"rescue": {"adminPass": "AABBCC112233"}} + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 200) + resp_json = jsonutils.loads(resp.body) + self.assertEqual("AABBCC112233", resp_json['adminPass']) + + def test_rescue_generates_password(self): + body = dict(rescue=None) + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 200) + resp_json = jsonutils.loads(resp.body) + self.assertEqual(CONF.password_length, len(resp_json['adminPass'])) + + def test_rescue_of_rescued_instance(self): + body = dict(rescue=None) + + def fake_rescue(*args, **kwargs): + raise exception.InstanceInvalidState('fake message') + + self.stubs.Set(compute.api.API, "rescue", fake_rescue) + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 409) + + def test_unrescue(self): + body = dict(unrescue=None) + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 202) + + def test_unrescue_of_active_instance(self): + body = dict(unrescue=None) + + def fake_unrescue(*args, **kwargs): + raise exception.InstanceInvalidState('fake message') + + self.stubs.Set(compute.api.API, "unrescue", fake_unrescue) + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 409) + + def test_rescue_raises_unrescuable(self): + body = dict(rescue=None) + + def fake_rescue(*args, **kwargs): + raise exception.InstanceNotRescuable('fake message') + + self.stubs.Set(compute.api.API, "rescue", fake_rescue) + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 400) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_server_diagnostics.py b/nova/tests/api/openstack/compute/plugins/v3/test_server_diagnostics.py new file mode 100644 index 000000000..61b78fea8 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_server_diagnostics.py @@ -0,0 +1,81 @@ +# Copyright 2011 Eldar Nugaev +# 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. + +from lxml import etree + +from nova.api.openstack import compute +from nova.api.openstack.compute.plugins.v3 import server_diagnostics +from nova.api.openstack import wsgi +from nova.compute import api as compute_api +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + + +UUID = 'abc' + + +def fake_get_diagnostics(self, _context, instance_uuid): + return {'data': 'Some diagnostic info'} + + +def fake_instance_get(self, _context, instance_uuid): + if instance_uuid != UUID: + raise Exception("Invalid UUID") + return {'uuid': instance_uuid} + + +class ServerDiagnosticsTest(test.TestCase): + + def setUp(self): + super(ServerDiagnosticsTest, self).setUp() + self.stubs.Set(compute_api.API, 'get_diagnostics', + fake_get_diagnostics) + self.stubs.Set(compute_api.API, 'get', fake_instance_get) + + self.router = compute.APIRouterV3(init_only=('servers', 'os-server-diagnostics')) + + def test_get_diagnostics(self): + req = fakes.HTTPRequestV3.blank( + '/servers/%s/os-server-diagnostics' % UUID) + res = req.get_response(self.router) + output = jsonutils.loads(res.body) + self.assertEqual(output, {'data': 'Some diagnostic info'}) + + +class TestServerDiagnosticsXMLSerializer(test.TestCase): + namespace = wsgi.XMLNS_V11 + + def _tag(self, elem): + tagname = elem.tag + self.assertEqual(tagname[0], '{') + tmp = tagname.partition('}') + namespace = tmp[0][1:] + self.assertEqual(namespace, self.namespace) + return tmp[2] + + def test_index_serializer(self): + serializer = server_diagnostics.ServerDiagnosticsTemplate() + exemplar = dict(diag1='foo', diag2='bar') + text = serializer.serialize(exemplar) + + tree = etree.fromstring(text) + + self.assertEqual('diagnostics', self._tag(tree)) + self.assertEqual(len(tree), len(exemplar)) + for child in tree: + tag = self._tag(child) + self.assertTrue(tag in exemplar) + self.assertEqual(child.text, exemplar[tag]) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_servers.py b/nova/tests/api/openstack/compute/plugins/v3/test_servers.py index da17d8fd5..4505a1750 100644 --- a/nova/tests/api/openstack/compute/plugins/v3/test_servers.py +++ b/nova/tests/api/openstack/compute/plugins/v3/test_servers.py @@ -228,7 +228,7 @@ class ServersControllerTest(test.TestCase): self.assertEquals(res, [(None, None, port)]) def test_get_server_by_uuid(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) res_dict = self.controller.show(req, FAKE_UUID) self.assertEqual(res_dict['server']['id'], FAKE_UUID) @@ -246,7 +246,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get', return_instance_with_host) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) server1 = self.controller.show(req, FAKE_UUID) server2 = self.controller.show(req, FAKE_UUID) @@ -255,11 +255,11 @@ class ServersControllerTest(test.TestCase): def test_get_server_by_id(self): self.flags(use_ipv6=True) - image_bookmark = "http://localhost/fake/images/10" - flavor_bookmark = "http://localhost/fake/flavors/1" + image_bookmark = "http://localhost/images/10" + flavor_bookmark = "http://localhost/flavors/1" uuid = FAKE_UUID - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % uuid) + req = fakes.HTTPRequestV3.blank('/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { @@ -305,11 +305,11 @@ class ServersControllerTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v3/fake/servers/%s" % uuid, + "href": "http://localhost/v3/servers/%s" % uuid, }, { "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, + "href": "http://localhost/servers/%s" % uuid, }, ], } @@ -318,15 +318,15 @@ class ServersControllerTest(test.TestCase): self.assertThat(res_dict, matchers.DictMatches(expected_server)) def test_get_server_with_active_status_by_id(self): - image_bookmark = "http://localhost/fake/images/10" - flavor_bookmark = "http://localhost/fake/flavors/1" + image_bookmark = "http://localhost/images/10" + flavor_bookmark = "http://localhost/flavors/1" new_return_server = fakes.fake_instance_get( vm_state=vm_states.ACTIVE, progress=100) self.stubs.Set(db, 'instance_get_by_uuid', new_return_server) uuid = FAKE_UUID - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % uuid) + req = fakes.HTTPRequestV3.blank('/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { "server": { @@ -371,11 +371,11 @@ class ServersControllerTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v3/fake/servers/%s" % uuid, + "href": "http://localhost/v3/servers/%s" % uuid, }, { "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, + "href": "http://localhost/servers/%s" % uuid, }, ], } @@ -385,9 +385,9 @@ class ServersControllerTest(test.TestCase): def test_get_server_with_id_image_ref_by_id(self): image_ref = "10" - image_bookmark = "http://localhost/fake/images/10" + image_bookmark = "http://localhost/images/10" flavor_id = "1" - flavor_bookmark = "http://localhost/fake/flavors/1" + flavor_bookmark = "http://localhost/flavors/1" new_return_server = fakes.fake_instance_get( vm_state=vm_states.ACTIVE, image_ref=image_ref, @@ -395,7 +395,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', new_return_server) uuid = FAKE_UUID - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % uuid) + req = fakes.HTTPRequestV3.blank('/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { "server": { @@ -440,11 +440,11 @@ class ServersControllerTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v3/fake/servers/%s" % uuid, + "href": "http://localhost/v3/servers/%s" % uuid, }, { "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, + "href": "http://localhost/servers/%s" % uuid, }, ], } @@ -484,7 +484,7 @@ class ServersControllerTest(test.TestCase): return_server = fakes.fake_instance_get(nw_cache=nw_cache) self.stubs.Set(db, 'instance_get_by_uuid', return_server) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s/ips' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s/ips' % FAKE_UUID) res_dict = self.ips_controller.index(req, FAKE_UUID) expected = { @@ -504,7 +504,7 @@ class ServersControllerTest(test.TestCase): self.assertThat(res_dict, matchers.DictMatches(expected)) def test_get_server_addresses_nonexistent_network(self): - url = '/v3/fake/servers/%s/ips/network_0' % FAKE_UUID + url = '/v3/servers/%s/ips/network_0' % FAKE_UUID req = fakes.HTTPRequestV3.blank(url) self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.show, req, FAKE_UUID, 'network_0') @@ -516,7 +516,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get) server_id = str(uuid.uuid4()) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s/ips' % server_id) + req = fakes.HTTPRequestV3.blank('/servers/%s/ips' % server_id) self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.index, req, server_id) @@ -524,14 +524,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_empty) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') res_dict = self.controller.index(req) num_servers = len(res_dict['servers']) self.assertEqual(0, num_servers) def test_get_server_list_with_reservation_id(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?reservation_id=foo') + req = fakes.HTTPRequestV3.blank('/servers?reservation_id=foo') res_dict = self.controller.index(req) i = 0 @@ -540,7 +540,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list_with_reservation_id_empty(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail?' + req = fakes.HTTPRequestV3.blank('/servers/detail?' 'reservation_id=foo') res_dict = self.controller.detail(req) @@ -550,7 +550,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list_with_reservation_id_details(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail?' + req = fakes.HTTPRequestV3.blank('/servers/detail?' 'reservation_id=foo') res_dict = self.controller.detail(req) @@ -560,7 +560,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') res_dict = self.controller.index(req) self.assertEqual(len(res_dict['servers']), 5) @@ -572,18 +572,18 @@ class ServersControllerTest(test.TestCase): expected_links = [ { "rel": "self", - "href": "http://localhost/v3/fake/servers/%s" % s['id'], + "href": "http://localhost/v3/servers/%s" % s['id'], }, { "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % s['id'], + "href": "http://localhost/servers/%s" % s['id'], }, ] self.assertEqual(s['links'], expected_links) def test_get_servers_with_limit(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?limit=3') + req = fakes.HTTPRequestV3.blank('/servers?limit=3') res_dict = self.controller.index(req) servers = res_dict['servers'] @@ -593,14 +593,14 @@ class ServersControllerTest(test.TestCase): servers_links = res_dict['servers_links'] self.assertEqual(servers_links[0]['rel'], 'next') href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v3/fake/servers', href_parts.path) + self.assertEqual('/v3/servers', href_parts.path) params = urlparse.parse_qs(href_parts.query) expected_params = {'limit': ['3'], 'marker': [fakes.get_fake_uuid(2)]} self.assertThat(params, matchers.DictMatches(expected_params)) def test_get_servers_with_limit_bad_value(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?limit=aaa') + req = fakes.HTTPRequestV3.blank('/servers?limit=aaa') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) @@ -608,14 +608,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_empty) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail') + req = fakes.HTTPRequestV3.blank('/servers/detail') res_dict = self.controller.index(req) num_servers = len(res_dict['servers']) self.assertEqual(0, num_servers) def test_get_server_details_with_limit(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail?limit=3') + req = fakes.HTTPRequestV3.blank('/servers/detail?limit=3') res = self.controller.detail(req) servers = res['servers'] @@ -626,18 +626,18 @@ class ServersControllerTest(test.TestCase): self.assertEqual(servers_links[0]['rel'], 'next') href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v3/fake/servers', href_parts.path) + self.assertEqual('/v3/servers', href_parts.path) params = urlparse.parse_qs(href_parts.query) expected = {'limit': ['3'], 'marker': [fakes.get_fake_uuid(2)]} self.assertThat(params, matchers.DictMatches(expected)) def test_get_server_details_with_limit_bad_value(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail?limit=aaa') + req = fakes.HTTPRequestV3.blank('/servers/detail?limit=aaa') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) def test_get_server_details_with_limit_and_other_params(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail' + req = fakes.HTTPRequestV3.blank('/servers/detail' '?limit=3&blah=2:t') res = self.controller.detail(req) @@ -649,36 +649,36 @@ class ServersControllerTest(test.TestCase): self.assertEqual(servers_links[0]['rel'], 'next') href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v3/fake/servers', href_parts.path) + self.assertEqual('/v3/servers', href_parts.path) params = urlparse.parse_qs(href_parts.query) expected = {'limit': ['3'], 'blah': ['2:t'], 'marker': [fakes.get_fake_uuid(2)]} self.assertThat(params, matchers.DictMatches(expected)) def test_get_servers_with_too_big_limit(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?limit=30') + req = fakes.HTTPRequestV3.blank('/servers?limit=30') res_dict = self.controller.index(req) self.assertTrue('servers_links' not in res_dict) def test_get_servers_with_bad_limit(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?limit=asdf') + req = fakes.HTTPRequestV3.blank('/servers?limit=asdf') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) def test_get_servers_with_marker(self): - url = '/v3/fake/servers?marker=%s' % fakes.get_fake_uuid(2) + url = '/v3/servers?marker=%s' % fakes.get_fake_uuid(2) req = fakes.HTTPRequestV3.blank(url) servers = self.controller.index(req)['servers'] self.assertEqual([s['name'] for s in servers], ["server4", "server5"]) def test_get_servers_with_limit_and_marker(self): - url = '/v3/fake/servers?limit=2&marker=%s' % fakes.get_fake_uuid(1) + url = '/v3/servers?limit=2&marker=%s' % fakes.get_fake_uuid(1) req = fakes.HTTPRequestV3.blank(url) servers = self.controller.index(req)['servers'] self.assertEqual([s['name'] for s in servers], ['server3', 'server4']) def test_get_servers_with_bad_marker(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?limit=2&marker=asdf') + req = fakes.HTTPRequestV3.blank('/servers?limit=2&marker=asdf') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) @@ -692,7 +692,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?unknownoption=whee') + req = fakes.HTTPRequestV3.blank('/servers?unknownoption=whee') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -711,7 +711,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?image=12345') + req = fakes.HTTPRequestV3.blank('/servers?image=12345') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -728,7 +728,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?tenant_id=fake', + req = fakes.HTTPRequestV3.blank('/servers?tenant_id=fake', use_admin_context=True) res = self.controller.index(req) @@ -744,7 +744,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers', + req = fakes.HTTPRequestV3.blank('/servers', use_admin_context=True) res = self.controller.index(req) @@ -769,7 +769,7 @@ class ServersControllerTest(test.TestCase): common_policy.set_rules(common_policy.Rules(rules)) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?all_tenants=1') + req = fakes.HTTPRequestV3.blank('/servers?all_tenants=1') res = self.controller.index(req) self.assertTrue('servers' in res) @@ -791,7 +791,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?all_tenants=1') + req = fakes.HTTPRequestV3.blank('/servers?all_tenants=1') self.assertRaises(exception.PolicyNotAuthorized, self.controller.index, req) @@ -809,14 +809,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?flavor=12345') + req = fakes.HTTPRequestV3.blank('/servers?flavor=12345') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) self.assertEqual(servers[0]['id'], server_uuid) def test_get_servers_with_bad_flavor(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?flavor=abcde') + req = fakes.HTTPRequestV3.blank('/servers?flavor=abcde') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 0) @@ -834,7 +834,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?status=active') + req = fakes.HTTPRequestV3.blank('/servers?status=active') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -842,13 +842,13 @@ class ServersControllerTest(test.TestCase): def test_get_servers_invalid_status(self): # Test getting servers by invalid status. - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?status=baloney', + req = fakes.HTTPRequestV3.blank('/servers?status=baloney', use_admin_context=False) servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 0) def test_get_servers_deleted_status_as_user(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?status=deleted', + req = fakes.HTTPRequestV3.blank('/servers?status=deleted', use_admin_context=False) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) @@ -866,7 +866,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?status=deleted', + req = fakes.HTTPRequestV3.blank('/servers?status=deleted', use_admin_context=True) servers = self.controller.detail(req)['servers'] @@ -886,7 +886,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?name=whee.*') + req = fakes.HTTPRequestV3.blank('/servers?name=whee.*') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -909,7 +909,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) params = 'changes-since=2011-01-24T17:08:01Z' - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?%s' % params) + req = fakes.HTTPRequestV3.blank('/servers?%s' % params) servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -917,7 +917,7 @@ class ServersControllerTest(test.TestCase): def test_get_servers_allows_changes_since_bad_value(self): params = 'changes-since=asdf' - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?%s' % params) + req = fakes.HTTPRequestV3.blank('/servers?%s' % params) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) def test_get_servers_admin_filters_as_user(self): @@ -943,7 +943,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v3/fake/servers?%s' % query_str) + req = fakes.HTTPRequest.blank('/servers?%s' % query_str) res = self.controller.index(req) servers = res['servers'] @@ -972,7 +972,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?%s' % query_str, + req = fakes.HTTPRequestV3.blank('/servers?%s' % query_str, use_admin_context=True) servers = self.controller.index(req)['servers'] @@ -994,7 +994,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?ip=10\..*') + req = fakes.HTTPRequestV3.blank('/servers?ip=10\..*') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -1016,7 +1016,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers?ip6=ffff.*', + req = fakes.HTTPRequestV3.blank('/servers?ip6=ffff.*', use_admin_context=True) servers = self.controller.index(req)['servers'] @@ -1028,7 +1028,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(name='server_test', access_ipv4='0.0.0.0', access_ipv6='beef::0123')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': { @@ -1045,7 +1045,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') def test_update_server_invalid_xml_raises_lookup(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/xml' #xml request which raises LookupError @@ -1057,7 +1057,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res.status_int, 400) def test_update_server_invalid_xml_raises_expat(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/xml' #xml request which raises ExpatError @@ -1071,7 +1071,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_name(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(name='server_test')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1084,7 +1084,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_name_too_long(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(name='server_test')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'x' * 256}} @@ -1095,7 +1095,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': '0.0.0.0'}} @@ -1108,7 +1108,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_bad_format(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': 'bad_format'}} @@ -1119,7 +1119,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_none(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': None}} @@ -1132,7 +1132,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_blank(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': ''}} @@ -1145,7 +1145,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': 'beef::0123'}} @@ -1158,7 +1158,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_bad_format(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': 'bad_format'}} @@ -1169,7 +1169,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_none(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': None}} @@ -1182,7 +1182,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_blank(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': ''}} @@ -1193,7 +1193,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res_dict['server']['accessIPv6'], '') def test_update_server_personality(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = { @@ -1223,7 +1223,7 @@ class ServersControllerTest(test.TestCase): # self.stubs.Set(db, 'instance_get', # return_server_with_attributes(name='server_test')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = "application/json" req.body = jsonutils.dumps(body) @@ -1237,7 +1237,7 @@ class ServersControllerTest(test.TestCase): raise exception.InstanceNotFound(instance_id='fake') self.stubs.Set(compute_api.API, 'get', fake_get) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1250,7 +1250,7 @@ class ServersControllerTest(test.TestCase): raise exception.InstanceNotFound(instance_id='fake') self.stubs.Set(compute_api.API, 'update', fake_update) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1263,7 +1263,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid access_ipv4 = 'bad_format' access_ipv6 = 'fead::1234' body = { @@ -1285,7 +1285,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1297,7 +1297,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid access_ipv4 = '0.0.0.0' access_ipv6 = 'fead::1234' body = { @@ -1319,7 +1319,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1331,7 +1331,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid access_ipv4 = '0.0.0.0' access_ipv6 = 'fead::1234' body = { @@ -1353,7 +1353,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1365,7 +1365,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid access_ipv4 = '0.0.0.0' access_ipv6 = 'fead::1234' body = { @@ -1387,7 +1387,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1407,7 +1407,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid body = { 'rebuild': { 'name': 'new_name', @@ -1415,7 +1415,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1435,7 +1435,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid body = { 'rebuild': { 'name': 'new_name', @@ -1443,7 +1443,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1464,7 +1464,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid body = { 'rebuild': { 'name': 'new_name', @@ -1472,7 +1472,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1490,7 +1490,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid body = { 'rebuild': { 'name': 'new_name', @@ -1498,7 +1498,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1510,7 +1510,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid + image_href = 'http://localhost/v3/images/%s' % image_uuid access_ipv4 = '1.2.3.4' access_ipv6 = 'bad_format' body = { @@ -1532,7 +1532,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/a/action') + req = fakes.HTTPRequestV3.blank('/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1545,7 +1545,7 @@ class ServersControllerTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/fake/flavors/1', + "href": 'http://localhost/flavors/1', }, ], } @@ -1554,11 +1554,11 @@ class ServersControllerTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/fake/images/10', + "href": 'http://localhost/images/10', }, ], } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail') + req = fakes.HTTPRequestV3.blank('/servers/detail') res_dict = self.controller.detail(req) for i, s in enumerate(res_dict['servers']): @@ -1586,7 +1586,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_with_host) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/detail') + req = fakes.HTTPRequestV3.blank('/servers/detail') res_dict = self.controller.detail(req) server_list = res_dict['servers'] @@ -1601,7 +1601,7 @@ class ServersControllerTest(test.TestCase): def _delete_server_instance(self, uuid=FAKE_UUID): fakes.stub_out_instance_quota(self.stubs, 0, 10) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % uuid) + req = fakes.HTTPRequestV3.blank('/servers/%s' % uuid) req.method = 'DELETE' self.server_delete_called = False @@ -1626,7 +1626,7 @@ class ServersControllerTest(test.TestCase): def test_delete_server_instance_while_building(self): fakes.stub_out_instance_quota(self.stubs, 0, 10) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'DELETE' self.server_delete_called = False @@ -1640,7 +1640,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(self.server_delete_called, True) def test_delete_server_instance_while_resize(self): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'DELETE' self.server_delete_called = False @@ -1673,7 +1673,7 @@ class ServerStatusTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_state, task_state=task_state)) - request = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + request = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) return self.controller.show(request, FAKE_UUID) def test_active(self): @@ -1699,7 +1699,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:reboot': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/1234/action') + req = fakes.HTTPRequestV3.blank('/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_reboot, req, '1234', {'reboot': {'type': 'HARD'}}) @@ -1727,7 +1727,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:confirm_resize': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/1234/action') + req = fakes.HTTPRequestV3.blank('/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_confirm_resize, req, '1234', {}) @@ -1749,7 +1749,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:revert_resize': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/1234/action') + req = fakes.HTTPRequestV3.blank('/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_revert_resize, req, '1234', {}) @@ -1881,7 +1881,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_uuid, flavorRef=2, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1902,7 +1902,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1943,7 +1943,7 @@ class ServersControllerCreateTest(test.TestCase): self.addCleanup(image_service.update, context, image_uuid, {'status': 'active'}) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' body = dict(server=dict( name='server_test', imageRef=image_uuid, flavorRef=2, @@ -1973,7 +1973,7 @@ class ServersControllerCreateTest(test.TestCase): self.addCleanup(image_service.update, context, image_uuid, {'size': orig_size}) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' body = dict(server=dict(name='server_test', imageRef=image_uuid, @@ -2001,7 +2001,7 @@ class ServersControllerCreateTest(test.TestCase): # 'flavorRef': flavor_ref, # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2025,7 +2025,7 @@ class ServersControllerCreateTest(test.TestCase): # 'flavorRef': flavor_ref, # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2049,7 +2049,7 @@ class ServersControllerCreateTest(test.TestCase): # 'flavorRef': flavor_ref, # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2073,7 +2073,7 @@ class ServersControllerCreateTest(test.TestCase): # 'flavorRef': flavor_ref, # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2103,7 +2103,7 @@ class ServersControllerCreateTest(test.TestCase): # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2134,7 +2134,7 @@ class ServersControllerCreateTest(test.TestCase): # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2165,7 +2165,7 @@ class ServersControllerCreateTest(test.TestCase): # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2247,7 +2247,7 @@ class ServersControllerCreateTest(test.TestCase): # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2272,7 +2272,7 @@ class ServersControllerCreateTest(test.TestCase): # } # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -2281,8 +2281,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_image_ref_is_bookmark(self): image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' body = { 'server': { 'name': 'server_test', @@ -2291,7 +2291,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2302,8 +2302,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_image_ref_is_invalid(self): image_uuid = 'this_is_not_a_valid_uuid' - image_href = 'http://localhost/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' body = { 'server': { 'name': 'server_test', @@ -2312,7 +2312,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2330,7 +2330,7 @@ class ServersControllerCreateTest(test.TestCase): server.pop('imageRef', None) server.update(params) body = dict(server=server) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2764,8 +2764,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_access_ip(self): # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/v3/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' access_ipv4 = '1.2.3.4' access_ipv6 = 'fead::1234' body = { @@ -2788,7 +2788,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2804,8 +2804,8 @@ class ServersControllerCreateTest(test.TestCase): # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/v3/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' access_ipv4 = '1.2.3.4' access_ipv6 = 'fead::1234' body = { @@ -2828,7 +2828,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2841,8 +2841,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_bad_format_access_ip_v4(self): # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/v3/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' access_ipv4 = 'bad_format' access_ipv6 = 'fead::1234' body = { @@ -2865,7 +2865,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2875,8 +2875,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_bad_format_access_ip_v6(self): # proper local hrefs must start with 'http://localhost/v3/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v3/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' + image_href = 'http://localhost/v3/images/%s' % image_uuid + flavor_ref = 'http://localhost/flavors/3' access_ipv4 = '1.2.3.4' access_ipv6 = 'bad_format' body = { @@ -2899,7 +2899,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2930,7 +2930,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2961,7 +2961,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2996,7 +2996,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3024,7 +3024,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3048,7 +3048,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3072,7 +3072,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3096,7 +3096,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3110,7 +3110,7 @@ class ServersControllerCreateTest(test.TestCase): body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, key_name='nonexistentkey')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3124,7 +3124,7 @@ class ServersControllerCreateTest(test.TestCase): body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, key_name='key')) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3140,7 +3140,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3149,13 +3149,13 @@ class ServersControllerCreateTest(test.TestCase): self.controller.create, req, body) def test_create_instance_invalid_flavor_id_int(self): - image_href = 'http://localhost/v3/fake/images/2' + image_href = 'http://localhost/v3/images/2' flavor_ref = -1 body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3170,7 +3170,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3198,7 +3198,7 @@ class ServersControllerCreateTest(test.TestCase): # }, # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -3227,7 +3227,7 @@ class ServersControllerCreateTest(test.TestCase): # }, # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -3256,7 +3256,7 @@ class ServersControllerCreateTest(test.TestCase): # }, # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -3283,7 +3283,7 @@ class ServersControllerCreateTest(test.TestCase): # }, # } - # req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + # req = fakes.HTTPRequestV3.blank('/servers') # req.method = 'POST' # req.body = jsonutils.dumps(body) # req.headers["content-type"] = "application/json" @@ -3311,7 +3311,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3330,7 +3330,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3350,7 +3350,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3371,7 +3371,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3392,7 +3392,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3430,7 +3430,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3438,8 +3438,8 @@ class ServersControllerCreateTest(test.TestCase): self.controller.create, req, body) def test_create_location(self): - selfhref = 'http://localhost/v3/fake/servers/%s' % FAKE_UUID - bookhref = 'http://localhost/fake/servers/%s' % FAKE_UUID + selfhref = 'http://localhost/v3/servers/%s' % FAKE_UUID + bookhref = 'http://localhost/servers/%s' % FAKE_UUID image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' image_href = 'http://localhost/v3/images/%s' % image_uuid flavor_ref = 'http://localhost/123/flavors/3' @@ -3461,7 +3461,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = 'application/json' @@ -3477,7 +3477,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_uuid, flavorRef=3, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -4255,10 +4255,10 @@ class ServersViewBuilderTest(test.TestCase): self.uuid = self.instance['uuid'] self.view_builder = views.servers.ViewBuilder() - self.request = fakes.HTTPRequestV3.blank("/v2") + self.request = fakes.HTTPRequestV3.blank("") def test_get_flavor_valid_instance_type(self): - flavor_bookmark = "http://localhost/fake/flavors/1" + flavor_bookmark = "http://localhost/flavors/1" expected = {"id": "1", "links": [{"rel": "bookmark", "href": flavor_bookmark}]} @@ -4266,8 +4266,8 @@ class ServersViewBuilderTest(test.TestCase): self.assertEqual(result, expected) def test_build_server(self): - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4296,12 +4296,12 @@ class ServersViewBuilderTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v3/fake/servers/%s" % + "href": "http://localhost/v3/servers/%s" % self.uuid, }, { "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % self.uuid, + "href": "http://localhost/servers/%s" % self.uuid, }, ], } @@ -4311,10 +4311,10 @@ class ServersViewBuilderTest(test.TestCase): self.assertThat(output, matchers.DictMatches(expected_server)) def test_build_server_detail(self): - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4384,10 +4384,10 @@ class ServersViewBuilderTest(test.TestCase): 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), } - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4517,10 +4517,10 @@ class ServersViewBuilderTest(test.TestCase): 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), } - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid output = self.view_builder.show(self.request, self.instance) self.assertFalse('fault' in output['server']) @@ -4529,10 +4529,10 @@ class ServersViewBuilderTest(test.TestCase): #set the power state of the instance to running self.instance['vm_state'] = vm_states.ACTIVE self.instance['progress'] = 100 - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4591,10 +4591,10 @@ class ServersViewBuilderTest(test.TestCase): self.instance['access_ip_v4'] = '1.2.3.4' - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4653,10 +4653,10 @@ class ServersViewBuilderTest(test.TestCase): self.instance['access_ip_v6'] = 'fead::1234' - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -4717,10 +4717,10 @@ class ServersViewBuilderTest(test.TestCase): metadata.append(models.InstanceMetadata(key="Open", value="Stack")) self.instance['metadata'] = metadata - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v3/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + self_link = "http://localhost/v3/servers/%s" % self.uuid + bookmark_link = "http://localhost/servers/%s" % self.uuid expected_server = { "server": { "id": self.uuid, @@ -5777,7 +5777,7 @@ class ServersUnprocessableEntityTestCase(test.TestCase): self.controller = servers.ServersController(extension_info=ext_info) def _unprocessable_server_create(self, body): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers') + req = fakes.HTTPRequestV3.blank('/servers') req.method = 'POST' self.assertRaises(webob.exc.HTTPUnprocessableEntity, @@ -5795,7 +5795,7 @@ class ServersUnprocessableEntityTestCase(test.TestCase): self._unprocessable_server_create(body=body) def _unprocessable_server_update(self, body): - req = fakes.HTTPRequestV3.blank('/v3/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID) req.method = 'PUT' self.assertRaises(webob.exc.HTTPUnprocessableEntity, diff --git a/nova/tests/api/openstack/compute/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py index 77e637044..3741fcd33 100644 --- a/nova/tests/api/openstack/compute/test_flavors.py +++ b/nova/tests/api/openstack/compute/test_flavors.py @@ -792,6 +792,7 @@ class ParseIsPublicTest(test.TestCase): def test_string_none(self): self.assertPublic(None, 'none') + self.assertPublic(None, 'None') def test_other(self): self.assertRaises( diff --git a/nova/tests/api/openstack/compute/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py index b9dc72b1f..3dbc7bfea 100644 --- a/nova/tests/api/openstack/compute/test_limits.py +++ b/nova/tests/api/openstack/compute/test_limits.py @@ -32,14 +32,18 @@ from nova.openstack.common import jsonutils from nova import test from nova.tests.api.openstack import fakes from nova.tests import matchers +from nova import utils TEST_LIMITS = [ - limits.Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE), - limits.Limit("POST", "*", ".*", 7, limits.PER_MINUTE), - limits.Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE), - limits.Limit("PUT", "*", "", 10, limits.PER_MINUTE), - limits.Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE), + limits.Limit("GET", "/delayed", "^/delayed", 1, + utils.TIME_UNITS['MINUTE']), + limits.Limit("POST", "*", ".*", 7, utils.TIME_UNITS['MINUTE']), + limits.Limit("POST", "/servers", "^/servers", 3, + utils.TIME_UNITS['MINUTE']), + limits.Limit("PUT", "*", "", 10, utils.TIME_UNITS['MINUTE']), + limits.Limit("PUT", "/servers", "^/servers", 5, + utils.TIME_UNITS['MINUTE']), ] NS = { 'atom': 'http://www.w3.org/2005/Atom', @@ -312,7 +316,7 @@ class LimitsControllerTest(BaseLimitTestSuite): req, 1, {}) -class TestLimiter(limits.Limiter): +class MockLimiter(limits.Limiter): pass @@ -331,12 +335,12 @@ class LimitMiddlewareTest(BaseLimitTestSuite): super(LimitMiddlewareTest, self).setUp() _limits = '(GET, *, .*, 1, MINUTE)' self.app = limits.RateLimitingMiddleware(self._empty_app, _limits, - "%s.TestLimiter" % + "%s.MockLimiter" % self.__class__.__module__) def test_limit_class(self): # Test that middleware selected correct limiter class. - assert isinstance(self.app._limiter, TestLimiter) + assert isinstance(self.app._limiter, MockLimiter) def test_good_request(self): # Test successful GET request through middleware. @@ -485,8 +489,8 @@ class ParseLimitsTest(BaseLimitTestSuite): self.assertEqual([t.value for t in l], expected) # ...and the units... - expected = [limits.PER_MINUTE, limits.PER_HOUR, - limits.PER_SECOND, limits.PER_DAY] + expected = [utils.TIME_UNITS['MINUTE'], utils.TIME_UNITS['HOUR'], + utils.TIME_UNITS['SECOND'], utils.TIME_UNITS['DAY']] self.assertEqual([t.unit for t in l], expected) diff --git a/nova/tests/api/openstack/compute/test_server_metadata.py b/nova/tests/api/openstack/compute/test_server_metadata.py index fa25ad4a3..f0548ffa0 100644 --- a/nova/tests/api/openstack/compute/test_server_metadata.py +++ b/nova/tests/api/openstack/compute/test_server_metadata.py @@ -26,6 +26,7 @@ from nova.compute import vm_states import nova.db from nova import exception from nova.openstack.common import jsonutils +from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack import fakes @@ -77,6 +78,7 @@ def return_server(context, server_id): 'uuid': '0cc3346e-9fef-4445-abe6-5d2b2690ec64', 'name': 'fake', 'locked': False, + 'launched_at': timeutils.utcnow(), 'vm_state': vm_states.ACTIVE} @@ -85,6 +87,7 @@ def return_server_by_uuid(context, server_uuid): 'uuid': '0cc3346e-9fef-4445-abe6-5d2b2690ec64', 'name': 'fake', 'locked': False, + 'launched_at': timeutils.utcnow(), 'vm_state': vm_states.ACTIVE} diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index af8a4a276..82bb6b868 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -231,7 +231,7 @@ class ServersControllerTest(test.TestCase): self.assertEquals(res, [(None, None, port)]) def test_get_server_by_uuid(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) res_dict = self.controller.show(req, FAKE_UUID) self.assertEqual(res_dict['server']['id'], FAKE_UUID) @@ -250,7 +250,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get', return_instance_with_host) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) server1 = self.controller.show(req, FAKE_UUID) server2 = self.controller.show(req, FAKE_UUID) @@ -263,7 +263,7 @@ class ServersControllerTest(test.TestCase): flavor_bookmark = "http://localhost/fake/flavors/1" uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % uuid) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { @@ -330,7 +330,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', new_return_server) uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % uuid) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { "server": { @@ -399,7 +399,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', new_return_server) uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % uuid) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % uuid) res_dict = self.controller.show(req, uuid) expected_server = { "server": { @@ -488,7 +488,7 @@ class ServersControllerTest(test.TestCase): return_server = fakes.fake_instance_get(nw_cache=nw_cache) self.stubs.Set(db, 'instance_get_by_uuid', return_server) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/ips' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s/ips' % FAKE_UUID) res_dict = self.ips_controller.index(req, FAKE_UUID) expected = { @@ -508,7 +508,7 @@ class ServersControllerTest(test.TestCase): self.assertThat(res_dict, matchers.DictMatches(expected)) def test_get_server_addresses_nonexistent_network(self): - url = '/v2/fake/servers/%s/ips/network_0' % FAKE_UUID + url = '/fake/servers/%s/ips/network_0' % FAKE_UUID req = fakes.HTTPRequest.blank(url) self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.show, req, FAKE_UUID, 'network_0') @@ -520,7 +520,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get) server_id = str(uuid.uuid4()) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/ips' % server_id) + req = fakes.HTTPRequest.blank('/fake/servers/%s/ips' % server_id) self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.index, req, server_id) @@ -528,14 +528,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_empty) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') res_dict = self.controller.index(req) num_servers = len(res_dict['servers']) self.assertEqual(0, num_servers) def test_get_server_list_with_reservation_id(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?reservation_id=foo') + req = fakes.HTTPRequest.blank('/fake/servers?reservation_id=foo') res_dict = self.controller.index(req) i = 0 @@ -544,7 +544,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list_with_reservation_id_empty(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail?' + req = fakes.HTTPRequest.blank('/fake/servers/detail?' 'reservation_id=foo') res_dict = self.controller.detail(req) @@ -554,7 +554,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list_with_reservation_id_details(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail?' + req = fakes.HTTPRequest.blank('/fake/servers/detail?' 'reservation_id=foo') res_dict = self.controller.detail(req) @@ -564,7 +564,7 @@ class ServersControllerTest(test.TestCase): i += 1 def test_get_server_list(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') res_dict = self.controller.index(req) self.assertEqual(len(res_dict['servers']), 5) @@ -587,7 +587,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(s['links'], expected_links) def test_get_servers_with_limit(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=3') + req = fakes.HTTPRequest.blank('/fake/servers?limit=3') res_dict = self.controller.index(req) servers = res_dict['servers'] @@ -604,7 +604,7 @@ class ServersControllerTest(test.TestCase): self.assertThat(params, matchers.DictMatches(expected_params)) def test_get_servers_with_limit_bad_value(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=aaa') + req = fakes.HTTPRequest.blank('/fake/servers?limit=aaa') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) @@ -612,14 +612,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_empty) - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail') + req = fakes.HTTPRequest.blank('/fake/servers/detail') res_dict = self.controller.index(req) num_servers = len(res_dict['servers']) self.assertEqual(0, num_servers) def test_get_server_details_with_limit(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail?limit=3') + req = fakes.HTTPRequest.blank('/fake/servers/detail?limit=3') res = self.controller.detail(req) servers = res['servers'] @@ -636,12 +636,12 @@ class ServersControllerTest(test.TestCase): self.assertThat(params, matchers.DictMatches(expected)) def test_get_server_details_with_limit_bad_value(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail?limit=aaa') + req = fakes.HTTPRequest.blank('/fake/servers/detail?limit=aaa') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) def test_get_server_details_with_limit_and_other_params(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail' + req = fakes.HTTPRequest.blank('/fake/servers/detail' '?limit=3&blah=2:t') res = self.controller.detail(req) @@ -660,12 +660,12 @@ class ServersControllerTest(test.TestCase): self.assertThat(params, matchers.DictMatches(expected)) def test_get_servers_with_too_big_limit(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=30') + req = fakes.HTTPRequest.blank('/fake/servers?limit=30') res_dict = self.controller.index(req) self.assertTrue('servers_links' not in res_dict) def test_get_servers_with_bad_limit(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=asdf') + req = fakes.HTTPRequest.blank('/fake/servers?limit=asdf') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) @@ -682,7 +682,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual([s['name'] for s in servers], ['server3', 'server4']) def test_get_servers_with_bad_marker(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=2&marker=asdf') + req = fakes.HTTPRequest.blank('/fake/servers?limit=2&marker=asdf') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) @@ -696,7 +696,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?unknownoption=whee') + req = fakes.HTTPRequest.blank('/fake/servers?unknownoption=whee') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -715,7 +715,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?image=12345') + req = fakes.HTTPRequest.blank('/fake/servers?image=12345') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -732,7 +732,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?tenant_id=fake', + req = fakes.HTTPRequest.blank('/fake/servers?tenant_id=fake', use_admin_context=True) res = self.controller.index(req) @@ -748,7 +748,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers', + req = fakes.HTTPRequest.blank('/fake/servers', use_admin_context=True) res = self.controller.index(req) @@ -773,7 +773,7 @@ class ServersControllerTest(test.TestCase): common_policy.set_rules(common_policy.Rules(rules)) - req = fakes.HTTPRequest.blank('/v2/fake/servers?all_tenants=1') + req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1') res = self.controller.index(req) self.assertTrue('servers' in res) @@ -795,7 +795,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?all_tenants=1') + req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1') self.assertRaises(exception.PolicyNotAuthorized, self.controller.index, req) @@ -813,14 +813,14 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?flavor=12345') + req = fakes.HTTPRequest.blank('/fake/servers?flavor=12345') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) self.assertEqual(servers[0]['id'], server_uuid) def test_get_servers_with_bad_flavor(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?flavor=abcde') + req = fakes.HTTPRequest.blank('/fake/servers?flavor=abcde') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 0) @@ -838,7 +838,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?status=active') + req = fakes.HTTPRequest.blank('/fake/servers?status=active') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -846,13 +846,13 @@ class ServersControllerTest(test.TestCase): def test_get_servers_invalid_status(self): # Test getting servers by invalid status. - req = fakes.HTTPRequest.blank('/v2/fake/servers?status=baloney', + req = fakes.HTTPRequest.blank('/fake/servers?status=baloney', use_admin_context=False) servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 0) def test_get_servers_deleted_status_as_user(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers?status=deleted', + req = fakes.HTTPRequest.blank('/fake/servers?status=deleted', use_admin_context=False) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) @@ -870,7 +870,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?status=deleted', + req = fakes.HTTPRequest.blank('/fake/servers?status=deleted', use_admin_context=True) servers = self.controller.detail(req)['servers'] @@ -890,7 +890,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?name=whee.*') + req = fakes.HTTPRequest.blank('/fake/servers?name=whee.*') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -913,7 +913,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) params = 'changes-since=2011-01-24T17:08:01Z' - req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % params) + req = fakes.HTTPRequest.blank('/fake/servers?%s' % params) servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -921,7 +921,7 @@ class ServersControllerTest(test.TestCase): def test_get_servers_allows_changes_since_bad_value(self): params = 'changes-since=asdf' - req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % params) + req = fakes.HTTPRequest.blank('/fake/servers?%s' % params) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) def test_get_servers_admin_filters_as_user(self): @@ -947,7 +947,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % query_str) + req = fakes.HTTPRequest.blank('/fake/servers?%s' % query_str) res = self.controller.index(req) servers = res['servers'] @@ -976,7 +976,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % query_str, + req = fakes.HTTPRequest.blank('/fake/servers?%s' % query_str, use_admin_context=True) servers = self.controller.index(req)['servers'] @@ -997,7 +997,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?ip=10\..*') + req = fakes.HTTPRequest.blank('/fake/servers?ip=10\..*') servers = self.controller.index(req)['servers'] self.assertEqual(len(servers), 1) @@ -1019,7 +1019,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) - req = fakes.HTTPRequest.blank('/v2/fake/servers?ip6=ffff.*', + req = fakes.HTTPRequest.blank('/fake/servers?ip6=ffff.*', use_admin_context=True) servers = self.controller.index(req)['servers'] @@ -1031,7 +1031,7 @@ class ServersControllerTest(test.TestCase): fakes.fake_instance_get(name='server_test', access_ipv4='0.0.0.0', access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': { @@ -1048,7 +1048,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') def test_update_server_invalid_xml_raises_lookup(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/xml' #xml request which raises LookupError @@ -1060,7 +1060,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res.status_int, 400) def test_update_server_invalid_xml_raises_expat(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/xml' #xml request which raises ExpatError @@ -1074,7 +1074,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_name(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(name='server_test')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1087,7 +1087,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_name_too_long(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(name='server_test')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'x' * 256}} @@ -1109,7 +1109,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': '0.0.0.0'}} @@ -1122,7 +1122,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_bad_format(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': 'bad_format'}} @@ -1133,7 +1133,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_none(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': None}} @@ -1146,7 +1146,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv4_blank(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv4': ''}} @@ -1159,7 +1159,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': 'beef::0123'}} @@ -1172,7 +1172,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_bad_format(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': 'bad_format'}} @@ -1183,7 +1183,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_none(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': None}} @@ -1196,7 +1196,7 @@ class ServersControllerTest(test.TestCase): def test_update_server_access_ipv6_blank(self): self.stubs.Set(db, 'instance_get', fakes.fake_instance_get(access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'accessIPv6': ''}} @@ -1207,7 +1207,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(res_dict['server']['accessIPv6'], '') def test_update_server_personality(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = { @@ -1237,7 +1237,7 @@ class ServersControllerTest(test.TestCase): # self.stubs.Set(db, 'instance_get', # return_server_with_attributes(name='server_test')) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = "application/json" req.body = jsonutils.dumps(body) @@ -1251,7 +1251,7 @@ class ServersControllerTest(test.TestCase): raise exception.InstanceNotFound(instance_id='fake') self.stubs.Set(compute_api.API, 'get', fake_get) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1264,7 +1264,7 @@ class ServersControllerTest(test.TestCase): raise exception.InstanceNotFound(instance_id='fake') self.stubs.Set(compute_api.API, 'update', fake_update) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' req.content_type = 'application/json' body = {'server': {'name': 'server_test'}} @@ -1299,7 +1299,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1333,7 +1333,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1367,7 +1367,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1401,7 +1401,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1429,7 +1429,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1457,7 +1457,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1486,7 +1486,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1512,7 +1512,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1546,7 +1546,7 @@ class ServersControllerTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req = fakes.HTTPRequest.blank('/fake/servers/a/action') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1572,7 +1572,7 @@ class ServersControllerTest(test.TestCase): }, ], } - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail') + req = fakes.HTTPRequest.blank('/fake/servers/detail') res_dict = self.controller.detail(req) for i, s in enumerate(res_dict['servers']): @@ -1600,7 +1600,7 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(db, 'instance_get_all_by_filters', return_servers_with_host) - req = fakes.HTTPRequest.blank('/v2/fake/servers/detail') + req = fakes.HTTPRequest.blank('/fake/servers/detail') res_dict = self.controller.detail(req) server_list = res_dict['servers'] @@ -1615,7 +1615,7 @@ class ServersControllerTest(test.TestCase): def _delete_server_instance(self, uuid=FAKE_UUID): fakes.stub_out_instance_quota(self.stubs, 0, 10) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % uuid) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % uuid) req.method = 'DELETE' self.server_delete_called = False @@ -1640,7 +1640,7 @@ class ServersControllerTest(test.TestCase): def test_delete_server_instance_while_building(self): fakes.stub_out_instance_quota(self.stubs, 0, 10) - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'DELETE' self.server_delete_called = False @@ -1654,7 +1654,7 @@ class ServersControllerTest(test.TestCase): self.assertEqual(self.server_delete_called, True) def test_delete_server_instance_while_resize(self): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'DELETE' self.server_delete_called = False @@ -1688,7 +1688,7 @@ class ServerStatusTest(test.TestCase): fakes.fake_instance_get(vm_state=vm_state, task_state=task_state)) - request = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + request = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) return self.controller.show(request, FAKE_UUID) def test_active(self): @@ -1714,7 +1714,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:reboot': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequest.blank('/v2/fake/servers/1234/action') + req = fakes.HTTPRequest.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_reboot, req, '1234', {'reboot': {'type': 'HARD'}}) @@ -1742,7 +1742,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:confirm_resize': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequest.blank('/v2/fake/servers/1234/action') + req = fakes.HTTPRequest.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_confirm_resize, req, '1234', {}) @@ -1764,7 +1764,7 @@ class ServerStatusTest(test.TestCase): rule = {'compute:revert_resize': common_policy.parse_rule('role:admin')} common_policy.set_rules(common_policy.Rules(rule)) - req = fakes.HTTPRequest.blank('/v2/fake/servers/1234/action') + req = fakes.HTTPRequest.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_revert_resize, req, '1234', {}) @@ -1900,7 +1900,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_uuid, flavorRef=2, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1921,7 +1921,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1942,7 +1942,7 @@ class ServersControllerCreateTest(test.TestCase): 'networks': {'uuid': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'}, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -1960,7 +1960,7 @@ class ServersControllerCreateTest(test.TestCase): self.addCleanup(image_service.update, context, image_uuid, {'status': 'active'}) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' body = dict(server=dict( name='server_test', imageRef=image_uuid, flavorRef=2, @@ -1990,7 +1990,7 @@ class ServersControllerCreateTest(test.TestCase): self.addCleanup(image_service.update, context, image_uuid, {'size': orig_size}) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' body = dict(server=dict(name='server_test', imageRef=image_uuid, @@ -2016,7 +2016,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2038,7 +2038,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2060,7 +2060,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2082,7 +2082,7 @@ class ServersControllerCreateTest(test.TestCase): 'flavorRef': flavor_ref, } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2110,7 +2110,7 @@ class ServersControllerCreateTest(test.TestCase): } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2139,7 +2139,7 @@ class ServersControllerCreateTest(test.TestCase): } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2168,7 +2168,7 @@ class ServersControllerCreateTest(test.TestCase): } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2244,7 +2244,7 @@ class ServersControllerCreateTest(test.TestCase): } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2267,7 +2267,7 @@ class ServersControllerCreateTest(test.TestCase): } } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2286,7 +2286,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2307,7 +2307,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2325,7 +2325,7 @@ class ServersControllerCreateTest(test.TestCase): server.pop('imageRef', None) server.update(params) body = dict(server=server) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2805,7 +2805,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2845,7 +2845,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2882,7 +2882,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2916,7 +2916,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -2947,7 +2947,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3009,7 +3009,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3044,7 +3044,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3072,7 +3072,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3096,7 +3096,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3120,7 +3120,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3144,7 +3144,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3158,7 +3158,7 @@ class ServersControllerCreateTest(test.TestCase): body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, key_name='nonexistentkey')) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3172,7 +3172,7 @@ class ServersControllerCreateTest(test.TestCase): body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, key_name='key')) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3188,7 +3188,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3203,7 +3203,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3218,7 +3218,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3244,34 +3244,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') - req.method = 'POST' - req.body = jsonutils.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body).obj - - server = res['server'] - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_with_config_drive_as_id(self): - self.ext_mgr.extensions = {'os-config-drive': 'fake'} - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v2/fake/flavors/3' - body = { - 'server': { - 'name': 'config_drive_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - 'config_drive': image_href, - }, - } - - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3281,6 +3254,7 @@ class ServersControllerCreateTest(test.TestCase): self.assertEqual(FAKE_UUID, server['id']) def test_create_instance_with_bad_config_drive(self): + # Test with an image href as config drive value. self.ext_mgr.extensions = {'os-config-drive': 'fake'} image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v2/fake/flavors/3' @@ -3294,15 +3268,14 @@ class ServersControllerCreateTest(test.TestCase): 'open': 'stack', }, 'personality': {}, - 'config_drive': 'asdf', + 'config_drive': image_href, }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, body) @@ -3323,7 +3296,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3351,7 +3324,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3370,7 +3343,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3390,7 +3363,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3411,7 +3384,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3432,7 +3405,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = "application/json" @@ -3470,7 +3443,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -3501,7 +3474,7 @@ class ServersControllerCreateTest(test.TestCase): }, } - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers['content-type'] = 'application/json' @@ -3517,7 +3490,7 @@ class ServersControllerCreateTest(test.TestCase): name='server_test', imageRef=image_uuid, flavorRef=3, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" @@ -4285,7 +4258,7 @@ class ServersViewBuilderTest(test.TestCase): self.uuid = self.instance['uuid'] self.view_builder = views.servers.ViewBuilder() - self.request = fakes.HTTPRequest.blank("/v2") + self.request = fakes.HTTPRequest.blank("/v2/fake") def test_get_flavor_valid_flavor(self): flavor_bookmark = "http://localhost/fake/flavors/1" @@ -5808,7 +5781,7 @@ class ServersUnprocessableEntityTestCase(test.TestCase): self.controller = servers.Controller(self.ext_mgr) def _unprocessable_server_create(self, body): - req = fakes.HTTPRequest.blank('/v2/fake/servers') + req = fakes.HTTPRequest.blank('/fake/servers') req.method = 'POST' self.assertRaises(webob.exc.HTTPUnprocessableEntity, @@ -5826,7 +5799,7 @@ class ServersUnprocessableEntityTestCase(test.TestCase): self._unprocessable_server_create(body=body) def _unprocessable_server_update(self, body): - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID) req.method = 'PUT' self.assertRaises(webob.exc.HTTPUnprocessableEntity, diff --git a/nova/tests/api/openstack/compute/test_v3_extensions.py b/nova/tests/api/openstack/compute/test_v3_extensions.py index 97429ca45..ec472f38a 100644 --- a/nova/tests/api/openstack/compute/test_v3_extensions.py +++ b/nova/tests/api/openstack/compute/test_v3_extensions.py @@ -16,10 +16,12 @@ from oslo.config import cfg import stevedore +import webob.exc from nova.api import openstack from nova.api.openstack import compute from nova.api.openstack.compute import plugins +from nova.api.openstack import extensions from nova import exception from nova import test @@ -139,3 +141,38 @@ class ExtensionLoadingTestCase(test.TestCase): self.stubs.Set(plugins, 'LoadedExtensionInfo', fake_loaded_extension_info) self.assertRaises(exception.CoreAPIMissing, compute.APIRouterV3) + + def test_extensions_expected_error(self): + @extensions.expected_errors(404) + def fake_func(): + raise webob.exc.HTTPNotFound() + + self.assertRaises(webob.exc.HTTPNotFound, fake_func) + + def test_extensions_expected_error_from_list(self): + @extensions.expected_errors((404, 403)) + def fake_func(): + raise webob.exc.HTTPNotFound() + + self.assertRaises(webob.exc.HTTPNotFound, fake_func) + + def test_extensions_unexpected_error(self): + @extensions.expected_errors(404) + def fake_func(): + raise webob.exc.HTTPConflict() + + self.assertRaises(webob.exc.HTTPInternalServerError, fake_func) + + def test_extensions_unexpected_error_from_list(self): + @extensions.expected_errors((404, 413)) + def fake_func(): + raise webob.exc.HTTPConflict() + + self.assertRaises(webob.exc.HTTPInternalServerError, fake_func) + + def test_extensions_unexpected_policy_not_authorized_error(self): + @extensions.expected_errors(404) + def fake_func(): + raise exception.PolicyNotAuthorized(action="foo") + + self.assertRaises(exception.PolicyNotAuthorized, fake_func) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index f05561ff2..d0239885d 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -419,7 +419,7 @@ def get_fake_uuid(token=0): def fake_instance_get(**kwargs): - def _return_server(context, uuid): + def _return_server(context, uuid, columns_to_join=None): return stub_instance(1, **kwargs) return _return_server @@ -435,6 +435,8 @@ def fake_instance_get_all_by_filters(num_servers=5, **kwargs): if "limit" in kwargs: limit = kwargs["limit"] + if 'columns_to_join' in kwargs: + kwargs.pop('columns_to_join') for i in xrange(num_servers): uuid = get_fake_uuid(i) server = stub_instance(id=i + 1, uuid=uuid, @@ -462,7 +464,8 @@ def stub_instance(id, user_id=None, project_id=None, host=None, security_groups=None, root_device_name=None, limit=None, marker=None, launched_at=datetime.datetime.utcnow(), - terminated_at=datetime.datetime.utcnow()): + terminated_at=datetime.datetime.utcnow(), + availability_zone=''): if user_id is None: user_id = 'fake_user' @@ -488,7 +491,10 @@ def stub_instance(id, user_id=None, project_id=None, host=None, key_data = '' if security_groups is None: - security_groups = [{"id": 1, "name": "test"}] + security_groups = [{"id": 1, "name": "test", "description": "Foo:", + "project_id": "project", "user_id": "user", + "created_at": None, "updated_at": None, + "deleted_at": None, "deleted": False}] # ReservationID isn't sent back, hack it in there. server_name = name or "server%s" % id @@ -501,6 +507,8 @@ def stub_instance(id, user_id=None, project_id=None, host=None, "id": int(id), "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "deleted_at": datetime.datetime(2010, 12, 12, 10, 0, 0), + "deleted": None, "user_id": user_id, "project_id": project_id, "image_ref": image_ref, @@ -528,7 +536,7 @@ def stub_instance(id, user_id=None, project_id=None, host=None, "scheduled_at": timeutils.utcnow(), "launched_at": launched_at, "terminated_at": terminated_at, - "availability_zone": "", + "availability_zone": availability_zone, "display_name": display_name or server_name, "display_description": "", "locked": False, @@ -553,6 +561,7 @@ def stub_instance(id, user_id=None, project_id=None, host=None, "os_type": ""} instance.update(info_cache) + instance['info_cache']['instance_uuid'] = instance['uuid'] return instance diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index 4e35cd818..89a60cb35 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -548,3 +548,27 @@ class CellsManagerClassTestCase(test.TestCase): instance_uuid=instance_uuid, console_port=console_port, console_type=console_type) self.assertEqual('fake-response', response) + + def test_bdm_update_or_create_at_top(self): + self.mox.StubOutWithMock(self.msg_runner, + 'bdm_update_or_create_at_top') + self.msg_runner.bdm_update_or_create_at_top(self.ctxt, + 'fake-bdm', + create='foo') + self.mox.ReplayAll() + self.cells_manager.bdm_update_or_create_at_top(self.ctxt, + 'fake-bdm', + create='foo') + + def test_bdm_destroy_at_top(self): + self.mox.StubOutWithMock(self.msg_runner, 'bdm_destroy_at_top') + self.msg_runner.bdm_destroy_at_top(self.ctxt, + 'fake_instance_uuid', + device_name='fake_device_name', + volume_id='fake_volume_id') + + self.mox.ReplayAll() + self.cells_manager.bdm_destroy_at_top(self.ctxt, + 'fake_instance_uuid', + device_name='fake_device_name', + volume_id='fake_volume_id') diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index d4d9b052e..734fecf0e 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -23,6 +23,7 @@ from nova.compute import vm_states from nova import context from nova import db from nova import exception +from nova.objects import instance as instance_obj from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import test @@ -641,6 +642,60 @@ class CellsTargetedMethodsTestCase(test.TestCase): result = response.value_or_raise() self.assertEqual('fake_result', result) + def test_run_compute_api_method_expects_obj(self): + instance_uuid = 'fake_instance_uuid' + method_info = {'method': 'start', + 'method_args': (instance_uuid, 2, 3), + 'method_kwargs': {'arg1': 'val1', 'arg2': 'val2'}} + self.mox.StubOutWithMock(self.tgt_compute_api, 'start') + self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid') + + self.tgt_db_inst.instance_get_by_uuid(self.ctxt, + instance_uuid).AndReturn('fake_instance') + + def get_instance_mock(): + # NOTE(comstud): This block of code simulates the following + # mox code: + # + # self.mox.StubOutWithMock(instance_obj, 'Instance', + # use_mock_anything=True) + # self.mox.StubOutWithMock(instance_obj.Instance, + # '_from_db_object') + # instance_mock = self.mox.CreateMock(instance_obj.Instance) + # instance_obj.Instance().AndReturn(instance_mock) + # + # Unfortunately, the above code fails on py27 do to some + # issue with the Mock object do to similar issue as this: + # https://code.google.com/p/pymox/issues/detail?id=35 + # + class FakeInstance(object): + def _from_db_object(obj, db_obj): + pass + + instance_mock = FakeInstance() + + def fake_instance(): + return instance_mock + + self.stubs.Set(instance_obj, 'Instance', fake_instance) + self.mox.StubOutWithMock(instance_mock, '_from_db_object') + return instance_mock + + instance = get_instance_mock() + instance._from_db_object( + instance, 'fake_instance').AndReturn(instance) + self.tgt_compute_api.start(self.ctxt, instance, 2, 3, + arg1='val1', arg2='val2').AndReturn('fake_result') + self.mox.ReplayAll() + + response = self.src_msg_runner.run_compute_api_method( + self.ctxt, + self.tgt_cell_name, + method_info, + True) + result = response.value_or_raise() + self.assertEqual('fake_result', result) + def test_run_compute_api_method_unknown_instance(self): # Unknown instance should send a broadcast up that instance # is gone. @@ -1426,3 +1481,173 @@ class CellsBroadcastMethodsTestCase(test.TestCase): self.mox.ReplayAll() self.src_msg_runner.consoleauth_delete_tokens(self.ctxt, fake_uuid) + + def test_bdm_update_or_create_with_none_create(self): + fake_bdm = {'id': 'fake_id', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update_or_create') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update_or_create') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update_or_create') + self.tgt_db_inst.block_device_mapping_update_or_create( + self.ctxt, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=None) + + def test_bdm_update_or_create_with_true_create(self): + fake_bdm = {'id': 'fake_id', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_create') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_create') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_create') + self.tgt_db_inst.block_device_mapping_create( + self.ctxt, fake_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=True) + + def test_bdm_update_or_create_with_false_create_vol_id(self): + fake_bdm = {'id': 'fake_id', + 'instance_uuid': 'fake_instance_uuid', + 'device_name': 'fake_device_name', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + fake_inst_bdms = [{'id': 1, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}, + {'id': 2, + 'volume_id': 'fake_volume_id', + 'device_name': 'not-a-match'}, + {'id': 3, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}] + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_get_all_by_instance') + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update') + + self.tgt_db_inst.block_device_mapping_get_all_by_instance( + self.ctxt, 'fake_instance_uuid').AndReturn( + fake_inst_bdms) + # Should try to update ID 2. + self.tgt_db_inst.block_device_mapping_update( + self.ctxt, 2, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=False) + + def test_bdm_update_or_create_with_false_create_dev_name(self): + fake_bdm = {'id': 'fake_id', + 'instance_uuid': 'fake_instance_uuid', + 'device_name': 'fake_device_name', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + fake_inst_bdms = [{'id': 1, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}, + {'id': 2, + 'volume_id': 'not-a-match', + 'device_name': 'fake_device_name'}, + {'id': 3, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}] + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_get_all_by_instance') + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update') + + self.tgt_db_inst.block_device_mapping_get_all_by_instance( + self.ctxt, 'fake_instance_uuid').AndReturn( + fake_inst_bdms) + # Should try to update ID 2. + self.tgt_db_inst.block_device_mapping_update( + self.ctxt, 2, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=False) + + def test_bdm_destroy_by_volume(self): + fake_instance_uuid = 'fake-instance-uuid' + fake_volume_id = 'fake-volume-name' + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + self.tgt_db_inst.block_device_mapping_destroy_by_instance_and_volume( + self.ctxt, fake_instance_uuid, fake_volume_id) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_destroy_at_top(self.ctxt, fake_instance_uuid, + volume_id=fake_volume_id) + + def test_bdm_destroy_by_device(self): + fake_instance_uuid = 'fake-instance-uuid' + fake_device_name = 'fake-device-name' + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + self.tgt_db_inst.block_device_mapping_destroy_by_instance_and_device( + self.ctxt, fake_instance_uuid, fake_device_name) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_destroy_at_top(self.ctxt, fake_instance_uuid, + device_name=fake_device_name) diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index 4d58bdb9e..6eeff1730 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -425,3 +425,29 @@ class CellsAPITestCase(test.TestCase): self._check_result(call_info, 'validate_console_port', expected_args, version='1.6') self.assertEqual(result, 'fake_response') + + def test_bdm_update_or_create_at_top(self): + fake_bdm = {'id': 2, 'other': 'meow'} + + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.bdm_update_or_create_at_top( + self.fake_context, fake_bdm, create='fake-create') + + expected_args = {'bdm': fake_bdm, 'create': 'fake-create'} + self._check_result(call_info, 'bdm_update_or_create_at_top', + expected_args, version='1.10') + + def test_bdm_destroy_at_top(self): + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.bdm_destroy_at_top(self.fake_context, + 'fake-uuid', + device_name='fake-device', + volume_id='fake-vol') + + expected_args = {'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device', + 'volume_id': 'fake-vol'} + self._check_result(call_info, 'bdm_destroy_at_top', + expected_args, version='1.10') diff --git a/nova/tests/cells/test_cells_scheduler.py b/nova/tests/cells/test_cells_scheduler.py index 9cd637cdf..46be492cc 100644 --- a/nova/tests/cells/test_cells_scheduler.py +++ b/nova/tests/cells/test_cells_scheduler.py @@ -84,10 +84,9 @@ class CellsSchedulerTestCase(test.TestCase): 'block_device_mapping': 'fake_bdm'} self.build_inst_kwargs = { 'instances': self.instances, - 'instance_type': 'fake_type', 'image': 'fake_image', - 'filter_properties': {}, - 'security_group': 'fake_sec_groups', + 'filter_properties': {'instance_type': 'fake_type'}, + 'security_groups': 'fake_sec_groups', 'block_device_mapping': 'fake_bdm'} def test_create_instances_here(self): @@ -178,7 +177,7 @@ class CellsSchedulerTestCase(test.TestCase): call_info['target_cell'] = target_cell call_info['build_inst_kwargs'] = build_inst_kwargs - def fake_build_request_spec(image, instances): + def fake_build_request_spec(ctxt, image, instances): request_spec = { 'instance_uuids': [inst['uuid'] for inst in instances], 'image': image} @@ -264,7 +263,7 @@ class CellsSchedulerTestCase(test.TestCase): def fake_rpc_build_instances(ctxt, **build_inst_kwargs): call_info['build_inst_kwargs'] = build_inst_kwargs - def fake_build_request_spec(image, instances): + def fake_build_request_spec(ctxt, image, instances): request_spec = { 'instance_uuids': [inst['uuid'] for inst in instances], 'image': image} @@ -284,10 +283,11 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(self.instance_uuids, call_info['instance_uuids']) self.assertEqual(self.build_inst_kwargs['instances'][0], call_info['instance_properties']) - self.assertEqual(self.build_inst_kwargs['instance_type'], - call_info['instance_type']) + self.assertEqual( + self.build_inst_kwargs['filter_properties']['instance_type'], + call_info['instance_type']) self.assertEqual(self.build_inst_kwargs['image'], call_info['image']) - self.assertEqual(self.build_inst_kwargs['security_group'], + self.assertEqual(self.build_inst_kwargs['security_groups'], call_info['security_groups']) self.assertEqual(self.build_inst_kwargs['block_device_mapping'], call_info['block_device_mapping']) @@ -341,7 +341,7 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(vm_states.ERROR, values['vm_state']) call_info['errored_uuids'].append(instance_uuid) - def fake_build_request_spec(image, instances): + def fake_build_request_spec(ctxt, image, instances): request_spec = { 'instance_uuids': [inst['uuid'] for inst in instances], 'image': image} @@ -385,7 +385,7 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(vm_states.ERROR, instance['vm_state']) call_info['errored_uuids2'].append(instance['uuid']) - def fake_build_request_spec(image, instances): + def fake_build_request_spec(ctxt, image, instances): request_spec = { 'instance_uuids': [inst['uuid'] for inst in instances], 'image': image} diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 8c6fce3a5..4fbf805f1 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -73,6 +73,11 @@ from nova.tests import fake_network_cache_model from nova.tests.image import fake as fake_image from nova.tests import matchers from nova import utils +from nova.virt.event import EVENT_LIFECYCLE_PAUSED +from nova.virt.event import EVENT_LIFECYCLE_RESUMED +from nova.virt.event import EVENT_LIFECYCLE_STARTED +from nova.virt.event import EVENT_LIFECYCLE_STOPPED +from nova.virt.event import LifecycleEvent from nova.virt import fake from nova.volume import cinder @@ -259,6 +264,10 @@ class BaseTestCase(test.TestCase): inst['os_type'] = 'Linux' inst['system_metadata'] = make_fake_sys_meta() inst['locked'] = False + inst['created_at'] = timeutils.utcnow() + inst['updated_at'] = timeutils.utcnow() + inst['launched_at'] = timeutils.utcnow() + inst['security_groups'] = [] inst.update(params) _create_service_entries(self.context.elevated(), {'fake_zone': [inst['host']]}) @@ -272,7 +281,10 @@ class BaseTestCase(test.TestCase): def _fake_db_create(_ctxt, inst): for k, v in inst.items(): - setattr(instance, k, v) + if k == 'security_groups': + setattr(instance, k, v or None) + else: + setattr(instance, k, v) return instance self.stubs.Set(db, 'instance_create', _fake_db_create) @@ -1076,6 +1088,103 @@ class ComputeTestCase(BaseTestCase): self._assert_state({'vm_state': vm_states.ERROR, 'task_state': None}) + def test_allocate_network_succeeds_after_retries(self): + # Undo setUp() stubs as this is a true unit test + self.stubs.UnsetAll() + self.flags(network_allocate_retries=8) + + nwapi = self.compute.network_api + self.mox.StubOutWithMock(nwapi, 'allocate_for_instance') + self.mox.StubOutWithMock(time, 'sleep') + + instance = {} + is_vpn = 'fake-is-vpn' + req_networks = 'fake-req-networks' + macs = 'fake-macs' + sec_groups = 'fake-sec-groups' + final_result = 'meow' + + expected_sleep_times = [1, 2, 4, 8, 16, 30, 30, 30] + + for sleep_time in expected_sleep_times: + nwapi.allocate_for_instance( + self.context, instance, vpn=is_vpn, + requested_networks=req_networks, macs=macs, + conductor_api=self.compute.conductor_api, + security_groups=sec_groups).AndRaise( + test.TestingException()) + time.sleep(sleep_time) + + nwapi.allocate_for_instance( + self.context, instance, vpn=is_vpn, + requested_networks=req_networks, macs=macs, + conductor_api=self.compute.conductor_api, + security_groups=sec_groups).AndReturn(final_result) + + self.mox.ReplayAll() + + res = self.compute._allocate_network_async(self.context, instance, + req_networks, + macs, + sec_groups, + is_vpn) + self.assertEqual(final_result, res) + + def test_allocate_network_fails(self): + # Undo setUp() stubs as this is a true unit test + self.stubs.UnsetAll() + self.flags(network_allocate_retries=0) + + nwapi = self.compute.network_api + self.mox.StubOutWithMock(nwapi, 'allocate_for_instance') + + instance = {} + is_vpn = 'fake-is-vpn' + req_networks = 'fake-req-networks' + macs = 'fake-macs' + sec_groups = 'fake-sec-groups' + + nwapi.allocate_for_instance( + self.context, instance, vpn=is_vpn, + requested_networks=req_networks, macs=macs, + conductor_api=self.compute.conductor_api, + security_groups=sec_groups).AndRaise(test.TestingException()) + + self.mox.ReplayAll() + + self.assertRaises(test.TestingException, + self.compute._allocate_network_async, + self.context, instance, req_networks, macs, + sec_groups, is_vpn) + + def test_allocate_network_neg_conf_value_treated_as_zero(self): + # Undo setUp() stubs as this is a true unit test + self.stubs.UnsetAll() + self.flags(network_allocate_retries=-1) + + nwapi = self.compute.network_api + self.mox.StubOutWithMock(nwapi, 'allocate_for_instance') + + instance = {} + is_vpn = 'fake-is-vpn' + req_networks = 'fake-req-networks' + macs = 'fake-macs' + sec_groups = 'fake-sec-groups' + + # Only attempted once. + nwapi.allocate_for_instance( + self.context, instance, vpn=is_vpn, + requested_networks=req_networks, macs=macs, + conductor_api=self.compute.conductor_api, + security_groups=sec_groups).AndRaise(test.TestingException()) + + self.mox.ReplayAll() + + self.assertRaises(test.TestingException, + self.compute._allocate_network_async, + self.context, instance, req_networks, macs, + sec_groups, is_vpn) + def test_run_instance_dealloc_network_instance_not_found(self): """spawn network deallocate test. @@ -1214,6 +1323,30 @@ class ComputeTestCase(BaseTestCase): LOG.info(_("After terminating instances: %s"), instances) self.assertEqual(len(instances), 0) + def test_terminate_no_fixed_ips(self): + # This is as reported in LP bug 1192893 + instance = jsonutils.to_primitive(self._create_fake_instance()) + + self.compute.run_instance(self.context, instance=instance) + + instances = db.instance_get_all(self.context) + LOG.info(_("Running instances: %s"), instances) + self.assertEqual(len(instances), 1) + + self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') + self.compute._get_instance_nw_info( + mox.IgnoreArg(), + mox.IgnoreArg()).AndRaise( + exception.NoMoreFixedIps() + ) + self.mox.ReplayAll() + + self.compute.terminate_instance(self.context, instance=instance) + + instances = db.instance_get_all(self.context) + LOG.info(_("After terminating instances: %s"), instances) + self.assertEqual(len(instances), 0) + def test_terminate_failure_leaves_task_state(self): """Ensure that a failure in terminate_instance does not result in the task state being reverted from DELETING (see LP 1046236). @@ -1248,6 +1381,7 @@ class ComputeTestCase(BaseTestCase): def test_run_terminate_timestamps(self): # Make sure timestamps are set for launched and destroyed. instance = jsonutils.to_primitive(self._create_fake_instance()) + instance['launched_at'] = None self.assertEqual(instance['launched_at'], None) self.assertEqual(instance['deleted_at'], None) launch = timeutils.utcnow() @@ -1399,7 +1533,8 @@ class ComputeTestCase(BaseTestCase): called = {'power_on': False} - def fake_driver_power_on(self, instance): + def fake_driver_power_on(self, context, instance, network_info, + block_device_info): called['power_on'] = True self.stubs.Set(nova.virt.fake.FakeDriver, 'power_on', @@ -2257,6 +2392,7 @@ class ComputeTestCase(BaseTestCase): self.assertTrue('display_name' in payload) self.assertTrue('created_at' in payload) self.assertTrue('launched_at' in payload) + self.assertTrue('fixed_ips' in payload) self.assertTrue(payload['launched_at']) image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF) self.assertEquals(payload['image_ref_url'], image_ref_url) @@ -4333,9 +4469,11 @@ class ComputeTestCase(BaseTestCase): instance_map = {} instances = [] for x in xrange(5): - uuid = 'fake-uuid-%s' % x - instance_map[uuid] = {'uuid': uuid, 'host': CONF.host} - instances.append(instance_map[uuid]) + inst_uuid = 'fake-uuid-%s' % x + instance_map[inst_uuid] = fake_instance.fake_db_instance( + uuid=inst_uuid, host=CONF.host, created_at=None) + # These won't be in our instance since they're not requested + instances.append(instance_map[inst_uuid]) call_info = {'get_all_by_host': 0, 'get_by_uuid': 0, 'get_nw_info': 0, 'expected_instance': None} @@ -4345,7 +4483,7 @@ class ComputeTestCase(BaseTestCase): self.assertEqual(columns_to_join, []) return instances[:] - def fake_instance_get_by_uuid(context, instance_uuid): + def fake_instance_get_by_uuid(context, instance_uuid, columns_to_join): if instance_uuid not in instance_map: raise exception.InstanceNotFound(instance_id=instance_uuid) call_info['get_by_uuid'] += 1 @@ -4357,12 +4495,13 @@ class ComputeTestCase(BaseTestCase): # and is ignored. However, the below increment of # 'get_nw_info' won't happen, and you'll get an assert # failure checking it below. - self.assertEqual(call_info['expected_instance'], instance) + self.assertEqual(call_info['expected_instance']['uuid'], + instance['uuid']) call_info['get_nw_info'] += 1 - self.stubs.Set(self.compute.conductor_api, 'instance_get_all_by_host', + self.stubs.Set(db, 'instance_get_all_by_host', fake_instance_get_all_by_host) - self.stubs.Set(self.compute.conductor_api, 'instance_get_by_uuid', + self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) self.stubs.Set(self.compute, '_get_instance_nw_info', fake_get_instance_nw_info) @@ -4437,19 +4576,27 @@ class ComputeTestCase(BaseTestCase): self.assertTrue(instance) def test_poll_unconfirmed_resizes(self): - instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.RESIZED, - 'task_state': None}, - {'uuid': 'noexist'}, - {'uuid': 'fake_uuid2', 'vm_state': vm_states.ERROR, - 'task_state': None}, - {'uuid': 'fake_uuid3', 'vm_state': vm_states.ACTIVE, - 'task_state': task_states.REBOOTING}, - {'uuid': 'fake_uuid4', 'vm_state': vm_states.RESIZED, - 'task_state': None}, - {'uuid': 'fake_uuid5', 'vm_state': vm_states.ACTIVE, - 'task_state': None}, - {'uuid': 'fake_uuid6', 'vm_state': vm_states.RESIZED, - 'task_state': 'deleting'}] + instances = [ + fake_instance.fake_db_instance(uuid='fake_uuid1', + vm_state=vm_states.RESIZED, + task_state=None), + fake_instance.fake_db_instance(uuid='noexist'), + fake_instance.fake_db_instance(uuid='fake_uuid2', + vm_state=vm_states.ERROR, + task_state=None), + fake_instance.fake_db_instance(uuid='fake_uuid3', + vm_state=vm_states.ACTIVE, + task_state= + task_states.REBOOTING), + fake_instance.fake_db_instance(uuid='fake_uuid4', + vm_state=vm_states.RESIZED, + task_state=None), + fake_instance.fake_db_instance(uuid='fake_uuid5', + vm_state=vm_states.ACTIVE, + task_state=None), + fake_instance.fake_db_instance(uuid='fake_uuid6', + vm_state=vm_states.RESIZED, + task_state='deleting')] expected_migration_status = {'fake_uuid1': 'confirmed', 'noexist': 'error', 'fake_uuid2': 'error', @@ -4811,7 +4958,8 @@ class ComputeTestCase(BaseTestCase): inst = dict(fakes.stub_instance(1), deleted_at=None, created_at=None, updated_at=None, deleted=0, info_cache={'instance_uuid': 'fake-uuid', - 'network_info': None}) + 'network_info': None}, + security_groups=None) startup_instances = [inst, inst, inst] def _do_mock_calls(defer_iptables_apply): @@ -5269,6 +5417,38 @@ class ComputeTestCase(BaseTestCase): self._test_sync_to_stop(power_state.RUNNING, vs, ps, stop=False) + def _test_lifecycle_event(self, lifecycle_event, power_state): + instance = self._create_fake_instance() + uuid = instance['uuid'] + + self.mox.StubOutWithMock(self.compute, '_sync_instance_power_state') + if power_state != None: + self.compute._sync_instance_power_state( + mox.IgnoreArg(), + mox.ContainsKeyValue('uuid', uuid), + power_state) + self.mox.ReplayAll() + self.compute.handle_events(LifecycleEvent(uuid, lifecycle_event)) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_lifecycle_events(self): + self._test_lifecycle_event(EVENT_LIFECYCLE_STOPPED, + power_state.SHUTDOWN) + self._test_lifecycle_event(EVENT_LIFECYCLE_STARTED, + power_state.RUNNING) + self._test_lifecycle_event(EVENT_LIFECYCLE_PAUSED, + power_state.PAUSED) + self._test_lifecycle_event(EVENT_LIFECYCLE_RESUMED, + power_state.RUNNING) + self._test_lifecycle_event(-1, None) + + def test_lifecycle_event_non_existent_instance(self): + # No error raised for non-existent instance because of inherent race + # between database updates and hypervisor events. See bug #1180501. + event = LifecycleEvent('does-not-exist', EVENT_LIFECYCLE_STOPPED) + self.compute.handle_events(event) + class ComputeAPITestCase(BaseTestCase): @@ -5710,6 +5890,21 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, instance['uuid']) + def test_delete_if_not_launched(self): + instance, instance_uuid = self._run_instance(params={ + 'host': CONF.host}) + + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_states.ERROR, + "launched_at": None}) + + self.compute_api.delete(self.context, instance) + + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.assertEqual(instance['task_state'], task_states.DELETING) + + db.instance_destroy(self.context, instance['uuid']) + def test_delete_in_resizing(self): def fake_quotas_reserve(context, expire=None, project_id=None, **deltas): @@ -5984,7 +6179,7 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, instance['uuid']) - def test_rebuild(self): + def _test_rebuild(self, vm_state): instance = jsonutils.to_primitive(self._create_fake_instance()) instance_uuid = instance['uuid'] self.compute.run_instance(self.context, instance=instance) @@ -6015,6 +6210,10 @@ class ComputeAPITestCase(BaseTestCase): image_ref = instance["image_ref"] + '-new_image_ref' password = "new_password" + + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_state}) + self.compute_api.rebuild(self.context, instance, image_ref, password) self.assertEqual(info['image_ref'], image_ref) @@ -6029,6 +6228,34 @@ class ComputeAPITestCase(BaseTestCase): 'preserved': 'preserve this!'}) db.instance_destroy(self.context, instance['uuid']) + def test_rebuild(self): + # Test we can rebuild an instance in the Error State + self._test_rebuild(vm_state=vm_states.ACTIVE) + + def test_rebuild_in_error_state(self): + # Test we can rebuild an instance in the Error State + self._test_rebuild(vm_state=vm_states.ERROR) + + def test_rebuild_in_error_not_launched(self): + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': ''})) + instance_uuid = instance['uuid'] + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + self.compute.run_instance(self.context, instance=instance) + + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_states.ERROR, + "launched_at": None}) + + instance = db.instance_get_by_uuid(self.context, instance['uuid']) + + self.assertRaises(exception.InstanceInvalidState, + self.compute_api.rebuild, + self.context, + instance, + instance['image_ref'], + "new password") + def test_rebuild_no_image(self): instance = jsonutils.to_primitive( self._create_fake_instance(params={'image_ref': ''})) @@ -6175,7 +6402,7 @@ class ComputeAPITestCase(BaseTestCase): self.stubs.Set(nova.virt.fake.FakeDriver, 'legacy_nwinfo', lambda x: False) - def test_reboot_soft(self): + def _test_reboot_soft(self, vm_state): # Ensure instance can be soft rebooted. instance = jsonutils.to_primitive(self._create_fake_instance()) self.compute.run_instance(self.context, instance=instance) @@ -6192,6 +6419,9 @@ class ComputeAPITestCase(BaseTestCase): inst_ref = db.instance_get_by_uuid(self.context, instance['uuid']) self.assertEqual(inst_ref['task_state'], None) + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_state}) + reboot_type = "SOFT" self._stub_out_reboot(device_name) self.compute_api.reboot(self.context, inst_ref, reboot_type) @@ -6201,7 +6431,15 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, inst_ref['uuid']) - def test_reboot_hard(self): + def test_soft_reboot(self): + # Ensure instance can be rebooted while in error state. + self._test_reboot_soft(vm_state=vm_states.ACTIVE) + + def test_soft_reboot_of_instance_in_error(self): + # Ensure instance can be rebooted while in error state. + self._test_reboot_soft(vm_state=vm_states.ERROR) + + def test_reboot_hard(self, vm_state=vm_states.ACTIVE): # Ensure instance can be hard rebooted. instance = jsonutils.to_primitive(self._create_fake_instance()) self.compute.run_instance(self.context, instance=instance) @@ -6218,6 +6456,9 @@ class ComputeAPITestCase(BaseTestCase): inst_ref = db.instance_get_by_uuid(self.context, instance['uuid']) self.assertEqual(inst_ref['task_state'], None) + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_state}) + reboot_type = "HARD" self._stub_out_reboot(device_name) self.compute_api.reboot(self.context, inst_ref, reboot_type) @@ -6227,6 +6468,10 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, inst_ref['uuid']) + def test_hard_reboot_of_instance_in_error(self): + # Ensure instance can be rebooted while in error state. + self.test_reboot_hard(vm_state=vm_states.ERROR) + def test_hard_reboot_of_soft_rebooting_instance(self): # Ensure instance can be hard rebooted while soft rebooting. instance = jsonutils.to_primitive(self._create_fake_instance()) @@ -6263,7 +6508,7 @@ class ComputeAPITestCase(BaseTestCase): inst_ref, reboot_type) - def test_soft_reboot_of_rescued_instance(self): + def test_reboot_of_rescued_instance(self): # Ensure instance can't be rebooted while in rescued state. instance = jsonutils.to_primitive(self._create_fake_instance()) self.compute.run_instance(self.context, instance=instance) @@ -6287,6 +6532,33 @@ class ComputeAPITestCase(BaseTestCase): inst_ref, 'HARD') + def test_reboot_of_instance_in_error_not_launched(self): + # Ensure instance can be not rebooted while in error states + # if they have never been booted at least once. + instance = jsonutils.to_primitive(self._create_fake_instance()) + self.compute.run_instance(self.context, instance=instance) + + inst_ref = db.instance_get_by_uuid(self.context, instance['uuid']) + self.assertEqual(inst_ref['task_state'], None) + + db.instance_update(self.context, instance['uuid'], + {"vm_state": vm_states.ERROR, + "launched_at": None}) + + inst_ref = db.instance_get_by_uuid(self.context, inst_ref['uuid']) + + self.assertRaises(exception.InstanceInvalidState, + self.compute_api.reboot, + self.context, + inst_ref, + 'SOFT') + + self.assertRaises(exception.InstanceInvalidState, + self.compute_api.reboot, + self.context, + inst_ref, + 'HARD') + def test_hostname_create(self): # Ensure instance hostname is set during creation. inst_type = flavors.get_flavor_by_name('m1.tiny') @@ -7726,7 +7998,8 @@ class ComputeAPITestCase(BaseTestCase): self.assertRaises(exception.InvalidDevicePath, self.compute_api.attach_volume, self.context, - {'locked': False, 'vm_state': vm_states.ACTIVE}, + {'locked': False, 'vm_state': vm_states.ACTIVE, + 'launched_at': timeutils.utcnow()}, None, '/invalid') @@ -8033,6 +8306,7 @@ class ComputeAPITestCase(BaseTestCase): # Ensure exception is raised while detaching an un-attached volume instance = {'uuid': 'uuid1', 'locked': False, + 'launched_at': timeutils.utcnow(), 'vm_state': vm_states.ACTIVE} volume = {'id': 1, 'attach_status': 'detached'} @@ -8045,6 +8319,7 @@ class ComputeAPITestCase(BaseTestCase): # instance doesn't match. instance = {'uuid': 'uuid1', 'locked': False, + 'launched_at': timeutils.utcnow(), 'vm_state': vm_states.ACTIVE} volume = {'id': 1, 'attach_status': 'in-use', 'instance_uuid': 'uuid2'} @@ -8359,6 +8634,7 @@ class ComputeAPITestCase(BaseTestCase): def test_fail_evacuate_from_non_existing_host(self): inst = {} inst['vm_state'] = vm_states.ACTIVE + inst['launched_at'] = timeutils.utcnow() inst['image_ref'] = FAKE_IMAGE_REF inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user_id @@ -9450,42 +9726,31 @@ class CheckConfigDriveTestCase(test.TestCase): def setUp(self): super(CheckConfigDriveTestCase, self).setUp() self.compute_api = compute.API() - self.context = context.RequestContext( - 'fake_user_id', 'fake_project_id') - - self.called = called = {'show': False} - - def fake_get_remote_image_service(context, image_id): - class FakeGlance(object): - def show(self, context, image_id): - called['show'] = True - return FakeGlance(), image_id - - self.stubs.Set(glance, 'get_remote_image_service', - fake_get_remote_image_service) - - def tearDown(self): - self.stubs.UnsetAll() - super(CheckConfigDriveTestCase, self).tearDown() - - def assertCheck(self, expected, config_drive): + def _assertCheck(self, expected, config_drive): self.assertEqual(expected, - self.compute_api._check_config_drive( - self.context, config_drive)) - - def test_value_is_none(self): - self.assertFalse(self.called['show']) - self.assertCheck((None, None), None) - self.assertFalse(self.called['show']) - - def test_bool_string_or_id(self): - self.assertCheck((None, True), "true") - self.assertCheck((None, True), 1) - self.assertCheck((None, True), 't') - - def test_value_is_image_id(self): - self.assertCheck(("fake-uuid", None), "fake-uuid") + self.compute_api._check_config_drive(config_drive)) + + def _assertInvalid(self, config_drive): + self.assertRaises(exception.ConfigDriveInvalidValue, + self.compute_api._check_config_drive, + config_drive) + + def test_config_drive_false_values(self): + self._assertCheck('', None) + self._assertCheck('', '') + self._assertCheck('', 'False') + self._assertCheck('', 'f') + self._assertCheck('', '0') + + def test_config_drive_true_values(self): + self._assertCheck(True, 'True') + self._assertCheck(True, 't') + self._assertCheck(True, '1') + + def test_config_drive_bogus_values_raise(self): + self._assertInvalid('asd') + self._assertInvalid(uuidutils.generate_uuid()) class CheckRequestedImageTestCase(test.TestCase): diff --git a/nova/tests/conductor/tasks/__init__.py b/nova/tests/conductor/tasks/__init__.py new file mode 100644 index 000000000..94e731d20 --- /dev/null +++ b/nova/tests/conductor/tasks/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/nova/tests/conductor/tasks/test_live_migrate.py b/nova/tests/conductor/tasks/test_live_migrate.py new file mode 100644 index 000000000..c54e53b1a --- /dev/null +++ b/nova/tests/conductor/tasks/test_live_migrate.py @@ -0,0 +1,311 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +from nova.compute import flavors +from nova.compute import power_state +from nova.conductor.tasks import live_migrate +from nova import db +from nova import exception +from nova import test + + +class LiveMigrationTaskTestCase(test.TestCase): + def setUp(self): + super(LiveMigrationTaskTestCase, self).setUp() + self.context = "context" + self.instance_host = "host" + self.instance_uuid = "uuid" + self.instance_image = "image_ref" + self.instance = { + "host": self.instance_host, + "uuid": self.instance_uuid, + "power_state": power_state.RUNNING, + "memory_mb": 512, + "image_ref": self.instance_image} + self.destination = "destination" + self.block_migration = "bm" + self.disk_over_commit = "doc" + self.select_hosts_callback = self._select_hosts_callback + self._generate_task() + + def _generate_task(self): + self.task = live_migrate.LiveMigrationTask(self.context, + self.instance, self.destination, self.block_migration, + self.disk_over_commit, self.select_hosts_callback) + + def _select_hosts_callback(self, *args): + return ["host1"] + + def test_execute_with_destination(self): + self.mox.StubOutWithMock(self.task, '_check_host_is_up') + self.mox.StubOutWithMock(self.task, '_check_requested_destination') + self.mox.StubOutWithMock(self.task.compute_rpcapi, 'live_migration') + + self.task._check_host_is_up(self.instance_host) + self.task._check_requested_destination() + self.task.compute_rpcapi.live_migration(self.context, + host=self.instance_host, + instance=self.instance, + dest=self.destination, + block_migration=self.block_migration, + migrate_data=None).AndReturn("bob") + + self.mox.ReplayAll() + self.assertEqual("bob", self.task.execute()) + + def test_execute_without_destination(self): + self.destination = None + self._generate_task() + self.assertEqual(None, self.task.destination) + + self.mox.StubOutWithMock(self.task, '_check_host_is_up') + self.mox.StubOutWithMock(self.task, '_find_destination') + self.mox.StubOutWithMock(self.task.compute_rpcapi, 'live_migration') + + self.task._check_host_is_up(self.instance_host) + self.task._find_destination().AndReturn("found_host") + self.task.compute_rpcapi.live_migration(self.context, + host=self.instance_host, + instance=self.instance, + dest="found_host", + block_migration=self.block_migration, + migrate_data=None).AndReturn("bob") + + self.mox.ReplayAll() + self.assertEqual("bob", self.task.execute()) + + def test_check_instance_is_running_passes(self): + self.task._check_instance_is_running() + + def test_check_instance_is_running_fails_when_shutdown(self): + self.task.instance['power_state'] = power_state.SHUTDOWN + self.assertRaises(exception.InstanceNotRunning, + self.task._check_instance_is_running) + + def test_check_instance_host_is_up(self): + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + self.mox.StubOutWithMock(self.task.servicegroup_api, 'service_is_up') + + db.service_get_by_compute_host(self.context, + "host").AndReturn("service") + self.task.servicegroup_api.service_is_up("service").AndReturn(True) + + self.mox.ReplayAll() + self.task._check_host_is_up("host") + + def test_check_instance_host_is_up_fails_if_not_up(self): + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + self.mox.StubOutWithMock(self.task.servicegroup_api, 'service_is_up') + + db.service_get_by_compute_host(self.context, + "host").AndReturn("service") + self.task.servicegroup_api.service_is_up("service").AndReturn(False) + + self.mox.ReplayAll() + self.assertRaises(exception.ComputeServiceUnavailable, + self.task._check_host_is_up, "host") + + def test_check_instance_host_is_up_fails_if_not_found(self): + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + + db.service_get_by_compute_host(self.context, + "host").AndRaise(exception.NotFound) + + self.mox.ReplayAll() + self.assertRaises(exception.ComputeServiceUnavailable, + self.task._check_host_is_up, "host") + + def test_check_requested_destination(self): + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + self.mox.StubOutWithMock(self.task, '_get_compute_info') + self.mox.StubOutWithMock(self.task.servicegroup_api, 'service_is_up') + self.mox.StubOutWithMock(self.task.compute_rpcapi, + 'check_can_live_migrate_destination') + + db.service_get_by_compute_host(self.context, + self.destination).AndReturn("service") + self.task.servicegroup_api.service_is_up("service").AndReturn(True) + hypervisor_details = { + "hypervisor_type": "a", + "hypervisor_version": 6.1, + "free_ram_mb": 513 + } + self.task._get_compute_info(self.destination)\ + .AndReturn(hypervisor_details) + self.task._get_compute_info(self.instance_host)\ + .AndReturn(hypervisor_details) + self.task._get_compute_info(self.destination)\ + .AndReturn(hypervisor_details) + + self.task.compute_rpcapi.check_can_live_migrate_destination( + self.context, self.instance, self.destination, + self.block_migration, self.disk_over_commit).AndReturn( + "migrate_data") + + self.mox.ReplayAll() + self.task._check_requested_destination() + self.assertEqual("migrate_data", self.task.migrate_data) + + def test_check_requested_destination_fails_with_same_dest(self): + self.task.destination = "same" + self.task.source = "same" + self.assertRaises(exception.UnableToMigrateToSelf, + self.task._check_requested_destination) + + def test_check_requested_destination_fails_when_destination_is_up(self): + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + + db.service_get_by_compute_host(self.context, + self.destination).AndRaise(exception.NotFound) + + self.mox.ReplayAll() + self.assertRaises(exception.ComputeServiceUnavailable, + self.task._check_requested_destination) + + def test_check_requested_destination_fails_with_not_enough_memory(self): + self.mox.StubOutWithMock(self.task, '_check_host_is_up') + self.mox.StubOutWithMock(db, 'service_get_by_compute_host') + + self.task._check_host_is_up(self.destination) + db.service_get_by_compute_host(self.context, + self.destination).AndReturn({ + "compute_node": [{"free_ram_mb": 511}] + }) + + self.mox.ReplayAll() + self.assertRaises(exception.MigrationPreCheckError, + self.task._check_requested_destination) + + def test_check_requested_destination_fails_with_hypervisor_diff(self): + self.mox.StubOutWithMock(self.task, '_check_host_is_up') + self.mox.StubOutWithMock(self.task, + '_check_destination_has_enough_memory') + self.mox.StubOutWithMock(self.task, '_get_compute_info') + + self.task._check_host_is_up(self.destination) + self.task._check_destination_has_enough_memory() + self.task._get_compute_info(self.instance_host).AndReturn({ + "hypervisor_type": "b" + }) + self.task._get_compute_info(self.destination).AndReturn({ + "hypervisor_type": "a" + }) + + self.mox.ReplayAll() + self.assertRaises(exception.InvalidHypervisorType, + self.task._check_requested_destination) + + def test_check_requested_destination_fails_with_hypervisor_too_old(self): + self.mox.StubOutWithMock(self.task, '_check_host_is_up') + self.mox.StubOutWithMock(self.task, + '_check_destination_has_enough_memory') + self.mox.StubOutWithMock(self.task, '_get_compute_info') + + self.task._check_host_is_up(self.destination) + self.task._check_destination_has_enough_memory() + self.task._get_compute_info(self.instance_host).AndReturn({ + "hypervisor_type": "a", + "hypervisor_version": 7 + }) + self.task._get_compute_info(self.destination).AndReturn({ + "hypervisor_type": "a", + "hypervisor_version": 6 + }) + + self.mox.ReplayAll() + self.assertRaises(exception.DestinationHypervisorTooOld, + self.task._check_requested_destination) + + def test_find_destination_works(self): + self.mox.StubOutWithMock(self.task.image_service, 'show') + self.mox.StubOutWithMock(flavors, 'extract_flavor') + self.mox.StubOutWithMock(self.task, + '_check_compatible_with_source_hypervisor') + self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') + + self.task.image_service.show(self.context, + self.instance_image).AndReturn("image") + flavors.extract_flavor(self.instance).AndReturn("inst_type") + self.task._check_compatible_with_source_hypervisor("host1") + self.task._call_livem_checks_on_host("host1") + + self.mox.ReplayAll() + self.assertEqual("host1", self.task._find_destination()) + + def _test_find_destination_retry_hypervisor_raises(self, error): + self.mox.StubOutWithMock(self.task.image_service, 'show') + self.mox.StubOutWithMock(flavors, 'extract_flavor') + self.mox.StubOutWithMock(self.task, + '_check_compatible_with_source_hypervisor') + self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') + + self.task.image_service.show(self.context, + self.instance_image).AndReturn("image") + flavors.extract_flavor(self.instance).AndReturn("inst_type") + self.task._check_compatible_with_source_hypervisor("host1")\ + .AndRaise(error) + + self.task._check_compatible_with_source_hypervisor("host1") + self.task._call_livem_checks_on_host("host1") + + self.mox.ReplayAll() + self.assertEqual("host1", self.task._find_destination()) + + def test_find_destination_retry_with_old_hypervisor(self): + self._test_find_destination_retry_hypervisor_raises( + exception.DestinationHypervisorTooOld) + + def test_find_destination_retry_with_invalid_hypervisor_type(self): + self._test_find_destination_retry_hypervisor_raises( + exception.InvalidHypervisorType) + + def test_find_destination_retry_with_invalid_livem_checks(self): + self.mox.StubOutWithMock(self.task.image_service, 'show') + self.mox.StubOutWithMock(flavors, 'extract_flavor') + self.mox.StubOutWithMock(self.task, + '_check_compatible_with_source_hypervisor') + self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') + + self.task.image_service.show(self.context, + self.instance_image).AndReturn("image") + flavors.extract_flavor(self.instance).AndReturn("inst_type") + self.task._check_compatible_with_source_hypervisor("host1") + self.task._call_livem_checks_on_host("host1")\ + .AndRaise(exception.Invalid) + + self.task._check_compatible_with_source_hypervisor("host1") + self.task._call_livem_checks_on_host("host1") + + self.mox.ReplayAll() + self.assertEqual("host1", self.task._find_destination()) + + def test_find_destination_retry_exceeds_max(self): + self.flags(scheduler_max_attempts=1) + self.mox.StubOutWithMock(self.task.image_service, 'show') + self.mox.StubOutWithMock(flavors, 'extract_flavor') + self.mox.StubOutWithMock(self.task, + '_check_compatible_with_source_hypervisor') + self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') + + self.task.image_service.show(self.context, + self.instance_image).AndReturn("image") + flavors.extract_flavor(self.instance).AndReturn("inst_type") + self.task._check_compatible_with_source_hypervisor("host1")\ + .AndRaise(exception.DestinationHypervisorTooOld) + + self.mox.ReplayAll() + self.assertRaises(exception.NoValidHost, self.task._find_destination) + + def test_not_implemented_rollback(self): + self.assertRaises(NotImplementedError, self.task.rollback) diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index 7a33cfbb9..609c2164c 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -246,9 +246,8 @@ class _BaseTestCase(object): self.mox.StubOutWithMock(db, 'aggregate_metadata_delete') db.aggregate_metadata_delete(mox.IgnoreArg(), aggregate['id'], 'fake') self.mox.ReplayAll() - result = self.conductor.aggregate_metadata_delete(self.context, - aggregate, - 'fake') + self.conductor.aggregate_metadata_delete(self.context, aggregate, + 'fake') def test_aggregate_metadata_get_by_host(self): self.mox.StubOutWithMock(db, 'aggregate_metadata_get_by_host') @@ -389,7 +388,7 @@ class _BaseTestCase(object): fake_inst['project_id'], fake_inst['user_id'], fake_inst['availability_zone'], - False, mox.IgnoreArg()).AndReturn('fake-usage') + False).AndReturn('fake-usage') compute_utils.usage_volume_info('fake-usage').AndReturn('fake-info') notifier_api.notify(self.context, 'conductor.%s' % self.conductor_manager.host, @@ -616,13 +615,28 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): self.conductor_manager = self.conductor def test_block_device_mapping_update_or_create(self): - fake_bdm = {'id': 'fake-id'} + fake_bdm = {'id': 'fake-id', 'device_name': 'foo'} + fake_bdm2 = {'id': 'fake-id', 'device_name': 'foo2'} + cells_rpcapi = self.conductor.cells_rpcapi self.mox.StubOutWithMock(db, 'block_device_mapping_create') self.mox.StubOutWithMock(db, 'block_device_mapping_update') self.mox.StubOutWithMock(db, 'block_device_mapping_update_or_create') - db.block_device_mapping_create(self.context, fake_bdm) - db.block_device_mapping_update(self.context, fake_bdm['id'], fake_bdm) - db.block_device_mapping_update_or_create(self.context, fake_bdm) + self.mox.StubOutWithMock(cells_rpcapi, + 'bdm_update_or_create_at_top') + db.block_device_mapping_create(self.context, + fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, fake_bdm2, + create=True) + db.block_device_mapping_update(self.context, fake_bdm['id'], + fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, + fake_bdm2, + create=False) + db.block_device_mapping_update_or_create( + self.context, fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, + fake_bdm2, + create=None) self.mox.ReplayAll() self.conductor.block_device_mapping_update_or_create(self.context, fake_bdm, @@ -634,22 +648,44 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): fake_bdm) def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} - fake_bdm2 = {'id': 'fake-bdm-2'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} + fake_bdm2 = {'id': 'fake-bdm-2', + 'instance_uuid': 'fake-uuid2', + 'device_name': '', + 'volume_id': 'fake-vol-id2'} fake_inst = {'uuid': 'fake-uuid'} + + cells_rpcapi = self.conductor.cells_rpcapi + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_volume') + self.mox.StubOutWithMock(cells_rpcapi, 'bdm_destroy_at_top') + db.block_device_mapping_destroy(self.context, 'fake-bdm') + cells_rpcapi.bdm_destroy_at_top(self.context, + fake_bdm['instance_uuid'], + device_name=fake_bdm['device_name']) db.block_device_mapping_destroy(self.context, 'fake-bdm-2') + cells_rpcapi.bdm_destroy_at_top(self.context, + fake_bdm2['instance_uuid'], + volume_id=fake_bdm2['volume_id']) db.block_device_mapping_destroy_by_instance_and_device(self.context, 'fake-uuid', 'fake-device') + cells_rpcapi.bdm_destroy_at_top(self.context, fake_inst['uuid'], + device_name='fake-device') db.block_device_mapping_destroy_by_instance_and_volume(self.context, 'fake-uuid', 'fake-volume') + cells_rpcapi.bdm_destroy_at_top(self.context, fake_inst['uuid'], + volume_id='fake-volume') + self.mox.ReplayAll() self.conductor.block_device_mapping_destroy(self.context, [fake_bdm, @@ -794,8 +830,12 @@ class ConductorRPCAPITestCase(_BaseTestCase, test.TestCase): fake_bdm) def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} fake_inst = {'uuid': 'fake-uuid'} + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') @@ -945,8 +985,12 @@ class ConductorAPITestCase(_BaseTestCase, test.TestCase): 'fake-bdm') def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} fake_inst = {'uuid': 'fake-uuid'} + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') @@ -1183,18 +1227,28 @@ class _BaseTaskTestCase(object): def test_build_instances(self): instance_type = flavors.get_default_flavor() system_metadata = flavors.save_flavor_info({}, instance_type) - # NOTE(alaski): instance_type -> system_metadata -> instance_type loses - # some data (extra_specs) so we need both for testing. - instance_type_extract = flavors.extract_flavor( + # NOTE(alaski): instance_type -> system_metadata -> instance_type + # loses some data (extra_specs). This build process is using + # scheduler/utils:build_request_spec() which extracts flavor from + # system_metadata and will re-query the DB for extra_specs.. so + # we need to test this properly + expected_instance_type = flavors.extract_flavor( {'system_metadata': system_metadata}) + expected_instance_type['extra_specs'] = 'fake-specs' + + self.mox.StubOutWithMock(db, 'instance_type_extra_specs_get') self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi, 'run_instance') + + db.instance_type_extra_specs_get( + self.context, + instance_type['flavorid']).AndReturn('fake-specs') self.conductor_manager.scheduler_rpcapi.run_instance(self.context, request_spec={ 'image': {'fake_data': 'should_pass_silently'}, 'instance_properties': {'system_metadata': system_metadata, 'uuid': 'fakeuuid'}, - 'instance_type': instance_type_extract, + 'instance_type': expected_instance_type, 'instance_uuids': ['fakeuuid', 'fakeuuid2'], 'block_device_mapping': 'block_device_mapping', 'security_group': 'security_groups'}, diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 0a5b75bf4..e7ae1b76d 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -30,6 +30,7 @@ from sqlalchemy.dialects import sqlite from sqlalchemy import exc from sqlalchemy.exc import IntegrityError from sqlalchemy import MetaData +from sqlalchemy.orm import exc as sqlalchemy_orm_exc from sqlalchemy.orm import query from sqlalchemy.sql.expression import select @@ -152,18 +153,18 @@ class DbApiTestCase(DbTestCase): self.flags(osapi_compute_unique_server_name_scope=None) def test_ec2_ids_not_found_are_printable(self): - def check_exc_format(method): + def check_exc_format(method, value): try: - method(self.context, 'fake') + method(self.context, value) except exception.NotFound as exc: - self.assertTrue('fake' in unicode(exc)) + self.assertTrue(unicode(value) in unicode(exc)) - check_exc_format(db.get_ec2_volume_id_by_uuid) - check_exc_format(db.get_volume_uuid_by_ec2_id) - check_exc_format(db.get_ec2_snapshot_id_by_uuid) - check_exc_format(db.get_snapshot_uuid_by_ec2_id) - check_exc_format(db.get_ec2_instance_id_by_uuid) - check_exc_format(db.get_instance_uuid_by_ec2_id) + check_exc_format(db.get_ec2_volume_id_by_uuid, 'fake') + check_exc_format(db.get_volume_uuid_by_ec2_id, 123456) + check_exc_format(db.get_ec2_snapshot_id_by_uuid, 'fake') + check_exc_format(db.get_snapshot_uuid_by_ec2_id, 123456) + check_exc_format(db.get_ec2_instance_id_by_uuid, 'fake') + check_exc_format(db.get_instance_uuid_by_ec2_id, 123456) def test_instance_get_all_with_meta(self): inst = self.create_instance_with_args() @@ -285,7 +286,7 @@ class DbApiTestCase(DbTestCase): def test_instance_get_all_by_filters_deleted_and_soft_deleted(self): inst1 = self.create_instance_with_args() inst2 = self.create_instance_with_args(vm_state=vm_states.SOFT_DELETED) - inst3 = self.create_instance_with_args() + self.create_instance_with_args() db.instance_destroy(self.context, inst1['uuid']) result = db.instance_get_all_by_filters(self.context, {'deleted': True}) @@ -295,8 +296,8 @@ class DbApiTestCase(DbTestCase): def test_instance_get_all_by_filters_deleted_no_soft_deleted(self): inst1 = self.create_instance_with_args() - inst2 = self.create_instance_with_args(vm_state=vm_states.SOFT_DELETED) - inst3 = self.create_instance_with_args() + self.create_instance_with_args(vm_state=vm_states.SOFT_DELETED) + self.create_instance_with_args() db.instance_destroy(self.context, inst1['uuid']) result = db.instance_get_all_by_filters(self.context, {'deleted': True, @@ -947,8 +948,8 @@ class AggregateDBApiTestCase(test.TestCase): values2 = {'name': 'fake_aggregate4'} a1 = _create_aggregate_with_hosts(context=ctxt, metadata={'goodkey': 'good'}) - a2 = _create_aggregate_with_hosts(context=ctxt, values=values) - a3 = _create_aggregate(context=ctxt, values=values2) + _create_aggregate_with_hosts(context=ctxt, values=values) + _create_aggregate(context=ctxt, values=values2) # filter result by key r1 = db.aggregate_get_by_host(ctxt, 'foo.openstack.org', key='goodkey') self.assertEqual([a1['id']], [x['id'] for x in r1]) @@ -957,9 +958,9 @@ class AggregateDBApiTestCase(test.TestCase): ctxt = context.get_admin_context() values = {'name': 'fake_aggregate2'} values2 = {'name': 'fake_aggregate3'} - a1 = _create_aggregate_with_hosts(context=ctxt) - a2 = _create_aggregate_with_hosts(context=ctxt, values=values) - a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, + _create_aggregate_with_hosts(context=ctxt) + _create_aggregate_with_hosts(context=ctxt, values=values) + _create_aggregate_with_hosts(context=ctxt, values=values2, hosts=['bar.openstack.org'], metadata={'badkey': 'bad'}) r1 = db.aggregate_metadata_get_by_host(ctxt, 'foo.openstack.org') self.assertEqual(r1['fake_key1'], set(['fake_value1'])) @@ -969,8 +970,8 @@ class AggregateDBApiTestCase(test.TestCase): ctxt = context.get_admin_context() values = {'name': 'fake_aggregate2'} values2 = {'name': 'fake_aggregate3'} - a1 = _create_aggregate_with_hosts(context=ctxt) - a2 = _create_aggregate_with_hosts(context=ctxt, values=values) + _create_aggregate_with_hosts(context=ctxt) + _create_aggregate_with_hosts(context=ctxt, values=values) a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, hosts=['foo.openstack.org'], metadata={'good': 'value'}) r1 = db.aggregate_metadata_get_by_host(ctxt, 'foo.openstack.org', @@ -987,9 +988,9 @@ class AggregateDBApiTestCase(test.TestCase): ctxt = context.get_admin_context() values = {'name': 'fake_aggregate2'} values2 = {'name': 'fake_aggregate3'} - a1 = _create_aggregate_with_hosts(context=ctxt) - a2 = _create_aggregate_with_hosts(context=ctxt, values=values) - a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, + _create_aggregate_with_hosts(context=ctxt) + _create_aggregate_with_hosts(context=ctxt, values=values) + _create_aggregate_with_hosts(context=ctxt, values=values2, hosts=['foo.openstack.org'], metadata={'good': 'value'}) r1 = db.aggregate_host_get_by_metadata_key(ctxt, key='good') self.assertEqual(r1, {'foo.openstack.org': set(['value'])}) @@ -1299,10 +1300,11 @@ class ModelsObjectComparatorMixin(object): self.assertEqual(value, obj2[key]) def _assertEqualListsOfObjects(self, objs1, objs2, ignored_keys=None): - self.assertEqual(len(objs1), len(objs2)) - objs2 = dict([(o['id'], o) for o in objs2]) - for o1 in objs1: - self._assertEqualObjects(o1, objs2[o1['id']], ignored_keys) + obj_to_dict = lambda o: self._dict_from_object(o, ignored_keys) + sort_key = lambda d: [d[k] for k in sorted(d)] + conv_and_sort = lambda obj: sorted(map(obj_to_dict, obj), key=sort_key) + + self.assertEqual(conv_and_sort(objs1), conv_and_sort(objs2)) def _assertEqualListsOfPrimitivesAsSets(self, primitives1, primitives2): self.assertEqual(len(primitives1), len(primitives2)) @@ -1313,6 +1315,54 @@ class ModelsObjectComparatorMixin(object): self.assertIn(primitive, primitives1) +class InstanceSystemMetadataTestCase(test.TestCase): + + """Tests for db.api.instance_system_metadata_* methods.""" + + def setUp(self): + super(InstanceSystemMetadataTestCase, self).setUp() + values = {'host': 'h1', 'project_id': 'p1', + 'system_metadata': {'key': 'value'}} + self.ctxt = context.get_admin_context() + self.instance = db.instance_create(self.ctxt, values) + + def test_instance_system_metadata_get(self): + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'value'}) + + def test_instance_system_metadata_update_new_pair(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'new_key': 'new_value'}, False) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'value', 'new_key': 'new_value'}) + + def test_instance_system_metadata_update_existent_pair(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'key': 'new_value'}, True) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'new_value'}) + + def test_instance_system_metadata_update_delete_true(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'new_key': 'new_value'}, True) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'new_key': 'new_value'}) + + @test.testtools.skip("bug 1189462") + def test_instance_system_metadata_update_nonexistent(self): + self.assertRaises(exception.InstanceNotFound, + db.instance_system_metadata_update, + self.ctxt, 'nonexistent-uuid', + {'key': 'value'}, True) + + class ReservationTestCase(test.TestCase, ModelsObjectComparatorMixin): """Tests for db.api.reservation_* methods.""" @@ -1414,7 +1464,7 @@ class ReservationTestCase(test.TestCase, ModelsObjectComparatorMixin): def test_reservation_expire(self): self.values['expire'] = datetime.datetime.utcnow() + datetime.\ timedelta(days=1) - reservations = self._quota_reserve() + self._quota_reserve() db.reservation_expire(self.ctxt) expected = {'project_id': 'project1', @@ -1425,6 +1475,119 @@ class ReservationTestCase(test.TestCase, ModelsObjectComparatorMixin): self.ctxt, 'project1')) +class SecurityGroupRuleTestCase(test.TestCase, ModelsObjectComparatorMixin): + def setUp(self): + super(SecurityGroupRuleTestCase, self).setUp() + self.ctxt = context.get_admin_context() + + def _get_base_values(self): + return { + 'name': 'fake_sec_group', + 'description': 'fake_sec_group_descr', + 'user_id': 'fake', + 'project_id': 'fake', + 'instances': [] + } + + def _get_base_rule_values(self): + return { + 'protocol': "tcp", + 'from_port': 80, + 'to_port': 8080, + 'cidr': None, + 'deleted': 0, + 'deleted_at': None, + 'grantee_group': None, + 'updated_at': None + } + + def _create_security_group(self, values): + v = self._get_base_values() + v.update(values) + return db.security_group_create(self.ctxt, v) + + def _create_security_group_rule(self, values): + v = self._get_base_rule_values() + v.update(values) + return db.security_group_rule_create(self.ctxt, v) + + def test_security_group_rule_create(self): + security_group_rule = self._create_security_group_rule({}) + self.assertIsNotNone(security_group_rule['id']) + for key, value in self._get_base_rule_values().items(): + self.assertEqual(value, security_group_rule[key]) + + def test_security_group_rule_get_by_security_group(self): + security_group = self._create_security_group({}) + security_group_rule = self._create_security_group_rule( + {'parent_group': security_group}) + security_group_rule1 = self._create_security_group_rule( + {'parent_group': security_group}) + found_rules = db.security_group_rule_get_by_security_group(self.ctxt, + security_group['id']) + self.assertEqual(len(found_rules), 2) + rules_ids = [security_group_rule['id'], security_group_rule1['id']] + for rule in found_rules: + self.assertIn(rule['id'], rules_ids) + + def test_security_group_rule_get_by_security_group_grantee(self): + security_group = self._create_security_group({}) + security_group_rule = self._create_security_group_rule( + {'grantee_group': security_group}) + rules = db.security_group_rule_get_by_security_group_grantee(self.ctxt, + security_group['id']) + self.assertEqual(len(rules), 1) + self.assertEqual(rules[0]['id'], security_group_rule['id']) + + def test_security_group_rule_destroy(self): + security_group1 = self._create_security_group({}) + security_group2 = self._create_security_group({}) + security_group_rule1 = self._create_security_group_rule({}) + security_group_rule2 = self._create_security_group_rule({}) + db.security_group_rule_destroy(self.ctxt, security_group_rule1['id']) + self.assertRaises(exception.SecurityGroupNotFound, + db.security_group_rule_get, + self.ctxt, security_group_rule1['id']) + self._assertEqualObjects(db.security_group_rule_get(self.ctxt, + security_group_rule2['id']), + security_group_rule2, ['grantee_group']) + + def test_security_group_rule_destroy_not_found_exception(self): + self.assertRaises(exception.SecurityGroupNotFound, + db.security_group_rule_destroy, self.ctxt, 100500) + + def test_security_group_rule_get(self): + security_group_rule1 = ( + self._create_security_group_rule({})) + security_group_rule2 = self._create_security_group_rule({}) + real_security_group_rule = db.security_group_rule_get(self.ctxt, + security_group_rule1['id']) + self._assertEqualObjects(security_group_rule1, + real_security_group_rule, ['grantee_group']) + + def test_security_group_rule_get_not_found_exception(self): + self.assertRaises(exception.SecurityGroupNotFound, + db.security_group_rule_get, self.ctxt, 100500) + + def test_security_group_rule_count_by_group(self): + sg1 = self._create_security_group({}) + sg2 = self._create_security_group({}) + rules_by_group = {sg1: [], sg2: []} + for group in rules_by_group: + rules = rules_by_group[group] + for i in range(0, 10): + rules.append( + self._create_security_group_rule({'parent_group_id': + group['id']})) + db.security_group_rule_destroy(self.ctxt, + rules_by_group[sg1][0]['id']) + counted_groups = [db.security_group_rule_count_by_group(self.ctxt, + group['id']) + for group in [sg1, sg2]] + expected = [9, 10] + self.assertEqual(counted_groups, expected) + + class SecurityGroupTestCase(test.TestCase, ModelsObjectComparatorMixin): def setUp(self): super(SecurityGroupTestCase, self).setUp() @@ -1459,19 +1622,39 @@ class SecurityGroupTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertRaises(exception.SecurityGroupNotFound, db.security_group_get, self.ctxt, security_group1['id']) - self._assertEqualObjects(db.security_group_get(self.ctxt, - security_group2['id']), - security_group2) + self._assertEqualObjects(db.security_group_get( + self.ctxt, security_group2['id'], + columns_to_join=['instances']), security_group2) def test_security_group_get(self): security_group1 = self._create_security_group({}) - security_group2 = self._create_security_group( - {'name': 'fake_sec_group2'}) + self._create_security_group({'name': 'fake_sec_group2'}) real_security_group = db.security_group_get(self.ctxt, - security_group1['id']) + security_group1['id'], + columns_to_join=['instances']) self._assertEqualObjects(security_group1, real_security_group) + def test_security_group_get_no_instances(self): + instance = db.instance_create(self.ctxt, {}) + sid = self._create_security_group({'instances': [instance]})['id'] + + session = get_session() + self.mox.StubOutWithMock(sqlalchemy_api, 'get_session') + sqlalchemy_api.get_session().AndReturn(session) + sqlalchemy_api.get_session().AndReturn(session) + self.mox.ReplayAll() + + security_group = db.security_group_get(self.ctxt, sid, + columns_to_join=['instances']) + session.expunge(security_group) + self.assertEqual(1, len(security_group['instances'])) + + security_group = db.security_group_get(self.ctxt, sid) + session.expunge(security_group) + self.assertRaises(sqlalchemy_orm_exc.DetachedInstanceError, + getattr, security_group, 'instances') + def test_security_group_get_not_found_exception(self): self.assertRaises(exception.SecurityGroupNotFound, db.security_group_get, self.ctxt, 100500) @@ -1558,8 +1741,8 @@ class SecurityGroupTestCase(test.TestCase, ModelsObjectComparatorMixin): {'name': 'fake2', 'project_id': 'fake_proj1'}, {'name': 'fake3', 'project_id': 'fake_proj2'}, ] - security_groups = [self._create_security_group(vals) - for vals in values] + for vals in values: + self._create_security_group(vals) real = [] for project in ('fake_proj1', 'fake_proj2'): @@ -1592,7 +1775,7 @@ class SecurityGroupTestCase(test.TestCase, ModelsObjectComparatorMixin): self.ctxt.project_id, 'default')) - default_group = db.security_group_ensure_default(self.ctxt) + db.security_group_ensure_default(self.ctxt) self.assertTrue(db.security_group_exists(self.ctxt, self.ctxt.project_id, @@ -1712,7 +1895,7 @@ class ServiceTestCase(test.TestCase, ModelsObjectComparatorMixin): def test_service_get(self): service1 = self._create_service({}) - service2 = self._create_service({'host': 'some_other_fake_host'}) + self._create_service({'host': 'some_other_fake_host'}) real_service1 = db.service_get(self.ctxt, service1['id']) self._assertEqualObjects(service1, real_service1, ignored_keys=['compute_node']) @@ -1737,7 +1920,7 @@ class ServiceTestCase(test.TestCase, ModelsObjectComparatorMixin): def test_service_get_by_host_and_topic(self): service1 = self._create_service({'host': 'host1', 'topic': 'topic1'}) - service2 = self._create_service({'host': 'host2', 'topic': 'topic2'}) + self._create_service({'host': 'host2', 'topic': 'topic2'}) real_service1 = db.service_get_by_host_and_topic(self.ctxt, host='host1', @@ -1854,292 +2037,264 @@ class BaseInstanceTypeTestCase(test.TestCase, ModelsObjectComparatorMixin): class InstanceActionTestCase(test.TestCase, ModelsObjectComparatorMixin): + IGNORED_FIELDS = [ + 'id', + 'created_at', + 'updated_at', + 'deleted_at', + 'deleted' + ] + + def setUp(self): + super(InstanceActionTestCase, self).setUp() + self.ctxt = context.get_admin_context() + + def _create_action_values(self, uuid, action='run_instance', ctxt=None): + if ctxt is None: + ctxt = self.ctxt + return { + 'action': action, + 'instance_uuid': uuid, + 'request_id': ctxt.request_id, + 'user_id': ctxt.user_id, + 'project_id': ctxt.project_id, + 'start_time': timeutils.utcnow(), + 'message': 'action-message' + } + + def _create_event_values(self, uuid, event='schedule', + ctxt=None, extra=None): + if ctxt is None: + ctxt = self.ctxt + values = { + 'event': event, + 'instance_uuid': uuid, + 'request_id': ctxt.request_id, + 'start_time': timeutils.utcnow() + } + if extra is not None: + values.update(extra) + return values + + def _assertActionSaved(self, action, uuid): + """Retrieve the action to ensure it was successfully added.""" + actions = db.actions_get(self.ctxt, uuid) + self.assertEqual(1, len(actions)) + self._assertEqualObjects(action, actions[0]) + + def _assertActionEventSaved(self, event, action_id): + # Retrieve the event to ensure it was successfully added + events = db.action_events_get(self.ctxt, action_id) + self.assertEqual(1, len(events)) + self._assertEqualObjects(event, events[0], + ['instance_uuid', 'request_id']) + def test_instance_action_start(self): """Create an instance action.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - db.action_start(ctxt, action_values) + action_values = self._create_action_values(uuid) + action = db.action_start(self.ctxt, action_values) - # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt, uuid) - self.assertEqual(1, len(actions)) - self.assertEqual('run_instance', actions[0]['action']) - self.assertEqual(start_time, actions[0]['start_time']) - self.assertEqual(ctxt.request_id, actions[0]['request_id']) - self.assertEqual(ctxt.user_id, actions[0]['user_id']) - self.assertEqual(ctxt.project_id, actions[0]['project_id']) + ignored_keys = self.IGNORED_FIELDS + ['finish_time'] + self._assertEqualObjects(action_values, action, ignored_keys) + + self._assertActionSaved(action, uuid) def test_instance_action_finish(self): """Create an instance action.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_start_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - db.action_start(ctxt, action_start_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - action_finish_values = {'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'finish_time': finish_time} - db.action_finish(ctxt, action_finish_values) + action_values = self._create_action_values(uuid) + db.action_start(self.ctxt, action_values) - # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt, uuid) - self.assertEqual(1, len(actions)) - self.assertEqual('run_instance', actions[0]['action']) - self.assertEqual(start_time, actions[0]['start_time']) - self.assertEqual(finish_time, actions[0]['finish_time']) - self.assertEqual(ctxt.request_id, actions[0]['request_id']) - self.assertEqual(ctxt.user_id, actions[0]['user_id']) - self.assertEqual(ctxt.project_id, actions[0]['project_id']) + action_values['finish_time'] = timeutils.utcnow() + action = db.action_finish(self.ctxt, action_values) + self._assertEqualObjects(action_values, action, self.IGNORED_FIELDS) + + self._assertActionSaved(action, uuid) + + def test_instance_action_finish_without_started_event(self): + """Create an instance finish action.""" + uuid = str(stdlib_uuid.uuid4()) + + action_values = self._create_action_values(uuid) + action_values['finish_time'] = timeutils.utcnow() + self.assertRaises(exception.InstanceActionNotFound, db.action_finish, + self.ctxt, action_values) def test_instance_actions_get_by_instance(self): """Ensure we can get actions by UUID.""" - ctxt1 = context.get_admin_context() - ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) - uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt1, action_values) + expected = [] + + action_values = self._create_action_values(uuid1) + action = db.action_start(self.ctxt, action_values) + expected.append(action) + action_values['action'] = 'resize' - db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} + action = db.action_start(self.ctxt, action_values) + expected.append(action) + + # Create some extra actions + uuid2 = str(stdlib_uuid.uuid4()) + ctxt2 = context.get_admin_context() + action_values = self._create_action_values(uuid2, 'reboot', ctxt2) db.action_start(ctxt2, action_values) db.action_start(ctxt2, action_values) # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt1, uuid1) - self.assertEqual(2, len(actions)) - self.assertEqual('resize', actions[0]['action']) - self.assertEqual('run_instance', actions[1]['action']) + actions = db.actions_get(self.ctxt, uuid1) + self._assertEqualListsOfObjects(expected, actions) def test_instance_action_get_by_instance_and_action(self): """Ensure we can get an action by instance UUID and action id.""" - ctxt1 = context.get_admin_context() ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt1, action_values) + action_values = self._create_action_values(uuid1) + db.action_start(self.ctxt, action_values) action_values['action'] = 'resize' - db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} + db.action_start(self.ctxt, action_values) + + action_values = self._create_action_values(uuid2, 'reboot', ctxt2) db.action_start(ctxt2, action_values) db.action_start(ctxt2, action_values) - actions = db.actions_get(ctxt1, uuid1) + actions = db.actions_get(self.ctxt, uuid1) request_id = actions[0]['request_id'] - action = db.action_get_by_request_id(ctxt1, uuid1, request_id) + action = db.action_get_by_request_id(self.ctxt, uuid1, request_id) self.assertEqual('run_instance', action['action']) - self.assertEqual(ctxt1.request_id, action['request_id']) + self.assertEqual(self.ctxt.request_id, action['request_id']) def test_instance_action_event_start(self): """Create an instance action event.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) + action_values = self._create_action_values(uuid) + action = db.action_start(self.ctxt, action_values) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) + event_values = self._create_event_values(uuid) + event = db.action_event_start(self.ctxt, event_values) + # self.fail(self._dict_from_object(event, None)) + event_values['action_id'] = action['id'] + ignored = self.IGNORED_FIELDS + ['finish_time', 'traceback', 'result'] + self._assertEqualObjects(event_values, event, ignored) + + self._assertActionEventSaved(event, action['id']) + + def test_instance_action_event_start_without_action(self): + """Create an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + event_values = self._create_event_values(uuid) + self.assertRaises(exception.InstanceActionNotFound, + db.action_event_start, self.ctxt, event_values) + + def test_instance_action_event_finish_without_started_event(self): + """Finish an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + db.action_start(self.ctxt, self._create_action_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + self.assertRaises(exception.InstanceActionEventNotFound, + db.action_event_finish, self.ctxt, event_values) + + def test_instance_action_event_finish_without_action(self): + """Finish an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + self.assertRaises(exception.InstanceActionNotFound, + db.action_event_finish, self.ctxt, event_values) def test_instance_action_event_finish_success(self): """Finish an instance action event.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - event_finish_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'finish_time': finish_time, - 'result': 'Success'} - db.action_event_finish(ctxt, event_finish_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - action = db.action_get_by_request_id(ctxt, uuid, ctxt.request_id) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) - self.assertEqual(finish_time, events[0]['finish_time']) - self.assertNotEqual(action['message'], 'Error') + db.action_event_start(self.ctxt, self._create_event_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_finish(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) + action = db.action_get_by_request_id(self.ctxt, uuid, + self.ctxt.request_id) + self.assertNotEqual('Error', action['message']) def test_instance_action_event_finish_error(self): """Finish an instance action event with an error.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - event_finish_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'finish_time': finish_time, - 'result': 'Error'} - db.action_event_finish(ctxt, event_finish_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - action = db.action_get_by_request_id(ctxt, uuid, ctxt.request_id) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) - self.assertEqual(finish_time, events[0]['finish_time']) - self.assertEqual(action['message'], 'Error') + db.action_event_start(self.ctxt, self._create_event_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Error' + } + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_finish(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) + action = db.action_get_by_request_id(self.ctxt, uuid, + self.ctxt.request_id) + self.assertEqual('Error', action['message']) def test_instance_action_and_event_start_string_time(self): """Create an instance action and event with a string start_time.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - start_time_str = timeutils.strtime(start_time) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time_str} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'start_time': start_time_str} - db.action_event_start(ctxt, event_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - # db api still returns models with datetime, not str, values - self.assertEqual(start_time, events[0]['start_time']) + event_values = {'start_time': timeutils.strtime(timeutils.utcnow())} + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_start(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) def test_instance_action_event_get_by_id(self): """Get a specific instance action event.""" - ctxt1 = context.get_admin_context() ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - added_action = db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt2, action_values) + action = db.action_start(self.ctxt, + self._create_action_values(uuid1)) - start_time = timeutils.utcnow() - event_values = {'event': 'schedule', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'start_time': start_time} - added_event = db.action_event_start(ctxt1, event_values) - - event_values = {'event': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'start_time': timeutils.utcnow()} + db.action_start(ctxt2, + self._create_action_values(uuid2, 'reboot', ctxt2)) + + event = db.action_event_start(self.ctxt, + self._create_event_values(uuid1)) + + event_values = self._create_event_values(uuid2, 'reboot', ctxt2) db.action_event_start(ctxt2, event_values) # Retrieve the event to ensure it was successfully added - event = db.action_event_get_by_id(ctxt1, added_action['id'], - added_event['id']) - self.assertEqual('schedule', event['event']) - self.assertEqual(start_time, event['start_time']) + saved_event = db.action_event_get_by_id(self.ctxt, + action['id'], + event['id']) + self._assertEqualObjects(event, saved_event, + ['instance_uuid', 'request_id']) class InstanceFaultTestCase(test.TestCase, ModelsObjectComparatorMixin): @@ -3537,24 +3692,24 @@ class VolumeUsageDBApiTestCase(test.TestCase): vol_usages = db.vol_get_usage_by_time(ctxt, start_time) self.assertEqual(len(vol_usages), 0) - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=10, rd_bytes=20, - wr_req=30, wr_bytes=40, - instance_id='fake-instance-uuid1', - project_id='fake-project-uuid1', - user_id='fake-user-uuid1', - availability_zone='fake-az') - vol_usage = db.vol_usage_update(ctxt, 2, rd_req=100, rd_bytes=200, - wr_req=300, wr_bytes=400, - instance_id='fake-instance-uuid2', - project_id='fake-project-uuid2', - user_id='fake-user-uuid2', - availability_zone='fake-az') - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=1000, rd_bytes=2000, - wr_req=3000, wr_bytes=4000, - instance_id='fake-instance-uuid1', - project_id='fake-project-uuid1', - user_id='fake-user-uuid1', - availability_zone='fake-az') + db.vol_usage_update(ctxt, 1, rd_req=10, rd_bytes=20, + wr_req=30, wr_bytes=40, + instance_id='fake-instance-uuid1', + project_id='fake-project-uuid1', + user_id='fake-user-uuid1', + availability_zone='fake-az') + db.vol_usage_update(ctxt, 2, rd_req=100, rd_bytes=200, + wr_req=300, wr_bytes=400, + instance_id='fake-instance-uuid2', + project_id='fake-project-uuid2', + user_id='fake-user-uuid2', + availability_zone='fake-az') + db.vol_usage_update(ctxt, 1, rd_req=1000, rd_bytes=2000, + wr_req=3000, wr_bytes=4000, + instance_id='fake-instance-uuid1', + project_id='fake-project-uuid1', + user_id='fake-user-uuid1', + availability_zone='fake-az') vol_usages = db.vol_get_usage_by_time(ctxt, start_time) self.assertEqual(len(vol_usages), 2) @@ -3576,44 +3731,44 @@ class VolumeUsageDBApiTestCase(test.TestCase): timeutils.utcnow().AndReturn(now3) self.mox.ReplayAll() - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=100, rd_bytes=200, - wr_req=300, wr_bytes=400, - instance_id='fake-instance-uuid', - project_id='fake-project-uuid', - user_id='fake-user-uuid', - availability_zone='fake-az') + db.vol_usage_update(ctxt, 1, rd_req=100, rd_bytes=200, + wr_req=300, wr_bytes=400, + instance_id='fake-instance-uuid', + project_id='fake-project-uuid', + user_id='fake-user-uuid', + availability_zone='fake-az') current_usage = db.vol_get_usage_by_time(ctxt, start_time)[0] self.assertEqual(current_usage['tot_reads'], 0) self.assertEqual(current_usage['curr_reads'], 100) - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=200, rd_bytes=300, - wr_req=400, wr_bytes=500, - instance_id='fake-instance-uuid', - project_id='fake-project-uuid', - user_id='fake-user-uuid', - availability_zone='fake-az', - update_totals=True) + db.vol_usage_update(ctxt, 1, rd_req=200, rd_bytes=300, + wr_req=400, wr_bytes=500, + instance_id='fake-instance-uuid', + project_id='fake-project-uuid', + user_id='fake-user-uuid', + availability_zone='fake-az', + update_totals=True) current_usage = db.vol_get_usage_by_time(ctxt, start_time)[0] self.assertEqual(current_usage['tot_reads'], 200) self.assertEqual(current_usage['curr_reads'], 0) - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=300, rd_bytes=400, - wr_req=500, wr_bytes=600, - instance_id='fake-instance-uuid', - project_id='fake-project-uuid', - availability_zone='fake-az', - user_id='fake-user-uuid') + db.vol_usage_update(ctxt, 1, rd_req=300, rd_bytes=400, + wr_req=500, wr_bytes=600, + instance_id='fake-instance-uuid', + project_id='fake-project-uuid', + availability_zone='fake-az', + user_id='fake-user-uuid') current_usage = db.vol_get_usage_by_time(ctxt, start_time)[0] self.assertEqual(current_usage['tot_reads'], 200) self.assertEqual(current_usage['curr_reads'], 300) - vol_usage = db.vol_usage_update(ctxt, 1, rd_req=400, rd_bytes=500, - wr_req=600, wr_bytes=700, - instance_id='fake-instance-uuid', - project_id='fake-project-uuid', - user_id='fake-user-uuid', - availability_zone='fake-az', - update_totals=True) + db.vol_usage_update(ctxt, 1, rd_req=400, rd_bytes=500, + wr_req=600, wr_bytes=700, + instance_id='fake-instance-uuid', + project_id='fake-project-uuid', + user_id='fake-user-uuid', + availability_zone='fake-az', + update_totals=True) vol_usages = db.vol_get_usage_by_time(ctxt, start_time) @@ -3830,12 +3985,15 @@ class BlockDeviceMappingTestCase(test.TestCase): def test_block_device_mapping_update(self): bdm = self._create_bdm({}) - db.block_device_mapping_update(self.ctxt, bdm['id'], - {'destination_type': 'moon'}, - legacy=False) + result = db.block_device_mapping_update( + self.ctxt, bdm['id'], {'destination_type': 'moon'}, + legacy=False) uuid = bdm['instance_uuid'] bdm_real = db.block_device_mapping_get_all_by_instance(self.ctxt, uuid) self.assertEqual(bdm_real[0]['destination_type'], 'moon') + # Also make sure the update call returned correct data + self.assertEqual(dict(bdm_real[0].iteritems()), + dict(result.iteritems())) def test_block_device_mapping_update_or_create(self): values = { @@ -4555,7 +4713,7 @@ class QuotaTestCase(test.TestCase, ModelsObjectComparatorMixin): self.ctxt, 'p1', 'nonexitent_resource') def test_quota_usage_get(self): - reservations = _quota_reserve(self.ctxt, 'p1') + _quota_reserve(self.ctxt, 'p1') quota_usage = db.quota_usage_get(self.ctxt, 'p1', 'res0') expected = {'resource': 'res0', 'project_id': 'p1', 'in_use': 0, 'reserved': 0, 'total': 0} @@ -4563,7 +4721,7 @@ class QuotaTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertEqual(value, quota_usage[key]) def test_quota_usage_get_all_by_project(self): - reservations = _quota_reserve(self.ctxt, 'p1') + _quota_reserve(self.ctxt, 'p1') expected = {'project_id': 'p1', 'res0': {'in_use': 0, 'reserved': 0}, 'res1': {'in_use': 1, 'reserved': 1}, @@ -4576,8 +4734,7 @@ class QuotaTestCase(test.TestCase, ModelsObjectComparatorMixin): self.ctxt, 'p1', 'resource', in_use=42) def test_quota_usage_update(self): - reservations = _quota_reserve(self.ctxt, 'p1') - until_refresh = datetime.datetime.now() + datetime.timedelta(days=1) + _quota_reserve(self.ctxt, 'p1') db.quota_usage_update(self.ctxt, 'p1', 'res0', in_use=42, reserved=43) quota_usage = db.quota_usage_get(self.ctxt, 'p1', 'res0') expected = {'resource': 'res0', 'project_id': 'p1', @@ -4633,7 +4790,7 @@ class QuotaClassTestCase(test.TestCase, ModelsObjectComparatorMixin): 'resource0': 0, 'resource1': 1, 'resource2': 2}) def test_quota_class_update(self): - qc = db.quota_class_create(self.ctxt, 'class name', 'resource', 42) + db.quota_class_create(self.ctxt, 'class name', 'resource', 42) db.quota_class_update(self.ctxt, 'class name', 'resource', 43) self.assertEqual(db.quota_class_get(self.ctxt, 'class name', 'resource').hard_limit, 43) @@ -4792,6 +4949,19 @@ class ComputeNodeTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertNotEqual(self.item['updated_at'], item_updated['updated_at']) + def test_compute_node_update_override_updated_at(self): + # Update the record once so updated_at is set. + first = db.compute_node_update(self.ctxt, self.item['id'], + {'free_ram_mb': '12'}) + self.assertIsNotNone(first['updated_at']) + + # Update a second time. Make sure that the updated_at value we send + # is overridden. + second = db.compute_node_update(self.ctxt, self.item['id'], + {'updated_at': first.updated_at, + 'free_ram_mb': '13'}) + self.assertNotEqual(first['updated_at'], second['updated_at']) + def test_compute_node_stat_unchanged(self): # don't update unchanged stat values: stats = self.item['stats'] @@ -4866,6 +5036,49 @@ class ProviderFwRuleTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertEqual([], db.provider_fw_rule_get_all(self.ctxt)) +class CertificateTestCase(test.TestCase, ModelsObjectComparatorMixin): + + def setUp(self): + super(CertificateTestCase, self).setUp() + self.ctxt = context.get_admin_context() + self.created = self._certificates_create() + + def _get_certs_values(self): + base_values = { + 'user_id': 'user', + 'project_id': 'project', + 'file_name': 'filename' + } + return [dict((k, v + str(x)) for k, v in base_values.iteritems()) + for x in xrange(1, 4)] + + def _certificates_create(self): + return [db.certificate_create(self.ctxt, cert) + for cert in self._get_certs_values()] + + def test_certificate_create(self): + ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at', + 'updated_at'] + for i, cert in enumerate(self._get_certs_values()): + self._assertEqualObjects(self.created[i], cert, + ignored_keys=ignored_keys) + + def test_certificate_get_all_by_project(self): + cert = db.certificate_get_all_by_project(self.ctxt, + self.created[1].project_id) + self._assertEqualObjects(self.created[1], cert[0]) + + def test_certificate_get_all_by_user(self): + cert = db.certificate_get_all_by_user(self.ctxt, + self.created[1].user_id) + self._assertEqualObjects(self.created[1], cert[0]) + + def test_certificate_get_all_by_user_and_project(self): + cert = db.certificate_get_all_by_user_and_project(self.ctxt, + self.created[1].user_id, self.created[1].project_id) + self._assertEqualObjects(self.created[1], cert[0]) + + class CellTestCase(test.TestCase, ModelsObjectComparatorMixin): _ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at', 'updated_at'] diff --git a/nova/tests/db/test_migration_utils.py b/nova/tests/db/test_migration_utils.py index 3674a7c45..af206632d 100644 --- a/nova/tests/db/test_migration_utils.py +++ b/nova/tests/db/test_migration_utils.py @@ -19,7 +19,7 @@ import warnings from migrate.changeset import UniqueConstraint from sqlalchemy.dialects import mysql from sqlalchemy import Boolean, Index, Integer, DateTime, String -from sqlalchemy import MetaData, Table, Column +from sqlalchemy import MetaData, Table, Column, ForeignKey from sqlalchemy.engine import reflection from sqlalchemy.exc import NoSuchTableError from sqlalchemy.exc import SAWarning @@ -514,3 +514,28 @@ class TestMigrationUtils(test_migrations.BaseMigrationTestCase): # but sqlalchemy will set it to NullType. self.assertTrue(isinstance(table.c.foo.type, NullType)) self.assertTrue(isinstance(table.c.deleted.type, Boolean)) + + def test_drop_unique_constraint_in_sqlite_fk_recreate(self): + engine = self.engines['sqlite'] + meta = MetaData() + meta.bind = engine + parent_table = Table('table0', meta, + Column('id', Integer, primary_key=True), + Column('foo', Integer)) + parent_table.create() + table_name = 'table1' + table = Table(table_name, meta, + Column('id', Integer, primary_key=True), + Column('baz', Integer), + Column('bar', Integer, ForeignKey("table0.id")), + UniqueConstraint('baz', name='constr1')) + table.create() + utils.drop_unique_constraint(engine, table_name, 'constr1', 'baz') + + insp = reflection.Inspector.from_engine(engine) + f_keys = insp.get_foreign_keys(table_name) + self.assertEqual(len(f_keys), 1) + f_key = f_keys[0] + self.assertEqual(f_key['referred_table'], 'table0') + self.assertEqual(f_key['referred_columns'], ['id']) + self.assertEqual(f_key['constrained_columns'], ['bar']) diff --git a/nova/tests/db/test_migrations.py b/nova/tests/db/test_migrations.py index 812f0d8ae..7e28fb1d5 100644 --- a/nova/tests/db/test_migrations.py +++ b/nova/tests/db/test_migrations.py @@ -806,8 +806,6 @@ class TestNovaMigrations(BaseMigrationTestCase, CommonTestsMixIn): def _check_153(self, engine, data): fake_types, fake_instances = data # NOTE(danms): Fetch all the tables and data from scratch after change - instances = db_utils.get_table(engine, 'instances') - instance_types = db_utils.get_table(engine, 'instance_types') sys_meta = db_utils.get_table(engine, 'instance_system_metadata') # Collect all system metadata, indexed by instance_uuid @@ -1073,7 +1071,6 @@ class TestNovaMigrations(BaseMigrationTestCase, CommonTestsMixIn): for result in results: the_id = result['id'] key = result['key'] - value = result['value'] original = data[the_id] if key == 'instance_type_baz': @@ -1775,6 +1772,13 @@ class TestBaremetalMigrations(BaseMigrationTestCase, CommonTestsMixIn): columns = [c.name for c in bm_nodes.columns] self.assertNotIn(u'prov_mac_address', columns) + def _check_008(self, engine, data): + self.assertRaises(sqlalchemy.exc.NoSuchTableError, + db_utils.get_table, engine, 'bm_pxe_ips') + + def _post_downgrade_008(self, engine): + db_utils.get_table(engine, 'bm_pxe_ips') + class ProjectTestCase(test.TestCase): diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 79af362bb..f4e8d3841 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -27,6 +27,9 @@ from nova.network import manager as network_manager from nova.network import model as network_model from nova.network import nova_ipam_lib from nova.network import rpcapi as network_rpcapi +from nova.objects import base as obj_base +from nova.objects import instance_info_cache +from nova.openstack.common import jsonutils from nova.virt.libvirt import config as libvirt_config @@ -456,7 +459,7 @@ def _get_fake_cache(): ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff' info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64', 'ips': [_ip(ipv6_addr)]}) - return info + return jsonutils.dumps(info) def _get_instances_with_cached_ips(orig_func, *args, **kwargs): @@ -464,9 +467,22 @@ def _get_instances_with_cached_ips(orig_func, *args, **kwargs): entries """ instances = orig_func(*args, **kwargs) - if isinstance(instances, list): + context = args[0] + + def _info_cache_for(instance): + info_cache = {'network_info': _get_fake_cache(), + 'instance_uuid': instance['uuid']} + if isinstance(instance, obj_base.NovaObject): + _info_cache = instance_info_cache.InstanceInfoCache() + instance_info_cache.InstanceInfoCache._from_db_object(context, + _info_cache, + info_cache) + info_cache = _info_cache + instance['info_cache'] = info_cache + + if isinstance(instances, (list, obj_base.ObjectListBase)): for instance in instances: - instance['info_cache'] = {'network_info': _get_fake_cache()} + _info_cache_for(instance) else: - instances['info_cache'] = {'network_info': _get_fake_cache()} + _info_cache_for(instances) return instances diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index ed8cc7424..cbd856f4b 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -111,7 +111,9 @@ policy_data = """ "compute_extension:attach_interfaces": "", "compute_extension:baremetal_nodes": "", "compute_extension:cells": "", + "compute_extension:v3:os-cells": "", "compute_extension:certificates": "", + "compute_extension:v3:os-certificates": "", "compute_extension:cloudpipe": "", "compute_extension:cloudpipe_update": "", "compute_extension:config_drive": "", @@ -122,6 +124,7 @@ policy_data = """ "compute_extension:deferred_delete": "", "compute_extension:disk_config": "", "compute_extension:evacuate": "is_admin:True", + "compute_extension:v3:os-evacuate": "is_admin:True", "compute_extension:extended_server_attributes": "", "compute_extension:extended_status": "", "compute_extension:extended_availability_zone": "", @@ -131,7 +134,9 @@ policy_data = """ "compute_extension:fixed_ips": "", "compute_extension:v3:os-fixed-ips": "", "compute_extension:flavor_access": "", + "compute_extension:v3:os-flavor-access": "", "compute_extension:flavor_disabled": "", + "compute_extension:v3:os-flavor-disabled": "", "compute_extension:flavor_rxtx": "", "compute_extension:flavor_swap": "", "compute_extension:flavorextradata": "", @@ -151,6 +156,7 @@ policy_data = """ "compute_extension:hosts": "", "compute_extension:hypervisors": "", "compute_extension:image_size": "", + "compute_extension:v3:os-images": "", "compute_extension:instance_actions": "", "compute_extension:instance_actions:events": "is_admin:True", "compute_extension:instance_usage_audit_log": "", @@ -164,11 +170,16 @@ policy_data = """ "compute_extension:quotas:show": "", "compute_extension:quotas:update": "", "compute_extension:quotas:delete": "", + "compute_extension:v3:os-quota-sets:show": "", + "compute_extension:v3:os-quota-sets:update": "", + "compute_extension:v3:os-quota-sets:delete": "", "compute_extension:quota_classes": "", "compute_extension:rescue": "", + "compute_extension:v3:os-rescue": "", "compute_extension:security_group_default_rules": "", "compute_extension:security_groups": "", "compute_extension:server_diagnostics": "", + "compute_extension:v3:os-server-diagnostics": "", "compute_extension:server_password": "", "compute_extension:server_usage": "", "compute_extension:services": "", diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 87ed6c6ce..00a989a70 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -47,7 +47,7 @@ from nova.openstack.common import jsonutils from nova.openstack.common import log as logging from nova.openstack.common import timeutils import nova.quota -from nova.scheduler import driver +from nova.scheduler import manager as scheduler_manager from nova.servicegroup import api as service_group_api from nova import test from nova.tests.api.openstack.compute.contrib import test_coverage_ext @@ -2213,23 +2213,13 @@ class AdminActionsSamplesJsonTest(ServersSampleBase): def test_post_live_migrate_server(self): # Get api samples to server live migrate request. - def fake_live_migration_src_check(self, context, instance_ref): - """Skip live migration scheduler checks.""" + def fake_live_migration(self, context, instance, dest, + block_migration, disk_over_commit): return - def fake_live_migration_dest_check(self, context, instance_ref, dest): - """Skip live migration scheduler checks.""" - return dest - - def fake_live_migration_common(self, context, instance_ref, dest): - """Skip live migration scheduler checks.""" - return - self.stubs.Set(driver.Scheduler, '_live_migration_src_check', - fake_live_migration_src_check) - self.stubs.Set(driver.Scheduler, '_live_migration_dest_check', - fake_live_migration_dest_check) - self.stubs.Set(driver.Scheduler, '_live_migration_common_check', - fake_live_migration_common) + self.stubs.Set(scheduler_manager.SchedulerManager, + 'live_migration', + fake_live_migration) def fake_get_compute(context, host): service = dict(host=host, @@ -3194,7 +3184,7 @@ class ExtendedAvailabilityZoneJsonTests(ServersSampleBase): self._verify_response('server-get-resp', subs, response, 200) def test_detail(self): - uuid = self._post_server() + self._post_server() response = self._do_get('servers/detail') subs = self._get_regexes() subs['hostid'] = '[a-f0-9]+' @@ -3422,18 +3412,18 @@ class ConfigDriveSampleJsonTest(ServersSampleBase): response = self._do_get('servers/%s' % uuid) subs = self._get_regexes() subs['hostid'] = '[a-f0-9]+' - # config drive can be an uuid or empty value - subs['cdrive'] = '(%s)?' % subs['uuid'] + # config drive can be a string for True or empty value for False + subs['cdrive'] = '.*' self._verify_response('server-config-drive-get-resp', subs, response, 200) def test_config_drive_detail(self): - uuid = self._post_server() + self._post_server() response = self._do_get('servers/detail') subs = self._get_regexes() subs['hostid'] = '[a-f0-9]+' - # config drive can be an uuid or empty value - subs['cdrive'] = '(%s)?' % subs['uuid'] + # config drive can be a string for True or empty value for False + subs['cdrive'] = '.*' self._verify_response('servers-config-drive-details-resp', subs, response, 200) @@ -3506,7 +3496,7 @@ class FlavorAccessSampleJsonTests(ApiSampleTestBase): def test_flavor_access_add_tenant(self): self._create_flavor() - response = self._add_tenant() + self._add_tenant() def test_flavor_access_remove_tenant(self): self._create_flavor() diff --git a/nova/tests/network/test_linux_net.py b/nova/tests/network/test_linux_net.py index 5c7f3828d..e8368d06a 100644 --- a/nova/tests/network/test_linux_net.py +++ b/nova/tests/network/test_linux_net.py @@ -861,7 +861,6 @@ class LinuxNetworkTestCase(test.TestCase): self.stubs.Set(ln, 'ensure_ebtables_rules', lambda *a, **kw: None) net = {'bridge': 'br100', 'cidr': '10.0.0.0/24'} ln.ensure_floating_forward('10.10.10.10', '10.0.0.1', 'eth0', net) - one_forward_rules = len(linux_net.iptables_manager.ipv4['nat'].rules) ln.ensure_floating_forward('10.10.10.11', '10.0.0.10', 'eth0', net) two_forward_rules = len(linux_net.iptables_manager.ipv4['nat'].rules) ln.ensure_floating_forward('10.10.10.10', '10.0.0.3', 'eth0', net) diff --git a/nova/tests/network/test_network_info.py b/nova/tests/network/test_network_info.py index bb3d91f55..38a27b51c 100644 --- a/nova/tests/network/test_network_info.py +++ b/nova/tests/network/test_network_info.py @@ -372,7 +372,7 @@ class NetworkInfoTests(test.TestCase): ninfo = model.NetworkInfo([fake_network_cache_model.new_vif(), fake_network_cache_model.new_vif( {'address': 'bb:bb:bb:bb:bb:bb'})]) - deserialized = model.NetworkInfo.hydrate(ninfo) + model.NetworkInfo.hydrate(ninfo) self.assertEqual(ninfo.fixed_ips(), [fake_network_cache_model.new_ip({'address': '10.10.0.2'}), fake_network_cache_model.new_ip( diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index 0b6f184ae..2ddeb72bf 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -19,8 +19,6 @@ import uuid import mox from oslo.config import cfg -from quantumclient import client as quantum_client -from quantumclient.common import exceptions as quantum_exceptions from quantumclient.v2_0 import client from nova.compute import flavors @@ -104,17 +102,6 @@ class TestQuantumClient(test.TestCase): self.mox.ReplayAll() quantumv2.get_client(my_context) - def test_cached_admin_client(self): - self.mox.StubOutWithMock(quantum_client.HTTPClient, "authenticate") - - # should be called one time only - quantum_client.HTTPClient.authenticate() - self.mox.ReplayAll() - - admin1 = quantumv2.get_client(None, admin=True) - admin2 = quantumv2.get_client(None, admin=True) - self.assertIs(admin1, admin2) - def test_withouttoken_keystone_connection_error(self): self.flags(quantum_auth_strategy='keystone') self.flags(quantum_url='http://anyhost/') @@ -727,7 +714,6 @@ class TestQuantumv2(test.TestCase): port_data = number == 1 and self.port_data1 or self.port_data2 self.moxed_client.delete_port(port_data[0]['id']) - nets = [port_data[0]['network_id']] quantumv2.get_client(mox.IgnoreArg(), admin=True).AndReturn( self.moxed_client) self.moxed_client.list_ports( @@ -1069,7 +1055,6 @@ class TestQuantumv2(test.TestCase): def test_allocate_floating_ip_with_pool_id(self): api = quantumapi.API() - pool_name = self.fip_pool['name'] pool_id = self.fip_pool['id'] search_opts = {'router:external': True, 'fields': 'id', @@ -1113,7 +1098,6 @@ class TestQuantumv2(test.TestCase): def test_release_floating_ip_associated(self): api = quantumapi.API() address = self.fip_associated['floating_ip_address'] - fip_id = self.fip_associated['id'] self.moxed_client.list_floatingips(floating_ip_address=address).\ AndReturn({'floatingips': [self.fip_associated]}) @@ -1157,7 +1141,6 @@ class TestQuantumv2(test.TestCase): api = quantumapi.API() address = self.fip_associated['floating_ip_address'] fixed_address = self.fip_associated['fixed_ip_address'] - fip_id = self.fip_associated['id'] search_opts = {'device_owner': 'compute:nova', 'device_id': self.instance['uuid']} @@ -1191,7 +1174,6 @@ class TestQuantumv2(test.TestCase): self.moxed_client.list_subnets( **search_opts).AndReturn({'subnets': self.subnet_data_n}) - zone = 'compute:%s' % self.instance['availability_zone'] search_opts = {'device_id': self.instance['uuid'], 'device_owner': 'compute:nova', 'network_id': network_id} @@ -1236,7 +1218,7 @@ class TestQuantumv2(test.TestCase): def test_list_floating_ips_without_l3_support(self): api = quantumapi.API() - QuantumNotFound = quantum_exceptions.QuantumClientException( + QuantumNotFound = quantumv2.exceptions.QuantumClientException( status_code=404) self.moxed_client.list_floatingips( fixed_ip_address='1.1.1.1', port_id=1).AndRaise(QuantumNotFound) @@ -1370,6 +1352,13 @@ class TestQuantumv2(test.TestCase): self.assertEqual(nw_info[0]['type'], model.VIF_TYPE_BRIDGE) self.assertEqual(nw_info[0]['network']['bridge'], 'brqnet-id') + def test_get_all_empty_list_networks(self): + api = quantumapi.API() + self.moxed_client.list_networks().AndReturn({'networks': []}) + self.mox.ReplayAll() + networks = api.get_all(self.context) + self.assertEqual(networks, []) + class TestQuantumv2ModuleMethods(test.TestCase): def test_ensure_requested_network_ordering_no_preference_ids(self): diff --git a/nova/tests/objects/test_instance.py b/nova/tests/objects/test_instance.py index fa0a536fd..9f222951a 100644 --- a/nova/tests/objects/test_instance.py +++ b/nova/tests/objects/test_instance.py @@ -20,8 +20,12 @@ from nova import context from nova import db from nova.objects import base from nova.objects import instance +from nova.objects import security_group from nova.openstack.common import timeutils +from nova import test from nova.tests.api.openstack import fakes +from nova.tests import fake_instance +from nova.tests.objects import test_instance_fault from nova.tests.objects import test_objects @@ -41,6 +45,7 @@ class _TestInstanceObject(object): tzinfo=iso8601.iso8601.Utc(), microsecond=0)) fake_instance['deleted'] = False fake_instance['info_cache']['instance_uuid'] = fake_instance['uuid'] + fake_instance['security_groups'] = None return fake_instance def test_datetime_deserialization(self): @@ -90,7 +95,7 @@ class _TestInstanceObject(object): self.mox.StubOutWithMock(db, 'instance_get_by_uuid') db.instance_get_by_uuid(ctxt, 'uuid', []).AndReturn(self.fake_instance) self.mox.ReplayAll() - inst = instance.Instance.get_by_uuid(ctxt, uuid='uuid') + inst = instance.Instance.get_by_uuid(ctxt, 'uuid') # Make sure these weren't loaded for attr in instance.INSTANCE_OPTIONAL_FIELDS: attrname = base.get_attrname(attr) @@ -102,7 +107,7 @@ class _TestInstanceObject(object): self.mox.StubOutWithMock(db, 'instance_get_by_uuid') db.instance_get_by_uuid( ctxt, 'uuid', - instance.INSTANCE_OPTIONAL_FIELDS).AndReturn(self.fake_instance) + ['metadata', 'system_metadata']).AndReturn(self.fake_instance) self.mox.ReplayAll() inst = instance.Instance.get_by_uuid( ctxt, 'uuid', expected_attrs=instance.INSTANCE_OPTIONAL_FIELDS) @@ -213,6 +218,58 @@ class _TestInstanceObject(object): inst.info_cache.network_info = 'bar' inst.save() + def test_with_security_groups(self): + ctxt = context.get_admin_context() + fake_inst = dict(self.fake_instance) + fake_uuid = fake_inst['uuid'] + fake_inst['security_groups'] = [ + {'id': 1, 'name': 'secgroup1', 'description': 'fake-desc', + 'user_id': 'fake-user', 'project_id': 'fake_project', + 'created_at': None, 'updated_at': None, 'deleted_at': None, + 'deleted': False}, + {'id': 2, 'name': 'secgroup2', 'description': 'fake-desc', + 'user_id': 'fake-user', 'project_id': 'fake_project', + 'created_at': None, 'updated_at': None, 'deleted_at': None, + 'deleted': False}, + ] + self.mox.StubOutWithMock(db, 'instance_get_by_uuid') + self.mox.StubOutWithMock(db, 'instance_update_and_get_original') + self.mox.StubOutWithMock(db, 'security_group_update') + db.instance_get_by_uuid(ctxt, fake_uuid, []).AndReturn(fake_inst) + db.security_group_update(ctxt, 1, {'description': 'changed'} + ).AndReturn(fake_inst['security_groups'][0]) + self.mox.ReplayAll() + inst = instance.Instance.get_by_uuid(ctxt, fake_uuid) + self.assertEqual(len(inst.security_groups), 2) + for index, group in enumerate(fake_inst['security_groups']): + for key in group: + self.assertEqual(group[key], + inst.security_groups[index][key]) + self.assertTrue(isinstance(inst.security_groups[index], + security_group.SecurityGroup)) + self.assertEqual(inst.security_groups.obj_what_changed(), set()) + inst.security_groups[0].description = 'changed' + inst.save() + self.assertEqual(inst.security_groups.obj_what_changed(), set()) + + def test_with_fault(self): + ctxt = context.get_admin_context() + fake_inst = dict(self.fake_instance) + fake_uuid = fake_inst['uuid'] + fake_faults = [dict(x, instance_uuid=fake_uuid) + for x in test_instance_fault.fake_faults['fake-uuid']] + self.mox.StubOutWithMock(db, 'instance_get_by_uuid') + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_get_by_uuid(ctxt, fake_uuid, []).AndReturn( + self.fake_instance) + db.instance_fault_get_by_instance_uuids(ctxt, [fake_uuid]).AndReturn( + {fake_uuid: fake_faults}) + self.mox.ReplayAll() + inst = instance.Instance.get_by_uuid(ctxt, fake_uuid, + expected_attrs=['fault']) + self.assertEqual(fake_faults[0], dict(inst.fault.items())) + self.assertRemotes() + def test_iteritems_with_extra_attrs(self): self.stubs.Set(instance.Instance, 'name', 'foo') inst = instance.Instance() @@ -248,6 +305,7 @@ class _TestInstanceListObject(object): tzinfo=iso8601.iso8601.Utc(), microsecond=0)) fake_instance['info_cache'] = {'network_info': 'foo', 'instance_uuid': fake_instance['uuid']} + fake_instance['security_groups'] = [] fake_instance['deleted'] = 0 if updates: fake_instance.update(updates) @@ -258,7 +316,7 @@ class _TestInstanceListObject(object): ctxt = context.get_admin_context() self.mox.StubOutWithMock(db, 'instance_get_all_by_filters') db.instance_get_all_by_filters(ctxt, {'foo': 'bar'}, 'uuid', 'asc', - None, None, + limit=None, marker=None, columns_to_join=['metadata']).AndReturn( fakes) self.mox.ReplayAll() @@ -284,6 +342,7 @@ class _TestInstanceListObject(object): self.assertTrue(isinstance(inst_list.objects[i], instance.Instance)) self.assertEqual(inst_list.objects[i].uuid, fakes[i]['uuid']) + self.assertEqual(inst_list.objects[i]._context, ctxt) self.assertEqual(inst_list.obj_what_changed(), set()) self.assertRemotes() @@ -336,6 +395,27 @@ class _TestInstanceListObject(object): self.assertEqual(inst_list.objects[i].uuid, fakes[i]['uuid']) self.assertRemotes() + def test_with_fault(self): + ctxt = context.get_admin_context() + fake_insts = [ + fake_instance.fake_db_instance(uuid='fake-uuid', host='host'), + fake_instance.fake_db_instance(uuid='fake-inst2', host='host'), + ] + fake_faults = test_instance_fault.fake_faults + self.mox.StubOutWithMock(db, 'instance_get_all_by_host') + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_get_all_by_host(ctxt, 'host', columns_to_join=[] + ).AndReturn(fake_insts) + db.instance_fault_get_by_instance_uuids( + ctxt, [x['uuid'] for x in fake_insts]).AndReturn(fake_faults) + self.mox.ReplayAll() + instances = instance.InstanceList.get_by_host(ctxt, 'host', + expected_attrs=['fault']) + self.assertEqual(2, len(instances)) + self.assertEqual(fake_faults['fake-uuid'][0], + dict(instances[0].fault.iteritems())) + self.assertEqual(None, instances[1].fault) + class TestInstanceListObject(test_objects._LocalTest, _TestInstanceListObject): @@ -345,3 +425,10 @@ class TestInstanceListObject(test_objects._LocalTest, class TestRemoteInstanceListObject(test_objects._RemoteTest, _TestInstanceListObject): pass + + +class TestInstanceObjectMisc(test.TestCase): + def test_expected_cols(self): + self.stubs.Set(instance, 'INSTANCE_OPTIONAL_NON_COLUMNS', ['bar']) + self.assertEqual(['foo'], instance.expected_cols(['foo', 'bar'])) + self.assertEqual(None, instance.expected_cols(None)) diff --git a/nova/tests/objects/test_instance_fault.py b/nova/tests/objects/test_instance_fault.py new file mode 100644 index 000000000..2f9840df1 --- /dev/null +++ b/nova/tests/objects/test_instance_fault.py @@ -0,0 +1,86 @@ +# Copyright 2013 IBM Corp. +# +# 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. + +from nova import context +from nova import db +from nova.objects import instance_fault +from nova.tests.objects import test_objects + + +fake_faults = { + 'fake-uuid': [ + {'id': 1, 'instance_uuid': 'fake-uuid', 'code': 123, 'message': 'msg1', + 'details': 'details', 'host': 'host', 'deleted': False, + 'created_at': None, 'updated_at': None, 'deleted_at': None}, + {'id': 2, 'instance_uuid': 'fake-uuid', 'code': 456, 'message': 'msg2', + 'details': 'details', 'host': 'host', 'deleted': False, + 'created_at': None, 'updated_at': None, 'deleted_at': None}, + ] + } + + +class _TestInstanceFault(object): + def test_get_latest_for_instance(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_fault_get_by_instance_uuids(ctxt, ['fake-uuid']).AndReturn( + fake_faults) + self.mox.ReplayAll() + fault = instance_fault.InstanceFault.get_latest_for_instance( + ctxt, 'fake-uuid') + for key in fake_faults['fake-uuid'][0]: + self.assertEqual(fake_faults['fake-uuid'][0][key], fault[key]) + + def test_get_latest_for_instance_with_none(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_fault_get_by_instance_uuids(ctxt, ['fake-uuid']).AndReturn( + {}) + self.mox.ReplayAll() + fault = instance_fault.InstanceFault.get_latest_for_instance( + ctxt, 'fake-uuid') + self.assertEqual(None, fault) + + def test_get_by_instance(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_fault_get_by_instance_uuids(ctxt, ['fake-uuid']).AndReturn( + fake_faults) + self.mox.ReplayAll() + faults = instance_fault.InstanceFaultList.get_by_instance_uuids( + ctxt, ['fake-uuid']) + for index, db_fault in enumerate(fake_faults['fake-uuid']): + for key in db_fault: + self.assertEqual(fake_faults['fake-uuid'][index][key], + faults[index][key]) + + def test_get_by_instance_with_none(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'instance_fault_get_by_instance_uuids') + db.instance_fault_get_by_instance_uuids(ctxt, ['fake-uuid']).AndReturn( + {}) + self.mox.ReplayAll() + faults = instance_fault.InstanceFaultList.get_by_instance_uuids( + ctxt, ['fake-uuid']) + self.assertEqual(0, len(faults)) + + +class TestInstanceFault(test_objects._LocalTest, + _TestInstanceFault): + pass + + +class TestInstanceFaultRemote(test_objects._RemoteTest, + _TestInstanceFault): + pass diff --git a/nova/tests/objects/test_objects.py b/nova/tests/objects/test_objects.py index 332833cb0..03a270386 100644 --- a/nova/tests/objects/test_objects.py +++ b/nova/tests/objects/test_objects.py @@ -178,6 +178,31 @@ class TestUtils(test.TestCase): self.assertEqual(utils.dt_deserializer(None, None), None) self.assertRaises(ValueError, utils.dt_deserializer, None, 'foo') + def test_obj_to_primitive_list(self): + class MyList(base.ObjectListBase, base.NovaObject): + pass + mylist = MyList() + mylist.objects = [1, 2, 3] + self.assertEqual([1, 2, 3], base.obj_to_primitive(mylist)) + + def test_obj_to_primitive_dict(self): + myobj = MyObj() + myobj.foo = 1 + myobj.bar = 'foo' + self.assertEqual({'foo': 1, 'bar': 'foo'}, + base.obj_to_primitive(myobj)) + + def test_obj_to_primitive_recursive(self): + class MyList(base.ObjectListBase, base.NovaObject): + pass + + mylist = MyList() + mylist.objects = [MyObj(), MyObj()] + for i, value in enumerate(mylist): + value.foo = i + self.assertEqual([{'foo': 0}, {'foo': 1}], + base.obj_to_primitive(mylist)) + class _BaseTestCase(test.TestCase): def setUp(self): @@ -441,6 +466,13 @@ class _TestObject(object): } self.assertEqual(obj.obj_to_primitive(), expected) + def test_contains(self): + obj = MyObj() + self.assertFalse('foo' in obj) + obj.foo = 1 + self.assertTrue('foo' in obj) + self.assertFalse('does_not_exist' in obj) + class TestObject(_LocalTest, _TestObject): pass @@ -502,3 +534,40 @@ class TestObjectListBase(test.TestCase): self.assertFalse(obj is obj2) self.assertEqual([x.foo for x in obj], [y.foo for y in obj2]) + + +class TestObjectSerializer(test.TestCase): + def test_serialize_entity_primitive(self): + ser = base.NovaObjectSerializer() + for thing in (1, 'foo', [1, 2], {'foo': 'bar'}): + self.assertEqual(thing, ser.serialize_entity(None, thing)) + + def test_deserialize_entity_primitive(self): + ser = base.NovaObjectSerializer() + for thing in (1, 'foo', [1, 2], {'foo': 'bar'}): + self.assertEqual(thing, ser.deserialize_entity(None, thing)) + + def test_object_serialization(self): + ser = base.NovaObjectSerializer() + ctxt = context.get_admin_context() + obj = MyObj() + primitive = ser.serialize_entity(ctxt, obj) + self.assertTrue('nova_object.name' in primitive) + obj2 = ser.deserialize_entity(ctxt, primitive) + self.assertTrue(isinstance(obj2, MyObj)) + self.assertEqual(ctxt, obj2._context) + + def test_object_serialization_iterables(self): + ser = base.NovaObjectSerializer() + ctxt = context.get_admin_context() + obj = MyObj() + for iterable in (list, tuple, set): + thing = iterable([obj]) + primitive = ser.serialize_entity(ctxt, thing) + self.assertEqual(1, len(primitive)) + for item in primitive: + self.assertFalse(isinstance(item, base.NovaObject)) + thing2 = ser.deserialize_entity(ctxt, primitive) + self.assertEqual(1, len(thing2)) + for item in thing2: + self.assertTrue(isinstance(item, MyObj)) diff --git a/nova/tests/objects/test_security_group.py b/nova/tests/objects/test_security_group.py new file mode 100644 index 000000000..ee2f79ad7 --- /dev/null +++ b/nova/tests/objects/test_security_group.py @@ -0,0 +1,186 @@ +# Copyright 2013 IBM Corp. +# +# 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. + +from nova import context +from nova import db +from nova.objects import instance +from nova.objects import security_group +from nova.tests.objects import test_objects + + +fake_secgroup = { + 'created_at': None, + 'updated_at': None, + 'deleted_at': None, + 'deleted': None, + 'id': 1, + 'name': 'fake-name', + 'description': 'fake-desc', + 'user_id': 'fake-user', + 'project_id': 'fake-project', + } + + +class _TestSecurityGroupObject(object): + def _fix_deleted(self, db_secgroup): + # NOTE(danms): Account for the difference in 'deleted' + return dict(db_secgroup.items(), deleted=False) + + def test_get(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_get') + db.security_group_get(ctxt, 1).AndReturn(fake_secgroup) + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup.get(ctxt, 1) + self.assertEqual(self._fix_deleted(fake_secgroup), + dict(secgroup.items())) + self.assertEqual(secgroup.obj_what_changed(), set()) + self.assertRemotes() + + def test_get_by_name(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_get_by_name') + db.security_group_get_by_name(ctxt, 'fake-project', + 'fake-name').AndReturn(fake_secgroup) + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup.get_by_name(ctxt, + 'fake-project', + 'fake-name') + self.assertEqual(self._fix_deleted(fake_secgroup), + dict(secgroup.items())) + self.assertEqual(secgroup.obj_what_changed(), set()) + self.assertRemotes() + + def test_in_use(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_in_use') + db.security_group_in_use(ctxt, 123).AndReturn(True) + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup() + secgroup.id = 123 + self.assertTrue(secgroup.in_use(ctxt)) + self.assertRemotes() + + def test_save(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_update') + updated_secgroup = dict(fake_secgroup, project_id='changed') + db.security_group_update(ctxt, 1, + {'description': 'foobar'}).AndReturn( + updated_secgroup) + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup._from_db_object( + security_group.SecurityGroup(), fake_secgroup) + secgroup.description = 'foobar' + secgroup.save(ctxt) + self.assertEqual(self._fix_deleted(updated_secgroup), + dict(secgroup.items())) + self.assertEqual(secgroup.obj_what_changed(), set()) + self.assertRemotes() + + def test_save_no_changes(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_update') + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup._from_db_object( + security_group.SecurityGroup(), fake_secgroup) + secgroup.save(ctxt) + + def test_refresh(self): + ctxt = context.get_admin_context() + updated_secgroup = dict(fake_secgroup, description='changed') + self.mox.StubOutWithMock(db, 'security_group_get') + db.security_group_get(ctxt, 1).AndReturn(updated_secgroup) + self.mox.ReplayAll() + secgroup = security_group.SecurityGroup._from_db_object( + security_group.SecurityGroup(), fake_secgroup) + secgroup.refresh(ctxt) + self.assertEqual(self._fix_deleted(updated_secgroup), + dict(secgroup.items())) + self.assertEqual(secgroup.obj_what_changed(), set()) + self.assertRemotes() + + +class TestSecurityGroupObject(test_objects._LocalTest, + _TestSecurityGroupObject): + pass + + +class TestSecurityGroupObjectRemote(test_objects._RemoteTest, + _TestSecurityGroupObject): + pass + + +fake_secgroups = [ + dict(fake_secgroup, id=1, name='secgroup1'), + dict(fake_secgroup, id=2, name='secgroup2'), + ] + + +class _TestSecurityGroupListObject(object): + def test_get_all(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_get_all') + db.security_group_get_all(ctxt).AndReturn(fake_secgroups) + self.mox.ReplayAll() + secgroup_list = security_group.SecurityGroupList.get_all(ctxt) + for i in range(len(fake_secgroups)): + self.assertTrue(isinstance(secgroup_list[i], + security_group.SecurityGroup)) + self.assertEqual(fake_secgroups[i]['id'], + secgroup_list[i]['id']) + self.assertEqual(secgroup_list[i]._context, ctxt) + + def test_get_by_project(self): + ctxt = context.get_admin_context() + self.mox.StubOutWithMock(db, 'security_group_get_by_project') + db.security_group_get_by_project(ctxt, + 'fake-project').AndReturn( + fake_secgroups) + self.mox.ReplayAll() + secgroup_list = security_group.SecurityGroupList.get_by_project( + ctxt, 'fake-project') + for i in range(len(fake_secgroups)): + self.assertTrue(isinstance(secgroup_list[i], + security_group.SecurityGroup)) + self.assertEqual(fake_secgroups[i]['id'], + secgroup_list[i]['id']) + + def test_get_by_instance(self): + ctxt = context.get_admin_context() + + inst = instance.Instance() + inst.uuid = 'fake-inst-uuid' + self.mox.StubOutWithMock(db, 'security_group_get_by_instance') + db.security_group_get_by_instance(ctxt, + 'fake-inst-uuid').AndReturn( + fake_secgroups) + self.mox.ReplayAll() + secgroup_list = security_group.SecurityGroupList.get_by_instance( + ctxt, inst) + for i in range(len(fake_secgroups)): + self.assertTrue(isinstance(secgroup_list[i], + security_group.SecurityGroup)) + self.assertEqual(fake_secgroups[i]['id'], + secgroup_list[i]['id']) + + +class TestSecurityGroupListObject(test_objects._LocalTest, + _TestSecurityGroupListObject): + pass + + +class TestSecurityGroupListObjectRemote(test_objects._RemoteTest, + _TestSecurityGroupListObject): + pass diff --git a/nova/tests/scheduler/test_chance_scheduler.py b/nova/tests/scheduler/test_chance_scheduler.py index cfe7f5d63..ba1701e93 100644 --- a/nova/tests/scheduler/test_chance_scheduler.py +++ b/nova/tests/scheduler/test_chance_scheduler.py @@ -68,15 +68,11 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase): def test_basic_schedule_run_instance(self): ctxt = context.RequestContext('fake', 'fake', False) ctxt_elevated = 'fake-context-elevated' - fake_args = (1, 2, 3) instance_opts = {'fake_opt1': 'meow', 'launch_index': -1} instance1 = {'uuid': 'fake-uuid1'} instance2 = {'uuid': 'fake-uuid2'} request_spec = {'instance_uuids': ['fake-uuid1', 'fake-uuid2'], 'instance_properties': instance_opts} - instance1_encoded = {'uuid': 'fake-uuid1', '_is_precooked': False} - instance2_encoded = {'uuid': 'fake-uuid2', '_is_precooked': False} - reservations = ['resv1', 'resv2'] def inc_launch_index(*args): request_spec['instance_properties']['launch_index'] = ( @@ -118,7 +114,6 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase): def test_basic_schedule_run_instance_no_hosts(self): ctxt = context.RequestContext('fake', 'fake', False) ctxt_elevated = 'fake-context-elevated' - fake_args = (1, 2, 3) uuid = 'fake-uuid1' instance_opts = {'fake_opt1': 'meow', 'launch_index': -1} request_spec = {'instance_uuids': [uuid], @@ -170,10 +165,7 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase): def test_select_hosts(self): ctxt = context.RequestContext('fake', 'fake', False) ctxt_elevated = 'fake-context-elevated' - fake_args = (1, 2, 3) instance_opts = {'fake_opt1': 'meow', 'launch_index': -1} - instance1 = {'uuid': 'fake-uuid1'} - instance2 = {'uuid': 'fake-uuid2'} request_spec = {'instance_uuids': ['fake-uuid1', 'fake-uuid2'], 'instance_properties': instance_opts} diff --git a/nova/tests/scheduler/test_filter_scheduler.py b/nova/tests/scheduler/test_filter_scheduler.py index d6cc7808e..ac2e73ec7 100644 --- a/nova/tests/scheduler/test_filter_scheduler.py +++ b/nova/tests/scheduler/test_filter_scheduler.py @@ -25,12 +25,10 @@ from nova.conductor import api as conductor_api from nova import context from nova import db from nova import exception -from nova.openstack.common import rpc from nova.scheduler import driver from nova.scheduler import filter_scheduler from nova.scheduler import host_manager from nova.scheduler import weights -from nova import servicegroup from nova.tests.scheduler import fakes from nova.tests.scheduler import test_scheduler @@ -393,143 +391,6 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.assertEqual([['host', 'node']], filter_properties['retry']['hosts']) - def test_live_migration_dest_check_service_memory_overcommit(self): - instance = self._live_migration_instance() - - # Live-migration should work since default is to overcommit memory. - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - self.mox.StubOutWithMock(self.driver, '_get_compute_info') - self.mox.StubOutWithMock(self.driver, '_live_migration_common_check') - self.mox.StubOutWithMock(rpc, 'call') - self.mox.StubOutWithMock(self.driver.compute_rpcapi, 'live_migration') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - - self.driver._live_migration_src_check(self.context, instance) - db.service_get_by_compute_host(self.context, - dest).AndReturn('fake_service3') - self.servicegroup_api.service_is_up('fake_service3').AndReturn(True) - - self.driver._get_compute_info(self.context, dest).AndReturn( - {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': 512, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None}) - - self.driver._live_migration_common_check(self.context, instance, dest) - - rpc.call(self.context, "compute.fake_host2", - {"method": 'check_can_live_migrate_destination', - "namespace": None, - "args": {'instance': instance, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit}, - "version": compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}, - None).AndReturn({}) - - self.driver.compute_rpcapi.live_migration(self.context, - host=instance['host'], instance=instance, dest=dest, - block_migration=block_migration, migrate_data={}) - - self.mox.ReplayAll() - result = self.driver.schedule_live_migration(self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - self.assertEqual(result, None) - - def test_live_migration_assert_memory_no_overcommit(self): - # Test that memory check passes with no memory overcommit. - def fake_get(context, host): - return {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': 1024, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None} - - self.stubs.Set(self.driver, '_get_compute_info', fake_get) - - self.flags(ram_allocation_ratio=1.0) - instance = self._live_migration_instance() - dest = 'fake_host2' - result = self.driver._assert_compute_node_has_enough_memory( - self.context, instance, dest) - self.assertEqual(result, None) - - def test_live_migration_assert_memory_no_overcommit_lack_memory(self): - # Test that memory check fails with no memory overcommit. - def fake_get(context, host): - return {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': 1023, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None} - - self.stubs.Set(self.driver, '_get_compute_info', fake_get) - - self.flags(ram_allocation_ratio=1.0) - instance = self._live_migration_instance() - dest = 'fake_host2' - self.assertRaises(exception.MigrationError, - self.driver._assert_compute_node_has_enough_memory, - context, instance, dest) - - def test_live_migration_assert_memory_overcommit(self): - # Test that memory check passes with memory overcommit. - def fake_get(context, host): - return {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': -1024, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None} - - self.stubs.Set(self.driver, '_get_compute_info', fake_get) - - self.flags(ram_allocation_ratio=2.0) - instance = self._live_migration_instance() - dest = 'fake_host2' - result = self.driver._assert_compute_node_has_enough_memory( - self.context, instance, dest) - self.assertEqual(result, None) - - def test_live_migration_assert_memory_overcommit_lack_memory(self): - # Test that memory check fails with memory overcommit. - def fake_get(context, host): - return {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': -1025, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None} - - self.stubs.Set(self.driver, '_get_compute_info', fake_get) - - self.flags(ram_allocation_ratio=2.0) - instance = self._live_migration_instance() - dest = 'fake_host2' - self.assertRaises(exception.MigrationError, - self.driver._assert_compute_node_has_enough_memory, - self.context, instance, dest) - def test_basic_schedule_run_instances_anti_affinity(self): filter_properties = {'scheduler_hints': {'group': 'cats'}} diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index b09e23f1d..8832f5c2b 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -316,9 +316,6 @@ class HostFiltersTestCase(test.NoDBTestCase): def test_affinity_different_filter_handles_none(self): filt_cls = self.class_map['DifferentHostFilter']() host = fakes.FakeHostState('host1', 'node1', {}) - instance = fakes.FakeInstance(context=self.context, - params={'host': 'host2'}) - instance_uuid = instance.uuid filter_properties = {'context': self.context.elevated(), 'scheduler_hints': None} @@ -381,9 +378,6 @@ class HostFiltersTestCase(test.NoDBTestCase): def test_affinity_same_filter_handles_none(self): filt_cls = self.class_map['SameHostFilter']() host = fakes.FakeHostState('host1', 'node1', {}) - instance = fakes.FakeInstance(context=self.context, - params={'host': 'host2'}) - instance_uuid = instance.uuid filter_properties = {'context': self.context.elevated(), 'scheduler_hints': None} @@ -542,6 +536,64 @@ class HostFiltersTestCase(test.NoDBTestCase): self.assertTrue(filt_cls.host_passes(host, filter_properties)) self.assertEqual(2048 * 2.0, host.limits['memory_mb']) + def test_aggregate_ram_filter_value_error(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['AggregateRamFilter']() + self.flags(ram_allocation_ratio=1.0) + filter_properties = {'context': self.context, + 'instance_type': {'memory_mb': 1024}} + capabilities = {'enabled': True} + service = {'disabled': False} + host = fakes.FakeHostState('host1', 'node1', + {'free_ram_mb': 1024, 'total_usable_ram_mb': 1024, + 'capabilities': capabilities, 'service': service}) + self._create_aggregate_with_host(name='fake_aggregate', + hosts=['host1'], + metadata={'ram_allocation_ratio': 'XXX'}) + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(1024 * 1.0, host.limits['memory_mb']) + + def test_aggregate_ram_filter_default_value(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['AggregateRamFilter']() + self.flags(ram_allocation_ratio=1.0) + filter_properties = {'context': self.context, + 'instance_type': {'memory_mb': 1024}} + capabilities = {'enabled': True} + service = {'disabled': False} + host = fakes.FakeHostState('host1', 'node1', + {'free_ram_mb': 1023, 'total_usable_ram_mb': 1024, + 'capabilities': capabilities, 'service': service}) + # False: fallback to default flag w/o aggregates + self.assertFalse(filt_cls.host_passes(host, filter_properties)) + self._create_aggregate_with_host(name='fake_aggregate', + hosts=['host1'], + metadata={'ram_allocation_ratio': '2.0'}) + # True: use ratio from aggregates + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(1024 * 2.0, host.limits['memory_mb']) + + def test_aggregate_ram_filter_conflict_values(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['AggregateRamFilter']() + self.flags(ram_allocation_ratio=1.0) + filter_properties = {'context': self.context, + 'instance_type': {'memory_mb': 1024}} + capabilities = {'enabled': True} + service = {'disabled': False} + host = fakes.FakeHostState('host1', 'node1', + {'free_ram_mb': 1023, 'total_usable_ram_mb': 1024, + 'capabilities': capabilities, 'service': service}) + self._create_aggregate_with_host(name='fake_aggregate1', + hosts=['host1'], + metadata={'ram_allocation_ratio': '1.5'}) + self._create_aggregate_with_host(name='fake_aggregate2', + hosts=['host1'], + metadata={'ram_allocation_ratio': '2.0'}) + # use the minimum ratio from aggregates + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(1024 * 1.5, host.limits['memory_mb']) + def test_disk_filter_passes(self): self._stub_service_is_up(True) filt_cls = self.class_map['DiskFilter']() @@ -708,6 +760,9 @@ class HostFiltersTestCase(test.NoDBTestCase): self.assertFalse(filt_cls.host_passes(host, filter_properties)) def _do_test_compute_filter_extra_specs(self, ecaps, especs, passes): + """In real Openstack runtime environment,compute capabilities + value may be number, so we should use number to do unit test. + """ self._stub_service_is_up(True) filt_cls = self.class_map['ComputeCapabilitiesFilter']() capabilities = {'enabled': True} @@ -723,33 +778,33 @@ class HostFiltersTestCase(test.NoDBTestCase): def test_compute_filter_passes_extra_specs_simple(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'opt1': '1', 'opt2': '2', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_fails_extra_specs_simple(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'opt1': '1', 'opt2': '222', 'trust:trusted_host': 'true'}, passes=False) def test_compute_filter_pass_extra_specs_simple_with_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'capabilities:opt1': '1', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_extra_specs_simple_with_wrong_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'wrong_scope:opt1': '1', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_extra_specs_pass_multi_level_with_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': {'a': '1', 'b': {'aa': '2'}}, 'opt2': '2'}, + ecaps={'opt1': {'a': 1, 'b': {'aa': 2}}, 'opt2': 2}, especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2', 'trust:trusted_host': 'true'}, passes=True) @@ -950,7 +1005,6 @@ class HostFiltersTestCase(test.NoDBTestCase): 'local_gb': 200}, 'scheduler_hints': {'query': json_query}} capabilities = {'enabled': True} - service = {'disabled': True} host = fakes.FakeHostState('host1', 'node1', {'free_ram_mb': 1024, 'free_disk_mb': 200 * 1024, @@ -1314,6 +1368,52 @@ class HostFiltersTestCase(test.NoDBTestCase): {'vcpus_total': 4, 'vcpus_used': 8}) self.assertFalse(filt_cls.host_passes(host, filter_properties)) + def test_aggregate_core_filter_value_error(self): + filt_cls = self.class_map['AggregateCoreFilter']() + filter_properties = {'context': self.context, + 'instance_type': {'vcpus': 1}} + self.flags(cpu_allocation_ratio=2) + host = fakes.FakeHostState('host1', 'node1', + {'vcpus_total': 4, 'vcpus_used': 7}) + self._create_aggregate_with_host(name='fake_aggregate', + hosts=['host1'], + metadata={'cpu_allocation_ratio': 'XXX'}) + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(4 * 2, host.limits['vcpu']) + + def test_aggregate_core_filter_default_value(self): + filt_cls = self.class_map['AggregateCoreFilter']() + filter_properties = {'context': self.context, + 'instance_type': {'vcpus': 1}} + self.flags(cpu_allocation_ratio=2) + host = fakes.FakeHostState('host1', 'node1', + {'vcpus_total': 4, 'vcpus_used': 8}) + # False: fallback to default flag w/o aggregates + self.assertFalse(filt_cls.host_passes(host, filter_properties)) + self._create_aggregate_with_host(name='fake_aggregate', + hosts=['host1'], + metadata={'cpu_allocation_ratio': '3'}) + # True: use ratio from aggregates + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(4 * 3, host.limits['vcpu']) + + def test_aggregate_core_filter_conflict_values(self): + filt_cls = self.class_map['AggregateCoreFilter']() + filter_properties = {'context': self.context, + 'instance_type': {'vcpus': 1}} + self.flags(cpu_allocation_ratio=1) + host = fakes.FakeHostState('host1', 'node1', + {'vcpus_total': 4, 'vcpus_used': 8}) + self._create_aggregate_with_host(name='fake_aggregate1', + hosts=['host1'], + metadata={'cpu_allocation_ratio': '2'}) + self._create_aggregate_with_host(name='fake_aggregate2', + hosts=['host1'], + metadata={'cpu_allocation_ratio': '3'}) + # use the minimum ratio from aggregates + self.assertFalse(filt_cls.host_passes(host, filter_properties)) + self.assertEqual(4 * 2, host.limits['vcpu']) + @staticmethod def _make_zone_request(zone, is_admin=False): ctxt = context.RequestContext('fake', 'fake', is_admin=is_admin) @@ -1382,6 +1482,8 @@ class HostFiltersTestCase(test.NoDBTestCase): filt_cls = self.class_map['IoOpsFilter']() host = fakes.FakeHostState('host1', 'node1', {'num_io_ops': 8}) + filter_properties = {} + self.assertFalse(filt_cls.host_passes(host, filter_properties)) def test_filter_num_instances_passes(self): self.flags(max_instances_per_host=5) diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index 0574f6d2e..149c6d3c8 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -22,20 +22,16 @@ Tests For Scheduler import mox from nova.compute import api as compute_api -from nova.compute import flavors -from nova.compute import power_state -from nova.compute import rpcapi as compute_rpcapi from nova.compute import task_states from nova.compute import utils as compute_utils from nova.compute import vm_states from nova.conductor import api as conductor_api +from nova.conductor.tasks import live_migrate from nova import context from nova import db from nova import exception from nova.image import glance -from nova.openstack.common import jsonutils from nova.openstack.common.notifier import api as notifier -from nova.openstack.common import rpc from nova.openstack.common.rpc import common as rpc_common from nova.scheduler import driver from nova.scheduler import manager @@ -45,7 +41,6 @@ from nova.tests import fake_instance_actions from nova.tests.image import fake as fake_image from nova.tests import matchers from nova.tests.scheduler import fakes -from nova import utils class SchedulerManagerTestCase(test.NoDBTestCase): @@ -88,7 +83,7 @@ class SchedulerManagerTestCase(test.NoDBTestCase): self.manager.driver.update_service_capabilities(service_name, host, {}) self.mox.ReplayAll() - result = self.manager.update_service_capabilities(self.context, + self.manager.update_service_capabilities(self.context, service_name=service_name, host=host, capabilities={}) self.mox.VerifyAll() @@ -98,7 +93,7 @@ class SchedulerManagerTestCase(test.NoDBTestCase): self.manager.driver.update_service_capabilities( service_name, host, capabilities) self.mox.ReplayAll() - result = self.manager.update_service_capabilities(self.context, + self.manager.update_service_capabilities(self.context, service_name=service_name, host=host, capabilities=capabilities) @@ -220,11 +215,11 @@ class SchedulerManagerTestCase(test.NoDBTestCase): block_migration = False disk_over_commit = False - self._mox_schedule_method_helper('schedule_live_migration') + self.mox.StubOutWithMock(self.manager, '_schedule_live_migration') self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.manager.driver.schedule_live_migration(self.context, + self.manager._schedule_live_migration(self.context, inst, dest, block_migration, disk_over_commit).AndRaise( exception.NoValidHost(reason="")) db.instance_update_and_get_original(self.context, inst["uuid"], @@ -253,11 +248,11 @@ class SchedulerManagerTestCase(test.NoDBTestCase): block_migration = False disk_over_commit = False - self._mox_schedule_method_helper('schedule_live_migration') + self.mox.StubOutWithMock(self.manager, '_schedule_live_migration') self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.manager.driver.schedule_live_migration(self.context, + self.manager._schedule_live_migration(self.context, inst, dest, block_migration, disk_over_commit).AndRaise( exception.ComputeServiceUnavailable(host="src")) db.instance_update_and_get_original(self.context, inst["uuid"], @@ -277,6 +272,17 @@ class SchedulerManagerTestCase(test.NoDBTestCase): self.context, inst, dest, block_migration, disk_over_commit) + def test_live_migrate(self): + instance = {'host': 'h'} + self.mox.StubOutClassWithMocks(live_migrate, "LiveMigrationTask") + task = live_migrate.LiveMigrationTask(self.context, instance, + "dest", "bm", "doc", self.manager.driver.select_hosts) + task.execute() + + self.mox.ReplayAll() + self.manager.live_migration(self.context, instance, "dest", + "bm", "doc") + def test_live_migration_set_vmstate_error(self): inst = {"uuid": "fake-instance-id", "vm_state": vm_states.ACTIVE, } @@ -285,11 +291,11 @@ class SchedulerManagerTestCase(test.NoDBTestCase): block_migration = False disk_over_commit = False - self._mox_schedule_method_helper('schedule_live_migration') + self.mox.StubOutWithMock(self.manager, '_schedule_live_migration') self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.manager.driver.schedule_live_migration(self.context, + self.manager._schedule_live_migration(self.context, inst, dest, block_migration, disk_over_commit).AndRaise( ValueError) db.instance_update_and_get_original(self.context, inst["uuid"], @@ -402,6 +408,17 @@ class SchedulerManagerTestCase(test.NoDBTestCase): self.manager._set_vm_state_and_notify('foo', {'vm_state': 'foo'}, self.context, None, request) + def test_select_hosts_throws_rpc_clientexception(self): + self.mox.StubOutWithMock(self.manager.driver, 'select_hosts') + + self.manager.driver.select_hosts(self.context, {}, {}).AndRaise( + exception.NoValidHost(reason="")) + + self.mox.ReplayAll() + self.assertRaises(rpc_common.ClientException, + self.manager.select_hosts, + self.context, {}, {}) + class SchedulerTestCase(test.NoDBTestCase): """Test case for base scheduler driver class.""" @@ -444,7 +461,7 @@ class SchedulerTestCase(test.NoDBTestCase): self.driver.host_manager.update_service_capabilities( service_name, host, capabilities) self.mox.ReplayAll() - result = self.driver.update_service_capabilities(service_name, + self.driver.update_service_capabilities(service_name, host, capabilities) def test_hosts_up(self): @@ -464,482 +481,6 @@ class SchedulerTestCase(test.NoDBTestCase): result = self.driver.hosts_up(self.context, self.topic) self.assertEqual(result, ['host2']) - def _live_migration_instance(self): - inst_type = {'memory_mb': 1024, 'root_gb': 40, 'deleted_at': None, - 'name': u'm1.medium', 'deleted': 0, 'created_at': None, - 'ephemeral_gb': 0, 'updated_at': None, 'disabled': False, - 'vcpus': 2, 'extra_specs': {}, 'swap': 0, - 'rxtx_factor': 1.0, 'is_public': True, 'flavorid': u'3', - 'vcpu_weight': None, 'id': 1} - - sys_meta = utils.dict_to_metadata( - flavors.save_flavor_info({}, inst_type)) - return {'id': 31337, - 'uuid': 'fake_uuid', - 'name': 'fake-instance', - 'host': 'fake_host1', - 'power_state': power_state.RUNNING, - 'memory_mb': 1024, - 'root_gb': 1024, - 'ephemeral_gb': 0, - 'vm_state': '', - 'task_state': '', - 'instance_type_id': inst_type['id'], - 'image_ref': 'fake-image-ref', - 'system_metadata': sys_meta} - - def test_live_migration_basic(self): - # Test basic schedule_live_migration functionality. - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(self.driver, '_live_migration_dest_check') - self.mox.StubOutWithMock(self.driver, '_live_migration_common_check') - self.mox.StubOutWithMock(self.driver.compute_rpcapi, - 'check_can_live_migrate_destination') - self.mox.StubOutWithMock(self.driver.compute_rpcapi, - 'live_migration') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = jsonutils.to_primitive(self._live_migration_instance()) - - self.driver._live_migration_src_check(self.context, instance) - self.driver._live_migration_dest_check(self.context, instance, - dest).AndReturn(dest) - self.driver._live_migration_common_check(self.context, instance, - dest) - self.driver.compute_rpcapi.check_can_live_migrate_destination( - self.context, instance, dest, block_migration, - disk_over_commit).AndReturn({}) - self.driver.compute_rpcapi.live_migration(self.context, - host=instance['host'], instance=instance, dest=dest, - block_migration=block_migration, migrate_data={}) - - self.mox.ReplayAll() - self.driver.schedule_live_migration(self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_all_checks_pass(self): - # Test live migration when all checks pass. - - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - self.mox.StubOutWithMock(rpc, 'call') - self.mox.StubOutWithMock(self.driver.compute_rpcapi, - 'live_migration') - - dest = 'fake_host2' - block_migration = True - disk_over_commit = True - instance = jsonutils.to_primitive(self._live_migration_instance()) - - # Source checks - db.service_get_by_compute_host(self.context, - instance['host']).AndReturn('fake_service2') - self.servicegroup_api.service_is_up('fake_service2').AndReturn(True) - - # Destination checks (compute is up, enough memory, disk) - db.service_get_by_compute_host(self.context, - dest).AndReturn('fake_service3') - self.servicegroup_api.service_is_up('fake_service3').AndReturn(True) - # assert_compute_node_has_enough_memory() - db.service_get_by_compute_host(self.context, dest).AndReturn( - {'compute_node': [{'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': 1280, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None, - 'hypervisor_version': 1}]}) - - # Common checks (same hypervisor, etc) - db.service_get_by_compute_host(self.context, dest).AndReturn( - {'compute_node': [{'hypervisor_type': 'xen', - 'hypervisor_version': 1}]}) - db.service_get_by_compute_host(self.context, - instance['host']).AndReturn( - {'compute_node': [{'hypervisor_type': 'xen', - 'hypervisor_version': 1, - 'cpu_info': 'fake_cpu_info'}]}) - - rpc.call(self.context, "compute.fake_host2", - {"method": 'check_can_live_migrate_destination', - "namespace": None, - "args": {'instance': instance, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit}, - "version": compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}, - None).AndReturn({}) - - self.driver.compute_rpcapi.live_migration(self.context, - host=instance['host'], instance=instance, dest=dest, - block_migration=block_migration, migrate_data={}) - - self.mox.ReplayAll() - result = self.driver.schedule_live_migration(self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - self.assertEqual(result, None) - - def test_live_migration_instance_not_running(self): - # The instance given by instance_id is not running. - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - instance['power_state'] = power_state.NOSTATE - - self.assertRaises(exception.InstanceNotRunning, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_compute_src_not_exist(self): - # Raise exception when src compute node is does not exist. - - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - # Compute down - db.service_get_by_compute_host(self.context, - instance['host']).AndRaise( - exception.ComputeHostNotFound(host='fake')) - - self.mox.ReplayAll() - self.assertRaises(exception.ComputeServiceUnavailable, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_compute_src_not_alive(self): - # Raise exception when src compute node is not alive. - - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - # Compute down - db.service_get_by_compute_host(self.context, - instance['host']).AndReturn('fake_service2') - self.servicegroup_api.service_is_up('fake_service2').AndReturn(False) - - self.mox.ReplayAll() - self.assertRaises(exception.ComputeServiceUnavailable, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_compute_dest_not_exist(self): - # Raise exception when dest compute node does not exist. - - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - self.driver._live_migration_src_check(self.context, instance) - # Compute down - db.service_get_by_compute_host(self.context, - dest).AndRaise(exception.NotFound()) - - self.mox.ReplayAll() - self.assertRaises(exception.ComputeServiceUnavailable, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_compute_dest_not_alive(self): - # Raise exception when dest compute node is not alive. - - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - self.driver._live_migration_src_check(self.context, instance) - db.service_get_by_compute_host(self.context, - dest).AndReturn('fake_service3') - # Compute is down - self.servicegroup_api.service_is_up('fake_service3').AndReturn(False) - - self.mox.ReplayAll() - self.assertRaises(exception.ComputeServiceUnavailable, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_dest_check_service_same_host(self): - # Confirms exception raises in case dest and src is same host. - - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - block_migration = False - instance = self._live_migration_instance() - # make dest same as src - dest = instance['host'] - - self.driver._live_migration_src_check(self.context, instance) - - self.mox.ReplayAll() - self.assertRaises(exception.UnableToMigrateToSelf, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=False) - - def test_live_migration_dest_check_service_lack_memory(self): - # Confirms exception raises when dest doesn't have enough memory. - - # Flag needed to make FilterScheduler test hit memory limit since the - # default for it is to allow memory overcommit by a factor of 1.5. - self.flags(ram_allocation_ratio=1.0) - - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - self.mox.StubOutWithMock(servicegroup.API, 'service_is_up') - self.mox.StubOutWithMock(self.driver, '_get_compute_info') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - self.driver._live_migration_src_check(self.context, instance) - db.service_get_by_compute_host(self.context, - dest).AndReturn('fake_service3') - self.servicegroup_api.service_is_up('fake_service3').AndReturn(True) - - self.driver._get_compute_info(self.context, dest).AndReturn( - {'memory_mb': 2048, - 'free_disk_gb': 512, - 'local_gb_used': 512, - 'free_ram_mb': 512, - 'local_gb': 1024, - 'vcpus': 4, - 'vcpus_used': 2, - 'updated_at': None}) - - self.mox.ReplayAll() - self.assertRaises(exception.MigrationError, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_different_hypervisor_type_raises(self): - # Confirm live_migration to hypervisor of different type raises. - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(self.driver, '_live_migration_dest_check') - self.mox.StubOutWithMock(rpc, 'queue_get_for') - self.mox.StubOutWithMock(rpc, 'call') - self.mox.StubOutWithMock(rpc, 'cast') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - self.driver._live_migration_src_check(self.context, instance) - self.driver._live_migration_dest_check(self.context, instance, - dest).AndReturn(dest) - - db.service_get_by_compute_host(self.context, dest).AndReturn( - {'compute_node': [{'hypervisor_type': 'xen', - 'hypervisor_version': 1}]}) - db.service_get_by_compute_host(self.context, - instance['host']).AndReturn( - {'compute_node': [{'hypervisor_type': 'not-xen', - 'hypervisor_version': 1}]}) - - self.mox.ReplayAll() - self.assertRaises(exception.InvalidHypervisorType, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_dest_hypervisor_version_older_raises(self): - # Confirm live migration to older hypervisor raises. - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(self.driver, '_live_migration_dest_check') - self.mox.StubOutWithMock(rpc, 'queue_get_for') - self.mox.StubOutWithMock(rpc, 'call') - self.mox.StubOutWithMock(rpc, 'cast') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - - dest = 'fake_host2' - block_migration = False - disk_over_commit = False - instance = self._live_migration_instance() - - self.driver._live_migration_src_check(self.context, instance) - self.driver._live_migration_dest_check(self.context, instance, - dest).AndReturn(dest) - - db.service_get_by_compute_host(self.context, dest).AndReturn( - {'compute_node': [{'hypervisor_type': 'xen', - 'hypervisor_version': 1}]}) - db.service_get_by_compute_host(self.context, - instance['host']).AndReturn( - {'compute_node': [{'hypervisor_type': 'xen', - 'hypervisor_version': 2}]}) - self.mox.ReplayAll() - self.assertRaises(exception.DestinationHypervisorTooOld, - self.driver.schedule_live_migration, self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - - def test_live_migration_dest_check_auto_set_host(self): - instance = self._live_migration_instance() - - # Confirm dest is picked by scheduler if not set. - self.mox.StubOutWithMock(self.driver, 'select_hosts') - self.mox.StubOutWithMock(flavors, 'extract_flavor') - - request_spec = {'instance_properties': instance, - 'instance_type': {}, - 'instance_uuids': [instance['uuid']], - 'image': self.image_service.show(self.context, - instance['image_ref']) - } - ignore_hosts = [instance['host']] - filter_properties = {'ignore_hosts': ignore_hosts} - - flavors.extract_flavor(instance).AndReturn({}) - self.driver.select_hosts(self.context, request_spec, - filter_properties).AndReturn(['fake_host2']) - - self.mox.ReplayAll() - result = self.driver._live_migration_dest_check(self.context, instance, - None, ignore_hosts) - self.assertEqual('fake_host2', result) - - def test_live_migration_dest_check_no_image(self): - instance = self._live_migration_instance() - instance['image_ref'] = '' - - # Confirm dest is picked by scheduler if not set. - self.mox.StubOutWithMock(self.driver, 'select_hosts') - self.mox.StubOutWithMock(flavors, 'extract_flavor') - - request_spec = {'instance_properties': instance, - 'instance_type': {}, - 'instance_uuids': [instance['uuid']], - 'image': None - } - ignore_hosts = [instance['host']] - filter_properties = {'ignore_hosts': ignore_hosts} - - flavors.extract_flavor(instance).AndReturn({}) - self.driver.select_hosts(self.context, request_spec, - filter_properties).AndReturn(['fake_host2']) - - self.mox.ReplayAll() - result = self.driver._live_migration_dest_check(self.context, instance, - None, ignore_hosts) - self.assertEqual('fake_host2', result) - - def test_live_migration_auto_set_dest(self): - instance = self._live_migration_instance() - - # Confirm scheduler picks target host if none given. - self.mox.StubOutWithMock(flavors, 'extract_flavor') - self.mox.StubOutWithMock(self.driver, '_live_migration_src_check') - self.mox.StubOutWithMock(self.driver, 'select_hosts') - self.mox.StubOutWithMock(self.driver, '_live_migration_common_check') - self.mox.StubOutWithMock(rpc, 'call') - self.mox.StubOutWithMock(self.driver.compute_rpcapi, 'live_migration') - - dest = None - block_migration = False - disk_over_commit = False - request_spec = {'instance_properties': instance, - 'instance_type': {}, - 'instance_uuids': [instance['uuid']], - 'image': self.image_service.show(self.context, - instance['image_ref']) - } - - self.driver._live_migration_src_check(self.context, instance) - - flavors.extract_flavor( - instance).MultipleTimes().AndReturn({}) - - # First selected host raises exception.InvalidHypervisorType - self.driver.select_hosts(self.context, request_spec, - {'ignore_hosts': [instance['host']]}).AndReturn(['fake_host2']) - self.driver._live_migration_common_check(self.context, instance, - 'fake_host2').AndRaise(exception.InvalidHypervisorType()) - - # Second selected host raises exception.InvalidCPUInfo - self.driver.select_hosts(self.context, request_spec, - {'ignore_hosts': [instance['host'], - 'fake_host2']}).AndReturn(['fake_host3']) - self.driver._live_migration_common_check(self.context, instance, - 'fake_host3') - rpc.call(self.context, "compute.fake_host3", - {"method": 'check_can_live_migrate_destination', - "namespace": None, - "args": {'instance': instance, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit}, - "version": compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}, - None).AndRaise(exception.InvalidCPUInfo(reason="")) - - # Third selected host pass all checks - self.driver.select_hosts(self.context, request_spec, - {'ignore_hosts': [instance['host'], - 'fake_host2', - 'fake_host3']}).AndReturn(['fake_host4']) - self.driver._live_migration_common_check(self.context, instance, - 'fake_host4') - rpc.call(self.context, "compute.fake_host4", - {"method": 'check_can_live_migrate_destination', - "namespace": None, - "args": {'instance': instance, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit}, - "version": compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}, - None).AndReturn({}) - self.driver.compute_rpcapi.live_migration(self.context, - host=instance['host'], instance=instance, dest='fake_host4', - block_migration=block_migration, migrate_data={}) - - self.mox.ReplayAll() - result = self.driver.schedule_live_migration(self.context, - instance=instance, dest=dest, - block_migration=block_migration, - disk_over_commit=disk_over_commit) - self.assertEqual(result, None) - def test_handle_schedule_error_adds_instance_fault(self): instance = {'uuid': 'fake-uuid'} self.mox.StubOutWithMock(db, 'instance_update_and_get_original') @@ -961,12 +502,10 @@ class SchedulerTestCase(test.NoDBTestCase): class SchedulerDriverBaseTestCase(SchedulerTestCase): """Test cases for base scheduler driver class methods - that can't will fail if the driver is changed. + that will fail if the driver is changed. """ def test_unimplemented_schedule_run_instance(self): - fake_args = (1, 2, 3) - fake_kwargs = {'cat': 'meow'} fake_request_spec = {'instance_properties': {'uuid': 'uuid'}} @@ -976,8 +515,6 @@ class SchedulerDriverBaseTestCase(SchedulerTestCase): None, None) def test_unimplemented_schedule_prep_resize(self): - fake_args = (1, 2, 3) - fake_kwargs = {'cat': 'meow'} fake_request_spec = {'instance_properties': {'uuid': 'uuid'}} diff --git a/nova/tests/scheduler/test_scheduler_options.py b/nova/tests/scheduler/test_scheduler_options.py index 6ab245ef9..eb7ccfeec 100644 --- a/nova/tests/scheduler/test_scheduler_options.py +++ b/nova/tests/scheduler/test_scheduler_options.py @@ -71,7 +71,6 @@ class SchedulerOptionsTestCase(test.NoDBTestCase): file_old = None file_now = datetime.datetime(2012, 1, 1, 1, 1, 1) - data = dict(a=1, b=2, c=3) jdata = "" fake = FakeSchedulerOptions(last_checked, now, file_old, file_now, diff --git a/nova/tests/servicegroup/test_mc_servicegroup.py b/nova/tests/servicegroup/test_mc_servicegroup.py index 9d5601ff8..9f27ecc31 100644 --- a/nova/tests/servicegroup/test_mc_servicegroup.py +++ b/nova/tests/servicegroup/test_mc_servicegroup.py @@ -103,15 +103,9 @@ class MemcachedServiceGroupTestCase(test.TestCase): ServiceFixture(host3, self._binary, self._topic)).serv serv3.start() - service_ref1 = db.service_get_by_args(self._ctx, - host1, - self._binary) - service_ref2 = db.service_get_by_args(self._ctx, - host2, - self._binary) - service_ref3 = db.service_get_by_args(self._ctx, - host3, - self._binary) + db.service_get_by_args(self._ctx, host1, self._binary) + db.service_get_by_args(self._ctx, host2, self._binary) + db.service_get_by_args(self._ctx, host3, self._binary) host1key = str("%s:%s" % (self._topic, host1)) host2key = str("%s:%s" % (self._topic, host2)) @@ -198,9 +192,7 @@ class MemcachedServiceGroupTestCase(test.TestCase): serv = self.useFixture( ServiceFixture(self._host, self._binary, self._topic)).serv serv.start() - service_ref = db.service_get_by_args(self._ctx, - self._host, - self._binary) + db.service_get_by_args(self._ctx, self._host, self._binary) self.servicegroup_api = servicegroup.API() # updating model_disconnected diff --git a/nova/tests/servicegroup/test_zk_driver.py b/nova/tests/servicegroup/test_zk_driver.py index eb6ddd4bf..716f62e4e 100644 --- a/nova/tests/servicegroup/test_zk_driver.py +++ b/nova/tests/servicegroup/test_zk_driver.py @@ -40,7 +40,7 @@ class ZKServiceGroupTestCase(test.TestCase): self.flags(servicegroup_driver='zk') self.flags(address='localhost:2181', group="zookeeper") try: - _unused = zk.ZooKeeperDriver() + zk.ZooKeeperDriver() except ImportError: self.skipTest("Unable to test due to lack of ZooKeeper") diff --git a/nova/tests/test_availability_zones.py b/nova/tests/test_availability_zones.py index 3923dd9a1..0c58cd08a 100644 --- a/nova/tests/test_availability_zones.py +++ b/nova/tests/test_availability_zones.py @@ -179,8 +179,8 @@ class AvailabilityZoneTestCases(test.TestCase): disabled=False) service4 = self._create_service_with_topic('compute', 'host4', disabled=True) - service5 = self._create_service_with_topic('compute', 'host5', - disabled=True) + self._create_service_with_topic('compute', 'host5', + disabled=True) self._add_to_aggregate(service1, self.agg) self._add_to_aggregate(service2, self.agg) diff --git a/nova/tests/test_cinder.py b/nova/tests/test_cinder.py index e8dff9a4a..eefc1bab6 100644 --- a/nova/tests/test_cinder.py +++ b/nova/tests/test_cinder.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import httplib2 import urlparse from cinderclient import exceptions as cinder_exception @@ -76,9 +75,9 @@ class FakeHTTPClient(cinder.cinder_client.client.HTTPClient): status, body = getattr(self, callback)(**kwargs) if hasattr(status, 'items'): - return httplib2.Response(status), body + return status, body else: - return httplib2.Response({"status": status}), body + return {"status": status}, body def get_volumes_1234(self, **kw): volume = {'volume': _stub_volume(id='1234')} @@ -154,7 +153,7 @@ class CinderTestCase(test.TestCase): self.fake_client_factory.assert_called(*args, **kwargs) def test_context_with_catalog(self): - volume = self.api.get(self.context, '1234') + self.api.get(self.context, '1234') self.assert_called('GET', '/volumes/1234') self.assertEquals( self.fake_client_factory.client.client.management_url, @@ -164,7 +163,7 @@ class CinderTestCase(test.TestCase): self.flags( cinder_endpoint_template='http://other_host:8776/v1/%(project_id)s' ) - volume = self.api.get(self.context, '1234') + self.api.get(self.context, '1234') self.assert_called('GET', '/volumes/1234') self.assertEquals( self.fake_client_factory.client.client.management_url, @@ -184,7 +183,7 @@ class CinderTestCase(test.TestCase): # The True/False negation is awkward, but better for the client # to pass us insecure=True and we check verify_cert == False self.flags(cinder_api_insecure=True) - volume = self.api.get(self.context, '1234') + self.api.get(self.context, '1234') self.assert_called('GET', '/volumes/1234') self.assertEquals( self.fake_client_factory.client.client.verify_cert, False) @@ -192,7 +191,7 @@ class CinderTestCase(test.TestCase): def test_cinder_api_cacert_file(self): cacert = "/etc/ssl/certs/ca-certificates.crt" self.flags(cinder_ca_certificates_file=cacert) - volume = self.api.get(self.context, '1234') + self.api.get(self.context, '1234') self.assert_called('GET', '/volumes/1234') self.assertEquals( self.fake_client_factory.client.client.verify_cert, cacert) @@ -200,7 +199,7 @@ class CinderTestCase(test.TestCase): def test_cinder_http_retries(self): retries = 42 self.flags(cinder_http_retries=retries) - volume = self.api.get(self.context, '1234') + self.api.get(self.context, '1234') self.assert_called('GET', '/volumes/1234') self.assertEquals( self.fake_client_factory.client.client.retries, retries) diff --git a/nova/tests/test_flavors.py b/nova/tests/test_flavors.py index bd3f805cd..6edbddb21 100644 --- a/nova/tests/test_flavors.py +++ b/nova/tests/test_flavors.py @@ -69,11 +69,8 @@ class InstanceTypeTestCase(test.TestCase): project_id = 'fake' ctxt = context.RequestContext(user_id, project_id, is_admin=True) flavor_id = 'flavor1' - type_ref = flavors.create('some flavor', 256, 1, 120, 100, - flavorid=flavor_id) - access_ref = flavors.add_flavor_access(flavor_id, - project_id, - ctxt=ctxt) + flavors.create('some flavor', 256, 1, 120, 100, flavorid=flavor_id) + flavors.add_flavor_access(flavor_id, project_id, ctxt=ctxt) self.assertRaises(exception.FlavorAccessExists, flavors.add_flavor_access, flavor_id, project_id, ctxt) @@ -92,10 +89,8 @@ class InstanceTypeTestCase(test.TestCase): project_id = 'fake' ctxt = context.RequestContext(user_id, project_id, is_admin=True) flavor_id = 'flavor1' - type_ref = flavors.create('some flavor', 256, 1, 120, 100, - flavorid=flavor_id) - access_ref = flavors.add_flavor_access(flavor_id, project_id, - ctxt) + flavors.create('some flavor', 256, 1, 120, 100, flavorid=flavor_id) + flavors.add_flavor_access(flavor_id, project_id, ctxt) flavors.remove_flavor_access(flavor_id, project_id, ctxt) projects = flavors.get_flavor_access_by_flavor_id(flavor_id, @@ -107,8 +102,7 @@ class InstanceTypeTestCase(test.TestCase): project_id = 'fake' ctxt = context.RequestContext(user_id, project_id, is_admin=True) flavor_id = 'flavor1' - type_ref = flavors.create('some flavor', 256, 1, 120, 100, - flavorid=flavor_id) + flavors.create('some flavor', 256, 1, 120, 100, flavorid=flavor_id) self.assertRaises(exception.FlavorAccessNotFound, flavors.remove_flavor_access, flavor_id, project_id, ctxt=ctxt) diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py index 8cdc3e7af..2aece7ac8 100644 --- a/nova/tests/test_metadata.py +++ b/nova/tests/test_metadata.py @@ -71,6 +71,7 @@ INSTANCES = ( 'info_cache': {'network_info': []}, 'hostname': 'test.novadomain', 'display_name': 'my_displayname', + 'metadata': {} }, ) diff --git a/nova/tests/test_notifications.py b/nova/tests/test_notifications.py index 23fe4c82b..0d7c0e7a8 100644 --- a/nova/tests/test_notifications.py +++ b/nova/tests/test_notifications.py @@ -295,13 +295,17 @@ class NotificationsTestCase(test.TestCase): self.assertEquals(payload["access_ip_v6"], access_ip_v6) def test_send_name_update(self): - notifications.send_update(self.context, self.instance, self.instance) + param = {"display_name": "new_display_name"} + new_name_inst = self._wrapped_create(params=param) + notifications.send_update(self.context, self.instance, new_name_inst) self.assertEquals(1, len(test_notifier.NOTIFICATIONS)) notif = test_notifier.NOTIFICATIONS[0] payload = notif["payload"] - display_name = self.instance["display_name"] + old_display_name = self.instance["display_name"] + new_display_name = new_name_inst["display_name"] - self.assertEquals(payload["display_name"], display_name) + self.assertEquals(payload["old_display_name"], old_display_name) + self.assertEquals(payload["display_name"], new_display_name) def test_send_no_state_change(self): called = [False] diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index be3669958..37009f3df 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -227,7 +227,7 @@ class QuotaIntegrationTestCase(test.TestCase): timeutils.advance_time_seconds(80) - result = quota.QUOTAS.expire(self.context) + quota.QUOTAS.expire(self.context) assertInstancesReserved(0) diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index f400bb899..ada649549 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -38,41 +38,6 @@ from nova import utils CONF = cfg.CONF -class ByteConversionTest(test.TestCase): - def test_string_conversions(self): - working_examples = { - '1024KB': 1048576, - '1024TB': 1125899906842624, - '1024K': 1048576, - '1024T': 1125899906842624, - '1TB': 1099511627776, - '1T': 1099511627776, - '1KB': 1024, - '1K': 1024, - '1B': 1, - '1B': 1, - '1': 1, - '1MB': 1048576, - '7MB': 7340032, - '0MB': 0, - '0KB': 0, - '0TB': 0, - '': 0, - } - for (in_value, expected_value) in working_examples.items(): - b_value = utils.to_bytes(in_value) - self.assertEquals(expected_value, b_value) - if len(in_value): - in_value = "-" + in_value - b_value = utils.to_bytes(in_value) - self.assertEquals(expected_value * -1, b_value) - breaking_examples = [ - 'junk1KB', '1023BBBB', - ] - for v in breaking_examples: - self.assertRaises(TypeError, utils.to_bytes, v) - - class GetFromPathTestCase(test.TestCase): def test_tolerates_nones(self): f = utils.get_from_path @@ -228,6 +193,92 @@ class GetFromPathTestCase(test.TestCase): self.assertEquals(['b_1'], f(input, "a/b")) +class GetMyIP4AddressTestCase(test.TestCase): + def test_get_my_ipv4_address_with_no_ipv4(self): + response = """172.16.0.0/16 via 172.16.251.13 dev tun1 +172.16.251.1 via 172.16.251.13 dev tun1 +172.16.251.13 dev tun1 proto kernel scope link src 172.16.251.14 +172.24.0.0/16 via 172.16.251.13 dev tun1 +192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1""" + + def fake_execute(*args, **kwargs): + return response, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '127.0.0.1') + + def test_get_my_ipv4_address_bad_process(self): + def fake_execute(*args, **kwargs): + raise processutils.ProcessExecutionError() + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '127.0.0.1') + + def test_get_my_ipv4_address_with_single_interface(self): + response_route = """default via 192.168.1.1 dev wlan0 proto static +192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.137 metric 9 +""" + response_addr = """ +1: lo inet 127.0.0.1/8 scope host lo +3: wlan0 inet 192.168.1.137/24 brd 192.168.1.255 scope global wlan0 +""" + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '192.168.1.137') + + def test_get_my_ipv4_address_with_multi_ipv4_on_single_interface(self): + response_route = """ +172.18.56.0/24 dev customer proto kernel scope link src 172.18.56.22 +169.254.0.0/16 dev customer scope link metric 1031 +default via 172.18.56.1 dev customer +""" + response_addr = ("" +"31: customer inet 172.18.56.22/24 brd 172.18.56.255 scope global" +" customer\n" +"31: customer inet 172.18.56.32/24 brd 172.18.56.255 scope global " +"secondary customer") + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '172.18.56.22') + + def test_get_my_ipv4_address_with_multiple_interfaces(self): + response_route = """ +169.1.9.0/24 dev eth1 proto kernel scope link src 169.1.9.10 +172.17.248.0/21 dev eth0 proto kernel scope link src 172.17.255.9 +169.254.0.0/16 dev eth0 scope link metric 1002 +169.254.0.0/16 dev eth1 scope link metric 1003 +default via 172.17.248.1 dev eth0 proto static +""" + response_addr = """ +1: lo inet 127.0.0.1/8 scope host lo +2: eth0 inet 172.17.255.9/21 brd 172.17.255.255 scope global eth0 +3: eth1 inet 169.1.9.10/24 scope global eth1 +""" + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '172.17.255.9') + + class GenericUtilsTestCase(test.TestCase): def test_parse_server_string(self): result = utils.parse_server_string('::1') diff --git a/nova/tests/test_wsgi.py b/nova/tests/test_wsgi.py index d1d659fe3..7118aa938 100644 --- a/nova/tests/test_wsgi.py +++ b/nova/tests/test_wsgi.py @@ -23,8 +23,7 @@ import tempfile import testtools import eventlet -import httplib2 -import paste +import requests import nova.exception from nova import test @@ -119,16 +118,16 @@ class TestWSGIServer(test.TestCase): server.start() uri = "http://127.0.0.1:%d/%s" % (server.port, 10000 * 'x') - resp, _ = httplib2.Http().request(uri) + resp = requests.get(uri) eventlet.sleep(0) - self.assertNotEqual(resp.status, - paste.httpexceptions.HTTPRequestURITooLong.code) + self.assertNotEqual(resp.status_code, + requests.codes.REQUEST_URI_TOO_LARGE) uri = "http://127.0.0.1:%d/%s" % (server.port, 20000 * 'x') - resp, _ = httplib2.Http().request(uri) + resp = requests.get(uri) eventlet.sleep(0) - self.assertEqual(resp.status, - paste.httpexceptions.HTTPRequestURITooLong.code) + self.assertEqual(resp.status_code, + requests.codes.REQUEST_URI_TOO_LARGE) server.stop() server.wait() diff --git a/nova/tests/utils.py b/nova/tests/utils.py index 994e4f220..d39d14950 100644 --- a/nova/tests/utils.py +++ b/nova/tests/utils.py @@ -12,7 +12,7 @@ # 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 errno import platform @@ -212,5 +212,5 @@ def is_ipv6_supported(): if e.errno == errno.EAFNOSUPPORT: has_ipv6_support = False else: - raise e + raise return has_ipv6_support diff --git a/nova/tests/virt/baremetal/db/test_bm_pxe_ip.py b/nova/tests/virt/baremetal/db/test_bm_pxe_ip.py deleted file mode 100644 index 85f3e2f4b..000000000 --- a/nova/tests/virt/baremetal/db/test_bm_pxe_ip.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2012 NTT DOCOMO, INC. -# 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. - -""" -Bare-metal DB testcase for BareMetalPxeIp -""" - -from nova import exception -from nova.openstack.common.db import exception as db_exc -from nova.tests.virt.baremetal.db import base -from nova.tests.virt.baremetal.db import utils -from nova.virt.baremetal import db - - -class BareMetalPxeIpTestCase(base.BMDBTestCase): - - def _create_pxe_ip(self): - i1 = utils.new_bm_pxe_ip(address='10.1.1.1', - server_address='10.1.1.101') - i2 = utils.new_bm_pxe_ip(address='10.1.1.2', - server_address='10.1.1.102') - - i1_ref = db.bm_pxe_ip_create_direct(self.context, i1) - self.assertTrue(i1_ref['id'] is not None) - self.assertEqual(i1_ref['address'], '10.1.1.1') - self.assertEqual(i1_ref['server_address'], '10.1.1.101') - - i2_ref = db.bm_pxe_ip_create_direct(self.context, i2) - self.assertTrue(i2_ref['id'] is not None) - self.assertEqual(i2_ref['address'], '10.1.1.2') - self.assertEqual(i2_ref['server_address'], '10.1.1.102') - - self.i1 = i1_ref - self.i2 = i2_ref - - def test_unuque_address(self): - self._create_pxe_ip() - - # address duplicates - i = utils.new_bm_pxe_ip(address='10.1.1.1', - server_address='10.1.1.201') - self.assertRaises(db_exc.DBError, - db.bm_pxe_ip_create_direct, - self.context, i) - - # server_address duplicates - i = utils.new_bm_pxe_ip(address='10.1.1.3', - server_address='10.1.1.101') - self.assertRaises(db_exc.DBError, - db.bm_pxe_ip_create_direct, - self.context, i) - - db.bm_pxe_ip_destroy(self.context, self.i1['id']) - i = utils.new_bm_pxe_ip(address='10.1.1.1', - server_address='10.1.1.101') - ref = db.bm_pxe_ip_create_direct(self.context, i) - self.assertTrue(ref is not None) - - def test_bm_pxe_ip_associate(self): - self._create_pxe_ip() - node = db.bm_node_create(self.context, utils.new_bm_node()) - ip_id = db.bm_pxe_ip_associate(self.context, node['id']) - ref = db.bm_pxe_ip_get(self.context, ip_id) - self.assertEqual(ref['bm_node_id'], node['id']) - - def test_bm_pxe_ip_associate_raise(self): - self._create_pxe_ip() - node_id = 123 - self.assertRaises(exception.NovaException, - db.bm_pxe_ip_associate, - self.context, node_id) - - def test_delete_by_address(self): - self._create_pxe_ip() - db.bm_pxe_ip_destroy_by_address(self.context, '10.1.1.1') - del_ref = db.bm_pxe_ip_get(self.context, self.i1['id']) - self.assertTrue(del_ref is None) - - def test_delete_by_address_not_exist(self): - self._create_pxe_ip() - del_ref = db.bm_pxe_ip_destroy_by_address(self.context, '10.11.12.13') - self.assertTrue(del_ref is None) diff --git a/nova/tests/virt/baremetal/db/utils.py b/nova/tests/virt/baremetal/db/utils.py index c3b3cff5f..6faeb13d8 100644 --- a/nova/tests/virt/baremetal/db/utils.py +++ b/nova/tests/virt/baremetal/db/utils.py @@ -39,18 +39,6 @@ def new_bm_node(**kwargs): return h -def new_bm_pxe_ip(**kwargs): - x = bm_models.BareMetalPxeIp() - x.id = kwargs.pop('id', None) - x.address = kwargs.pop('address', None) - x.server_address = kwargs.pop('server_address', None) - x.bm_node_id = kwargs.pop('bm_node_id', None) - if len(kwargs) > 0: - raise test.TestingException("unknown field: %s" - % ','.join(kwargs.keys())) - return x - - def new_bm_interface(**kwargs): x = bm_models.BareMetalInterface() x.id = kwargs.pop('id', None) diff --git a/nova/tests/virt/baremetal/test_pxe.py b/nova/tests/virt/baremetal/test_pxe.py index 022f9c692..cd4e5c143 100644 --- a/nova/tests/virt/baremetal/test_pxe.py +++ b/nova/tests/virt/baremetal/test_pxe.py @@ -116,6 +116,7 @@ class PXEClassMethodsTestCase(BareMetalPXETestCase): 'deployment_ari_path': 'eee', 'aki_path': 'fff', 'ari_path': 'ggg', + 'network_info': self.test_network_info, } config = pxe.build_pxe_config(**args) self.assertThat(config, matchers.StartsWith('default deploy')) @@ -140,6 +141,21 @@ class PXEClassMethodsTestCase(BareMetalPXETestCase): matchers.Not(matchers.Contains('kernel ddd')), )) + def test_build_pxe_network_config(self): + self.flags( + pxe_network_config=True, + group='baremetal', + ) + net = utils.get_test_network_info(1) + config = pxe.build_pxe_network_config(net) + self.assertIn('eth0:off', config) + self.assertNotIn('eth1', config) + + net = utils.get_test_network_info(2) + config = pxe.build_pxe_network_config(net) + self.assertIn('eth0:off', config) + self.assertIn('eth1:off', config) + def test_build_network_config(self): net = utils.get_test_network_info(1) config = pxe.build_network_config(net) @@ -458,7 +474,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase): bm_utils.random_alnum(32).AndReturn('alnum') pxe.build_pxe_config( self.node['id'], 'alnum', iqn, - 'aaaa', 'bbbb', 'cccc', 'dddd').AndReturn(pxe_config) + 'aaaa', 'bbbb', 'cccc', 'dddd', + self.test_network_info).AndReturn(pxe_config) bm_utils.write_to_file(pxe_path, pxe_config) for mac in macs: bm_utils.create_link_without_raise( @@ -466,7 +483,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase): self.mox.ReplayAll() - self.driver.activate_bootloader(self.context, self.node, self.instance) + self.driver.activate_bootloader(self.context, self.node, self.instance, + network_info=self.test_network_info) self.mox.VerifyAll() @@ -515,8 +533,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase): row = db.bm_node_get(self.context, 1) self.assertTrue(row['deploy_key'] is None) - self.driver.activate_bootloader(self.context, self.node, - self.instance) + self.driver.activate_bootloader(self.context, self.node, self.instance, + network_info=self.test_network_info) row = db.bm_node_get(self.context, 1) self.assertTrue(row['deploy_key'] is not None) diff --git a/nova/tests/virt/baremetal/test_tilera.py b/nova/tests/virt/baremetal/test_tilera.py index 488cba4df..7ad5c4b6a 100755 --- a/nova/tests/virt/baremetal/test_tilera.py +++ b/nova/tests/virt/baremetal/test_tilera.py @@ -317,7 +317,8 @@ class TileraPublicMethodsTestCase(BareMetalTileraTestCase): self.mox.ReplayAll() - self.driver.activate_bootloader(self.context, self.node, self.instance) + self.driver.activate_bootloader(self.context, self.node, self.instance, + network_info=self.test_network_info) self.mox.VerifyAll() @@ -334,8 +335,8 @@ class TileraPublicMethodsTestCase(BareMetalTileraTestCase): row = db.bm_node_get(self.context, 1) self.assertTrue(row['deploy_key'] is None) - self.driver.activate_bootloader(self.context, self.node, - self.instance) + self.driver.activate_bootloader(self.context, self.node, self.instance, + network_info=self.test_network_info) row = db.bm_node_get(self.context, 1) self.assertTrue(row['deploy_key'] is not None) diff --git a/nova/tests/virt/hyperv/test_hypervapi.py b/nova/tests/virt/hyperv/test_hypervapi.py index cfc79c388..9f2d745ff 100644 --- a/nova/tests/virt/hyperv/test_hypervapi.py +++ b/nova/tests/virt/hyperv/test_hypervapi.py @@ -495,13 +495,24 @@ class HyperVAPITestCase(test.TestCase): constants.HYPERV_VM_STATE_DISABLED) def test_power_on(self): - self._test_vm_state_change(self._conn.power_on, - constants.HYPERV_VM_STATE_DISABLED, - constants.HYPERV_VM_STATE_ENABLED) + self._instance_data = self._get_instance_data() + network_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name), + constants.HYPERV_VM_STATE_ENABLED) + self._mox.ReplayAll() + self._conn.power_on(self._context, self._instance_data, network_info) + self._mox.VerifyAll() def test_power_on_already_running(self): - self._test_vm_state_change(self._conn.power_on, None, - constants.HYPERV_VM_STATE_ENABLED) + self._instance_data = self._get_instance_data() + network_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name), + constants.HYPERV_VM_STATE_ENABLED) + self._mox.ReplayAll() + self._conn.power_on(self._context, self._instance_data, network_info) + self._mox.VerifyAll() def test_reboot(self): diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py index 3c658a7f5..5636e1706 100644 --- a/nova/tests/virt/libvirt/test_libvirt.py +++ b/nova/tests/virt/libvirt/test_libvirt.py @@ -26,6 +26,7 @@ import re import shutil import tempfile +from eventlet import greenthread from lxml import etree from oslo.config import cfg from xml.dom import minidom @@ -43,6 +44,7 @@ from nova.openstack.common import fileutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils from nova.openstack.common import loopingcall +from nova.openstack.common import processutils from nova.openstack.common import uuidutils from nova import test from nova.tests import fake_network @@ -77,6 +79,7 @@ CONF.import_opt('compute_manager', 'nova.service') CONF.import_opt('host', 'nova.netconf') CONF.import_opt('my_ip', 'nova.netconf') CONF.import_opt('base_dir_name', 'nova.virt.libvirt.imagecache') +CONF.import_opt('instances_path', 'nova.compute.manager') _fake_network_info = fake_network.fake_get_instance_nw_info _fake_stub_out_get_nw_info = fake_network.stub_out_nw_api_get_instance_nw_info @@ -286,8 +289,9 @@ class LibvirtConnTestCase(test.TestCase): self.user_id = 'fake' self.project_id = 'fake' self.context = context.get_admin_context() - self.flags(instances_path='') - self.flags(libvirt_snapshots_directory='') + temp_dir = self.useFixture(fixtures.TempDir()).path + self.flags(instances_path=temp_dir) + self.flags(libvirt_snapshots_directory=temp_dir) self.useFixture(fixtures.MonkeyPatch( 'nova.virt.libvirt.driver.libvirt_utils', fake_libvirt_utils)) @@ -343,6 +347,9 @@ class LibvirtConnTestCase(test.TestCase): 'extra_specs': {}, 'system_metadata': sys_meta} + def relpath(self, path): + return os.path.relpath(path, CONF.instances_path) + def tearDown(self): nova.tests.image.fake.FakeImageService_reset() super(LibvirtConnTestCase, self).tearDown() @@ -589,8 +596,8 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - # make configdrive.enabled_for() return True - instance_ref['config_drive'] = 'ANY_ID' + # make configdrive.required_by() return True + instance_ref['config_drive'] = True disk_info = blockinfo.get_disk_info(CONF.libvirt_type, instance_ref) @@ -2078,8 +2085,8 @@ class LibvirtConnTestCase(test.TestCase): else: suffix = '' if expect_kernel: - check = (lambda t: t.find('./os/kernel').text.split( - '/')[1], 'kernel' + suffix) + check = (lambda t: self.relpath(t.find('./os/kernel').text). + split('/')[1], 'kernel' + suffix) else: check = (lambda t: t.find('./os/kernel'), None) check_list.append(check) @@ -2094,8 +2101,8 @@ class LibvirtConnTestCase(test.TestCase): check_list.append(check) if expect_ramdisk: - check = (lambda t: t.find('./os/initrd').text.split( - '/')[1], 'ramdisk' + suffix) + check = (lambda t: self.relpath(t.find('./os/initrd').text). + split('/')[1], 'ramdisk' + suffix) else: check = (lambda t: t.find('./os/initrd'), None) check_list.append(check) @@ -2146,8 +2153,9 @@ class LibvirtConnTestCase(test.TestCase): check = (lambda t: t.findall('./devices/serial')[1].get( 'type'), 'pty') check_list.append(check) - check = (lambda t: t.findall('./devices/serial/source')[0].get( - 'path').split('/')[1], 'console.log') + check = (lambda t: self.relpath(t.findall( + './devices/serial/source')[0].get('path')). + split('/')[1], 'console.log') check_list.append(check) else: check = (lambda t: t.find('./devices/console').get( @@ -2159,16 +2167,16 @@ class LibvirtConnTestCase(test.TestCase): (lambda t: t.find('./memory').text, '2097152')] if rescue: common_checks += [ - (lambda t: t.findall('./devices/disk/source')[0].get( - 'file').split('/')[1], 'disk.rescue'), - (lambda t: t.findall('./devices/disk/source')[1].get( - 'file').split('/')[1], 'disk')] + (lambda t: self.relpath(t.findall('./devices/disk/source')[0]. + get('file')).split('/')[1], 'disk.rescue'), + (lambda t: self.relpath(t.findall('./devices/disk/source')[1]. + get('file')).split('/')[1], 'disk')] else: - common_checks += [(lambda t: t.findall( - './devices/disk/source')[0].get('file').split('/')[1], + common_checks += [(lambda t: self.relpath(t.findall( + './devices/disk/source')[0].get('file')).split('/')[1], 'disk')] - common_checks += [(lambda t: t.findall( - './devices/disk/source')[1].get('file').split('/')[1], + common_checks += [(lambda t: self.relpath(t.findall( + './devices/disk/source')[1].get('file')).split('/')[1], 'disk.local')] for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems(): @@ -2683,6 +2691,80 @@ class LibvirtConnTestCase(test.TestCase): db.instance_destroy(self.context, instance_ref['uuid']) + def test_get_instance_disk_info_excludes_volumes(self): + # Test data + instance_ref = db.instance_create(self.context, self.test_instance) + dummyxml = ("<domain type='kvm'><name>instance-0000000a</name>" + "<devices>" + "<disk type='file'><driver name='qemu' type='raw'/>" + "<source file='/test/disk'/>" + "<target dev='vda' bus='virtio'/></disk>" + "<disk type='file'><driver name='qemu' type='qcow2'/>" + "<source file='/test/disk.local'/>" + "<target dev='vdb' bus='virtio'/></disk>" + "<disk type='file'><driver name='qemu' type='qcow2'/>" + "<source file='/fake/path/to/volume1'/>" + "<target dev='vdc' bus='virtio'/></disk>" + "<disk type='file'><driver name='qemu' type='qcow2'/>" + "<source file='/fake/path/to/volume2'/>" + "<target dev='vdd' bus='virtio'/></disk>" + "</devices></domain>") + + # Preparing mocks + vdmock = self.mox.CreateMock(libvirt.virDomain) + self.mox.StubOutWithMock(vdmock, "XMLDesc") + vdmock.XMLDesc(0).AndReturn(dummyxml) + + def fake_lookup(instance_name): + if instance_name == instance_ref['name']: + return vdmock + self.create_fake_libvirt_mock(lookupByName=fake_lookup) + + GB = 1024 * 1024 * 1024 + fake_libvirt_utils.disk_sizes['/test/disk'] = 10 * GB + fake_libvirt_utils.disk_sizes['/test/disk.local'] = 20 * GB + fake_libvirt_utils.disk_backing_files['/test/disk.local'] = 'file' + + self.mox.StubOutWithMock(os.path, "getsize") + os.path.getsize('/test/disk').AndReturn((10737418240)) + os.path.getsize('/test/disk.local').AndReturn((3328599655)) + + ret = ("image: /test/disk\n" + "file format: raw\n" + "virtual size: 20G (21474836480 bytes)\n" + "disk size: 3.1G\n" + "cluster_size: 2097152\n" + "backing file: /test/dummy (actual path: /backing/file)\n") + + self.mox.StubOutWithMock(os.path, "exists") + os.path.exists('/test/disk.local').AndReturn(True) + + self.mox.StubOutWithMock(utils, "execute") + utils.execute('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', + '/test/disk.local').AndReturn((ret, '')) + + self.mox.ReplayAll() + conn_info = {'driver_volume_type': 'fake'} + info = {'block_device_mapping': [ + {'connection_info': conn_info, 'mount_device': '/dev/vdc'}, + {'connection_info': conn_info, 'mount_device': '/dev/vdd'}]} + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + info = conn.get_instance_disk_info(instance_ref['name'], + block_device_info=info) + info = jsonutils.loads(info) + self.assertEquals(info[0]['type'], 'raw') + self.assertEquals(info[0]['path'], '/test/disk') + self.assertEquals(info[0]['disk_size'], 10737418240) + self.assertEquals(info[0]['backing_file'], "") + self.assertEquals(info[0]['over_committed_disk_size'], 0) + self.assertEquals(info[1]['type'], 'qcow2') + self.assertEquals(info[1]['path'], '/test/disk.local') + self.assertEquals(info[1]['virt_disk_size'], 21474836480) + self.assertEquals(info[1]['backing_file'], "file") + self.assertEquals(info[1]['over_committed_disk_size'], 18146236825) + + db.instance_destroy(self.context, instance_ref['uuid']) + def test_spawn_with_network_info(self): # Preparing mocks def fake_none(*args, **kwargs): @@ -3165,6 +3247,90 @@ class LibvirtConnTestCase(test.TestCase): self.stubs.Set(os.path, 'exists', fake_os_path_exists) conn.destroy(instance, [], None, False) + def test_reboot_different_ids(self): + class FakeLoopingCall: + def start(self, *a, **k): + return self + + def wait(self): + return None + + self.flags(libvirt_wait_soft_reboot_seconds=1) + info_tuple = ('fake', 'fake', 'fake', 'also_fake') + self.reboot_create_called = False + + # Mock domain + mock_domain = self.mox.CreateMock(libvirt.virDomain) + mock_domain.info().AndReturn( + (libvirt_driver.VIR_DOMAIN_RUNNING,) + info_tuple) + mock_domain.ID().AndReturn('some_fake_id') + mock_domain.shutdown() + mock_domain.info().AndReturn( + (libvirt_driver.VIR_DOMAIN_CRASHED,) + info_tuple) + mock_domain.ID().AndReturn('some_other_fake_id') + + self.mox.ReplayAll() + + def fake_lookup_by_name(instance_name): + return mock_domain + + def fake_create_domain(**kwargs): + self.reboot_create_called = True + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = {"name": "instancename", "id": "instanceid", + "uuid": "875a8070-d0b9-4949-8b31-104d125c9a64"} + self.stubs.Set(conn, '_lookup_by_name', fake_lookup_by_name) + self.stubs.Set(conn, '_create_domain', fake_create_domain) + self.stubs.Set(loopingcall, 'FixedIntervalLoopingCall', + lambda *a, **k: FakeLoopingCall()) + conn.reboot(None, instance, []) + self.assertTrue(self.reboot_create_called) + + def test_reboot_same_ids(self): + class FakeLoopingCall: + def start(self, *a, **k): + return self + + def wait(self): + return None + + self.flags(libvirt_wait_soft_reboot_seconds=1) + info_tuple = ('fake', 'fake', 'fake', 'also_fake') + self.reboot_hard_reboot_called = False + + # Mock domain + mock_domain = self.mox.CreateMock(libvirt.virDomain) + mock_domain.info().AndReturn( + (libvirt_driver.VIR_DOMAIN_RUNNING,) + info_tuple) + mock_domain.ID().AndReturn('some_fake_id') + mock_domain.shutdown() + mock_domain.info().AndReturn( + (libvirt_driver.VIR_DOMAIN_CRASHED,) + info_tuple) + mock_domain.ID().AndReturn('some_fake_id') + + self.mox.ReplayAll() + + def fake_lookup_by_name(instance_name): + return mock_domain + + def fake_hard_reboot(*args, **kwargs): + self.reboot_hard_reboot_called = True + + def fake_sleep(interval): + pass + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = {"name": "instancename", "id": "instanceid", + "uuid": "875a8070-d0b9-4949-8b31-104d125c9a64"} + self.stubs.Set(conn, '_lookup_by_name', fake_lookup_by_name) + self.stubs.Set(greenthread, 'sleep', fake_sleep) + self.stubs.Set(conn, '_hard_reboot', fake_hard_reboot) + self.stubs.Set(loopingcall, 'FixedIntervalLoopingCall', + lambda *a, **k: FakeLoopingCall()) + conn.reboot(None, instance, []) + self.assertTrue(self.reboot_hard_reboot_called) + def test_destroy_undefines(self): mock = self.mox.CreateMock(libvirt.virDomain) mock.ID() @@ -3931,6 +4097,88 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() self.assertTrue(conn._is_storage_shared_with('foo', '/path')) + def test_create_domain_define_xml_fails(self): + """ + Tests that the xml is logged when defining the domain fails. + """ + fake_xml = "<test>this is a test</test>" + + def fake_defineXML(xml): + self.assertEquals(fake_xml, xml) + raise libvirt.libvirtError('virDomainDefineXML() failed') + + self.log_error_called = False + + def fake_error(msg): + self.log_error_called = True + self.assertTrue(fake_xml in msg) + + self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error) + + self.create_fake_libvirt_mock(defineXML=fake_defineXML) + self.mox.ReplayAll() + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + self.assertRaises(libvirt.libvirtError, conn._create_domain, fake_xml) + self.assertTrue(self.log_error_called) + + def test_create_domain_with_flags_fails(self): + """ + Tests that the xml is logged when creating the domain with flags fails. + """ + fake_xml = "<test>this is a test</test>" + fake_domain = FakeVirtDomain(fake_xml) + + def fake_createWithFlags(launch_flags): + raise libvirt.libvirtError('virDomainCreateWithFlags() failed') + + self.log_error_called = False + + def fake_error(msg): + self.log_error_called = True + self.assertTrue(fake_xml in msg) + + self.stubs.Set(fake_domain, 'createWithFlags', fake_createWithFlags) + self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error) + + self.create_fake_libvirt_mock() + self.mox.ReplayAll() + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + self.assertRaises(libvirt.libvirtError, conn._create_domain, + domain=fake_domain) + self.assertTrue(self.log_error_called) + + def test_create_domain_enable_hairpin_fails(self): + """ + Tests that the xml is logged when enabling hairpin mode for the domain + fails. + """ + fake_xml = "<test>this is a test</test>" + fake_domain = FakeVirtDomain(fake_xml) + + def fake_enable_hairpin(launch_flags): + raise processutils.ProcessExecutionError('error') + + self.log_error_called = False + + def fake_error(msg): + self.log_error_called = True + self.assertTrue(fake_xml in msg) + + self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error) + + self.create_fake_libvirt_mock() + self.mox.ReplayAll() + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + self.stubs.Set(conn, '_enable_hairpin', fake_enable_hairpin) + + self.assertRaises(processutils.ProcessExecutionError, + conn._create_domain, + domain=fake_domain, + power_on=False) + self.assertTrue(self.log_error_called) + class HostStateTestCase(test.TestCase): @@ -4945,10 +5193,9 @@ class LibvirtDriverTestCase(test.TestCase): inst['host'] = 'host1' inst['root_gb'] = 10 inst['ephemeral_gb'] = 20 - inst['config_drive'] = 1 + inst['config_drive'] = True inst['kernel_id'] = 2 inst['ramdisk_id'] = 3 - inst['config_drive_id'] = 1 inst['key_data'] = 'ABCDEFG' inst['system_metadata'] = sys_meta @@ -4963,7 +5210,8 @@ class LibvirtDriverTestCase(test.TestCase): self.counter = 0 self.checked_shared_storage = False - def fake_get_instance_disk_info(instance, xml=None): + def fake_get_instance_disk_info(instance, xml=None, + block_device_info=None): return '[]' def fake_destroy(instance): @@ -5015,7 +5263,8 @@ class LibvirtDriverTestCase(test.TestCase): 'disk_size': '83886080'}] disk_info_text = jsonutils.dumps(disk_info) - def fake_get_instance_disk_info(instance, xml=None): + def fake_get_instance_disk_info(instance, xml=None, + block_device_info=None): return disk_info_text def fake_destroy(instance): diff --git a/nova/tests/virt/libvirt/test_libvirt_vif.py b/nova/tests/virt/libvirt/test_libvirt_vif.py index c9c6aef12..7cbf67b07 100644 --- a/nova/tests/virt/libvirt/test_libvirt_vif.py +++ b/nova/tests/virt/libvirt/test_libvirt_vif.py @@ -103,6 +103,17 @@ class LibvirtVifTestCase(test.TestCase): 'ovs_interfaceid': 'aaa-bbb-ccc', } + mapping_ivs = { + 'mac': 'ca:fe:de:ad:be:ef', + 'gateway_v6': net_ovs['gateway_v6'], + 'ips': [{'ip': '101.168.1.9'}], + 'dhcp_server': '191.168.1.1', + 'vif_uuid': 'vif-xxx-yyy-zzz', + 'vif_devname': 'tap-xxx-yyy-zzz', + 'vif_type': network_model.VIF_TYPE_IVS, + 'ivs_interfaceid': 'aaa-bbb-ccc', + } + mapping_ovs_legacy = { 'mac': 'ca:fe:de:ad:be:ef', 'gateway_v6': net_ovs['gateway_v6'], @@ -411,6 +422,24 @@ class LibvirtVifTestCase(test.TestCase): self.mapping_bridge_quantum, br_want) + def _check_ivs_ethernet_driver(self, d, net, mapping, dev_prefix): + self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") + xml = self._get_instance_xml(d, net, mapping) + + doc = etree.fromstring(xml) + ret = doc.findall('./devices/interface') + self.assertEqual(len(ret), 1) + node = ret[0] + ret = node.findall("filterref") + self.assertEqual(len(ret), 0) + self.assertEqual(node.get("type"), "ethernet") + dev_name = node.find("target").get("dev") + self.assertTrue(dev_name.startswith(dev_prefix)) + mac = node.find("mac").get("address") + self.assertEqual(mac, self.mapping_ivs['mac']) + script = node.find("script").get("path") + self.assertEquals(script, "") + def _check_ovs_ethernet_driver(self, d, net, mapping, dev_prefix): self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") xml = self._get_instance_xml(d, net, mapping) @@ -451,6 +480,33 @@ class LibvirtVifTestCase(test.TestCase): self.mapping_ovs, "tap") + def test_ivs_ethernet_driver(self): + def get_connection(): + return fakelibvirt.Connection("qemu:///session", + False, + 9010) + d = vif.LibvirtGenericVIFDriver(get_connection) + self._check_ivs_ethernet_driver(d, + self.net_ovs, + self.mapping_ivs, + "tap") + + def _check_ivs_virtualport_driver(self, d, net, mapping, want_iface_id): + self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") + xml = self._get_instance_xml(d, net, mapping) + doc = etree.fromstring(xml) + ret = doc.findall('./devices/interface') + self.assertEqual(len(ret), 1) + node = ret[0] + ret = node.findall("filterref") + self.assertEqual(len(ret), 0) + self.assertEqual(node.get("type"), "ethernet") + + tap_name = node.find("target").get("dev") + self.assertEqual(tap_name, mapping['vif_devname']) + mac = node.find("mac").get("address") + self.assertEqual(mac, mapping['mac']) + def _check_ovs_virtualport_driver(self, d, net, mapping, want_iface_id): self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") xml = self._get_instance_xml(d, net, mapping) @@ -502,6 +558,18 @@ class LibvirtVifTestCase(test.TestCase): self.mapping_ovs, want_iface_id) + def test_generic_ivs_virtualport_driver(self): + def get_connection(): + return fakelibvirt.Connection("qemu:///session", + False, + 9011) + d = vif.LibvirtGenericVIFDriver(get_connection) + want_iface_id = self.mapping_ivs['ivs_interfaceid'] + self._check_ivs_virtualport_driver(d, + self.net_ovs, + self.mapping_ivs, + want_iface_id) + def _check_quantum_hybrid_driver(self, d, net, mapping, br_want): self.flags(firewall_driver="nova.virt.firewall.IptablesFirewallDriver") xml = self._get_instance_xml(d, net, mapping) @@ -542,6 +610,18 @@ class LibvirtVifTestCase(test.TestCase): self.mapping_ovs, br_want) + def test_ivs_hybrid_driver(self): + def get_connection(): + return fakelibvirt.Connection("qemu:///session", + False) + d = vif.LibvirtGenericVIFDriver(get_connection) + br_want = "qbr" + self.mapping_ivs['vif_uuid'] + br_want = br_want[:network_model.NIC_NAME_LEN] + self._check_quantum_hybrid_driver(d, + self.net_ovs, + self.mapping_ivs, + br_want) + def test_generic_8021qbh_driver(self): def get_connection(): return fakelibvirt.Connection("qemu:///session", diff --git a/nova/tests/virt/test_virt_drivers.py b/nova/tests/virt/test_virt_drivers.py index 47f983258..6cc9bef43 100644 --- a/nova/tests/virt/test_virt_drivers.py +++ b/nova/tests/virt/test_virt_drivers.py @@ -60,7 +60,7 @@ class _FakeDriverBackendTestCase(object): # So that the _supports_direct_io does the test based # on the current working directory, instead of the # default instances_path which doesn't exist - self.flags(instances_path='') + self.flags(instances_path=self.useFixture(fixtures.TempDir()).path) # Put fakelibvirt in place if 'libvirt' in sys.modules: @@ -106,7 +106,8 @@ class _FakeDriverBackendTestCase(object): def fake_make_drive(_self, _path): pass - def fake_get_instance_disk_info(_self, instance, xml=None): + def fake_get_instance_disk_info(_self, instance, xml=None, + block_device_info=None): return '[]' self.stubs.Set(nova.virt.libvirt.driver.LibvirtDriver, @@ -311,13 +312,14 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): @catch_notimplementederror def test_power_on_running(self): instance_ref, network_info = self._get_running_instance() - self.connection.power_on(instance_ref) + self.connection.power_on(self.ctxt, instance_ref, + network_info, None) @catch_notimplementederror def test_power_on_powered_off(self): instance_ref, network_info = self._get_running_instance() self.connection.power_off(instance_ref) - self.connection.power_on(instance_ref) + self.connection.power_on(self.ctxt, instance_ref, network_info, None) @catch_notimplementederror def test_soft_delete(self): @@ -407,7 +409,24 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): self.connection.attach_volume({'driver_volume_type': 'fake'}, instance_ref, '/dev/sda') - self.connection.power_on(instance_ref) + + bdm = { + 'root_device_name': None, + 'swap': None, + 'ephemerals': [], + 'block_device_mapping': [{ + 'instance_uuid': instance_ref['uuid'], + 'connection_info': {'driver_volume_type': 'fake'}, + 'mount_device': '/dev/sda', + 'delete_on_termination': False, + 'virtual_name': None, + 'snapshot_id': None, + 'volume_id': 'abcdedf', + 'volume_size': None, + 'no_device': None + }] + } + self.connection.power_on(self.ctxt, instance_ref, network_info, bdm) self.connection.detach_volume({'driver_volume_type': 'fake'}, instance_ref, '/dev/sda') diff --git a/nova/tests/virt/vmwareapi/db_fakes.py b/nova/tests/virt/vmwareapi/db_fakes.py index 93fcf6e13..87c3dde67 100644 --- a/nova/tests/virt/vmwareapi/db_fakes.py +++ b/nova/tests/virt/vmwareapi/db_fakes.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack Foundation # @@ -74,6 +75,7 @@ def stub_out_db_instance_api(stubs): 'vcpus': type_data['vcpus'], 'mac_addresses': [{'address': values['mac_address']}], 'root_gb': type_data['root_gb'], + 'node': values['node'], } return FakeModel(base_options) diff --git a/nova/tests/virt/vmwareapi/test_vmwareapi.py b/nova/tests/virt/vmwareapi/test_vmwareapi.py index 69a9ffab8..afda26b0d 100644 --- a/nova/tests/virt/vmwareapi/test_vmwareapi.py +++ b/nova/tests/virt/vmwareapi/test_vmwareapi.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # Copyright (c) 2012 VMware, Inc. # Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack Foundation @@ -111,6 +112,7 @@ class VMwareAPIVMTestCase(test.TestCase): use_linked_clone=False) self.user_id = 'fake' self.project_id = 'fake' + self.node_name = 'test_url' self.context = context.RequestContext(self.user_id, self.project_id) vmwareapi_fake.reset() db_fakes.stub_out_db_instance_api(self.stubs) @@ -143,6 +145,7 @@ class VMwareAPIVMTestCase(test.TestCase): 'ramdisk_id': "1", 'mac_address': "de:ad:be:ef:be:ef", 'instance_type': 'm1.large', + 'node': self.node_name, } self.instance = db.instance_create(None, values) @@ -332,14 +335,14 @@ class VMwareAPIVMTestCase(test.TestCase): self.conn.power_off(self.instance) info = self.conn.get_info({'uuid': 'fake-uuid'}) self._check_vm_info(info, power_state.SHUTDOWN) - self.conn.power_on(self.instance) + self.conn.power_on(self.context, self.instance, self.network_info) info = self.conn.get_info({'uuid': 'fake-uuid'}) self._check_vm_info(info, power_state.RUNNING) def test_power_on_non_existent(self): self._create_instance_in_the_db() self.assertRaises(exception.InstanceNotFound, self.conn.power_on, - self.instance) + self.context, self.instance, self.network_info) def test_power_off(self): self._create_vm() @@ -424,7 +427,7 @@ class VMwareAPIVMTestCase(test.TestCase): self.assertEquals(4, step) self.assertEqual(vmops.RESIZE_TOTAL_STEPS, total_steps) - self.stubs.Set(self.conn._vmops, "power_on", fake_power_on) + self.stubs.Set(self.conn._vmops, "_power_on", fake_power_on) self.stubs.Set(self.conn._vmops, "_update_instance_progress", fake_vmops_update_instance_progress) @@ -436,6 +439,7 @@ class VMwareAPIVMTestCase(test.TestCase): instance=self.instance, disk_info=None, network_info=None, + block_device_info=None, image_meta=None, power_on=power_on) # verify the results @@ -472,15 +476,20 @@ class VMwareAPIVMTestCase(test.TestCase): self.assertEquals(self.vm_name, vm_name) return vmwareapi_fake._get_objects("VirtualMachine")[0] + def fake_get_vm_ref_from_uuid(session, vm_uuid): + return vmwareapi_fake._get_objects("VirtualMachine")[0] + def fake_call_method(*args, **kwargs): pass def fake_wait_for_task(*args, **kwargs): pass - self.stubs.Set(self.conn._vmops, "power_on", fake_power_on) + self.stubs.Set(self.conn._vmops, "_power_on", fake_power_on) self.stubs.Set(self.conn._vmops, "_get_orig_vm_name_label", fake_get_orig_vm_name_label) + self.stubs.Set(vm_util, "get_vm_ref_from_uuid", + fake_get_vm_ref_from_uuid) self.stubs.Set(vm_util, "get_vm_ref_from_name", fake_get_vm_ref_from_name) self.stubs.Set(self.conn._session, "_call_method", fake_call_method) @@ -500,6 +509,41 @@ class VMwareAPIVMTestCase(test.TestCase): def test_finish_revert_migration_power_off(self): self._test_finish_revert_migration(power_on=False) + def test_diagnostics_non_existent_vm(self): + self._create_instance_in_the_db() + self.assertRaises(exception.InstanceNotFound, + self.conn.get_diagnostics, + self.instance) + + def test_get_console_pool_info(self): + info = self.conn.get_console_pool_info("console_type") + self.assertEquals(info['address'], 'test_url') + self.assertEquals(info['username'], 'test_username') + self.assertEquals(info['password'], 'test_pass') + + def test_get_vnc_console_non_existent(self): + self._create_instance_in_the_db() + self.assertRaises(exception.InstanceNotFound, + self.conn.get_vnc_console, + self.instance) + + def test_get_vnc_console(self): + self._create_instance_in_the_db() + self._create_vm() + vnc_dict = self.conn.get_vnc_console(self.instance) + self.assertEquals(vnc_dict['host'], "ha-host") + self.assertEquals(vnc_dict['port'], 5910) + + def test_host_ip_addr(self): + self.assertEquals(self.conn.get_host_ip_addr(), "test_url") + + def test_get_volume_connector(self): + self._create_instance_in_the_db() + connector_dict = self.conn.get_volume_connector(self.instance) + self.assertEquals(connector_dict['ip'], "test_url") + self.assertEquals(connector_dict['initiator'], "iscsi-name") + self.assertEquals(connector_dict['host'], "test_url") + class VMwareAPIHostTestCase(test.TestCase): """Unit tests for Vmware API host calls.""" @@ -547,3 +591,30 @@ class VMwareAPIHostTestCase(test.TestCase): def test_host_maintenance_off(self): self._test_host_action(self.conn.host_maintenance_mode, False) + + +class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase): + + def setUp(self): + super(VMwareAPIVCDriverTestCase, self).setUp() + self.flags( + vmwareapi_cluster_name='test_cluster', + vmwareapi_task_poll_interval=10, + vnc_enabled=False + ) + self.conn = driver.VMwareVCDriver(None, False) + + def tearDown(self): + super(VMwareAPIVCDriverTestCase, self).tearDown() + vmwareapi_fake.cleanup() + + def test_get_available_resource(self): + stats = self.conn.get_available_resource(self.node_name) + self.assertEquals(stats['vcpus'], 16) + self.assertEquals(stats['local_gb'], 1024) + self.assertEquals(stats['local_gb_used'], 1024 - 500) + self.assertEquals(stats['memory_mb'], 1024) + self.assertEquals(stats['memory_mb_used'], 1024 - 524) + self.assertEquals(stats['hypervisor_type'], 'VMware ESXi') + self.assertEquals(stats['hypervisor_version'], '5.0.0') + self.assertEquals(stats['hypervisor_hostname'], 'test_url') diff --git a/nova/tests/virt/vmwareapi/test_vmwareapi_vm_util.py b/nova/tests/virt/vmwareapi/test_vmwareapi_vm_util.py index 123a314c1..0456dfece 100644 --- a/nova/tests/virt/vmwareapi/test_vmwareapi_vm_util.py +++ b/nova/tests/virt/vmwareapi/test_vmwareapi_vm_util.py @@ -16,6 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. +from collections import namedtuple + from nova import exception from nova import test from nova.virt.vmwareapi import fake @@ -33,9 +35,11 @@ class fake_session(object): class VMwareVMUtilTestCase(test.TestCase): def setUp(self): super(VMwareVMUtilTestCase, self).setUp() + fake.reset() def tearDown(self): super(VMwareVMUtilTestCase, self).tearDown() + fake.reset() def test_get_datastore_ref_and_name(self): result = vm_util.get_datastore_ref_and_name( @@ -54,3 +58,95 @@ class VMwareVMUtilTestCase(test.TestCase): self.assertRaises(exception.DatastoreNotFound, vm_util.get_datastore_ref_and_name, fake_session(), cluster="fake-cluster") + + def test_get_host_ref_from_id(self): + + fake_host_sys = fake.HostSystem( + fake.ManagedObjectReference("HostSystem", "host-123")) + + fake_host_id = fake_host_sys.obj.value + fake_host_name = "ha-host" + + ref = vm_util.get_host_ref_from_id( + fake_session([fake_host_sys]), fake_host_id, ['name']) + + self.assertIsInstance(ref, fake.HostSystem) + self.assertEqual(fake_host_id, ref.obj.value) + + host_name = vm_util.get_host_name_from_host_ref(ref) + + self.assertEquals(fake_host_name, host_name) + + def test_get_host_name_for_vm(self): + + fake_vm = fake.ManagedObject( + "VirtualMachine", fake.ManagedObjectReference( + "vm-123", "VirtualMachine")) + fake_vm.propSet.append( + fake.Property('name', 'vm-123')) + + vm_ref = vm_util.get_vm_ref_from_name( + fake_session([fake_vm]), 'vm-123') + + self.assertIsNotNone(vm_ref) + + fake_results = [ + fake.ObjectContent( + None, [ + fake.Property('runtime.host', + fake.ManagedObjectReference( + 'host-123', 'HostSystem')) + ])] + + host_id = vm_util.get_host_id_from_vm_ref( + fake_session(fake_results), vm_ref) + + self.assertEqual('host-123', host_id) + + def test_property_from_property_set(self): + + ObjectContent = namedtuple('ObjectContent', ['propSet']) + DynamicProperty = namedtuple('Property', ['name', 'val']) + MoRef = namedtuple('Val', ['value']) + + results_good = [ + ObjectContent(propSet=[ + DynamicProperty(name='name', val=MoRef(value='vm-123'))]), + ObjectContent(propSet=[ + DynamicProperty(name='foo', val=MoRef(value='bar1')), + DynamicProperty( + name='runtime.host', val=MoRef(value='host-123')), + DynamicProperty(name='foo', val=MoRef(value='bar2')), + ]), + ObjectContent(propSet=[ + DynamicProperty( + name='something', val=MoRef(value='thing'))]), ] + + results_bad = [ + ObjectContent(propSet=[ + DynamicProperty(name='name', val=MoRef(value='vm-123'))]), + ObjectContent(propSet=[ + DynamicProperty(name='foo', val='bar1'), + DynamicProperty(name='foo', val='bar2'), ]), + ObjectContent(propSet=[ + DynamicProperty( + name='something', val=MoRef(value='thing'))]), ] + + prop = vm_util.property_from_property_set( + 'runtime.host', results_good) + self.assertIsNotNone(prop) + value = prop.val.value + self.assertEqual('host-123', value) + + prop2 = vm_util.property_from_property_set( + 'runtime.host', results_bad) + self.assertIsNone(prop2) + + prop3 = vm_util.property_from_property_set('foo', results_good) + self.assertIsNotNone(prop3) + val3 = prop3.val.value + self.assertEqual('bar1', val3) + + prop4 = vm_util.property_from_property_set('foo', results_bad) + self.assertIsNotNone(prop4) + self.assertEqual('bar1', prop4.val) diff --git a/nova/tests/virt/xenapi/test_vm_utils.py b/nova/tests/virt/xenapi/test_vm_utils.py index 68caab651..6884ab5a8 100644 --- a/nova/tests/virt/xenapi/test_vm_utils.py +++ b/nova/tests/virt/xenapi/test_vm_utils.py @@ -266,7 +266,7 @@ class FetchVhdImageTestCase(test.TestCase): self._apply_stubouts() self._common_params_setup(True) - vm_utils._add_bittorrent_params(self.params) + vm_utils._add_bittorrent_params(self.image_id, self.params) vm_utils._fetch_using_dom0_plugin_with_retry(self.context, self.session, self.image_id, "bittorrent", self.params, @@ -289,7 +289,7 @@ class FetchVhdImageTestCase(test.TestCase): self._common_params_setup(True) self.mox.StubOutWithMock(self.session, 'call_xenapi') - vm_utils._add_bittorrent_params(self.params) + vm_utils._add_bittorrent_params(self.image_id, self.params) vm_utils._fetch_using_dom0_plugin_with_retry(self.context, self.session, self.image_id, "bittorrent", self.params, diff --git a/nova/tests/virt/xenapi/test_xenapi.py b/nova/tests/virt/xenapi/test_xenapi.py index 91d4f0770..f6ff23aba 100644 --- a/nova/tests/virt/xenapi/test_xenapi.py +++ b/nova/tests/virt/xenapi/test_xenapi.py @@ -31,6 +31,7 @@ from nova.compute import power_state from nova.compute import task_states from nova.compute import vm_states from nova import context +from nova import crypto from nova import db from nova import exception from nova.openstack.common import importutils @@ -983,13 +984,14 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): actual_injected_files.append((path, contents)) return jsonutils.dumps({'returncode': '0', 'message': 'success'}) - def noop(*args, **kwargs): - pass - self.stubs.Set(stubs.FakeSessionForVMTests, '_plugin_agent_inject_file', fake_inject_file) - self.stubs.Set(agent.XenAPIBasedAgent, - 'set_admin_password', noop) + + def fake_encrypt_text(sshkey, new_pass): + self.assertEqual("fake_keydata", sshkey) + return "fake" + + self.stubs.Set(crypto, 'ssh_encrypt_text', fake_encrypt_text) expected_data = ('\n# The following ssh key was injected by ' 'Nova\nfake_keydata\n') @@ -1020,6 +1022,93 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): self.check_vm_params_for_linux() self.assertEquals(actual_injected_files, injected_files) + def test_spawn_agent_upgrade(self): + self.flags(xenapi_use_agent_default=True) + actual_injected_files = [] + + def fake_agent_build(_self, *args): + return {"version": "1.1.0", "architecture": "x86-64", + "hypervisor": "xen", "os": "windows", + "url": "url", "md5hash": "asdf"} + + self.stubs.Set(self.conn.virtapi, 'agent_build_get_by_triple', + fake_agent_build) + + self._test_spawn(IMAGE_VHD, None, None, + os_type="linux", architecture="x86-64") + + def test_spawn_agent_upgrade_fails_silently(self): + self.flags(xenapi_use_agent_default=True) + actual_injected_files = [] + + def fake_agent_build(_self, *args): + return {"version": "1.1.0", "architecture": "x86-64", + "hypervisor": "xen", "os": "windows", + "url": "url", "md5hash": "asdf"} + + self.stubs.Set(self.conn.virtapi, 'agent_build_get_by_triple', + fake_agent_build) + + def fake_agent_update(self, method, args): + raise xenapi_fake.Failure(["fake_error"]) + + self.stubs.Set(stubs.FakeSessionForVMTests, + '_plugin_agent_agentupdate', fake_agent_update) + + self._test_spawn(IMAGE_VHD, None, None, + os_type="linux", architecture="x86-64") + + def _test_spawn_fails_with(self, trigger, expected_exception): + self.flags(xenapi_use_agent_default=True) + self.flags(agent_version_timeout=0) + actual_injected_files = [] + + def fake_agent_version(self, method, args): + raise xenapi_fake.Failure([trigger]) + + self.stubs.Set(stubs.FakeSessionForVMTests, + '_plugin_agent_version', fake_agent_version) + + self.assertRaises(expected_exception, self._test_spawn, + IMAGE_VHD, None, None, os_type="linux", architecture="x86-64") + + def test_spawn_fails_with_agent_timeout(self): + self._test_spawn_fails_with("TIMEOUT:fake", exception.AgentTimeout) + + def test_spawn_fails_with_agent_not_implemented(self): + self._test_spawn_fails_with("NOT IMPLEMENTED:fake", + exception.AgentNotImplemented) + + def test_spawn_fails_with_agent_error(self): + self._test_spawn_fails_with("fake_error", exception.AgentError) + + def test_spawn_fails_with_agent_bad_return(self): + self.flags(xenapi_use_agent_default=True) + self.flags(agent_version_timeout=0) + actual_injected_files = [] + + def fake_agent_version(self, method, args): + return xenapi_fake.as_json(returncode='-1', message='fake') + self.stubs.Set(stubs.FakeSessionForVMTests, + '_plugin_agent_version', fake_agent_version) + + self.assertRaises(exception.AgentError, self._test_spawn, + IMAGE_VHD, None, None, os_type="linux", architecture="x86-64") + + def test_spawn_fails_agent_not_implemented(self): + # Test spawning with injected_files. + self.flags(xenapi_use_agent_default=True) + self.flags(agent_version_timeout=0) + actual_injected_files = [] + + def fake_agent_version(self, method, args): + raise xenapi_fake.Failure(["NOT IMPLEMENTED:fake"]) + self.stubs.Set(stubs.FakeSessionForVMTests, + '_plugin_agent_version', fake_agent_version) + + self.assertRaises(exception.AgentNotImplemented, self._test_spawn, + IMAGE_VHD, None, None, os_type="linux", architecture="x86-64") + def test_rescue(self): instance = self._create_instance() session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass', |