Added volume type description for volume type

Added the following features for admin volumes types

1. "Create Volume Type" dialog should include a description field.
2. The volume types table should include description column.
3. The volume types table name and description columns are in-line
editable.
4. The 'Edit Volume Type' action is added for the volume type. User should
   be able to update volume type name and description.

Added the following features in project volumes

1. "Create Volume" dialog will have description for the selected volume
   type when volume type select is available.
2. "No Volume type" will have some description as well.

Implements: blueprint volume-type-description
Change-Id: I7c8548756bcd3566873876bbc59f9b9c21d6846b
This commit is contained in:
Gloria Gu 2014-11-11 11:50:44 -08:00 committed by Rich Hagarty
parent 5796aa97a7
commit 84074ce020
17 changed files with 454 additions and 19 deletions

View File

@ -0,0 +1,75 @@
horizon.Volumes = {
selected_volume_type: null,
volume_types: [],
initWithTypes: function(volume_types) {
this.volume_types = volume_types;
this._attachInputHandlers();
this.getSelectedType();
this.showTypeDescription();
},
/*
*Returns the type object for the selected type in the form.
*/
getSelectedType: function() {
this.selected_volume_type = $.grep(this.volume_types, function(type) {
var selected_name = $("#id_type").children(":selected").val();
return type.name === selected_name;
})[0];
return this.selected_volume_type;
},
showTypeDescription: function() {
this.getSelectedType();
if (this.selected_volume_type) {
var description = this.selected_volume_type.description;
var name = this.selected_volume_type.name;
if (name === 'no_type') {
$("#id_show_volume_type_name").html("");
} else {
$("#id_show_volume_type_name").html(name);
}
if (description) {
$("#id_show_volume_type_desc").html(description);
} else {
$("#id_show_volume_type_desc").html(
gettext('No description available.'));
}
}
},
toggleTypeDescription: function() {
var selected_volume_source =
$("#id_volume_source_type").children(":selected").val();
if(selected_volume_source === 'volume_source' ||
selected_volume_source === 'snapshot_source') {
$("#id_show_volume_type_desc_div").hide();
}
else {
$("#id_show_volume_type_desc_div").show();
}
},
_attachInputHandlers: function() {
var scope = this;
var eventCallback_type = function() {
scope.showTypeDescription();
};
$('#id_type').on('change', eventCallback_type);
var eventCallback_volume_source_type = function() {
scope.toggleTypeDescription();
};
$('#id_volume_source_type').on('change', eventCallback_volume_source_type);
}
};

View File

@ -448,6 +448,24 @@ def volume_type_list_with_qos_associations(request):
return vol_types
def volume_type_get_with_qos_association(request, volume_type_id):
vol_type = volume_type_get(request, volume_type_id)
vol_type.associated_qos_spec = ""
# get all currently defined qos specs
qos_specs = qos_spec_list(request)
for qos_spec in qos_specs:
# get all volume types this qos spec is associated with
assoc_vol_types = qos_spec_get_associations(request, qos_spec.id)
for assoc_vol_type in assoc_vol_types:
if vol_type.id == assoc_vol_type.id:
# update volume type to hold this association info
vol_type.associated_qos_spec = qos_spec.name
return vol_type
return vol_type
def default_quota_update(request, **kwargs):
cinderclient(request).quota_classes.update(DEFAULT_QUOTA_NAME, **kwargs)
@ -456,8 +474,18 @@ def volume_type_list(request):
return cinderclient(request).volume_types.list()
def volume_type_create(request, name):
return cinderclient(request).volume_types.create(name)
def volume_type_create(request, name, description=None):
return cinderclient(request).volume_types.create(name, description)
def volume_type_update(request, volume_type_id, name=None, description=None):
return cinderclient(request).volume_types.update(volume_type_id,
name,
description)
def volume_type_default(request):
return cinderclient(request).volume_types.default()
def volume_type_delete(request, volume_type_id):

View File

