From c400024de45073ccc23a6738c78518365a511562 Mon Sep 17 00:00:00 2001 From: John Tran Date: Fri, 25 Mar 2011 13:17:51 -0700 Subject: added a simple test for describe_images with mock for detail funciton --- nova/tests/test_cloud.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index cf8ee7eff..2f0571ca3 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -81,7 +81,12 @@ class CloudTestCase(test.TestCase): def fake_show(meh, context, id): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} + def fake_detail(meh, context): + return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type':'machine'}}] + self.stubs.Set(local.LocalImageService, 'show', fake_show) + self.stubs.Set(local.LocalImageService, 'detail', fake_detail) self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) def tearDown(self): @@ -224,6 +229,11 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp1['id']) db.service_destroy(self.context, comp2['id']) + def test_describe_images(self): + result = self.cloud.describe_images(self.context) + result = result['imagesSet'][0] + self.assertEqual(result['imageId'], 'ami-00000001') + def test_console_output(self): instance_type = FLAGS.default_instance_type max_count = 1 -- cgit From 9ce24afab007a9b5144c8c8a8f2fcc4157ba34d7 Mon Sep 17 00:00:00 2001 From: John Tran Date: Mon, 28 Mar 2011 11:29:23 -0700 Subject: when image_id provided cannot be found, returns more informative error message. --- nova/api/ec2/cloud.py | 6 +++++- nova/tests/test_cloud.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e257e44e7..2f47f0927 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -909,7 +909,11 @@ class CloudController(object): def deregister_image(self, context, image_id, **kwargs): LOG.audit(_("De-registering image %s"), image_id, context=context) - image = self._get_image(context, image_id) + try: + image = self._get_image(context, image_id) + except exception.NotFound: + raise exception.NotFound(_('Image %s not found') % + image_id) internal_id = image['id'] self.image_service.delete(context, internal_id) return {'imageId': image_id} diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 2f0571ca3..8043d4670 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -41,6 +41,7 @@ from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils from nova.image import local from nova.objectstore import image +from nova.exception import NotEmpty, NotFound FLAGS = flags.FLAGS @@ -85,8 +86,12 @@ class CloudTestCase(test.TestCase): return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, 'type':'machine'}}] + def fake_delete(meh, context, id): + return None + self.stubs.Set(local.LocalImageService, 'show', fake_show) self.stubs.Set(local.LocalImageService, 'detail', fake_detail) + self.stubs.Set(local.LocalImageService, 'delete', fake_delete) self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) def tearDown(self): @@ -234,6 +239,16 @@ class CloudTestCase(test.TestCase): result = result['imagesSet'][0] self.assertEqual(result['imageId'], 'ami-00000001') + def test_deregister_image(self): + deregister_image = self.cloud.deregister_image + """When provided a valid image, should be successful""" + result1 = deregister_image(self.context, 'ami-00000001') + self.assertEqual(result1['imageId'], 'ami-00000001') + """Invalid image should throw an NotFound exception""" + self.stubs.UnsetAll() + self.assertRaises(NotFound, deregister_image, + self.context, 'ami-bad001') + def test_console_output(self): instance_type = FLAGS.default_instance_type max_count = 1 -- cgit From 00afedaec5c6544bf9ff982d5f9d8e7b6b2a4b19 Mon Sep 17 00:00:00 2001 From: John Tran Date: Mon, 28 Mar 2011 18:16:55 -0700 Subject: made changes per code review: 1) removed import of image from objectstore 2) changed to comments instaed of triple quotes. --- nova/tests/test_cloud.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 20c85d79c..b8a95e451 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -41,8 +41,7 @@ from nova.compute import power_state from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils from nova.image import local -from nova.objectstore import image -from nova.exception import NotEmpty, NotFound +from nova.exception import NotFound FLAGS = flags.FLAGS @@ -75,16 +74,7 @@ class CloudTestCase(test.TestCase): def fake_show(meh, context, id): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} - def fake_detail(meh, context): - return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type':'machine'}}] - - def fake_delete(meh, context, id): - return None - self.stubs.Set(local.LocalImageService, 'show', fake_show) - self.stubs.Set(local.LocalImageService, 'detail', fake_detail) - self.stubs.Set(local.LocalImageService, 'delete', fake_delete) self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) def tearDown(self): @@ -228,17 +218,27 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp2['id']) def test_describe_images(self): + def fake_detail(meh, context): + return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type':'machine'}}] + self.stubs.Set(local.LocalImageService, 'detail', fake_detail) result = self.cloud.describe_images(self.context) result = result['imagesSet'][0] self.assertEqual(result['imageId'], 'ami-00000001') def test_deregister_image(self): deregister_image = self.cloud.deregister_image - """When provided a valid image, should be successful""" + def fake_delete(meh, context, id): + return None + self.stubs.Set(local.LocalImageService, 'delete', fake_delete) + # valid image result1 = deregister_image(self.context, 'ami-00000001') self.assertEqual(result1['imageId'], 'ami-00000001') - """Invalid image should throw an NotFound exception""" + # invalid image self.stubs.UnsetAll() + def fake_detail_empty(meh, context): + return [] + self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) self.assertRaises(NotFound, deregister_image, self.context, 'ami-bad001') -- cgit From 1b67237d05e7103dc6b2beadd5782466682a136b Mon Sep 17 00:00:00 2001 From: John Tran Date: Mon, 28 Mar 2011 18:19:56 -0700 Subject: cleaned up var name --- nova/tests/test_cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index b8a95e451..07e52a6be 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -232,8 +232,8 @@ class CloudTestCase(test.TestCase): return None self.stubs.Set(local.LocalImageService, 'delete', fake_delete) # valid image - result1 = deregister_image(self.context, 'ami-00000001') - self.assertEqual(result1['imageId'], 'ami-00000001') + result = deregister_image(self.context, 'ami-00000001') + self.assertEqual(result['imageId'], 'ami-00000001') # invalid image self.stubs.UnsetAll() def fake_detail_empty(meh, context): -- cgit From ee00cb8057eac328c98dd9c040ffa324f11a87be Mon Sep 17 00:00:00 2001 From: John Tran Date: Tue, 29 Mar 2011 13:43:00 -0700 Subject: added blank lines in between functions & removed the test_describe_images (was meant for a diff bug lp682888) --- nova/tests/test_cloud.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 07e52a6be..582e40e08 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -217,27 +217,22 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp1['id']) db.service_destroy(self.context, comp2['id']) - def test_describe_images(self): - def fake_detail(meh, context): - return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type':'machine'}}] - self.stubs.Set(local.LocalImageService, 'detail', fake_detail) - result = self.cloud.describe_images(self.context) - result = result['imagesSet'][0] - self.assertEqual(result['imageId'], 'ami-00000001') - def test_deregister_image(self): deregister_image = self.cloud.deregister_image + def fake_delete(meh, context, id): return None + self.stubs.Set(local.LocalImageService, 'delete', fake_delete) # valid image result = deregister_image(self.context, 'ami-00000001') self.assertEqual(result['imageId'], 'ami-00000001') # invalid image self.stubs.UnsetAll() + def fake_detail_empty(meh, context): return [] + self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) self.assertRaises(NotFound, deregister_image, self.context, 'ami-bad001') -- cgit From 655906ee7be1d906033bde7887293e6d61bae3d6 Mon Sep 17 00:00:00 2001 From: John Tran Date: Wed, 30 Mar 2011 12:37:56 -0700 Subject: updated per code review, replaced NotFound with exception.NotFound --- nova/tests/test_cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 582e40e08..145da8ad2 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -36,12 +36,12 @@ from nova import rpc from nova import service from nova import test from nova import utils +from nova import exception from nova.auth import manager from nova.compute import power_state from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils from nova.image import local -from nova.exception import NotFound FLAGS = flags.FLAGS @@ -234,7 +234,7 @@ class CloudTestCase(test.TestCase): return [] self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) - self.assertRaises(NotFound, deregister_image, + self.assertRaises(exception.NotFound, deregister_image, self.context, 'ami-bad001') def test_console_output(self): -- cgit From 8482d87e3fe380704fac121240ebd29b9057283c Mon Sep 17 00:00:00 2001 From: John Tran Date: Wed, 30 Mar 2011 12:44:22 -0700 Subject: removed trailing whitespace --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 145da8ad2..cde8041f7 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -36,7 +36,7 @@ from nova import rpc from nova import service from nova import test from nova import utils -from nova import exception +from nova import exception from nova.auth import manager from nova.compute import power_state from nova.api.ec2 import cloud -- cgit From d733eaf6749a5163165119ad164c817c3d7110b4 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Wed, 4 May 2011 17:29:29 -0500 Subject: Adding a test case to show the xml deserialization failure for imageRef and flavorRef --- nova/tests/api/openstack/test_servers.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 5c643fcef..6d3177e64 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1648,6 +1648,19 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""", request = self.deserializer.deserialize(serial_request) self.assertEqual(request, expected) + def test_request_xmlser_with_flavor_image_ref(self): + serial_request = """ + + """ + request = self.deserializer.deserialize(serial_request) + self.assertEquals(request["server"]["flavorRef"], + "http://localhost:8774/v1.1/flavors/1") + self.assertEquals(request["server"]["imageRef"], + "http://localhost:8774/v1.1/images/1") + class TestServerInstanceCreation(test.TestCase): -- cgit From 8d3f20e776af0fe174474a9fe8ee02eabe64053b Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 5 May 2011 15:56:04 -0400 Subject: Added interfaces to server controller --- nova/api/openstack/servers.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3cf78e32c..1d3eab65b 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -75,6 +75,26 @@ class Controller(common.OpenstackController): """ Returns a list of server details for a given user """ return self._items(req, is_detail=True) + def _image_id_from_req_data(self, data): + raise NotImplementedError + + def _flavor_id_from_req_data(self, data): + raise NotImplementedError + + def _get_view_builder(self, req): + raise NotImplementedError + + def _limit_items(self, items, req): + raise NotImplementedError + + def _limit_items(self, items, req): + raise NotImplementedError + + def _action_rebuild(self, info, request, instance_id): + raise NotImplementedError + + + def _items(self, req, is_detail): """Returns a list of servers for a given user. -- cgit From 5a3e8eea45bc11978112e3fed93768a1daf71530 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 5 May 2011 16:29:31 -0400 Subject: Added interface function to ViewBilder --- nova/api/openstack/flavors.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 40787bd17..dee70bb2b 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -45,6 +45,9 @@ class Controller(common.OpenstackController): items = self._get_flavors(req, is_detail=True) return dict(flavors=items) + def _get_view_builder(self, req): + raise NotImplementedError + def _get_flavors(self, req, is_detail=True): """Helper function that returns a list of flavor dicts.""" ctxt = req.environ['nova.context'] -- cgit From a8919644dfd91ea83654aa34d41680523af27234 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 09:26:40 -0400 Subject: Removed incorrect, unreachable code --- nova/tests/db/fakes.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 58d251b1e..8bdea359a 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -124,7 +124,6 @@ def stub_out_db_instance_api(stubs, injected=True): return FakeModel(vlan_network_fields) else: return FakeModel(flat_network_fields) - return FakeModel(network_fields) def fake_network_get_all_by_instance(context, instance_id): # Even instance numbers are on vlan networks -- cgit From c02ba694e9c5793980f0678c616fac16687f7407 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 10:02:21 -0400 Subject: Explicitly casted a str to a str to please pylint --- nova/tests/test_virt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 1311ba361..d743f94f7 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -642,7 +642,7 @@ class LibvirtConnTestCase(test.TestCase): try: conn.spawn(instance, network_info) except Exception, e: - count = (0 <= e.message.find('Unexpected method call')) + count = (0 <= str(e.message).find('Unexpected method call')) self.assertTrue(count) -- cgit From bbb2bba0f05493ec40c70279b532b26a4a4c235c Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 10:48:11 -0400 Subject: Added stub function for a referenced, previously non-existant function --- nova/api/ec2/cloud.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 092b80fa2..40ae06750 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -366,6 +366,9 @@ class CloudController(object): g['ipPermissions'] += [r] return g + def _get_instance(instance_id): + raise NotImplementedError + def _revoke_rule_args_to_dict(self, context, to_port=None, from_port=None, ip_protocol=None, cidr_ip=None, user_id=None, source_security_group_name=None, -- cgit From 2946a21f78e4fd2b18bd6eb8c85eb2cc0c764f8a Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 14:13:27 -0400 Subject: Added interface functions --- nova/api/ec2/cloud.py | 2 +- nova/api/openstack/servers.py | 14 ++++++-------- nova/api/openstack/views/limits.py | 9 +++++++++ 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 40ae06750..9aa4e7778 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -367,7 +367,7 @@ class CloudController(object): return g def _get_instance(instance_id): - raise NotImplementedError + raise NotImplementedError() def _revoke_rule_args_to_dict(self, context, to_port=None, from_port=None, ip_protocol=None, cidr_ip=None, user_id=None, diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1d3eab65b..2bf405545 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -76,24 +76,22 @@ class Controller(common.OpenstackController): return self._items(req, is_detail=True) def _image_id_from_req_data(self, data): - raise NotImplementedError + raise NotImplementedError() def _flavor_id_from_req_data(self, data): - raise NotImplementedError + raise NotImplementedError() def _get_view_builder(self, req): - raise NotImplementedError + raise NotImplementedError() def _limit_items(self, items, req): - raise NotImplementedError + raise NotImplementedError() def _limit_items(self, items, req): - raise NotImplementedError + raise NotImplementedError() def _action_rebuild(self, info, request, instance_id): - raise NotImplementedError - - + raise NotImplementedError() def _items(self, req, is_detail): """Returns a list of servers for a given user. diff --git a/nova/api/openstack/views/limits.py b/nova/api/openstack/views/limits.py index 552db39ee..22d1c260d 100644 --- a/nova/api/openstack/views/limits.py +++ b/nova/api/openstack/views/limits.py @@ -23,6 +23,15 @@ from nova.api.openstack import common class ViewBuilder(object): """Openstack API base limits view builder.""" + def _build_rate_limits(self, rate_limits): + raise NotImplementedError() + + def _build_rate_limit(self, rate_limit): + raise NotImplementedError() + + def _build_absolute_limits(self, absolute_limit): + raise NotImplementedError() + def build(self, rate_limits, absolute_limits): rate_limits = self._build_rate_limits(rate_limits) absolute_limits = self._build_absolute_limits(absolute_limits) -- cgit From 3a7d1422d52f551e870542305ce9bab9e9e6ebad Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 16:13:35 -0400 Subject: Fixed method in flavors --- nova/api/openstack/flavors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index dee70bb2b..4c5971cf6 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -46,7 +46,7 @@ class Controller(common.OpenstackController): return dict(flavors=items) def _get_view_builder(self, req): - raise NotImplementedError + raise NotImplementedError() def _get_flavors(self, req, is_detail=True): """Helper function that returns a list of flavor dicts.""" -- cgit From e1cfa28fc9e1194bbd4c9ce9c2f06ea3f6e5548e Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 6 May 2011 16:48:38 -0400 Subject: Fixed duplicate function --- nova/api/openstack/servers.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 2bf405545..71cb31c72 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -87,9 +87,6 @@ class Controller(common.OpenstackController): def _limit_items(self, items, req): raise NotImplementedError() - def _limit_items(self, items, req): - raise NotImplementedError() - def _action_rebuild(self, info, request, instance_id): raise NotImplementedError() -- cgit From 50404512a84971fb895f8f174230a7230b8f9474 Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Fri, 6 May 2011 17:37:35 -0400 Subject: convert quota table to key-value --- nova/db/api.py | 30 ++-- nova/db/sqlalchemy/api.py | 50 ++++-- .../versions/016_make_quotas_key_and_value.py | 176 +++++++++++++++++++++ nova/db/sqlalchemy/models.py | 18 ++- nova/quota.py | 44 ++++-- nova/tests/test_quota.py | 9 +- 6 files changed, 275 insertions(+), 52 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index f9a4b5b4b..b5550fcbc 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -756,24 +756,34 @@ def auth_token_create(context, token): ################### -def quota_create(context, values): - """Create a quota from the values dictionary.""" - return IMPL.quota_create(context, values) +def quota_create(context, project_id, resource, limit): + """Create a quota for the given project and resource.""" + return IMPL.quota_create(context, project_id, resource, limit) -def quota_get(context, project_id): +def quota_get(context, project_id, resource): """Retrieve a quota or raise if it does not exist.""" - return IMPL.quota_get(context, project_id) + return IMPL.quota_get(context, project_id, resource) -def quota_update(context, project_id, values): - """Update a quota from the values dictionary.""" - return IMPL.quota_update(context, project_id, values) +def quota_get_all_by_project(context, project_id): + """Retrieve all quotas associated with a given project.""" + return IMPL.quota_get_all_by_project(context, project_id) -def quota_destroy(context, project_id): +def quota_update(context, project_id, resource, limit): + """Update a quota or raise if it does not exist""" + return IMPL.quota_update(context, project_id, resource, limit) + + +def quota_destroy(context, project_id, resource): """Destroy the quota or raise if it does not exist.""" - return IMPL.quota_destroy(context, project_id) + return IMPL.quota_destroy(context, project_id, resource) + + +def quota_destroy_all_by_project(context, project_id): + """Destroy all quotas associated with a given project.""" + return IMPL.quota_get_all_by_project(context, project_id) ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 285b22a04..929959d8e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1496,45 +1496,71 @@ def auth_token_create(_context, token): @require_admin_context -def quota_get(context, project_id, session=None): +def quota_get(context, project_id, resource, session=None): if not session: session = get_session() - result = session.query(models.Quota).\ - filter_by(project_id=project_id).\ - filter_by(deleted=can_read_deleted(context)).\ - first() + filter_by(project_id=project_id).\ + filter_by(resource=resource).\ + filter_by(deleted=False).\ + first() if not result: raise exception.ProjectQuotaNotFound(project_id=project_id) + return result + +@require_admin_context +def quota_get_all_by_project(context, project_id): + session = get_session() + result = {'project_id': project_id} + rows = session.query(models.Quota).\ + filter_by(project_id=project_id).\ + filter_by(deleted=False).\ + all() + for row in rows: + result[row.resource] = row.limit return result @require_admin_context -def quota_create(context, values): +def quota_create(context, project_id, resource, limit): quota_ref = models.Quota() - quota_ref.update(values) + quota_ref.project_id = project_id + quota_ref.resource = resource + quota_ref.limit = limit quota_ref.save() return quota_ref @require_admin_context -def quota_update(context, project_id, values): +def quota_update(context, project_id, resource, limit): session = get_session() with session.begin(): - quota_ref = quota_get(context, project_id, session=session) - quota_ref.update(values) + quota_ref = quota_get(context, project_id, resource, session=session) + quota_ref.limit = limit quota_ref.save(session=session) @require_admin_context -def quota_destroy(context, project_id): +def quota_destroy(context, project_id, resource): session = get_session() with session.begin(): - quota_ref = quota_get(context, project_id, session=session) + quota_ref = quota_get(context, project_id, resource, session=session) quota_ref.delete(session=session) +@require_admin_context +def quota_destroy_all_by_project(context, project_id): + session = get_session() + with session.begin(): + quotas = session.query(models.Quota).\ + filter_by(project_id=project_id).\ + filter_by(deleted=False).\ + all() + for quota_ref in quotas: + quota_ref.delete(session=session) + + ################### diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py new file mode 100644 index 000000000..6bf2b19af --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -0,0 +1,176 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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 sqlalchemy import * +from migrate import * + +import datetime + +meta = MetaData() + +resources = [ + 'instances', + 'cores', + 'volumes', + 'gigabytes', + 'floating_ips', + 'metadata_items', +] + + +def old_style_quotas_table(name): + return Table(name, meta, + Column('id', Integer(), primary_key=True), + Column('created_at', DateTime(), + default=datetime.datetime.utcnow), + Column('updated_at', DateTime(), + onupdate=datetime.datetime.utcnow), + Column('deleted_at', DateTime()), + Column('deleted', Boolean(), default=False), + Column('project_id', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False)), + Column('instances', Integer()), + Column('cores', Integer()), + Column('volumes', Integer()), + Column('gigabytes', Integer()), + Column('floating_ips', Integer()), + Column('metadata_items', Integer()), + ) + + +def new_style_quotas_table(name): + return Table(name, meta, + Column('id', Integer(), primary_key=True), + Column('created_at', DateTime(), + default=datetime.datetime.utcnow), + Column('updated_at', DateTime(), + onupdate=datetime.datetime.utcnow), + Column('deleted_at', DateTime()), + Column('deleted', Boolean(), default=False), + Column('project_id', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False)), + Column('resource', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False), + nullable=False), + Column('limit', Integer(), nullable=True), + ) + + +def existing_quotas_table(migrate_engine): + return Table('quotas', meta, autoload=True, autoload_with=migrate_engine) + + +def _assert_no_duplicate_project_ids(quotas): + project_ids = set() + message = 'There are duplicate active quotas for project %s' + for quota in quotas: + assert quota.project_id not in project_ids, message % quota.project_id + project_ids.add(quota.project_id) + + +def assert_old_quotas_have_no_active_duplicates(migrate_engine, quotas): + """Ensure that there are no duplicate non-deleted quota entries.""" + select = quotas.select().where(quotas.c.deleted == False) + results = migrate_engine.execute(select) + _assert_no_duplicate_project_ids(list(results)) + + +def assert_new_quotas_have_no_active_duplicates(migrate_engine, quotas): + """Ensure that there are no duplicate non-deleted quota entries.""" + for resource in resources: + select = quotas.select().\ + where(quotas.c.deleted == False).\ + where(quotas.c.resource == resource) + results = migrate_engine.execute(select) + _assert_no_duplicate_project_ids(list(results)) + + +def convert_forward(migrate_engine, old_quotas, new_quotas): + quotas = list(migrate_engine.execute(old_quotas.select())) + for quota in quotas: + for resource in resources: + limit = getattr(old_quota, resource) + if limit is None: + continue + insert = new_quotas.insert().values( + created_at=quota.created_at, + updated_at=quota.updated_at, + deleted_at=quota.deleted_at, + deleted=quota.deleted, + project_id=quota.project_id, + resource=resource, + limit=limit) + migrate_engine.execute(insert) + + +def convert_backward(migrate_engine, old_quotas, new_quotas): + quotas = {} + for quota in migrate_engine.execute(new_quotas.select()): + if (quota.resource not in resources + or quota.limit is None or quota.deleted): + continue + if not quota.project_id in quotas: + quotas[quota.project_id] = { + 'project_id': quota.project_id, + 'created_at': quota.created_at, + 'updated_at': quota.updated_at, + quota.resource: quota.limit + } + else: + if quota.created_at < quotas[quota.project_id]['created_at']: + quotas[quota.project_id]['created_at'] = quota.created_at + if quota.updated_at > quotas[quota.project_id]['updated_at']: + quotas[quota.project_id]['updated_at'] = quota.updated_at + quotas[quota.project_id][quota.resource] = quota.limit + + for quota in quotas.itervalues(): + insert = old_quotas.insert().values(**quota) + migrate_engine.execute(insert) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + old_quotas = existing_quotas_table(migrate_engine) + assert_old_quotas_have_no_active_duplicates(migrate_engine, old_quotas) + + new_quotas = new_style_quotas_table('quotas_new') + new_quotas.create() + convert_forward(migrate_engine, old_quotas, new_quotas) + old_quotas.drop() + new_quotas.rename('quotas') + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + meta.bind = migrate_engine + + new_quotas = existing_quotas_table(migrate_engine) + assert_new_quotas_have_no_active_duplicates(migrate_engine, new_quotas) + + old_quotas = old_style_quotas_table('quotas_old') + old_quotas.create() + convert_backward(migrate_engine, old_quotas, new_quotas) + new_quotas.drop() + old_quotas.rename('quotas') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 36a084a1d..e477040d3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -313,18 +313,20 @@ class Volume(BASE, NovaBase): class Quota(BASE, NovaBase): - """Represents quota overrides for a project.""" + """Represents a single quota override for a project. + + If there is no row for a given project id and resource, then + the default for the deployment is used. If the row is present + but the limit is Null, then the resource is unlimited. + """ + __tablename__ = 'quotas' id = Column(Integer, primary_key=True) - project_id = Column(String(255)) + project_id = Column(String(255), index=True) - instances = Column(Integer) - cores = Column(Integer) - volumes = Column(Integer) - gigabytes = Column(Integer) - floating_ips = Column(Integer) - metadata_items = Column(Integer) + resource = Column(String(255)) + limit = Column(Integer, nullable=True) class ExportDevice(BASE, NovaBase): diff --git a/nova/quota.py b/nova/quota.py index d8b5d9a93..b6090c5b3 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -52,26 +52,31 @@ def get_quota(context, project_id): 'floating_ips': FLAGS.quota_floating_ips, 'metadata_items': FLAGS.quota_metadata_items} - try: - quota = db.quota_get(context, project_id) - for key in rval.keys(): - if quota[key] is not None: - rval[key] = quota[key] - except exception.NotFound: - pass + quota = db.quota_get_all_by_project(context, project_id) + for key in rval.keys(): + if key in quota: + rval[key] = quota[key] return rval +def _get_allowed_resources(requested, used, quota): + if quota is None: + return requested + return quota - used + + def allowed_instances(context, num_instances, instance_type): """Check quota and return min(num_instances, allowed_instances).""" project_id = context.project_id context = context.elevated() + num_cores = num_instances * instance_type['vcpus'] used_instances, used_cores = db.instance_data_get_for_project(context, project_id) quota = get_quota(context, project_id) - allowed_instances = quota['instances'] - used_instances - allowed_cores = quota['cores'] - used_cores - num_cores = num_instances * instance_type['vcpus'] + allowed_instances = _get_allowed_resources(num_instances, used_instances, + quota['instances']) + allowed_cores = _get_allowed_resources(num_cores, used_cores, + quota['cores']) allowed_instances = min(allowed_instances, int(allowed_cores // instance_type['vcpus'])) return min(num_instances, allowed_instances) @@ -81,13 +86,15 @@ def allowed_volumes(context, num_volumes, size): """Check quota and return min(num_volumes, allowed_volumes).""" project_id = context.project_id context = context.elevated() + size = int(size) + num_gigabytes = num_volumes * size used_volumes, used_gigabytes = db.volume_data_get_for_project(context, project_id) quota = get_quota(context, project_id) - allowed_volumes = quota['volumes'] - used_volumes - allowed_gigabytes = quota['gigabytes'] - used_gigabytes - size = int(size) - num_gigabytes = num_volumes * size + allowed_volumes = _get_allowed_resources(num_volumes, used_volumes, + quota['volumes']) + allowed_gigabytes = _get_allowed_resources(num_gigabytes, used_gigabytes, + quota['gigabytes']) allowed_volumes = min(allowed_volumes, int(allowed_gigabytes // size)) return min(num_volumes, allowed_volumes) @@ -99,7 +106,9 @@ def allowed_floating_ips(context, num_floating_ips): context = context.elevated() used_floating_ips = db.floating_ip_count_by_project(context, project_id) quota = get_quota(context, project_id) - allowed_floating_ips = quota['floating_ips'] - used_floating_ips + allowed_floating_ips = _get_allowed_resources(num_floating_ips, + used_floating_ips, + quota['floating_ips']) return min(num_floating_ips, allowed_floating_ips) @@ -108,8 +117,9 @@ def allowed_metadata_items(context, num_metadata_items): project_id = context.project_id context = context.elevated() quota = get_quota(context, project_id) - num_allowed_metadata_items = quota['metadata_items'] - return min(num_metadata_items, num_allowed_metadata_items) + allowed_metadata_items = _get_allowed_resources(num_metadata_items, 0, + quota['metadata_items']) + return min(num_metadata_items, allowed_metadata_items) def allowed_injected_files(context): diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 39a123158..518a76150 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -96,12 +96,11 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 2) - db.quota_create(self.context, {'project_id': self.project.id, - 'instances': 10}) + db.quota_create(self.context, self.project.id, 'instances', 10) num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 4) - db.quota_update(self.context, self.project.id, {'cores': 100}) + db.quota_create(self.context, self.project.id, 'cores', 100) num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 10) @@ -111,13 +110,13 @@ class QuotaTestCase(test.TestCase): num_metadata_items = quota.allowed_metadata_items(self.context, too_many_items) self.assertEqual(num_metadata_items, FLAGS.quota_metadata_items) - db.quota_update(self.context, self.project.id, {'metadata_items': 5}) + db.quota_create(self.context, self.project.id, 'metadata_items', 5) num_metadata_items = quota.allowed_metadata_items(self.context, too_many_items) self.assertEqual(num_metadata_items, 5) # Cleanup - db.quota_destroy(self.context, self.project.id) + db.quota_destroy_all_by_project(self.context, self.project.id) def test_too_many_instances(self): instance_ids = [] -- cgit From d9220c1af021b6c019207e7b9d24e30522bed149 Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Mon, 9 May 2011 14:44:39 -0400 Subject: update tests to handle unlimited resources in the db --- nova/tests/test_quota.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 518a76150..7ace2ad7d 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -118,6 +118,78 @@ class QuotaTestCase(test.TestCase): # Cleanup db.quota_destroy_all_by_project(self.context, self.project.id) + def test_unlimited_instances(self): + FLAGS.quota_instances = 2 + FLAGS.quota_cores = 1000 + instance_type = self._get_instance_type('m1.small') + num_instances = quota.allowed_instances(self.context, 100, + instance_type) + self.assertEqual(num_instances, 2) + db.quota_create(self.context, self.project.id, 'instances', None) + num_instances = quota.allowed_instances(self.context, 100, + instance_type) + self.assertEqual(num_instances, 100) + num_instances = quota.allowed_instances(self.context, 101, + instance_type) + self.assertEqual(num_instances, 101) + + def test_unlimited_cores(self): + FLAGS.quota_instances = 1000 + FLAGS.quota_cores = 2 + instance_type = self._get_instance_type('m1.small') + num_instances = quota.allowed_instances(self.context, 100, + instance_type) + self.assertEqual(num_instances, 2) + db.quota_create(self.context, self.project.id, 'cores', None) + num_instances = quota.allowed_instances(self.context, 100, + instance_type) + self.assertEqual(num_instances, 100) + num_instances = quota.allowed_instances(self.context, 101, + instance_type) + self.assertEqual(num_instances, 101) + + def test_unlimited_volumes(self): + FLAGS.quota_volumes = 10 + FLAGS.quota_gigabytes = 1000 + volumes = quota.allowed_volumes(self.context, 100, 1) + self.assertEqual(volumes, 10) + db.quota_create(self.context, self.project.id, 'volumes', None) + volumes = quota.allowed_volumes(self.context, 100, 1) + self.assertEqual(volumes, 100) + volumes = quota.allowed_volumes(self.context, 101, 1) + self.assertEqual(volumes, 101) + + def test_unlimited_gigabytes(self): + FLAGS.quota_volumes = 1000 + FLAGS.quota_gigabytes = 10 + volumes = quota.allowed_volumes(self.context, 100, 1) + self.assertEqual(volumes, 10) + db.quota_create(self.context, self.project.id, 'gigabytes', None) + volumes = quota.allowed_volumes(self.context, 100, 1) + self.assertEqual(volumes, 100) + volumes = quota.allowed_volumes(self.context, 101, 1) + self.assertEqual(volumes, 101) + + def test_unlimited_floating_ips(self): + FLAGS.quota_floating_ips = 10 + floating_ips = quota.allowed_floating_ips(self.context, 100) + self.assertEqual(floating_ips, 10) + db.quota_create(self.context, self.project.id, 'floating_ips', None) + floating_ips = quota.allowed_floating_ips(self.context, 100) + self.assertEqual(floating_ips, 100) + floating_ips = quota.allowed_floating_ips(self.context, 101) + self.assertEqual(floating_ips, 101) + + def test_unlimited_metadata_items(self): + FLAGS.quota_metadata_items = 10 + items = quota.allowed_metadata_items(self.context, 100) + self.assertEqual(items, 10) + db.quota_create(self.context, self.project.id, 'metadata_items', None) + items = quota.allowed_metadata_items(self.context, 100) + self.assertEqual(items, 100) + items = quota.allowed_metadata_items(self.context, 101) + self.assertEqual(items, 101) + def test_too_many_instances(self): instance_ids = [] for i in range(FLAGS.quota_instances): -- cgit From aa73995f9ba7e4aaaee00ddd0db0b92dcd92dd54 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 10 May 2011 16:11:59 +0000 Subject: Change xenapi's wait_for_task to handle multiple simultaenous queries to fix lp:766404 --- nova/virt/xenapi_conn.py | 86 ++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0cabccf08..5dc6b034f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -326,7 +326,6 @@ class XenAPISession(object): "(is the Dom0 disk full?)")) with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): self._session.login_with_password(user, pw) - self.loop = None def get_imported_xenapi(self): """Stubout point. This can be replaced with a mock xenapi module.""" @@ -363,57 +362,52 @@ class XenAPISession(object): def wait_for_task(self, task, id=None): """Return the result of the given task. The task is polled - until it completes. Not re-entrant.""" + until it completes.""" done = event.Event() - self.loop = utils.LoopingCall(self._poll_task, id, task, done) - self.loop.start(FLAGS.xenapi_task_poll_interval, now=True) - rv = done.wait() - self.loop.stop() - return rv - - def _stop_loop(self): - """Stop polling for task to finish.""" - #NOTE(sandy-walsh) Had to break this call out to support unit tests. - if self.loop: - self.loop.stop() + loop = utils.LoopingCall(f=None) + + def _poll_task(): + """Poll the given XenAPI task, and return the result if the + action was completed successfully or not. + """ + try: + name = self._session.xenapi.task.get_name_label(task) + status = self._session.xenapi.task.get_status(task) + if id: + action = dict( + instance_id=int(id), + action=name[0:255], # Ensure action is never > 255 + error=None) + if status == "pending": + return + elif status == "success": + result = self._session.xenapi.task.get_result(task) + LOG.info(_("Task [%(name)s] %(task)s status:" + " success %(result)s") % locals()) + done.send(_parse_xmlrpc_value(result)) + else: + error_info = self._session.xenapi.task.get_error_info(task) + action["error"] = str(error_info) + LOG.warn(_("Task [%(name)s] %(task)s status:" + " %(status)s %(error_info)s") % locals()) + done.send_exception(self.XenAPI.Failure(error_info)) + + if id: + db.instance_action_create(context.get_admin_context(), + action) + except self.XenAPI.Failure, exc: + LOG.warn(exc) + done.send_exception(*sys.exc_info()) + loop.stop() + + loop.f = _poll_task + loop.start(FLAGS.xenapi_task_poll_interval, now=True) + return done.wait() def _create_session(self, url): """Stubout point. This can be replaced with a mock session.""" return self.XenAPI.Session(url) - def _poll_task(self, id, task, done): - """Poll the given XenAPI task, and fire the given action if we - get a result. - """ - try: - name = self._session.xenapi.task.get_name_label(task) - status = self._session.xenapi.task.get_status(task) - if id: - action = dict( - instance_id=int(id), - action=name[0:255], # Ensure action is never > 255 - error=None) - if status == "pending": - return - elif status == "success": - result = self._session.xenapi.task.get_result(task) - LOG.info(_("Task [%(name)s] %(task)s status:" - " success %(result)s") % locals()) - done.send(_parse_xmlrpc_value(result)) - else: - error_info = self._session.xenapi.task.get_error_info(task) - action["error"] = str(error_info) - LOG.warn(_("Task [%(name)s] %(task)s status:" - " %(status)s %(error_info)s") % locals()) - done.send_exception(self.XenAPI.Failure(error_info)) - - if id: - db.instance_action_create(context.get_admin_context(), action) - except self.XenAPI.Failure, exc: - LOG.warn(exc) - done.send_exception(*sys.exc_info()) - self._stop_loop() - def _unwrap_plugin_exceptions(self, func, *args, **kwargs): """Parse exception details""" try: -- cgit From f0c4bc2ff0182292e667bbcafb349e407596148a Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Tue, 10 May 2011 14:49:47 -0400 Subject: migration bug fixes --- .../versions/016_make_quotas_key_and_value.py | 34 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index 6bf2b19af..c03015b5e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -108,7 +108,7 @@ def convert_forward(migrate_engine, old_quotas, new_quotas): quotas = list(migrate_engine.execute(old_quotas.select())) for quota in quotas: for resource in resources: - limit = getattr(old_quota, resource) + limit = getattr(old_quotas, resource) if limit is None: continue insert = new_quotas.insert().values( @@ -122,6 +122,30 @@ def convert_forward(migrate_engine, old_quotas, new_quotas): migrate_engine.execute(insert) +def earliest(date1, date2): + if date1 is None and date2 is None: + return None + if date1 is None: + return date2 + if date2 is None: + return date1 + if date1 < date2: + return date1 + return date2 + + +def latest(date1, date2): + if date1 is None and date2 is None: + return None + if date1 is None: + return date2 + if date2 is None: + return date1 + if date1 > date2: + return date1 + return date2 + + def convert_backward(migrate_engine, old_quotas, new_quotas): quotas = {} for quota in migrate_engine.execute(new_quotas.select()): @@ -136,10 +160,10 @@ def convert_backward(migrate_engine, old_quotas, new_quotas): quota.resource: quota.limit } else: - if quota.created_at < quotas[quota.project_id]['created_at']: - quotas[quota.project_id]['created_at'] = quota.created_at - if quota.updated_at > quotas[quota.project_id]['updated_at']: - quotas[quota.project_id]['updated_at'] = quota.updated_at + quotas[quota.project_id]['created_at'] = earliest( + quota.created_at, quotas[quota.project_id]['created_at']) + quotas[quota.project_id]['updated_at'] = latest( + quota.created_at, quotas[quota.project_id]['updated_at']) quotas[quota.project_id][quota.resource] = quota.limit for quota in quotas.itervalues(): -- cgit From a67c77ce504ad2e15b013ed40421d0d0b823767f Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 10 May 2011 18:55:07 +0000 Subject: remove stubbing of XenAPISession.wait_for_task for xenapi tests as it doesn't need to be faked. Also removed duplicate code that stubbed xenapi_conn._parse_xmlrpc_value. --- nova/tests/xenapi/stubs.py | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'nova') diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 205f6c902..6db061444 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -28,29 +28,6 @@ def stubout_instance_snapshot(stubs): @classmethod def fake_fetch_image(cls, session, instance_id, image, user, project, type): - # Stubout wait_for_task - def fake_wait_for_task(self, task, id): - class FakeEvent: - - def send(self, value): - self.rv = value - - def wait(self): - return self.rv - - done = FakeEvent() - self._poll_task(id, task, done) - rv = done.wait() - return rv - - def fake_loop(self): - pass - - stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', - fake_wait_for_task) - - stubs.Set(xenapi_conn.XenAPISession, '_stop_loop', fake_loop) - from nova.virt.xenapi.fake import create_vdi name_label = "instance-%s" % instance_id #TODO: create fake SR record @@ -63,11 +40,6 @@ def stubout_instance_snapshot(stubs): stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image) - def fake_parse_xmlrpc_value(val): - return val - - stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value) - def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): from nova.virt.xenapi.fake import create_vdi -- cgit From de9b191905803ff8742c3dde4335682d53b01fcd Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Tue, 10 May 2011 15:54:05 -0400 Subject: fix migration bug --- .../sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index c03015b5e..d7c4fb960 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -108,7 +108,7 @@ def convert_forward(migrate_engine, old_quotas, new_quotas): quotas = list(migrate_engine.execute(old_quotas.select())) for quota in quotas: for resource in resources: - limit = getattr(old_quotas, resource) + limit = getattr(quota, resource) if limit is None: continue insert = new_quotas.insert().values( -- cgit From 59b593fb70b57864b84677644786d5175b0811be Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Tue, 10 May 2011 16:38:04 -0400 Subject: give a more informative message if pre-migration assertions fail --- .../migrate_repo/versions/016_make_quotas_key_and_value.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index d7c4fb960..25bd1f4de 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -81,7 +81,10 @@ def existing_quotas_table(migrate_engine): def _assert_no_duplicate_project_ids(quotas): project_ids = set() - message = 'There are duplicate active quotas for project %s' + message = ('There are multiple active quotas for project "%s" ' + '(among others, possibly). ' + 'Please resolve all ambiguous quotas before ' + 'reattempting the migration.') for quota in quotas: assert quota.project_id not in project_ids, message % quota.project_id project_ids.add(quota.project_id) -- cgit From a5c31944708f7afe75c51ef84f2712df3e8ad416 Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Tue, 10 May 2011 20:10:10 -0400 Subject: migrate back updated_at correctly --- .../sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index 25bd1f4de..82129cfc4 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -166,7 +166,7 @@ def convert_backward(migrate_engine, old_quotas, new_quotas): quotas[quota.project_id]['created_at'] = earliest( quota.created_at, quotas[quota.project_id]['created_at']) quotas[quota.project_id]['updated_at'] = latest( - quota.created_at, quotas[quota.project_id]['updated_at']) + quota.updated_at, quotas[quota.project_id]['updated_at']) quotas[quota.project_id][quota.resource] = quota.limit for quota in quotas.itervalues(): -- cgit From 43fa5afac9e5af74e2e3977a5dafd9640d064cf1 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 11 May 2011 15:12:12 +0000 Subject: Abstract out IPv6 address generation to pluggable backends --- nova/api/ec2/cloud.py | 3 ++- nova/db/sqlalchemy/api.py | 5 +++-- nova/ipv6/__init__.py | 17 +++++++++++++++++ nova/ipv6/api.py | 34 ++++++++++++++++++++++++++++++++++ nova/ipv6/rfc2462.py | 42 ++++++++++++++++++++++++++++++++++++++++++ nova/tests/network/base.py | 8 ++++---- nova/utils.py | 20 -------------------- nova/virt/libvirt_conn.py | 3 ++- nova/virt/xenapi/vmops.py | 3 ++- 9 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 nova/ipv6/__init__.py create mode 100644 nova/ipv6/api.py create mode 100644 nova/ipv6/rfc2462.py (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 092b80fa2..993c91fe1 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -39,6 +39,7 @@ from nova import log as logging from nova import network from nova import utils from nova import volume +from nova import ipv6 from nova.api.ec2 import ec2utils from nova.compute import instance_types from nova.image import s3 @@ -718,7 +719,7 @@ class CloudController(object): fixed = instance['fixed_ip'] floating_addr = fixed['floating_ips'][0]['address'] if instance['fixed_ip']['network'] and 'use_v6' in kwargs: - i['dnsNameV6'] = utils.to_global_ipv6( + i['dnsNameV6'] = ipv6.to_global( instance['fixed_ip']['network']['cidr_v6'], instance['mac_address']) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 285b22a04..6c76248ce 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -26,6 +26,7 @@ from nova import db from nova import exception from nova import flags from nova import utils +from nova import ipv6 from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ @@ -744,7 +745,7 @@ def fixed_ip_get_all_by_instance(context, instance_id): @require_context def fixed_ip_get_instance_v6(context, address): session = get_session() - mac = utils.to_mac(address) + mac = ipv6.to_mac(address) result = session.query(models.Instance).\ filter_by(mac_address=mac).\ @@ -974,7 +975,7 @@ def instance_get_fixed_address_v6(context, instance_id): network_ref = network_get_by_instance(context, instance_id) prefix = network_ref.cidr_v6 mac = instance_ref.mac_address - return utils.to_global_ipv6(prefix, mac) + return ipv6.to_global(prefix, mac) @require_context diff --git a/nova/ipv6/__init__.py b/nova/ipv6/__init__.py new file mode 100644 index 000000000..da4567cfb --- /dev/null +++ b/nova/ipv6/__init__.py @@ -0,0 +1,17 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Openstack, LLC. +# +# 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.ipv6.api import * diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py new file mode 100644 index 000000000..95b20c945 --- /dev/null +++ b/nova/ipv6/api.py @@ -0,0 +1,34 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Openstack, LLC. +# +# 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 flags +from nova import utils + + +FLAGS = flags.FLAGS +flags.DEFINE_string('ipv6_backend', + 'rfc2462', + 'Backend to use for IPv6 generation') + +IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], + rfc2462='nova.ipv6.rfc2462') + + +def to_global(prefix, mac): + return IMPL.to_global(prefix, mac) + +def to_mac(ipv6_address): + return IMPL.to_mac(ipv6_address) diff --git a/nova/ipv6/rfc2462.py b/nova/ipv6/rfc2462.py new file mode 100644 index 000000000..3af4556e7 --- /dev/null +++ b/nova/ipv6/rfc2462.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Justin Santa Barbara +# 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. + +"""RFC2462 style IPv6 address generation""" + +import netaddr + + +def to_global(prefix, mac): + try: + mac64 = netaddr.EUI(mac).eui64().words + int_addr = int(''.join(['%02x' % i for i in mac64]), 16) + mac64_addr = netaddr.IPAddress(int_addr) + maskIP = netaddr.IPNetwork(prefix).ip + return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\ + format() + except TypeError: + raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac) + + +def to_mac(ipv6_address): + address = netaddr.IPAddress(ipv6_address) + mask1 = netaddr.IPAddress('::ffff:ffff:ffff:ffff') + mask2 = netaddr.IPAddress('::0200:0:0:0') + mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words + return ':'.join(['%02x' % i for i in mac64[0:3] + mac64[5:8]]) diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py index 988a1de72..5de1255cd 100644 --- a/nova/tests/network/base.py +++ b/nova/tests/network/base.py @@ -28,6 +28,7 @@ from nova import flags from nova import log as logging from nova import test from nova import utils +from nova import ipv6 from nova.auth import manager FLAGS = flags.FLAGS @@ -117,15 +118,14 @@ class NetworkTestCase(test.TestCase): context.get_admin_context(), instance_ref['id']) self.assertEqual(instance_ref['mac_address'], - utils.to_mac(address_v6)) + ipv6.to_mac(address_v6)) instance_ref2 = db.fixed_ip_get_instance_v6( context.get_admin_context(), address_v6) self.assertEqual(instance_ref['id'], instance_ref2['id']) self.assertEqual(address_v6, - utils.to_global_ipv6( - network_ref['cidr_v6'], - instance_ref['mac_address'])) + ipv6.to_global(network_ref['cidr_v6'], + instance_ref['mac_address'])) self._deallocate_address(0, address) db.instance_destroy(context.get_admin_context(), instance_ref['id']) diff --git a/nova/utils.py b/nova/utils.py index 80bf1197f..aa77caf71 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -303,26 +303,6 @@ def get_my_linklocal(interface): " :%(ex)s") % locals()) -def to_global_ipv6(prefix, mac): - try: - mac64 = netaddr.EUI(mac).eui64().words - int_addr = int(''.join(['%02x' % i for i in mac64]), 16) - mac64_addr = netaddr.IPAddress(int_addr) - maskIP = netaddr.IPNetwork(prefix).ip - return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\ - format() - except TypeError: - raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac) - - -def to_mac(ipv6_address): - address = netaddr.IPAddress(ipv6_address) - mask1 = netaddr.IPAddress('::ffff:ffff:ffff:ffff') - mask2 = netaddr.IPAddress('::0200:0:0:0') - mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words - return ':'.join(['%02x' % i for i in mac64[0:3] + mac64[5:8]]) - - def utcnow(): """Overridable version of datetime.datetime.utcnow.""" if utcnow.override_time: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9780c69a6..4dce3b41f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -60,6 +60,7 @@ from nova import flags from nova import log as logging from nova import utils from nova import vnc +from nova import ipv6 from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state @@ -185,7 +186,7 @@ def _get_network_info(instance): prefix = network['cidr_v6'] mac = instance['mac_address'] return { - 'ip': utils.to_global_ipv6(prefix, mac), + 'ip': ipv6.to_global(prefix, mac), 'netmask': network['netmask_v6'], 'enabled': '1'} diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fe9a74dd6..0b05e702a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -34,6 +34,7 @@ from nova import log as logging from nova import exception from nova import utils from nova import flags +from nova import ipv6 from nova.auth.manager import AuthManager from nova.compute import power_state @@ -808,7 +809,7 @@ class VMOps(object): def ip6_dict(): return { - "ip": utils.to_global_ipv6(network['cidr_v6'], + "ip": ipv6.to_global(network['cidr_v6'], instance['mac_address']), "netmask": network['netmask_v6'], "enabled": "1"} -- cgit From c1fdb9a2e26b8d2d4f8faa4b7412e8f17ea939e9 Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Wed, 11 May 2011 13:00:06 -0400 Subject: better pylint scores on imports --- .../sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index 82129cfc4..03d346af4 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -14,8 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * -from migrate import * +from sqlalchemy import Boolean, Column, DateTime, Integer +from sqlalchemy import MetaData, String, Table import datetime -- cgit From 44a482081b44d25738549a5a445c4d581f6816ae Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Wed, 11 May 2011 13:21:57 -0400 Subject: align filters on query --- nova/db/sqlalchemy/api.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 929959d8e..84b472264 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1500,10 +1500,10 @@ def quota_get(context, project_id, resource, session=None): if not session: session = get_session() result = session.query(models.Quota).\ - filter_by(project_id=project_id).\ - filter_by(resource=resource).\ - filter_by(deleted=False).\ - first() + filter_by(project_id=project_id).\ + filter_by(resource=resource).\ + filter_by(deleted=False).\ + first() if not result: raise exception.ProjectQuotaNotFound(project_id=project_id) return result @@ -1514,9 +1514,9 @@ def quota_get_all_by_project(context, project_id): session = get_session() result = {'project_id': project_id} rows = session.query(models.Quota).\ - filter_by(project_id=project_id).\ - filter_by(deleted=False).\ - all() + filter_by(project_id=project_id).\ + filter_by(deleted=False).\ + all() for row in rows: result[row.resource] = row.limit return result -- cgit From afac61da9bb77cb2b4b0d6e79f47d4579ba9c9fc Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Wed, 11 May 2011 13:34:01 -0400 Subject: more filter alignment --- nova/db/sqlalchemy/api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 84b472264..56b40f95b 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1554,9 +1554,9 @@ def quota_destroy_all_by_project(context, project_id): session = get_session() with session.begin(): quotas = session.query(models.Quota).\ - filter_by(project_id=project_id).\ - filter_by(deleted=False).\ - all() + filter_by(project_id=project_id).\ + filter_by(deleted=False).\ + all() for quota_ref in quotas: quota_ref.delete(session=session) -- cgit From d19d03fcfecfe51b63b1e681d9e94b9996cd9aef Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Wed, 11 May 2011 14:01:41 -0400 Subject: better function name --- nova/quota.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/quota.py b/nova/quota.py index b6090c5b3..a93cd0766 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -59,7 +59,7 @@ def get_quota(context, project_id): return rval -def _get_allowed_resources(requested, used, quota): +def _get_request_allotment(requested, used, quota): if quota is None: return requested return quota - used @@ -73,9 +73,9 @@ def allowed_instances(context, num_instances, instance_type): used_instances, used_cores = db.instance_data_get_for_project(context, project_id) quota = get_quota(context, project_id) - allowed_instances = _get_allowed_resources(num_instances, used_instances, + allowed_instances = _get_request_allotment(num_instances, used_instances, quota['instances']) - allowed_cores = _get_allowed_resources(num_cores, used_cores, + allowed_cores = _get_request_allotment(num_cores, used_cores, quota['cores']) allowed_instances = min(allowed_instances, int(allowed_cores // instance_type['vcpus'])) @@ -91,9 +91,9 @@ def allowed_volumes(context, num_volumes, size): used_volumes, used_gigabytes = db.volume_data_get_for_project(context, project_id) quota = get_quota(context, project_id) - allowed_volumes = _get_allowed_resources(num_volumes, used_volumes, + allowed_volumes = _get_request_allotment(num_volumes, used_volumes, quota['volumes']) - allowed_gigabytes = _get_allowed_resources(num_gigabytes, used_gigabytes, + allowed_gigabytes = _get_request_allotment(num_gigabytes, used_gigabytes, quota['gigabytes']) allowed_volumes = min(allowed_volumes, int(allowed_gigabytes // size)) @@ -106,7 +106,7 @@ def allowed_floating_ips(context, num_floating_ips): context = context.elevated() used_floating_ips = db.floating_ip_count_by_project(context, project_id) quota = get_quota(context, project_id) - allowed_floating_ips = _get_allowed_resources(num_floating_ips, + allowed_floating_ips = _get_request_allotment(num_floating_ips, used_floating_ips, quota['floating_ips']) return min(num_floating_ips, allowed_floating_ips) @@ -117,7 +117,7 @@ def allowed_metadata_items(context, num_metadata_items): project_id = context.project_id context = context.elevated() quota = get_quota(context, project_id) - allowed_metadata_items = _get_allowed_resources(num_metadata_items, 0, + allowed_metadata_items = _get_request_allotment(num_metadata_items, 0, quota['metadata_items']) return min(num_metadata_items, allowed_metadata_items) -- cgit From 79466a3a7b67478871f178115d95378643caf29f Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Wed, 11 May 2011 14:32:28 -0400 Subject: oops fixed a docstring --- nova/db/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index b5550fcbc..ef8aa1143 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -772,7 +772,7 @@ def quota_get_all_by_project(context, project_id): def quota_update(context, project_id, resource, limit): - """Update a quota or raise if it does not exist""" + """Update a quota or raise if it does not exist.""" return IMPL.quota_update(context, project_id, resource, limit) -- cgit From d2b8350a026e0f00eae7cadbacaa15d4b44331af Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 11 May 2011 21:04:40 +0000 Subject: Implement IPv6 address generation that includes account identifier --- nova/api/ec2/cloud.py | 3 ++- nova/db/sqlalchemy/api.py | 3 ++- nova/ipv6/account_identifier.py | 45 +++++++++++++++++++++++++++++++++++++++++ nova/ipv6/api.py | 7 ++++--- nova/virt/libvirt_conn.py | 3 ++- nova/virt/xenapi/vmops.py | 3 ++- 6 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 nova/ipv6/account_identifier.py (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 1b51b5463..63baf8036 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -721,7 +721,8 @@ class CloudController(object): if instance['fixed_ip']['network'] and 'use_v6' in kwargs: i['dnsNameV6'] = ipv6.to_global( instance['fixed_ip']['network']['cidr_v6'], - instance['mac_address']) + instance['mac_address'], + instance['project_id']) i['privateDnsName'] = fixed_addr i['privateIpAddress'] = fixed_addr diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6c76248ce..11d07c9e9 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -975,7 +975,8 @@ def instance_get_fixed_address_v6(context, instance_id): network_ref = network_get_by_instance(context, instance_id) prefix = network_ref.cidr_v6 mac = instance_ref.mac_address - return ipv6.to_global(prefix, mac) + project_id = instance_ref.project_id + return ipv6.to_global(prefix, mac, project_id) @require_context diff --git a/nova/ipv6/account_identifier.py b/nova/ipv6/account_identifier.py new file mode 100644 index 000000000..258678f0a --- /dev/null +++ b/nova/ipv6/account_identifier.py @@ -0,0 +1,45 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Justin Santa Barbara +# 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. + +"""IPv6 address generation with account identifier embedded""" + +import hashlib +import netaddr + + +def to_global(prefix, mac, project_id): + project_hash = netaddr.IPAddress(int(hashlib.sha1(project_id).\ + hexdigest()[:8], 16) << 32) + static_num = netaddr.IPAddress(0xff << 24) + + try: + mac_suffix = netaddr.EUI(mac).words[3:] + int_addr = int(''.join(['%02x' % i for i in mac_suffix]), 16) + mac_addr = netaddr.IPAddress(int_addr) + maskIP = netaddr.IPNetwork(prefix).ip + return (project_hash ^ static_num ^ mac_addr | maskIP).format() + except TypeError: + raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac) + + +def to_mac(ipv6_address): + address = netaddr.IPAddress(ipv6_address) + mask1 = netaddr.IPAddress('::ff:ffff') + mac = netaddr.EUI(int(address & mask1)).words + return ':'.join(['02', '16', '3e'] + ['%02x' % i for i in mac[3:6]]) diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py index 95b20c945..b7fa6bd8f 100644 --- a/nova/ipv6/api.py +++ b/nova/ipv6/api.py @@ -24,11 +24,12 @@ flags.DEFINE_string('ipv6_backend', 'Backend to use for IPv6 generation') IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], - rfc2462='nova.ipv6.rfc2462') + rfc2462='nova.ipv6.rfc2462', + account_identifier='nova.ipv6.account_identifier') -def to_global(prefix, mac): - return IMPL.to_global(prefix, mac) +def to_global(prefix, mac, project_id): + return IMPL.to_global(prefix, mac, project_id) def to_mac(ipv6_address): return IMPL.to_mac(ipv6_address) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index cde864b0d..80e1a1f85 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -185,8 +185,9 @@ def _get_network_info(instance): def ip6_dict(): prefix = network['cidr_v6'] mac = instance['mac_address'] + project_id = instance['project_id'] return { - 'ip': ipv6.to_global(prefix, mac), + 'ip': ipv6.to_global(prefix, mac, project_id), 'netmask': network['netmask_v6'], 'enabled': '1'} diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0b05e702a..cc2b54331 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -810,7 +810,8 @@ class VMOps(object): def ip6_dict(): return { "ip": ipv6.to_global(network['cidr_v6'], - instance['mac_address']), + instance['mac_address'], + instance['project_id']), "netmask": network['netmask_v6'], "enabled": "1"} -- cgit From 81b1cfc2db7f898263c0c40665769424ca5530ef Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Thu, 12 May 2011 02:27:47 -0400 Subject: rename quota column to 'hard_limit' to make it simpler to avoid collisions with sql keyword 'limit' --- nova/db/sqlalchemy/api.py | 6 +++--- .../migrate_repo/versions/016_make_quotas_key_and_value.py | 14 +++++++------- nova/db/sqlalchemy/models.py | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 56b40f95b..ea0bbb06e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1518,7 +1518,7 @@ def quota_get_all_by_project(context, project_id): filter_by(deleted=False).\ all() for row in rows: - result[row.resource] = row.limit + result[row.resource] = row.hard_limit return result @@ -1527,7 +1527,7 @@ def quota_create(context, project_id, resource, limit): quota_ref = models.Quota() quota_ref.project_id = project_id quota_ref.resource = resource - quota_ref.limit = limit + quota_ref.hard_limit = limit quota_ref.save() return quota_ref @@ -1537,7 +1537,7 @@ def quota_update(context, project_id, resource, limit): session = get_session() with session.begin(): quota_ref = quota_get(context, project_id, resource, session=session) - quota_ref.limit = limit + quota_ref.hard_limit = limit quota_ref.save(session=session) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index 03d346af4..a2d8192ca 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -71,7 +71,7 @@ def new_style_quotas_table(name): assert_unicode=None, unicode_error=None, _warn_on_bytestring=False), nullable=False), - Column('limit', Integer(), nullable=True), + Column('hard_limit', Integer(), nullable=True), ) @@ -111,8 +111,8 @@ def convert_forward(migrate_engine, old_quotas, new_quotas): quotas = list(migrate_engine.execute(old_quotas.select())) for quota in quotas: for resource in resources: - limit = getattr(quota, resource) - if limit is None: + hard_limit = getattr(quota, resource) + if hard_limit is None: continue insert = new_quotas.insert().values( created_at=quota.created_at, @@ -121,7 +121,7 @@ def convert_forward(migrate_engine, old_quotas, new_quotas): deleted=quota.deleted, project_id=quota.project_id, resource=resource, - limit=limit) + hard_limit=hard_limit) migrate_engine.execute(insert) @@ -153,21 +153,21 @@ def convert_backward(migrate_engine, old_quotas, new_quotas): quotas = {} for quota in migrate_engine.execute(new_quotas.select()): if (quota.resource not in resources - or quota.limit is None or quota.deleted): + or quota.hard_limit is None or quota.deleted): continue if not quota.project_id in quotas: quotas[quota.project_id] = { 'project_id': quota.project_id, 'created_at': quota.created_at, 'updated_at': quota.updated_at, - quota.resource: quota.limit + quota.resource: quota.hard_limit } else: quotas[quota.project_id]['created_at'] = earliest( quota.created_at, quotas[quota.project_id]['created_at']) quotas[quota.project_id]['updated_at'] = latest( quota.updated_at, quotas[quota.project_id]['updated_at']) - quotas[quota.project_id][quota.resource] = quota.limit + quotas[quota.project_id][quota.resource] = quota.hard_limit for quota in quotas.itervalues(): insert = old_quotas.insert().values(**quota) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index e477040d3..0b46d5a05 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -317,7 +317,7 @@ class Quota(BASE, NovaBase): If there is no row for a given project id and resource, then the default for the deployment is used. If the row is present - but the limit is Null, then the resource is unlimited. + but the hard limit is Null, then the resource is unlimited. """ __tablename__ = 'quotas' @@ -326,7 +326,7 @@ class Quota(BASE, NovaBase): project_id = Column(String(255), index=True) resource = Column(String(255)) - limit = Column(Integer, nullable=True) + hard_limit = Column(Integer, nullable=True) class ExportDevice(BASE, NovaBase): -- cgit From 33466d3ca067b8fec75380a27d5a2a196515bb50 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 12 May 2011 18:40:56 +0000 Subject: Accept and ignore project_id --- nova/ipv6/rfc2462.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/ipv6/rfc2462.py b/nova/ipv6/rfc2462.py index 3af4556e7..0074efe98 100644 --- a/nova/ipv6/rfc2462.py +++ b/nova/ipv6/rfc2462.py @@ -22,7 +22,7 @@ import netaddr -def to_global(prefix, mac): +def to_global(prefix, mac, project_id): try: mac64 = netaddr.EUI(mac).eui64().words int_addr = int(''.join(['%02x' % i for i in mac64]), 16) -- cgit From 6d140b61cd146613b282c2f1f046c529d3112553 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 12 May 2011 18:41:22 +0000 Subject: Add test suite for IPv6 address generation --- nova/ipv6/api.py | 11 ++++++--- nova/tests/test_ipv6.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 nova/tests/test_ipv6.py (limited to 'nova') diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py index b7fa6bd8f..cdda2c253 100644 --- a/nova/ipv6/api.py +++ b/nova/ipv6/api.py @@ -23,13 +23,18 @@ flags.DEFINE_string('ipv6_backend', 'rfc2462', 'Backend to use for IPv6 generation') -IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], - rfc2462='nova.ipv6.rfc2462', - account_identifier='nova.ipv6.account_identifier') +def reset_backend(): + global IMPL + IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], + rfc2462='nova.ipv6.rfc2462', + account_identifier= + 'nova.ipv6.account_identifier') def to_global(prefix, mac, project_id): return IMPL.to_global(prefix, mac, project_id) def to_mac(ipv6_address): return IMPL.to_mac(ipv6_address) + +reset_backend() diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py new file mode 100644 index 000000000..01d28df73 --- /dev/null +++ b/nova/tests/test_ipv6.py @@ -0,0 +1,59 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 OpenStack LLC +# +# 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. + +"""Test suite for IPv6.""" + +from nova import ipv6 +from nova import flags +from nova import log as logging +from nova import test + +LOG = logging.getLogger('nova.tests.test_ipv6') + +FLAGS = flags.FLAGS + +import sys + +class IPv6RFC2462TestCase(test.TestCase): + """Unit tests for IPv6 rfc2462 backend operations.""" + def setUp(self): + super(IPv6RFC2462TestCase, self).setUp() + self.flags(ipv6_backend='rfc2462') + ipv6.reset_backend() + + def test_to_global(self): + addr = ipv6.to_global('2001:db8::', '02:16:3e:33:44:55', 'test') + self.assertEquals(addr, '2001:db8::16:3eff:fe33:4455') + + def test_to_mac(self): + mac = ipv6.to_mac('2001:db8::216:3eff:fe33:4455') + self.assertEquals(mac, '00:16:3e:33:44:55') + + +class IPv6AccountIdentiferTestCase(test.TestCase): + """Unit tests for IPv6 account_identifier backend operations.""" + def setUp(self): + super(IPv6AccountIdentiferTestCase, self).setUp() + self.flags(ipv6_backend='account_identifier') + ipv6.reset_backend() + + def test_to_global(self): + addr = ipv6.to_global('2001:db8::', '02:16:3e:33:44:55', 'test') + self.assertEquals(addr, '2001:db8::a94a:8fe5:ff33:4455') + + def test_to_mac(self): + mac = ipv6.to_mac('2001:db8::a94a:8fe5:ff33:4455') + self.assertEquals(mac, '02:16:3e:33:44:55') -- cgit From 0cf0b89f57392688c0a443b29408813ccb028c38 Mon Sep 17 00:00:00 2001 From: John Tran Date: Thu, 12 May 2011 12:51:03 -0700 Subject: incorporated ImageNotFound instead of NotFound --- nova/api/ec2/cloud.py | 6 +----- nova/tests/test_cloud.py | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 2fcf647fb..be5dd38a0 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -963,11 +963,7 @@ class CloudController(object): def deregister_image(self, context, image_id, **kwargs): LOG.audit(_("De-registering image %s"), image_id, context=context) - try: - image = self._get_image(context, image_id) - except exception.NotFound: - raise exception.NotFound(_('Image %s not found') % - image_id) + image = self._get_image(context, image_id) internal_id = image['id'] self.image_service.delete(context, internal_id) return {'imageId': image_id} diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 82dd14cb2..af1dbfd4d 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -282,7 +282,7 @@ class CloudTestCase(test.TestCase): def test_deregister_image(self): deregister_image = self.cloud.deregister_image - def fake_delete(meh, context, id): + def fake_delete(self, context, id): return None self.stubs.Set(local.LocalImageService, 'delete', fake_delete) @@ -292,11 +292,11 @@ class CloudTestCase(test.TestCase): # invalid image self.stubs.UnsetAll() - def fake_detail_empty(meh, context): + def fake_detail_empty(self, context): return [] self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) - self.assertRaises(exception.NotFound, deregister_image, + self.assertRaises(exception.ImageNotFound, deregister_image, self.context, 'ami-bad001') def test_console_output(self): -- cgit From e0dab6d678867e11e107a9418c7baeb5ac055de7 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Thu, 12 May 2011 20:01:32 +0000 Subject: Add a test for parallel builds. verified this test fails before this fix and succeeds after this fix --- nova/tests/test_xenapi.py | 23 +++++++++++++++++++++++ nova/tests/xenapi/stubs.py | 9 +++++++++ 2 files changed, 32 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 375480a2e..a4e679817 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -16,6 +16,7 @@ """Test suite for XenAPI.""" +import eventlet import functools import os import re @@ -197,6 +198,28 @@ class XenAPIVMTestCase(test.TestCase): self.context = context.RequestContext('fake', 'fake', False) self.conn = xenapi_conn.get_connection(False) + def test_parallel_builds(self): + stubs.stubout_loopingcall_delay(self.stubs) + + def _do_build(id, proj, user, *args): + values = { + 'id': id, + 'project_id': proj, + 'user_id': user, + 'image_id': 1, + 'kernel_id': 2, + 'ramdisk_id': 3, + 'instance_type_id': '3', # m1.large + 'mac_address': 'aa:bb:cc:dd:ee:ff', + 'os_type': 'linux'} + instance = db.instance_create(self.context, values) + self.conn.spawn(instance) + + gt1 = eventlet.spawn(_do_build, 1, self.project.id, self.user.id) + gt2 = eventlet.spawn(_do_build, 2, self.project.id, self.user.id) + gt1.wait() + gt2.wait() + def test_list_instances_0(self): instances = self.conn.list_instances() self.assertEquals(instances, []) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 6db061444..f3d3d0ceb 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -16,6 +16,7 @@ """Stubouts, mocks and fixtures for the test suite""" +import eventlet from nova.virt import xenapi_conn from nova.virt.xenapi import fake from nova.virt.xenapi import volume_utils @@ -115,6 +116,14 @@ def stubout_loopingcall_start(stubs): self.f(*self.args, **self.kw) stubs.Set(utils.LoopingCall, 'start', fake_start) +def stubout_loopingcall_delay(stubs): + def fake_start(self, interval, now=True): + self._running = True + eventlet.sleep(1) + self.f(*self.args, **self.kw) + # This would fail before parallel xenapi calls were fixed + assert self._running == False + stubs.Set(utils.LoopingCall, 'start', fake_start) class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ -- cgit From 7dc76508d38370f3cf68029fae2ffb7749a580b2 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Thu, 12 May 2011 20:12:22 +0000 Subject: if a LoopingCall has canceled the loop, break out early instead of sleeping any more than needed --- nova/utils.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 80bf1197f..44ee1b623 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -459,6 +459,8 @@ class LoopingCall(object): try: while self._running: self.f(*self.args, **self.kw) + if not self._running: + break greenthread.sleep(interval) except LoopingCallDone, e: self.stop() -- cgit From 1aad930383fa425b88e59929aa1698e31978eb62 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 12 May 2011 22:19:52 +0000 Subject: Make sure imports are in alphabetical order --- nova/api/ec2/cloud.py | 2 +- nova/db/sqlalchemy/api.py | 2 +- nova/tests/network/base.py | 2 +- nova/tests/test_ipv6.py | 2 +- nova/virt/libvirt_conn.py | 2 +- nova/virt/xenapi/vmops.py | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 63baf8036..1fa07d042 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -35,11 +35,11 @@ from nova import crypto from nova import db from nova import exception from nova import flags +from nova import ipv6 from nova import log as logging from nova import network from nova import utils from nova import volume -from nova import ipv6 from nova.api.ec2 import ec2utils from nova.compute import instance_types from nova.image import s3 diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 11d07c9e9..2949eec49 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -25,8 +25,8 @@ import warnings from nova import db from nova import exception from nova import flags -from nova import utils from nova import ipv6 +from nova import utils from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py index 5de1255cd..5236b1dfe 100644 --- a/nova/tests/network/base.py +++ b/nova/tests/network/base.py @@ -25,10 +25,10 @@ from nova import context from nova import db from nova import exception from nova import flags +from nova import ipv6 from nova import log as logging from nova import test from nova import utils -from nova import ipv6 from nova.auth import manager FLAGS = flags.FLAGS diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py index 01d28df73..45b64cba8 100644 --- a/nova/tests/test_ipv6.py +++ b/nova/tests/test_ipv6.py @@ -16,8 +16,8 @@ """Test suite for IPv6.""" -from nova import ipv6 from nova import flags +from nova import ipv6 from nova import log as logging from nova import test diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 80e1a1f85..6ee23d1df 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -57,10 +57,10 @@ from nova import context from nova import db from nova import exception from nova import flags +from nova import ipv6 from nova import log as logging from nova import utils from nova import vnc -from nova import ipv6 from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index cc2b54331..13d7d215b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -28,13 +28,13 @@ import subprocess import tempfile import uuid -from nova import db from nova import context -from nova import log as logging +from nova import db from nova import exception -from nova import utils from nova import flags from nova import ipv6 +from nova import log as logging +from nova import utils from nova.auth.manager import AuthManager from nova.compute import power_state -- cgit From a84e484d1401d7c82373203b6964c54ab7166dea Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 12 May 2011 20:05:41 -0700 Subject: added flag lost in migration --- nova/compute/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 556b3b3b9..a2dffb16e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -77,7 +77,8 @@ flags.DEFINE_integer("rescue_timeout", 0, " Set to 0 to disable.") flags.DEFINE_bool('auto_assign_floating_ip', False, 'Autoassigning floating ip to VM') - +flags.DEFINE_integer('host_state_interval', 120, + 'Interval in seconds for querying the host status') LOG = logging.getLogger('nova.compute.manager') -- cgit From 31820248e8d886d37add963f0709f3658fd6087d Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Fri, 13 May 2011 10:22:04 -0400 Subject: Update the compute manager so that it breaks out of a loop if set_admin_password is not implemented by the driver. --- nova/compute/manager.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 556b3b3b9..149147460 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -426,6 +426,12 @@ class ComputeManager(manager.SchedulerDependentManager): LOG.audit(_("Instance %s: Root password set"), instance_ref["name"]) break + except NotImplementedError: + # NOTE(dprince): if the driver doesn't implement + # set_admin_password we break to avoid a loop + LOG.warn(_('set_admin_password is not implemented ' + 'by this driver.')) + break except Exception, e: # Catch all here because this could be anything. LOG.exception(e) -- cgit From a8941345c97cccb9171e3d1e172f607dc8d17e6a Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 13 May 2011 09:47:08 -0500 Subject: Make set_admin_password non-blocking to API. --- nova/api/openstack/servers.py | 6 ++++-- nova/compute/api.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 547310613..aaff57b4a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -14,6 +14,7 @@ # under the License. import base64 +import eventlet import traceback from webob import exc @@ -175,8 +176,9 @@ class Controller(common.OpenstackController): builder = self._get_view_builder(req) server = builder.build(inst, is_detail=True) server['server']['adminPass'] = password - self.compute_api.set_admin_password(context, server['server']['id'], - password) + # We don't want this to block + eventlet.spawn(self.compute_api.set_admin_password( + context, server['server']['id'], password)) return server def _deserialize_create(self, request): diff --git a/nova/compute/api.py b/nova/compute/api.py index 63884be97..1c28b6a89 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -484,7 +484,7 @@ class API(base.Base): def _find_host(self, context, instance_id): """Find the host associated with an instance.""" - for attempts in xrange(10): + for attempts in xrange(30): instance = self.get(context, instance_id) host = instance["host"] if host: -- cgit From b4357fef25a0f6a402979d15c1ecbf4abb8643a8 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 13 May 2011 10:04:37 -0500 Subject: Make host timeout configurable. --- nova/compute/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 1c28b6a89..29347bb4c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -42,6 +42,8 @@ LOG = logging.getLogger('nova.compute.api') FLAGS = flags.FLAGS flags.DECLARE('vncproxy_topic', 'nova.vnc') +flags.DEFINE_integer('find_host_timeout', 30, + 'Timeout after NN seconds when looking for a host.') def generate_default_hostname(instance_id): @@ -484,7 +486,7 @@ class API(base.Base): def _find_host(self, context, instance_id): """Find the host associated with an instance.""" - for attempts in xrange(30): + for attempts in xrange(FLAGS.find_host_timeout): instance = self.get(context, instance_id) host = instance["host"] if host: -- cgit From 60dcc66db2a08bd4acfe2f3fc3e66cc382e7d008 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 13 May 2011 10:26:50 -0500 Subject: Review feedback. --- nova/api/openstack/servers.py | 6 ++---- nova/compute/api.py | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index aaff57b4a..547310613 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -14,7 +14,6 @@ # under the License. import base64 -import eventlet import traceback from webob import exc @@ -176,9 +175,8 @@ class Controller(common.OpenstackController): builder = self._get_view_builder(req) server = builder.build(inst, is_detail=True) server['server']['adminPass'] = password - # We don't want this to block - eventlet.spawn(self.compute_api.set_admin_password( - context, server['server']['id'], password)) + self.compute_api.set_admin_password(context, server['server']['id'], + password) return server def _deserialize_create(self, request): diff --git a/nova/compute/api.py b/nova/compute/api.py index 29347bb4c..a12f8d515 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -19,6 +19,7 @@ """Handles all requests relating to instances (guest vms).""" import datetime +import eventlet import re import time @@ -495,6 +496,15 @@ class API(base.Base): raise exception.Error(_("Unable to find host for Instance %s") % instance_id) + def _set_admin_password(self, context, instance_id, password): + """Set the root/admin password for the given instance.""" + host = self._find_host(context, instance_id) + + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "set_admin_password", + "args": {"instance_id": instance_id, "new_pass": password}}) + def snapshot(self, context, instance_id, name): """Snapshot the given instance. @@ -648,12 +658,8 @@ class API(base.Base): def set_admin_password(self, context, instance_id, password=None): """Set the root/admin password for the given instance.""" - host = self._find_host(context, instance_id) - - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "set_admin_password", - "args": {"instance_id": instance_id, "new_pass": password}}) + eventlet.spawn_n(self._set_admin_password(context, instance_id, + password)) def inject_file(self, context, instance_id): """Write a file to the given instance.""" -- cgit From 3f247a628c954d5d4d97def6e6a2f889ab7ec7e3 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Fri, 13 May 2011 16:47:18 +0000 Subject: pep8 fixes --- nova/tests/xenapi/stubs.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index f3d3d0ceb..4833ccb07 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -116,6 +116,7 @@ def stubout_loopingcall_start(stubs): self.f(*self.args, **self.kw) stubs.Set(utils.LoopingCall, 'start', fake_start) + def stubout_loopingcall_delay(stubs): def fake_start(self, interval, now=True): self._running = True @@ -125,6 +126,7 @@ def stubout_loopingcall_delay(stubs): assert self._running == False stubs.Set(utils.LoopingCall, 'start', fake_start) + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri): -- cgit From b098428155b36551cfd84d4b2faf87a104d58f27 Mon Sep 17 00:00:00 2001 From: Justin Shepherd Date: Sat, 14 May 2011 22:47:12 -0500 Subject: renamed test cases to use a consistent naming convention as used in nova/tests/api/openstack/test_images.py --- nova/tests/api/openstack/test_servers.py | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 89edece42..308271167 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -183,7 +183,7 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['id'], 1) self.assertEqual(res_dict['server']['name'], 'server1') - def test_get_server_by_id_v11(self): + def test_get_server_by_id_v1_1(self): req = webob.Request.blank('/v1.1/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -246,7 +246,7 @@ class ServersTest(test.TestCase): self.assertEqual(len(addresses["private"]), 1) self.assertEqual(addresses["private"][0], private) - def test_get_server_addresses_V10(self): + def test_get_server_addresses_v1_0(self): private = '192.168.0.3' public = ['1.2.3.4'] new_return_server = return_server_with_addresses(private, public) @@ -257,7 +257,7 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict, { 'addresses': {'public': public, 'private': [private]}}) - def test_get_server_addresses_xml_V10(self): + def test_get_server_addresses_xml_v1_0(self): private_expected = "192.168.0.3" public_expected = ["1.2.3.4"] new_return_server = return_server_with_addresses(private_expected, @@ -276,7 +276,7 @@ class ServersTest(test.TestCase): (ip,) = private.getElementsByTagName('ip') self.assertEquals(ip.getAttribute('addr'), private_expected) - def test_get_server_addresses_public_V10(self): + def test_get_server_addresses_public_v1_0(self): private = "192.168.0.3" public = ["1.2.3.4"] new_return_server = return_server_with_addresses(private, public) @@ -286,7 +286,7 @@ class ServersTest(test.TestCase): res_dict = json.loads(res.body) self.assertEqual(res_dict, {'public': public}) - def test_get_server_addresses_private_V10(self): + def test_get_server_addresses_private_v1_0(self): private = "192.168.0.3" public = ["1.2.3.4"] new_return_server = return_server_with_addresses(private, public) @@ -296,7 +296,7 @@ class ServersTest(test.TestCase): res_dict = json.loads(res.body) self.assertEqual(res_dict, {'private': [private]}) - def test_get_server_addresses_public_xml_V10(self): + def test_get_server_addresses_public_xml_v1_0(self): private = "192.168.0.3" public = ["1.2.3.4"] new_return_server = return_server_with_addresses(private, public) @@ -310,7 +310,7 @@ class ServersTest(test.TestCase): (ip,) = public_node.getElementsByTagName('ip') self.assertEquals(ip.getAttribute('addr'), public[0]) - def test_get_server_addresses_private_xml_V10(self): + def test_get_server_addresses_private_xml_v1_0(self): private = "192.168.0.3" public = ["1.2.3.4"] new_return_server = return_server_with_addresses(private, public) @@ -324,7 +324,7 @@ class ServersTest(test.TestCase): (ip,) = private_node.getElementsByTagName('ip') self.assertEquals(ip.getAttribute('addr'), private) - def test_get_server_by_id_with_addresses_v11(self): + def test_get_server_by_id_with_addresses_v1_1(self): private = "192.168.0.3" public = ["1.2.3.4"] new_return_server = return_server_with_addresses(private, public) @@ -354,7 +354,7 @@ class ServersTest(test.TestCase): self.assertEqual(s.get('imageId', None), None) i += 1 - def test_get_server_list_v11(self): + def test_get_server_list_v1_1(self): req = webob.Request.blank('/v1.1/servers') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -576,7 +576,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_create_instance_v11(self): + def test_create_instance_v1_1(self): self._setup_for_create_instance() imageRef = 'http://localhost/v1.1/images/2' @@ -609,7 +609,7 @@ class ServersTest(test.TestCase): self.assertEqual(imageRef, server['imageRef']) self.assertEqual(res.status_int, 200) - def test_create_instance_v11_bad_href(self): + def test_create_instance_v1_1_bad_href(self): self._setup_for_create_instance() imageRef = 'http://localhost/v1.1/images/asdf' @@ -625,7 +625,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_create_instance_v11_local_href(self): + def test_create_instance_v1_1_local_href(self): self._setup_for_create_instance() imageRef = 'http://localhost/v1.1/images/2' @@ -652,7 +652,7 @@ class ServersTest(test.TestCase): self.assertEqual(imageRef, server['imageRef']) self.assertEqual(res.status_int, 200) - def test_create_instance_with_admin_pass_v10(self): + def test_create_instance_with_admin_pass_v1_0(self): self._setup_for_create_instance() body = { @@ -673,7 +673,7 @@ class ServersTest(test.TestCase): self.assertNotEqual(res['server']['adminPass'], body['server']['adminPass']) - def test_create_instance_with_admin_pass_v11(self): + def test_create_instance_with_admin_pass_v1_1(self): self._setup_for_create_instance() imageRef = 'http://localhost/v1.1/images/2' @@ -695,7 +695,7 @@ class ServersTest(test.TestCase): server = json.loads(res.body)['server'] self.assertEqual(server['adminPass'], body['server']['adminPass']) - def test_create_instance_with_empty_admin_pass_v11(self): + def test_create_instance_with_empty_admin_pass_v1_1(self): self._setup_for_create_instance() imageRef = 'http://localhost/v1.1/images/2' @@ -758,7 +758,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_update_server_v10(self): + def test_update_server_v1_0(self): inst_dict = dict(name='server_test', adminPass='bacon') self.body = json.dumps(dict(server=inst_dict)) @@ -781,7 +781,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 204) - def test_update_server_adminPass_ignored_v11(self): + def test_update_server_adminPass_ignored_v1_1(self): inst_dict = dict(name='server_test', adminPass='bacon') self.body = json.dumps(dict(server=inst_dict)) @@ -822,7 +822,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 501) - def test_server_backup_schedule_deprecated_v11(self): + def test_server_backup_schedule_deprecated_v1_1(self): req = webob.Request.blank('/v1.1/servers/1/backup_schedule') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 404) @@ -1113,7 +1113,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_server_rebuild_accepted_minimum_v11(self): + def test_server_rebuild_accepted_minimum_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -1128,7 +1128,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) - def test_server_rebuild_rejected_when_building_v11(self): + def test_server_rebuild_rejected_when_building_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -1147,7 +1147,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 409) - def test_server_rebuild_accepted_with_metadata_v11(self): + def test_server_rebuild_accepted_with_metadata_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -1165,7 +1165,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) - def test_server_rebuild_accepted_with_bad_metadata_v11(self): + def test_server_rebuild_accepted_with_bad_metadata_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -1181,7 +1181,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_server_rebuild_bad_entity_v11(self): + def test_server_rebuild_bad_entity_v1_1(self): body = { "rebuild": { "imageId": 2, @@ -1196,7 +1196,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_server_rebuild_bad_personality_v11(self): + def test_server_rebuild_bad_personality_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -1215,7 +1215,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) - def test_server_rebuild_personality_v11(self): + def test_server_rebuild_personality_v1_1(self): body = { "rebuild": { "imageRef": "http://localhost/images/2", -- cgit From 9350dc2cee8d18e7b68921f5135504b68a25f95d Mon Sep 17 00:00:00 2001 From: Justin Shepherd Date: Sat, 14 May 2011 23:00:56 -0500 Subject: fixed a few C0103 errors in test_servers.py --- nova/tests/api/openstack/test_servers.py | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 308271167..eebc7f754 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -579,13 +579,13 @@ class ServersTest(test.TestCase): def test_create_instance_v1_1(self): self._setup_for_create_instance() - imageRef = 'http://localhost/v1.1/images/2' - flavorRef = 'http://localhost/v1.1/flavors/3' + image_ref = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': imageRef, - 'flavorRef': flavorRef, + 'imageRef': image_ref, + 'flavorRef': flavor_ref, 'metadata': { 'hello': 'world', 'open': 'stack', @@ -605,17 +605,17 @@ class ServersTest(test.TestCase): self.assertEqual(16, len(server['adminPass'])) self.assertEqual('server_test', server['name']) self.assertEqual(1, server['id']) - self.assertEqual(flavorRef, server['flavorRef']) - self.assertEqual(imageRef, server['imageRef']) + self.assertEqual(flavor_ref, server['flavorRef']) + self.assertEqual(image_ref, server['imageRef']) self.assertEqual(res.status_int, 200) def test_create_instance_v1_1_bad_href(self): self._setup_for_create_instance() - imageRef = 'http://localhost/v1.1/images/asdf' - flavorRef = 'http://localhost/v1.1/flavors/3' + image_ref = 'http://localhost/v1.1/images/asdf' + flavor_ref = 'http://localhost/v1.1/flavors/3' body = dict(server=dict( - name='server_test', imageRef=imageRef, flavorRef=flavorRef, + name='server_test', imageRef=image_ref, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) req = webob.Request.blank('/v1.1/servers') @@ -628,14 +628,14 @@ class ServersTest(test.TestCase): def test_create_instance_v1_1_local_href(self): self._setup_for_create_instance() - imageRef = 'http://localhost/v1.1/images/2' - imageRefLocal = '2' - flavorRef = 'http://localhost/v1.1/flavors/3' + image_ref = 'http://localhost/v1.1/images/2' + image_ref_local = '2' + flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': imageRefLocal, - 'flavorRef': flavorRef, + 'imageRef': image_ref_local, + 'flavorRef': flavor_ref, }, } @@ -648,8 +648,8 @@ class ServersTest(test.TestCase): server = json.loads(res.body)['server'] self.assertEqual(1, server['id']) - self.assertEqual(flavorRef, server['flavorRef']) - self.assertEqual(imageRef, server['imageRef']) + self.assertEqual(flavor_ref, server['flavorRef']) + self.assertEqual(image_ref, server['imageRef']) self.assertEqual(res.status_int, 200) def test_create_instance_with_admin_pass_v1_0(self): @@ -676,13 +676,13 @@ class ServersTest(test.TestCase): def test_create_instance_with_admin_pass_v1_1(self): self._setup_for_create_instance() - imageRef = 'http://localhost/v1.1/images/2' - flavorRef = 'http://localhost/v1.1/flavors/3' + image_ref = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': imageRef, - 'flavorRef': flavorRef, + 'imageRef': image_ref, + 'flavorRef': flavor_ref, 'adminPass': 'testpass', }, } @@ -698,13 +698,13 @@ class ServersTest(test.TestCase): def test_create_instance_with_empty_admin_pass_v1_1(self): self._setup_for_create_instance() - imageRef = 'http://localhost/v1.1/images/2' - flavorRef = 'http://localhost/v1.1/flavors/3' + image_ref = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': imageRef, - 'flavorRef': flavorRef, + 'imageRef': image_ref, + 'flavorRef': flavor_ref, 'adminPass': '', }, } -- cgit From 262dec736fa6ef54a1101a0a17671ff2a19cbd95 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Mon, 16 May 2011 11:12:16 -0500 Subject: Added the imageRef and flavorRef attributes in the xml deserialization --- nova/api/openstack/servers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3cf78e32c..1289c1e0a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -746,8 +746,9 @@ class ServerCreateRequestXMLDeserializer(object): """Marshal the server attribute of a parsed request""" server = {} server_node = self._find_first_child_named(node, 'server') - for attr in ["name", "imageId", "flavorId"]: - server[attr] = server_node.getAttribute(attr) + for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]: + if server_node.getAttribute(attr): + server[attr] = server_node.getAttribute(attr) metadata = self._extract_metadata(server_node) if metadata is not None: server["metadata"] = metadata -- cgit From e7f699706089919274055fc5c57c276f36d7a301 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 16 May 2011 14:02:56 -0400 Subject: Removed obsolete method and test --- nova/api/ec2/cloud.py | 3 --- nova/tests/test_cloud.py | 35 ----------------------------------- 2 files changed, 38 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 9aa4e7778..092b80fa2 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -366,9 +366,6 @@ class CloudController(object): g['ipPermissions'] += [r] return g - def _get_instance(instance_id): - raise NotImplementedError() - def _revoke_rule_args_to_dict(self, context, to_port=None, from_port=None, ip_protocol=None, cidr_ip=None, user_id=None, source_security_group_name=None, diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index c45bdd12c..7835ded28 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -338,41 +338,6 @@ class CloudTestCase(test.TestCase): self._create_key('test') self.cloud.delete_key_pair(self.context, 'test') - def test_run_instances(self): - if FLAGS.connection_type == 'fake': - LOG.debug(_("Can't test instances without a real virtual env.")) - return - image_id = FLAGS.default_image - instance_type = FLAGS.default_instance_type - max_count = 1 - kwargs = {'image_id': image_id, - 'instance_type': instance_type, - 'max_count': max_count} - rv = self.cloud.run_instances(self.context, **kwargs) - # TODO: check for proper response - instance_id = rv['reservationSet'][0].keys()[0] - instance = rv['reservationSet'][0][instance_id][0] - LOG.debug(_("Need to watch instance %s until it's running..."), - instance['instance_id']) - while True: - greenthread.sleep(1) - info = self.cloud._get_instance(instance['instance_id']) - LOG.debug(info['state']) - if info['state'] == power_state.RUNNING: - break - self.assert_(rv) - - if FLAGS.connection_type != 'fake': - time.sleep(45) # Should use boto for polling here - for reservations in rv['reservationSet']: - # for res_id in reservations.keys(): - # LOG.debug(reservations[res_id]) - # for instance in reservations[res_id]: - for instance in reservations[reservations.keys()[0]]: - instance_id = instance['instance_id'] - LOG.debug(_("Terminating instance %s"), instance_id) - rv = self.compute.terminate_instance(instance_id) - def test_terminate_instances(self): inst1 = db.instance_create(self.context, {'reservation_id': 'a', 'image_id': 1, -- cgit From 15ef81b2138afa4fd22e0926fcadf3acfb31f2c5 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Mon, 16 May 2011 18:55:46 +0000 Subject: Use new 3-argument API --- nova/tests/network/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py index 5236b1dfe..b06271c99 100644 --- a/nova/tests/network/base.py +++ b/nova/tests/network/base.py @@ -125,7 +125,8 @@ class NetworkTestCase(test.TestCase): self.assertEqual(instance_ref['id'], instance_ref2['id']) self.assertEqual(address_v6, ipv6.to_global(network_ref['cidr_v6'], - instance_ref['mac_address'])) + instance_ref['mac_address'], + 'test')) self._deallocate_address(0, address) db.instance_destroy(context.get_admin_context(), instance_ref['id']) -- cgit From 428dc895a3495a4800e57162cd7db8d79013a414 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Mon, 16 May 2011 19:33:18 +0000 Subject: PEP8 cleanups --- nova/ipv6/api.py | 5 +++-- nova/tests/test_ipv6.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py index cdda2c253..d24cea066 100644 --- a/nova/ipv6/api.py +++ b/nova/ipv6/api.py @@ -28,12 +28,13 @@ def reset_backend(): global IMPL IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], rfc2462='nova.ipv6.rfc2462', - account_identifier= - 'nova.ipv6.account_identifier') + account_identifier='nova.ipv6.account_identifier') + def to_global(prefix, mac, project_id): return IMPL.to_global(prefix, mac, project_id) + def to_mac(ipv6_address): return IMPL.to_mac(ipv6_address) diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py index 45b64cba8..11dc2ec98 100644 --- a/nova/tests/test_ipv6.py +++ b/nova/tests/test_ipv6.py @@ -27,6 +27,7 @@ FLAGS = flags.FLAGS import sys + class IPv6RFC2462TestCase(test.TestCase): """Unit tests for IPv6 rfc2462 backend operations.""" def setUp(self): -- cgit From 3d1cef9e56d7fac8a1b89861b7443e4ca660e4a8 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Mon, 16 May 2011 20:06:49 +0000 Subject: Reduce indentation to avoid PEP8 failures --- nova/ipv6/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py index d24cea066..da003645a 100644 --- a/nova/ipv6/api.py +++ b/nova/ipv6/api.py @@ -27,8 +27,8 @@ flags.DEFINE_string('ipv6_backend', def reset_backend(): global IMPL IMPL = utils.LazyPluggable(FLAGS['ipv6_backend'], - rfc2462='nova.ipv6.rfc2462', - account_identifier='nova.ipv6.account_identifier') + rfc2462='nova.ipv6.rfc2462', + account_identifier='nova.ipv6.account_identifier') def to_global(prefix, mac, project_id): -- cgit