From 4a85ec5c2aac998f92b40eb3f61e2963db346aa9 Mon Sep 17 00:00:00 2001 From: Duk Loi Date: Mon, 29 Feb 2016 17:34:29 -0500 Subject: [PATCH] Add volume type field to launch instance dialog With the addition of Cinder volume type support in the CLI the corresponding change is needed in Horizon. In the launch instance dialog added the volume type field pulldown. It will be populated by a call to cinder volume_type_list. The default value will be prepended to the list as "no_type". This default is used in the cinder panels. Updated api and tests to account for new cinder calls and add the default value to the post commands in the tests. Change-Id: I7fb2404ccf9d42c293bde1a708518e108f9a1625 Closes-Bug: #1551340 --- trove_dashboard/api/trove.py | 4 ++- trove_dashboard/content/databases/tests.py | 26 ++++++++++++--- .../databases/workflows/create_instance.py | 32 ++++++++++++++++--- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/trove_dashboard/api/trove.py b/trove_dashboard/api/trove.py index 44ba17f..80625ef 100644 --- a/trove_dashboard/api/trove.py +++ b/trove_dashboard/api/trove.py @@ -121,11 +121,13 @@ def instance_delete(request, instance_id): def instance_create(request, name, volume, flavor, databases=None, users=None, restore_point=None, nics=None, datastore=None, datastore_version=None, - replica_of=None): + replica_of=None, volume_type=None): # TODO(dklyle): adding conditional to support trove without volume # support for now until API supports checking for volume support if volume > 0: volume_params = {'size': volume} + if volume_type: + volume_params['type'] = volume_type else: volume_params = None return troveclient(request).instances.create( diff --git a/trove_dashboard/content/databases/tests.py b/trove_dashboard/content/databases/tests.py index 44f126c..8d174f5 100644 --- a/trove_dashboard/content/databases/tests.py +++ b/trove_dashboard/content/databases/tests.py @@ -129,6 +129,7 @@ class DatabaseTests(test.TestCase): api.trove: ('flavor_list', 'backup_list', 'datastore_list', 'datastore_version_list', 'instance_list'), + dash_api.cinder: ('volume_type_list',), dash_api.neutron: ('network_list',)}) def test_launch_instance(self): api.trove.flavor_list(IsA(http.HttpRequest)).AndReturn( @@ -144,6 +145,8 @@ class DatabaseTests(test.TestCase): api.trove.datastore_version_list(IsA(http.HttpRequest), IsA(str)).\ MultipleTimes().AndReturn(self.datastore_versions.list()) + dash_api.cinder.volume_type_list(IsA(http.HttpRequest)).AndReturn([]) + dash_api.neutron.network_list(IsA(http.HttpRequest), tenant_id=self.tenant.id, shared=False).AndReturn( @@ -192,6 +195,7 @@ class DatabaseTests(test.TestCase): api.trove: ('flavor_list', 'backup_list', 'instance_create', 'datastore_list', 'datastore_version_list', 'instance_list'), + dash_api.cinder: ('volume_type_list',), dash_api.neutron: ('network_list',)}) def test_create_simple_instance(self): api.trove.flavor_list(IsA(http.HttpRequest)).AndReturn( @@ -211,6 +215,8 @@ class DatabaseTests(test.TestCase): api.trove.datastore_version_list(IsA(http.HttpRequest), IsA(str))\ .MultipleTimes().AndReturn(self.datastore_versions.list()) + dash_api.cinder.volume_type_list(IsA(http.HttpRequest)).AndReturn([]) + dash_api.neutron.network_list(IsA(http.HttpRequest), tenant_id=self.tenant.id, shared=False).AndReturn( @@ -234,7 +240,8 @@ class DatabaseTests(test.TestCase): restore_point=None, replica_of=None, users=None, - nics=nics).AndReturn(self.databases.first()) + nics=nics, + volume_type=None).AndReturn(self.databases.first()) self.mox.ReplayAll() post = { @@ -243,6 +250,7 @@ class DatabaseTests(test.TestCase): 'flavor': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'network': self.networks.first().id, 'datastore': 'mysql,5.5', + 'volume_type': 'no_type' } res = self.client.post(LAUNCH_URL, post) @@ -252,6 +260,7 @@ class DatabaseTests(test.TestCase): api.trove: ('flavor_list', 'backup_list', 'instance_create', 'datastore_list', 'datastore_version_list', 'instance_list'), + dash_api.cinder: ('volume_type_list',), dash_api.neutron: ('network_list',)}) def test_create_simple_instance_exception(self): trove_exception = self.exceptions.nova @@ -272,6 +281,8 @@ class DatabaseTests(test.TestCase): api.trove.datastore_version_list(IsA(http.HttpRequest), IsA(str))\ .MultipleTimes().AndReturn(self.datastore_versions.list()) + dash_api.cinder.volume_type_list(IsA(http.HttpRequest)).AndReturn([]) + dash_api.neutron.network_list(IsA(http.HttpRequest), tenant_id=self.tenant.id, shared=False).AndReturn( @@ -295,7 +306,8 @@ class DatabaseTests(test.TestCase): restore_point=None, replica_of=None, users=None, - nics=nics).AndRaise(trove_exception) + nics=nics, + volume_type=None).AndRaise(trove_exception) self.mox.ReplayAll() post = { @@ -304,6 +316,7 @@ class DatabaseTests(test.TestCase): 'flavor': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'network': self.networks.first().id, 'datastore': 'mysql,5.5', + 'volume_type': 'no_type' } res = self.client.post(LAUNCH_URL, post) @@ -939,6 +952,7 @@ class DatabaseTests(test.TestCase): api.trove: ('flavor_list', 'backup_list', 'instance_create', 'datastore_list', 'datastore_version_list', 'instance_list', 'instance_get'), + dash_api.cinder: ('volume_type_list',), dash_api.neutron: ('network_list',)}) def test_create_replica_instance(self): api.trove.flavor_list(IsA(http.HttpRequest)).AndReturn( @@ -957,6 +971,8 @@ class DatabaseTests(test.TestCase): IsA(str))\ .MultipleTimes().AndReturn(self.datastore_versions.list()) + dash_api.cinder.volume_type_list(IsA(http.HttpRequest)).AndReturn([]) + dash_api.neutron.network_list(IsA(http.HttpRequest), tenant_id=self.tenant.id, shared=False).\ @@ -983,7 +999,8 @@ class DatabaseTests(test.TestCase): restore_point=None, replica_of=self.databases.first().id, users=None, - nics=nics).AndReturn(self.databases.first()) + nics=nics, + volume_type=None).AndReturn(self.databases.first()) self.mox.ReplayAll() post = { @@ -993,7 +1010,8 @@ class DatabaseTests(test.TestCase): 'network': self.networks.first().id, 'datastore': 'mysql,5.5', 'initial_state': 'master', - 'master': self.databases.first().id + 'master': self.databases.first().id, + 'volume_type': 'no_type' } res = self.client.post(LAUNCH_URL, post) diff --git a/trove_dashboard/content/databases/workflows/create_instance.py b/trove_dashboard/content/databases/workflows/create_instance.py index 532bf3d..c4a72bf 100644 --- a/trove_dashboard/content/databases/workflows/create_instance.py +++ b/trove_dashboard/content/databases/workflows/create_instance.py @@ -39,6 +39,10 @@ class SetInstanceDetailsAction(workflows.Action): min_value=0, initial=1, help_text=_("Size of the volume in GB.")) + volume_type = forms.ChoiceField( + label=_("Volume Type"), + required=False, + help_text=_("Applicable only if the volume size is specified.")) datastore = forms.ChoiceField(label=_("Datastore"), help_text=_( "Type and version of datastore.")) @@ -70,6 +74,17 @@ class SetInstanceDetailsAction(workflows.Action): return instance_utils.sort_flavor_list(request, flavors) return [] + @memoized.memoized_method + def populate_volume_type_choices(self, request, context): + try: + volume_types = dash_api.cinder.volume_type_list(request) + return ([("no_type", _("No volume type"))] + + [(type.name, type.name) + for type in volume_types]) + except Exception: + LOG.exception("Exception while obtaining volume types list") + self._volume_types = [] + @memoized.memoized_method def datastores(self, request): try: @@ -124,7 +139,7 @@ TROVE_ADD_PERMS = TROVE_ADD_USER_PERMS + TROVE_ADD_DATABASE_PERMS class SetInstanceDetails(workflows.Step): action_class = SetInstanceDetailsAction - contributes = ("name", "volume", "flavor", "datastore") + contributes = ("name", "volume", "volume_type", "flavor", "datastore") class SetNetworkAction(workflows.Action): @@ -388,16 +403,23 @@ class LaunchInstance(workflows.Workflow): else: return None + def _get_volume_type(self, context): + volume_type = None + if context.get('volume_type') != 'no_type': + volume_type = context['volume_type'] + return volume_type + def handle(self, request, context): try: datastore = self.context['datastore'].split(',')[0] datastore_version = self.context['datastore'].split(',')[1] LOG.info("Launching database instance with parameters " - "{name=%s, volume=%s, flavor=%s, " + "{name=%s, volume=%s, volume_type=%s, flavor=%s, " "datastore=%s, datastore_version=%s, " "dbs=%s, users=%s, " "backups=%s, nics=%s, replica_of=%s}", - context['name'], context['volume'], context['flavor'], + context['name'], context['volume'], + self._get_volume_type(context), context['flavor'], datastore, datastore_version, self._get_databases(context), self._get_users(context), self._get_backup(context), self._get_nics(context), @@ -412,7 +434,9 @@ class LaunchInstance(workflows.Workflow): users=self._get_users(context), restore_point=self._get_backup(context), nics=self._get_nics(context), - replica_of=context.get('master')) + replica_of=context.get('master'), + volume_type=self._get_volume_type( + context)) return True except Exception: exceptions.handle(request)