@ -0,0 +1,21 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% load url from future %}
{% block form_id %}{% endblock %}
{% block form_action %}{% url 'horizon:admin:volumes:volume_types:update_type' volume_type.id %}{% endblock %}
{% block modal_id %}update_volume_type_modal{% endblock %}
{% block modal-header %}{% trans "Edit Volume Type" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description:" %}</h3>
<p>{% trans "Modify volume type name and description." %}</p>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Edit Volume Type" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Edit Volume Type") %}
{% endblock page_header %}
{% block main %}
{% include 'admin/volumes/volume_types/_update_volume_type.html' %}
{% endblock %}

View File

@ -10,8 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
@ -166,3 +166,41 @@ class EditQosSpecConsumer(forms.SelfHandlingForm):
redirect = reverse("horizon:admin:volumes:index")
exceptions.handle(request, _('Error editing QoS Spec consumer.'),
redirect=redirect)
class EditVolumeType(forms.SelfHandlingForm):
name = forms.CharField(max_length=255,
label=_("Name"))
description = forms.CharField(max_length=255,
widget=forms.Textarea(attrs={'rows': 4}),
label=_("Description"),
required=False)
def clean_name(self):
cleaned_name = self.cleaned_data['name']
if len(cleaned_name.strip()) == 0:
msg = _('New name cannot be empty.')
self._errors['name'] = self.error_class([msg])
return cleaned_name
def handle(self, request, data):
volume_type_id = self.initial['id']
try:
cinder.volume_type_update(request,
volume_type_id,
data['name'],
data['description'])
message = _('Successfully updated volume type.')
messages.success(request, message)
return True
except Exception as ex:
redirect = reverse("horizon:admin:volumes:index")
if ex.code == 409:
error_message = _('New name conflicts with another '
'volume type.')
else:
error_message = _('Unable to update volume type.')
exceptions.handle(request, error_message,
redirect=redirect)

View File

