From 7e7b41aaf4e7ace4b6423e282b3ef13ca21b16c2 Mon Sep 17 00:00:00 2001 From: daniel-a-nguyen Date: Fri, 30 Aug 2013 13:02:03 -0700 Subject: [PATCH] Allow optional availability_zone to be passed Change-Id: I4eca69c1690cf641164def4bb320b6fd77c09c14 Implements: blueprint availability-zone-instance-create --- trove/common/apischema.py | 5 +-- trove/instance/models.py | 6 ++-- trove/instance/service.py | 7 +++- trove/taskmanager/api.py | 6 ++-- trove/taskmanager/manager.py | 5 +-- trove/taskmanager/models.py | 29 +++++++++++------ trove/tests/api/instances.py | 12 ++++++- trove/tests/fakes/nova.py | 3 +- .../unittests/taskmanager/test_models.py | 32 ++++++++++++++++--- 9 files changed, 79 insertions(+), 26 deletions(-) diff --git a/trove/common/apischema.py b/trove/common/apischema.py index a1165ebb1d..96a9b64481 100644 --- a/trove/common/apischema.py +++ b/trove/common/apischema.py @@ -176,7 +176,7 @@ instance = { "type": "object", "required": ["name", "flavorRef", "volume" if CONF.trove_volume_support else None], - "additionalProperties": False, + "additionalProperties": True, "properties": { "name": non_empty_string, "flavorRef": flavorref, @@ -190,7 +190,8 @@ instance = { "properties": { "backupRef": uuid } - } + }, + "availability_zone": non_empty_string } } } diff --git a/trove/instance/models.py b/trove/instance/models.py index d6db114d15..d852b16658 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -418,7 +418,8 @@ class Instance(BuiltInstance): @classmethod def create(cls, context, name, flavor_id, image_id, - databases, users, service_type, volume_size, backup_id): + databases, users, service_type, volume_size, backup_id, + availability_zone=None): client = create_nova_client(context) try: @@ -486,7 +487,8 @@ class Instance(BuiltInstance): task_api.API(context).create_instance(db_info.id, name, flavor, image_id, databases, users, service_type, volume_size, - security_groups, backup_id) + security_groups, backup_id, + availability_zone) return SimpleInstance(context, db_info, service_status) diff --git a/trove/instance/service.py b/trove/instance/service.py index ba02482cd2..af30dd8802 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -205,10 +205,15 @@ class InstanceController(wsgi.Controller): else: backup_id = None + if 'availability_zone' in body['instance']: + availability_zone = body['instance']['availability_zone'] + else: + availability_zone = None + instance = models.Instance.create(context, name, flavor_id, image_id, databases, users, service_type, volume_size, - backup_id) + backup_id, availability_zone) view = views.InstanceDetailView(instance, req=req) return wsgi.Result(view.data(), 200) diff --git a/trove/taskmanager/api.py b/trove/taskmanager/api.py index 8d69a123da..48b93891d7 100644 --- a/trove/taskmanager/api.py +++ b/trove/taskmanager/api.py @@ -109,10 +109,12 @@ class API(ManagerAPI): def create_instance(self, instance_id, name, flavor, image_id, databases, users, service_type, - volume_size, security_groups, backup_id=None): + volume_size, security_groups, backup_id=None, + availability_zone=None): LOG.debug("Making async call to create instance %s " % instance_id) self._cast("create_instance", instance_id=instance_id, name=name, flavor=self._transform_obj(flavor), image_id=image_id, databases=databases, users=users, service_type=service_type, volume_size=volume_size, - security_groups=security_groups, backup_id=backup_id) + security_groups=security_groups, backup_id=backup_id, + availability_zone=availability_zone) diff --git a/trove/taskmanager/manager.py b/trove/taskmanager/manager.py index 72b2882af7..624cd48dd0 100644 --- a/trove/taskmanager/manager.py +++ b/trove/taskmanager/manager.py @@ -82,12 +82,13 @@ class Manager(periodic_task.PeriodicTasks): def create_instance(self, context, instance_id, name, flavor, image_id, databases, users, service_type, - volume_size, security_groups, backup_id): + volume_size, security_groups, backup_id, + availability_zone): instance_tasks = FreshInstanceTasks.load(context, instance_id) instance_tasks.create_instance(flavor, image_id, databases, users, service_type, volume_size, security_groups, - backup_id) + backup_id, availability_zone) if CONF.exists_notification_transformer: @periodic_task.periodic_task( diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index 6d2fee937a..6a12e6295b 100644 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -135,28 +135,31 @@ class ConfigurationMixin(object): class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): def create_instance(self, flavor, image_id, databases, users, service_type, volume_size, security_groups, - backup_id): + backup_id, availability_zone): if use_heat: server, volume_info = self._create_server_volume_heat( flavor, image_id, security_groups, service_type, - volume_size) + volume_size, + availability_zone) elif use_nova_server_volume: server, volume_info = self._create_server_volume( flavor['id'], image_id, security_groups, service_type, - volume_size) + volume_size, + availability_zone) else: server, volume_info = self._create_server_volume_individually( flavor['id'], image_id, security_groups, service_type, - volume_size) + volume_size, + availability_zone) try: self._create_dns_entry() @@ -216,7 +219,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): return False def _create_server_volume(self, flavor_id, image_id, security_groups, - service_type, volume_size): + service_type, volume_size, + availability_zone): server = None try: files = {"/etc/guest_info": ("[DEFAULT]\n--guest_id=" @@ -231,7 +235,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): server = self.nova_client.servers.create( name, image_id, flavor_id, files=files, volume=volume_ref, - security_groups=security_groups) + security_groups=security_groups, + availability_zone=availability_zone) LOG.debug(_("Created new compute instance %s.") % server.id) server_dict = server._info @@ -291,13 +296,15 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): def _create_server_volume_individually(self, flavor_id, image_id, security_groups, service_type, - volume_size): + volume_size, + availability_zone): server = None volume_info = self._build_volume_info(volume_size) block_device_mapping = volume_info['block_device'] try: server = self._create_server(flavor_id, image_id, security_groups, - service_type, block_device_mapping) + service_type, block_device_mapping, + availability_zone) server_id = server.id # Save server ID. self.update_db(compute_instance_id=server_id) @@ -384,7 +391,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): return volume_info def _create_server(self, flavor_id, image_id, security_groups, - service_type, block_device_mapping): + service_type, block_device_mapping, + availability_zone): files = {"/etc/guest_info": ("[DEFAULT]\nguest_id=%s\n" "service_type=%s\n" % (self.id, service_type))} @@ -401,7 +409,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): bdmap = block_device_mapping server = self.nova_client.servers.create( name, image_id, flavor_id, files=files, userdata=userdata, - security_groups=security_groups, block_device_mapping=bdmap) + security_groups=security_groups, block_device_mapping=bdmap, + availability_zone=availability_zone) LOG.debug(_("Created new compute instance %s.") % server.id) return server diff --git a/trove/tests/api/instances.py b/trove/tests/api/instances.py index d83d3e4f12..888eeb1edc 100644 --- a/trove/tests/api/instances.py +++ b/trove/tests/api/instances.py @@ -363,7 +363,8 @@ class CreateInstance(object): instance_info.dbaas_flavor_href, instance_info.volume, databases, - users) + users, + availability_zone="nova") assert_equal(200, dbaas.last_http_code) else: id = existing_instance() @@ -404,6 +405,15 @@ class CreateInstance(object): if VOLUME_SUPPORT: check.volume() + @test + def test_create_with_bad_availability_zone(self): + instance_name = "instance-failure-with-bad-ephemeral" + databases = [] + assert_raises(exceptions.BadRequest, dbaas.instances.create, + instance_name, instance_info.dbaas_flavor_href, + None, databases, availability_zone="NOOP") + assert_equal(400, dbaas.last_http_code) + @test(enabled=VOLUME_SUPPORT) def test_create_failure_with_empty_volume(self): instance_name = "instance-failure-with-no-volume-size" diff --git a/trove/tests/fakes/nova.py b/trove/tests/fakes/nova.py index bd793f64b9..89c49559a9 100644 --- a/trove/tests/fakes/nova.py +++ b/trove/tests/fakes/nova.py @@ -263,7 +263,8 @@ class FakeServers(object): server.owner.tenant == self.context.tenant) def create(self, name, image_id, flavor_ref, files=None, userdata=None, - block_device_mapping=None, volume=None, security_groups=None): + block_device_mapping=None, volume=None, security_groups=None, + availability_zone=None): id = "FAKE_%s" % uuid.uuid4() if volume: volume = self.volumes.create(volume['size'], volume['name'], diff --git a/trove/tests/unittests/taskmanager/test_models.py b/trove/tests/unittests/taskmanager/test_models.py index 8ddae8e1c7..b204c9c7bf 100644 --- a/trove/tests/unittests/taskmanager/test_models.py +++ b/trove/tests/unittests/taskmanager/test_models.py @@ -37,7 +37,7 @@ class fake_Server: class fake_ServerManager: def create(self, name, image_id, flavor_id, files, userdata, - security_groups, block_device_mapping): + security_groups, block_device_mapping, availability_zone=None): server = fake_Server() server.id = "server_id" server.name = name @@ -47,6 +47,7 @@ class fake_ServerManager: server.userdata = userdata server.security_groups = security_groups server.block_device_mapping = block_device_mapping + server.availability_zone = availability_zone return server @@ -87,19 +88,40 @@ class FreshInstanceTasksTest(testtools.TestCase): service_type = os.path.splitext(os.path.basename(self.cloudinit))[0] when(taskmanager_models.CONF).get("cloudinit_location").thenReturn( cloudinit_location) - server = self.freshinstancetasks._create_server(None, None, None, - service_type, None) + server = self.freshinstancetasks._create_server( + None, None, None, service_type, None, None) self.assertEqual(server.userdata, self.userdata) def test_create_instance_guestconfig(self): when(taskmanager_models.CONF).get("guest_config").thenReturn( self.guestconfig) - server = self.freshinstancetasks._create_server(None, None, None, - "test", None) + server = self.freshinstancetasks._create_server( + None, None, None, "test", None, None) self.assertTrue('/etc/trove-guestagent.conf' in server.files) self.assertEqual(server.files['/etc/trove-guestagent.conf'], self.guestconfig_content) + def test_create_instance_with_az_kwarg(self): + service_type = 'mysql' + server = self.freshinstancetasks._create_server( + None, None, None, service_type, None, availability_zone='nova') + + self.assertIsNotNone(server) + + def test_create_instance_with_az(self): + service_type = 'mysql' + server = self.freshinstancetasks._create_server( + None, None, None, service_type, None, 'nova') + + self.assertIsNotNone(server) + + def test_create_instance_with_az_none(self): + service_type = 'mysql' + server = self.freshinstancetasks._create_server( + None, None, None, service_type, None, None) + + self.assertIsNotNone(server) + class BackupTasksTest(testtools.TestCase): def setUp(self):