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
This commit is contained in:
Duk Loi 2016-02-29 17:34:29 -05:00
parent 731e005b6b
commit 4a85ec5c2a
3 changed files with 53 additions and 9 deletions

View File

@ -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(

View File

@ -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)

View File

@ -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)