@ -15,9 +15,11 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import forms
from horizon import tables
from openstack_dashboard.api import cinder
from openstack_dashboard import policy
class CreateVolumeType(tables.LinkAction):
@ -29,11 +31,21 @@ class CreateVolumeType(tables.LinkAction):
policy_rules = (("volume", "volume_extension:types_manage"),)
class EditVolumeType(tables.LinkAction):
name = "edit"
verbose_name = _("Edit Volume Type")
url = "horizon:admin:volumes:volume_types:update_type"
classes = ("ajax-modal",)
icon = "pencil"
policy_rules = (("volume", "volume_extension:types_manage"),)
class ViewVolumeTypeExtras(tables.LinkAction):
name = "extras"
verbose_name = _("View Extra Specs")
url = "horizon:admin:volumes:volume_types:extras:index"
classes = ("btn-edit",)
classes = ("ajax-modal",)
icon = "pencil"
policy_rules = (("volume", "volume_extension:types_manage"),)
@ -142,8 +154,64 @@ class VolumeTypesFilterAction(tables.FilterAction):
if query in volume_type.name.lower()]
class UpdateRow(tables.Row):
ajax = True
def get_data(self, request, volume_type_id):
try:
volume_type = \
cinder.volume_type_get_with_qos_association(request,
volume_type_id)
except Exception:
exceptions.handle(request,
_('Unable to retrieve volume type qos.'))
return volume_type
class UpdateCell(tables.UpdateAction):
def allowed(self, request, volume_type, cell):
return policy.check(
("volume_extension", "volume_extension:types_manage"), request)
def update_cell(self, request, data, volume_type_id,
cell_name, new_cell_value):
# inline update volume type name and/or description
try:
vol_type_obj = data
# updating changed value by new value
setattr(vol_type_obj, cell_name, new_cell_value)
name_value = getattr(vol_type_obj, 'name', None)
desc_value = getattr(vol_type_obj, 'description', None)
cinder.volume_type_update(
request,
volume_type_id,
name=name_value,
description=desc_value)
except Exception as ex:
if ex.code and ex.code == 409:
error_message = _('New name conflicts with another '
'volume type.')
else:
error_message = _('Unable to update the volume type.')
exceptions.handle(request, error_message)
return False
return True
class VolumeTypesTable(tables.DataTable):
name = tables.Column("name", verbose_name=_("Name"))
name = tables.Column("name", verbose_name=_("Name"),
form_field=forms.CharField(
max_length=64, required=True),
update_action=UpdateCell)
description = tables.Column(lambda obj: getattr(obj, 'description', None),
verbose_name=_('Description'),
form_field=forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False),
update_action=UpdateCell)
assoc_qos_spec = tables.Column("associated_qos_spec",
verbose_name=_("Associated QoS Spec"))
encryption = tables.Column(get_volume_type_encryption,
@ -166,8 +234,10 @@ class VolumeTypesTable(tables.DataTable):
row_actions = (CreateVolumeTypeEncryption,
ViewVolumeTypeExtras,
ManageQosSpecAssociation,
EditVolumeType,
DeleteVolumeTypeEncryption,
DeleteVolumeType,)
row_class = UpdateRow
# QOS Specs section of panel

View File

@ -23,18 +23,42 @@ from openstack_dashboard.test import helpers as test
class VolumeTypeTests(test.BaseAdminViewTests):
@test.create_stubs({cinder: ('volume_type_create',)})
def test_create_volume_type(self):
formData = {'name': 'volume type 1'}
cinder.volume_type_create(IsA(http.HttpRequest),
formData['name']).\
AndReturn(self.volume_types.first())
formData = {'name': 'volume type 1',
'vol_type_description': 'test desc'}
cinder.volume_type_create(
IsA(http.HttpRequest),
formData['name'],
formData['vol_type_description']).AndReturn(
self.volume_types.first())
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:admin:volumes:volume_types:create_type'),
formData)
redirect = reverse('horizon:admin:volumes:volume_types_tab')
self.assertNoFormErrors(res)
redirect = reverse('horizon:admin:volumes:volume_types_tab')
self.assertRedirectsNoFollow(res, redirect)
@test.create_stubs({cinder: ('volume_type_get',
'volume_type_update')})
def test_update_volume_type(self):
volume_type = self.cinder_volume_types.first()
formData = {'name': volume_type.name,
'description': 'test desc updated'}
volume_type = cinder.volume_type_get(
IsA(http.HttpRequest), volume_type.id).AndReturn(volume_type)
cinder.volume_type_update(
IsA(http.HttpRequest),
volume_type.id,
formData['name'],
formData['description']).AndReturn(volume_type)
self.mox.ReplayAll()
url = reverse('horizon:admin:volumes:volume_types:update_type',
args=[volume_type.id])
res = self.client.post(url, formData)
self.assertNoFormErrors(res)
redirect = reverse('horizon:admin:volumes:volume_types_tab')
self.assertRedirectsNoFollow(res, redirect)
@test.create_stubs({api.nova: ('server_list',),
@ -45,7 +69,7 @@ class VolumeTypeTests(test.BaseAdminViewTests):
'volume_encryption_type_list'),
keystone: ('tenant_list',)})
def test_delete_volume_type(self):
volume_type = self.volume_types.first()
volume_type = self.cinder_volume_types.first()
formData = {'action': 'volume_types__delete__%s' % volume_type.id}
encryption_list = (self.cinder_volume_encryption_types.list()[0],
self.cinder_volume_encryption_types.list()[1])
@ -58,7 +82,7 @@ class VolumeTypeTests(test.BaseAdminViewTests):
cinder.volume_encryption_type_list(IsA(http.HttpRequest))\
.AndReturn(encryption_list)
cinder.volume_type_delete(IsA(http.HttpRequest),
str(volume_type.id))
volume_type.id)
self.mox.ReplayAll()
res = self.client.post(

View File

@ -27,6 +27,9 @@ urlpatterns = patterns(
'VIEWS_MOD',
url(r'^create_type$', views.CreateVolumeTypeView.as_view(),
name='create_type'),
url(r'^(?P<type_id>[^/]+)/update_type/$',
views.EditVolumeTypeView.as_view(),
name='update_type'),
url(r'^create_qos_spec$', views.CreateQosSpecView.as_view(),
name='create_qos_spec'),
url(r'^(?P<type_id>[^/]+)/manage_qos_spec_association/$',

View File

@ -116,6 +116,45 @@ class CreateVolumeTypeEncryptionView(forms.ModalFormView):
'volume_type_id': self.kwargs['volume_type_id']}
class EditVolumeTypeView(forms.ModalFormView):
form_class = volume_types_forms.EditVolumeType
template_name = 'admin/volumes/volume_types/update_volume_type.html'
success_url = 'horizon:admin:volumes:volume_types_tab'
cancel_url = 'horizon:admin:volumes:volume_types_tab'
submit_label = _('Edit')
def get_success_url(self):
return reverse(self.success_url)
@memoized.memoized_method
def get_data(self):
try:
volume_type_id = self.kwargs['type_id']
volume_type = api.cinder.volume_type_get(self.request,
volume_type_id)
except Exception:
error_message = _(
'Unable to retrieve volume type for: "%s"') \
% volume_type_id
exceptions.handle(self.request,
error_message,
redirect=self.success_url)
return volume_type
def get_context_data(self, **kwargs):
context = super(EditVolumeTypeView, self).get_context_data(**kwargs)
context['volume_type'] = self.get_data()
return context
def get_initial(self):
volume_type = self.get_data()
return {'id': self.kwargs['type_id'],
'name': volume_type.name,
'description': getattr(volume_type, 'description', "")}
class CreateQosSpecView(forms.ModalFormView):
form_class = volumes_forms.CreateQosSpec
modal_header = _("Create QoS Spec")

View File

@ -152,6 +152,13 @@ class UnmanageVolume(forms.SelfHandlingForm):
class CreateVolumeType(forms.SelfHandlingForm):
name = forms.CharField(max_length=255, label=_("Name"))
vol_type_description = forms.CharField(
max_length=255,
widget=forms.Textarea(
attrs={'class': 'modal-body-fixed-width',
'rows': 4}),
label=_("Description"),
required=False)
def clean_name(self):
cleaned_name = self.cleaned_data['name']
@ -163,8 +170,10 @@ class CreateVolumeType(forms.SelfHandlingForm):
def handle(self, request, data):
try:
# Remove any new lines in the public key
volume_type = cinder.volume_type_create(request,
data['name'])
volume_type = cinder.volume_type_create(
request,
data['name'],
data['vol_type_description'])
messages.success(request, _('Successfully created volume type: %s')
% data['name'])
return volume_type

View File

@ -2,7 +2,16 @@
<h3>{% trans "Description:" %}</h3>
<p>{% block title %}{% trans "Volumes are block devices that can be attached to instances." %}{% endblock %}</p>
<p>{% blocktrans %}
Volumes are block devices that can be attached to instances.
{% endblocktrans %}
</p>
<div id="id_show_volume_type_desc_div">
<h3>{% trans "Volume Type Description:" %}</h3>
<h4><b><span id="id_show_volume_type_name"></span></b></h4>
<p id="id_show_volume_type_desc"></p>
</div>
<h3>{% block head %}{% trans "Volume Limits" %}{% endblock %}</h3>
@ -22,7 +31,6 @@
<div id={% block type_id %}"quota_volumes"{% endblock %} data-progress-indicator-step-by="1" data-quota-limit={% block total_progress %}"{{ usages.maxTotalVolumes }}"{% endblock %} data-quota-used={% block used_progress %}"{{ usages.volumesUsed }}"{% endblock %} class="quota_bar">
</div>
<script type="text/javascript" charset="utf-8">
if(typeof horizon.Quota !== 'undefined') {
horizon.Quota.init();
@ -31,4 +39,12 @@
horizon.Quota.init();
});
}
if(typeof horizon.Volumes !== 'undefined'){
horizon.Volumes.initWithTypes({{ volume_types|safe|default:"{}" }});
} else {
addHorizonLoadEvent(function() {
horizon.Volumes.initWithTypes({{ volume_types|safe|default:"{}" }});
});
}
</script>

View File

@ -265,7 +265,7 @@ class CreateForm(forms.SelfHandlingForm):
def __init__(self, request, *args, **kwargs):
super(CreateForm, self).__init__(request, *args, **kwargs)
volume_types = cinder.volume_type_list(request)
self.fields['type'].choices = [("", _("No volume type"))] + \
self.fields['type'].choices = [("no_type", _("No volume type"))] + \
[(type.name, type.name)
for type in volume_types]
@ -379,6 +379,9 @@ class CreateForm(forms.SelfHandlingForm):
metadata = {}
if data['type'] == 'no_type':
data['type'] = ''
volume = cinder.volume_create(request,
data['size'],
data['name'],

View File

@ -103,6 +103,7 @@ class VolumeViewTests(test.TestCase):
url = reverse('horizon:project:volumes:volumes:create')
res = self.client.post(url, formData)
self.assertNoFormErrors(res)
redirect_url = VOLUME_VOLUMES_TAB_URL
self.assertRedirectsNoFollow(res, redirect_url)
@ -436,6 +437,7 @@ class VolumeViewTests(test.TestCase):
@test.create_stubs({cinder: ('volume_snapshot_get',
'volume_type_list',
'volume_type_default',
'volume_get'),
api.glance: ('image_list_detailed',),
quotas: ('tenant_limit_usages',)})
@ -452,6 +454,10 @@ class VolumeViewTests(test.TestCase):
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_default(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.first())
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
AndReturn(usage_limit)
cinder.volume_snapshot_get(IsA(http.HttpRequest),
@ -600,6 +606,7 @@ class VolumeViewTests(test.TestCase):
self.assertRedirectsNoFollow(res, redirect_url)
@test.create_stubs({cinder: ('volume_type_list',
'volume_type_default',
'availability_zone_list',
'extension_supported'),
api.glance: ('image_get',
@ -618,6 +625,10 @@ class VolumeViewTests(test.TestCase):
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_default(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.first())
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
AndReturn(usage_limit)
api.glance.image_get(IsA(http.HttpRequest),
@ -664,6 +675,8 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm',
'size': 5, 'image_source': image.id}
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
@ -701,6 +714,7 @@ class VolumeViewTests(test.TestCase):
@test.create_stubs({cinder: ('volume_snapshot_list',
'volume_type_list',
'volume_type_default',
'volume_list',
'availability_zone_list',
'extension_supported'),
@ -718,6 +732,10 @@ class VolumeViewTests(test.TestCase):
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_default(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.first())
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
AndReturn(usage_limit)
cinder.volume_snapshot_list(IsA(http.HttpRequest),
@ -768,6 +786,8 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm',
'size': 10}
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\

View File

@ -16,8 +16,11 @@
Views for managing volumes.
"""
import json
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils import encoding
from django.utils.translation import ugettext_lazy as _
from django.views import generic
@ -29,6 +32,7 @@ from horizon.utils import memoized
from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard import exceptions as dashboard_exception
from openstack_dashboard.usage import quotas
from openstack_dashboard.dashboards.project.volumes \
@ -97,10 +101,49 @@ class CreateView(forms.ModalFormView):
context = super(CreateView, self).get_context_data(**kwargs)
try:
context['usages'] = quotas.tenant_limit_usages(self.request)
context['volume_types'] = self._get_volume_types()
except Exception:
exceptions.handle(self.request)
return context
def _get_volume_types(self):
try:
volume_types = cinder.volume_type_list(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve volume type list.'))
# check if we have default volume type so we can present the
# description of no volume type differently
default_type = None
try:
default_type = cinder.volume_type_default(self.request)
except dashboard_exception.NOT_FOUND:
pass
if default_type is not None:
d_name = getattr(default_type, "name", "")
message =\
_("If \"No volume type\" is selected, the default "
"volume type \"%(name)s\" will be set for the "
"created volume.")
params = {'name': d_name}
no_type_description = encoding.force_text(message % params)
else:
message = \
_("If \"No volume type\" is selected, the volume will be "
"created without a volume type.")
no_type_description = encoding.force_text(message)
type_descriptions = [{'name': 'no_type',
'description': no_type_description}] + \
[{'name': type.name,
'description': getattr(type, "description", "")}
for type in volume_types]
return json.dumps(type_descriptions)
class ExtendView(forms.ModalFormView):
form_class = project_forms.ExtendForm

View File

@ -53,6 +53,7 @@
<script src='{{ STATIC_URL }}horizon/js/horizon.d3linechart.js'></script>
<script src='{{ STATIC_URL }}horizon/js/horizon.d3barchart.js'></script>
<script src='{{ STATIC_URL }}horizon/js/horizon.firewalls.js'></script>
<script src='{{ STATIC_URL }}horizon/js/horizon.volumes.js'></script>
<script src='{{ STATIC_URL }}horizon/lib/jsencrypt/jsencrypt.js'></script>
{% for file in HORIZON_CONFIG.js_files %}

View File

@ -94,6 +94,28 @@ class CinderApiTests(test.APITestCase):
associate_spec = assoc_vol_types[0].associated_qos_spec
self.assertTrue(associate_spec, qos_specs_only_one[0].name)
def test_volume_type_get_with_qos_association(self):
volume_type = self.cinder_volume_types.first()
qos_specs_full = self.cinder_qos_specs.list()
qos_specs_only_one = [qos_specs_full[0]]
associations = self.cinder_qos_spec_associations.list()
cinderclient = self.stub_cinderclient()
cinderclient.volume_types = self.mox.CreateMockAnything()
cinderclient.volume_types.get(volume_type.id).AndReturn(volume_type)
cinderclient.qos_specs = self.mox.CreateMockAnything()
cinderclient.qos_specs.list().AndReturn(qos_specs_only_one)
cinderclient.qos_specs.get_associations = self.mox.CreateMockAnything()
cinderclient.qos_specs.get_associations(qos_specs_only_one[0].id).\
AndReturn(associations)
self.mox.ReplayAll()
assoc_vol_type = \
api.cinder.volume_type_get_with_qos_association(self.request,
volume_type.id)
associate_spec = assoc_vol_type.associated_qos_spec
self.assertTrue(associate_spec, qos_specs_only_one[0].name)
def test_absolute_limits_with_negative_values(self):
values = {"maxTotalVolumes": -1, "totalVolumesUsed": -1}
expected_results = {"maxTotalVolumes": float("inf"),
@ -126,6 +148,16 @@ class CinderApiTests(test.APITestCase):
# No assertions are necessary. Verification is handled by mox.
api.cinder.pool_list(self.request, detailed=True)
def test_volume_type_default(self):
volume_type = self.cinder_volume_types.first()
cinderclient = self.stub_cinderclient()
cinderclient.volume_types = self.mox.CreateMockAnything()
cinderclient.volume_types.default().AndReturn(volume_type)
self.mox.ReplayAll()
default_volume_type = api.cinder.volume_type_default(self.request)
self.assertEqual(default_volume_type, volume_type)
class CinderApiVersionTests(test.TestCase):

View File

@ -144,10 +144,12 @@ def data(TEST):
vol_type1 = volume_types.VolumeType(volume_types.VolumeTypeManager(None),
{'id': u'1',
'name': u'vol_type_1',
'description': 'type 1 description',
'extra_specs': {'foo': 'bar'}})
vol_type2 = volume_types.VolumeType(volume_types.VolumeTypeManager(None),
{'id': u'2',
'name': u'vol_type_2'})
'name': u'vol_type_2',
'description': 'type 2 description'})
TEST.cinder_volume_types.add(vol_type1, vol_type2)
# Volumes - Cinder v2