diff --git a/horizon/static/horizon/js/horizon.forms.js b/horizon/static/horizon/js/horizon.forms.js index 78510edd5f..ce73066957 100644 --- a/horizon/static/horizon/js/horizon.forms.js +++ b/horizon/static/horizon/js/horizon.forms.js @@ -6,7 +6,7 @@ horizon.forms = { var $form = $(this).closest('form'); var $volName = $form.find('input#id_name'); if ($volName.val() == "") { - $volName.val($option.data("display_name")); + $volName.val($option.data("name")); } var $volSize = $form.find('input#id_size'); var volSize = parseInt($volSize.val(), 10) || -1; @@ -23,7 +23,7 @@ horizon.forms = { var $form = $(this).closest('form'); var $volName = $form.find('input#id_name'); if ($volName.val() == "") { - $volName.val($option.data("display_name")); + $volName.val($option.data("name")); } var $volSize = $form.find('input#id_size'); var volSize = parseInt($volSize.val(), 10) || -1; diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index db45d1e28f..7b123c94fd 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -27,7 +27,6 @@ import logging from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from cinderclient.v1 import client as cinder_client from cinderclient.v1.contrib import list_extensions as cinder_list_extensions from horizon import exceptions @@ -44,29 +43,100 @@ VOLUME_STATE_AVAILABLE = "available" DEFAULT_QUOTA_NAME = 'default' +VERSIONS = base.APIVersionManager("volume", preferred_version=1) + +try: + from cinderclient.v1 import client as cinder_client_v1 + VERSIONS.load_supported_version(1, {"client": cinder_client_v1, + "version": 1}) +except ImportError: + pass + +try: + from cinderclient.v2 import client as cinder_client_v2 + VERSIONS.load_supported_version(2, {"client": cinder_client_v2, + "version": 2}) +except ImportError: + pass + + +class BaseCinderAPIResourceWrapper(base.APIResourceWrapper): + + @property + def name(self): + # If a volume doesn't have a name, use its id. + return (getattr(self._apiresource, 'name', None) or + getattr(self._apiresource, 'display_name', None) or + getattr(self._apiresource, 'id', None)) + + @property + def description(self): + return (getattr(self._apiresource, 'description', None) or + getattr(self._apiresource, 'display_description', None)) + + +class Volume(BaseCinderAPIResourceWrapper): + + _attrs = ['id', 'name', 'description', 'size', 'status', 'created_at', + 'volume_type', 'availability_zone', 'imageRef', 'bootable' + 'snapshot_id', 'source_volid', 'attachments', 'tenant_name', + 'os-vol-host-attr:host', 'os-vol-tenant-attr:tenant_id', + 'metadata'] + + +class VolumeSnapshot(BaseCinderAPIResourceWrapper): + + _attrs = ['id', 'name', 'description', 'size', 'status', + 'created_at', 'volume_id', + 'os-extended-snapshot-attributes:project_id'] + + def cinderclient(request): + api_version = VERSIONS.get_active_version() + insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) cinder_url = "" try: - cinder_url = base.url_for(request, 'volume') + # The cinder client assumes that the v2 endpoint type will be + # 'volumev2'. However it also allows 'volume' type as a + # fallback if the requested version is 2 and there is no + # 'volumev2' endpoint. + if api_version['version'] == 2: + try: + cinder_url = base.url_for(request, 'volumev2') + except exceptions.ServiceCatalogException: + LOG.warning("Cinder v2 requested but no 'volumev2' service " + "type available in Keystone catalog. Falling back " + "to 'volume'.") + if cinder_url == "": + cinder_url = base.url_for(request, 'volume') except exceptions.ServiceCatalogException: LOG.debug('no volume service configured.') return None LOG.debug('cinderclient connection created using token "%s" and url "%s"' % (request.user.token.id, cinder_url)) - c = cinder_client.Client(request.user.username, - request.user.token.id, - project_id=request.user.tenant_id, - auth_url=cinder_url, - insecure=insecure, - cacert=cacert, - http_log_debug=settings.DEBUG) + c = api_version['client'].Client(request.user.username, + request.user.token.id, + project_id=request.user.tenant_id, + auth_url=cinder_url, + insecure=insecure, + cacert=cacert, + http_log_debug=settings.DEBUG) c.client.auth_token = request.user.token.id c.client.management_url = cinder_url return c +def _replace_v2_parameters(data): + if VERSIONS.active < 2: + data['display_name'] = data['name'] + data['display_description'] = data['description'] + del data['name'] + del data['description'] + return data + + def volume_list(request, search_opts=None): """To see all volumes in the cloud as an admin you can pass in a special search option: {'all_tenants': 1} @@ -74,7 +144,7 @@ def volume_list(request, search_opts=None): c_client = cinderclient(request) if c_client is None: return [] - return c_client.volumes.list(search_opts=search_opts) + return [Volume(v) for v in c_client.volumes.list(search_opts=search_opts)] def volume_get(request, volume_id): @@ -89,16 +159,24 @@ def volume_get(request, volume_id): # the lack a server_id property; to work around that we'll # give the attached instance a generic name. attachment['instance_name'] = _("Unknown instance") - return volume_data + return Volume(volume_data) def volume_create(request, size, name, description, volume_type, snapshot_id=None, metadata=None, image_id=None, availability_zone=None, source_volid=None): - return cinderclient(request).volumes.create(size, display_name=name, - display_description=description, volume_type=volume_type, - snapshot_id=snapshot_id, metadata=metadata, imageRef=image_id, - availability_zone=availability_zone, source_volid=source_volid) + data = {'name': name, + 'description': description, + 'volume_type': volume_type, + 'snapshot_id': snapshot_id, + 'metadata': metadata, + 'imageRef': image_id, + 'availability_zone': availability_zone, + 'source_volid': source_volid} + data = _replace_v2_parameters(data) + + volume = cinderclient(request).volumes.create(size, **data) + return Volume(volume) def volume_extend(request, volume_id, new_size): @@ -110,28 +188,34 @@ def volume_delete(request, volume_id): def volume_update(request, volume_id, name, description): - vol_data = {'display_name': name, - 'display_description': description} + vol_data = {'name': name, + 'description': description} + vol_data = _replace_v2_parameters(vol_data) return cinderclient(request).volumes.update(volume_id, **vol_data) def volume_snapshot_get(request, snapshot_id): - return cinderclient(request).volume_snapshots.get(snapshot_id) + snapshot = cinderclient(request).volume_snapshots.get(snapshot_id) + return VolumeSnapshot(snapshot) def volume_snapshot_list(request): c_client = cinderclient(request) if c_client is None: return [] - return c_client.volume_snapshots.list() + return [VolumeSnapshot(s) for s in c_client.volume_snapshots.list()] def volume_snapshot_create(request, volume_id, name, description=None, force=False): - return cinderclient(request).volume_snapshots.create( - volume_id, force=force, display_name=name, - display_description=description) + data = {'name': name, + 'description': description, + 'force': force} + data = _replace_v2_parameters(data) + + return VolumeSnapshot(cinderclient(request).volume_snapshots.create( + volume_id, **data)) def volume_snapshot_delete(request, snapshot_id): diff --git a/openstack_dashboard/dashboards/admin/volumes/tables.py b/openstack_dashboard/dashboards/admin/volumes/tables.py index 9befae86a8..0d704b362a 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tables.py +++ b/openstack_dashboard/dashboards/admin/volumes/tables.py @@ -41,11 +41,11 @@ class VolumesFilterAction(tables.FilterAction): """Naive case-insensitive search.""" q = filter_string.lower() return [volume for volume in volumes - if q in volume.display_name.lower()] + if q in volume.name.lower()] class VolumesTable(project_tables.VolumesTable): - name = tables.Column("display_name", + name = tables.Column("name", verbose_name=_("Name"), link="horizon:admin:volumes:detail") host = tables.Column("os-vol-host-attr:host", verbose_name=_("Host")) diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/detail.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/detail.html index 260f9f4379..59dbb7d5a5 100644 --- a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/detail.html +++ b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/detail.html @@ -3,7 +3,7 @@ {% block title %}{% trans "Volume Details" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.display_name|default:_("Volume Details:") %} + {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.name|default:_("Volume Details:") %} {% endblock page_header %} {% block main %} diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index 67851868eb..5acc7467f1 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -31,7 +31,7 @@ class VolumeTests(test.BaseAdminViewTests): keystone: ('tenant_list',)}) def test_index(self): cinder.volume_list(IsA(http.HttpRequest), search_opts={ - 'all_tenants': True}).AndReturn(self.volumes.list()) + 'all_tenants': True}).AndReturn(self.cinder_volumes.list()) api.nova.server_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': True}) \ .AndReturn([self.servers.list(), False]) @@ -47,7 +47,7 @@ class VolumeTests(test.BaseAdminViewTests): self.assertTemplateUsed(res, 'admin/volumes/index.html') volumes = res.context['volumes_table'].data - self.assertItemsEqual(volumes, self.volumes.list()) + self.assertItemsEqual(volumes, self.cinder_volumes.list()) @test.create_stubs({cinder: ('volume_type_create',)}) def test_create_volume_type(self): @@ -74,7 +74,7 @@ class VolumeTests(test.BaseAdminViewTests): formData = {'action': 'volume_types__delete__%s' % volume_type.id} cinder.volume_list(IsA(http.HttpRequest), search_opts={ - 'all_tenants': True}).AndReturn(self.volumes.list()) + 'all_tenants': True}).AndReturn(self.cinder_volumes.list()) api.nova.server_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': True}) \ .AndReturn([self.servers.list(), False]) diff --git a/openstack_dashboard/dashboards/admin/volumes/views.py b/openstack_dashboard/dashboards/admin/volumes/views.py index 858befb10d..5667327f44 100644 --- a/openstack_dashboard/dashboards/admin/volumes/views.py +++ b/openstack_dashboard/dashboards/admin/volumes/views.py @@ -48,7 +48,6 @@ class IndexView(tables.MultiTableView, project_tabs.VolumeTableMixIn): def get_volumes_data(self): volumes = self._get_volumes(search_opts={'all_tenants': True}) instances = self._get_instances(search_opts={'all_tenants': True}) - self._set_id_if_nameless(volumes) self._set_attachments_string(volumes, instances) # Gather our tenants to correlate against IDs diff --git a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py index 5294593e9b..b206c5f681 100644 --- a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py +++ b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py @@ -342,7 +342,7 @@ class SetInstanceDetailsAction(workflows.Action): visible_label = _("Volume") return (("%s:%s" % (volume.id, vol_type)), (_("%(name)s - %(size)s GB (%(label)s)") % - {'name': volume.display_name or volume.id, + {'name': volume.name, 'size': volume.size, 'label': visible_label})) diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py index 1993e1e7ca..5004e1156b 100644 --- a/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py +++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py @@ -79,7 +79,7 @@ class SnapshotVolumeNameColumn(tables.Column): def get_raw_data(self, snapshot): volume = snapshot._volume if volume: - volume_name = volume.display_name or volume.id + volume_name = volume.name volume_name = html.escape(volume_name) else: volume_name = _("Unknown") @@ -93,11 +93,10 @@ class SnapshotVolumeNameColumn(tables.Column): class VolumeSnapshotsTable(volume_tables.VolumesTableBase): - name = tables.Column("display_name", + name = tables.Column("name", verbose_name=_("Name"), link="horizon:project:volumes:detail") - volume_name = SnapshotVolumeNameColumn( - "display_name", + volume_name = SnapshotVolumeNameColumn("name", verbose_name=_("Volume Name"), link="horizon:project:volumes:volumes:detail") diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py index 50b9f3e59a..f3b434c211 100644 --- a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py @@ -35,12 +35,12 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), quotas: ('tenant_limit_usages',)}) def test_create_snapshot_get(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) @@ -56,15 +56,15 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get', 'volume_snapshot_create',)}) def test_create_snapshot_post(self): - volume = self.volumes.first() - snapshot = self.volume_snapshots.first() + volume = self.cinder_volumes.first() + snapshot = self.cinder_volume_snapshots.first() cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) cinder.volume_snapshot_create(IsA(http.HttpRequest), volume.id, - snapshot.display_name, - snapshot.display_description, + snapshot.name, + snapshot.description, force=False) \ .AndReturn(snapshot) self.mox.ReplayAll() @@ -72,8 +72,8 @@ class VolumeSnapshotsViewTests(test.TestCase): formData = {'method': 'CreateSnapshotForm', 'tenant_id': self.tenant.id, 'volume_id': volume.id, - 'name': snapshot.display_name, - 'description': snapshot.display_description} + 'name': snapshot.name, + 'description': snapshot.description} url = reverse('horizon:project:volumes:volumes:create_snapshot', args=[volume.id]) res = self.client.post(url, formData) @@ -82,15 +82,15 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get', 'volume_snapshot_create',)}) def test_force_create_snapshot(self): - volume = self.volumes.get(name='my_volume') - snapshot = self.volume_snapshots.first() + volume = self.cinder_volumes.get(name='my_volume') + snapshot = self.cinder_volume_snapshots.first() cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) cinder.volume_snapshot_create(IsA(http.HttpRequest), volume.id, - snapshot.display_name, - snapshot.display_description, + snapshot.name, + snapshot.description, force=True) \ .AndReturn(snapshot) self.mox.ReplayAll() @@ -98,8 +98,8 @@ class VolumeSnapshotsViewTests(test.TestCase): formData = {'method': 'CreateSnapshotForm', 'tenant_id': self.tenant.id, 'volume_id': volume.id, - 'name': snapshot.display_name, - 'description': snapshot.display_description} + 'name': snapshot.name, + 'description': snapshot.description} url = reverse('horizon:project:volumes:volumes:create_snapshot', args=[volume.id]) res = self.client.post(url, formData) @@ -111,9 +111,9 @@ class VolumeSnapshotsViewTests(test.TestCase): 'volume_snapshot_delete'), quotas: ('tenant_quota_usages',)}) def test_delete_volume_snapshot(self): - vol_snapshots = self.volume_snapshots.list() - volumes = self.volumes.list() - snapshot = self.volume_snapshots.first() + vol_snapshots = self.cinder_volume_snapshots.list() + volumes = self.cinder_volumes.list() + snapshot = self.cinder_volume_snapshots.first() api.cinder.volume_snapshot_list(IsA(http.HttpRequest)). \ AndReturn(vol_snapshots) @@ -142,8 +142,8 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')}) def test_volume_snapshot_detail_get(self): - volume = self.volumes.first() - snapshot = self.volume_snapshots.first() + volume = self.cinder_volumes.first() + snapshot = self.cinder_volume_snapshots.first() api.cinder.volume_get(IsA(http.HttpRequest), volume.id). \ AndReturn(volume) @@ -158,19 +158,16 @@ class VolumeSnapshotsViewTests(test.TestCase): self.assertContains(res, "

Volume Snapshot Details: %s

" % - snapshot.display_name, + snapshot.name, 1, 200) self.assertContains(res, "
test snapshot
", 1, 200) - self.assertContains(res, - "
40f3fabf-3613-4f5e-90e5-6c9a08333fc3
", - 1, - 200) + self.assertContains(res, "
%s
" % snapshot.id, 1, 200) self.assertContains(res, "
Available
", 1, 200) @test.create_stubs({api.cinder: ('volume_snapshot_get',)}) def test_volume_snapshot_detail_get_with_exception(self): # Test to verify redirect if get volume snapshot fails - snapshot = self.volume_snapshots.first() + snapshot = self.cinder_volume_snapshots.first() api.cinder.volume_snapshot_get(IsA(http.HttpRequest), snapshot.id).\ AndRaise(self.exceptions.cinder) @@ -185,8 +182,8 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')}) def test_volume_snapshot_detail_with_volume_get_exception(self): # Test to verify redirect if get volume fails - volume = self.volumes.first() - snapshot = self.volume_snapshots.first() + volume = self.cinder_volumes.first() + snapshot = self.cinder_volume_snapshots.first() api.cinder.volume_get(IsA(http.HttpRequest), volume.id). \ AndRaise(self.exceptions.cinder) diff --git a/openstack_dashboard/dashboards/project/volumes/tabs.py b/openstack_dashboard/dashboards/project/volumes/tabs.py index 45c1166d10..2527536d15 100644 --- a/openstack_dashboard/dashboards/project/volumes/tabs.py +++ b/openstack_dashboard/dashboards/project/volumes/tabs.py @@ -49,13 +49,6 @@ class VolumeTableMixIn(object): "attachment information")) return [] - def _set_id_if_nameless(self, volumes): - for volume in volumes: - # It is possible to create a volume with no name through the - # EC2 API, use the ID in those cases. - if not volume.display_name: - volume.display_name = volume.id - def _set_attachments_string(self, volumes, instances): instances = SortedDict([(inst.id, inst) for inst in instances]) for volume in volumes: @@ -73,7 +66,6 @@ class VolumeTab(tabs.TableTab, VolumeTableMixIn): def get_volumes_data(self): volumes = self._get_volumes() instances = self._get_instances() - self._set_id_if_nameless(volumes) self._set_attachments_string(volumes, instances) return volumes diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_detail_overview.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_detail_overview.html index 11fb958be4..bb2dcbf58f 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_detail_overview.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_detail_overview.html @@ -8,20 +8,20 @@
{% trans "Name" %}
-
{{ snapshot.display_name }}
+
{{ snapshot.name }}
{% trans "ID" %}
{{ snapshot.id }}
- {% if snapshot.display_description %} + {% if snapshot.description %}
{% trans "Description" %}
-
{{ snapshot.display_description }}
+
{{ snapshot.description }}
{% endif %}
{% trans "Status" %}
{{ snapshot.status|capfirst }}
{% trans "Volume" %}
- {% if volume.display_name %} - {{ volume.display_name }} + {% if volume.name %} + {{ volume.name }} {% else %} {{ snapshot.volume_id }} {% endif %} diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/detail.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/detail.html index 5bca132066..9e19f59a86 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/detail.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/detail.html @@ -3,7 +3,7 @@ {% block title %}{% trans "Volume Snapshot Details" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Volume Snapshot Details: ")|add:snapshot.display_name|default:_("Volume Snapshot Details:") %} + {% include "horizon/common/_page_header.html" with title=_("Volume Snapshot Details: ")|add:snapshot.name|default:_("Volume Snapshot Details:") %} {% endblock page_header %} {% block main %}
diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_detail_overview.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_detail_overview.html index 0aecff68a8..e51f282792 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_detail_overview.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_detail_overview.html @@ -8,12 +8,12 @@
{% trans "Name" %}
-
{{ volume.display_name }}
+
{{ volume.name }}
{% trans "ID" %}
{{ volume.id }}
- {% if volume.display_description %} + {% if volume.description %}
{% trans "Description" %}
-
{{ volume.display_description }}
+
{{ volume.description }}
{% endif %}
{% trans "Status" %}
{{ volume.status|capfirst }}
diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/detail.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/detail.html index 260f9f4379..59dbb7d5a5 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/detail.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/detail.html @@ -3,7 +3,7 @@ {% block title %}{% trans "Volume Details" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.display_name|default:_("Volume Details:") %} + {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.name|default:_("Volume Details:") %} {% endblock page_header %} {% block main %} diff --git a/openstack_dashboard/dashboards/project/volumes/test.py b/openstack_dashboard/dashboards/project/volumes/test.py index a0f05622a7..d0dba6a0d7 100644 --- a/openstack_dashboard/dashboards/project/volumes/test.py +++ b/openstack_dashboard/dashboards/project/volumes/test.py @@ -33,8 +33,8 @@ class VolumeAndSnapshotsTests(test.TestCase): api.nova: ('server_list',), quotas: ('tenant_quota_usages',)}) def test_index(self): - vol_snaps = self.volume_snapshots.list() - volumes = self.volumes.list() + vol_snaps = self.cinder_volume_snapshots.list() + volumes = self.cinder_volumes.list() api.cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\ AndReturn(volumes) @@ -48,4 +48,5 @@ class VolumeAndSnapshotsTests(test.TestCase): self.mox.ReplayAll() res = self.client.get(INDEX_URL) + self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'project/volumes/index.html') diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/forms.py b/openstack_dashboard/dashboards/project/volumes/volumes/forms.py index 4d80238663..fea238f9ee 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/forms.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/forms.py @@ -56,8 +56,8 @@ class CreateForm(forms.SelfHandlingForm): label=_("Use snapshot as a source"), widget=fields.SelectWidget( attrs={'class': 'snapshot-selector'}, - data_attrs=('size', 'display_name'), - transform=lambda x: "%s (%sGB)" % (x.display_name, x.size)), + data_attrs=('size', 'name'), + transform=lambda x: "%s (%sGB)" % (x.name, x.size)), required=False) image_source = forms.ChoiceField( label=_("Use image as a source"), @@ -70,8 +70,8 @@ class CreateForm(forms.SelfHandlingForm): label=_("Use a volume as source"), widget=fields.SelectWidget( attrs={'class': 'image-selector'}, - data_attrs=('size', 'display_name'), - transform=lambda x: "%s (%s)" % (x.display_name, + data_attrs=('size', 'name'), + transform=lambda x: "%s (%s)" % (x.name, filesizeformat(x.size * 1024 * 1024 * 1024))), required=False) availability_zone = forms.ChoiceField( @@ -94,7 +94,7 @@ class CreateForm(forms.SelfHandlingForm): try: snapshot = self.get_snapshot(request, request.GET["snapshot_id"]) - self.fields['name'].initial = snapshot.display_name + self.fields['name'].initial = snapshot.name self.fields['size'].initial = snapshot.size self.fields['snapshot_source'].choices = ((snapshot.id, snapshot),) @@ -153,8 +153,8 @@ class CreateForm(forms.SelfHandlingForm): exceptions.handle(request, msg % request.GET['volume_id']) if volume is not None: - self.fields['name'].initial = volume.display_name - self.fields['description'].initial = volume.display_description + self.fields['name'].initial = volume.name + self.fields['description'].initial = volume.description min_vol_size = volume.size size_help_text = _('Volume size must be equal to or greater ' 'than the origin volume size (%s)') \ @@ -417,12 +417,8 @@ class AttachForm(forms.SelfHandlingForm): data['instance'], data.get('device', '')) volume = cinder.volume_get(request, data['volume_id']) - if not volume.display_name: - volume_name = volume.id - else: - volume_name = volume.display_name message = _('Attaching volume %(vol)s to instance ' - '%(inst)s on %(dev)s.') % {"vol": volume_name, + '%(inst)s on %(dev)s.') % {"vol": volume.name, "inst": instance_name, "dev": attach.device} messages.info(request, message) diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tables.py b/openstack_dashboard/dashboards/project/volumes/volumes/tables.py index f2969df8de..f7a00af3dd 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/tables.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/tables.py @@ -165,8 +165,6 @@ class UpdateRow(tables.Row): def get_data(self, request, volume_id): volume = cinder.volume_get(request, volume_id) - if not volume.display_name: - volume.display_name = volume_id return volume @@ -226,10 +224,10 @@ class VolumesTableBase(tables.DataTable): ("creating", None), ("error", False), ) - name = tables.Column("display_name", + name = tables.Column("name", verbose_name=_("Name"), link="horizon:project:volumes:volumes:detail") - description = tables.Column("display_description", + description = tables.Column("description", verbose_name=_("Description"), truncate=40) size = tables.Column(get_size, @@ -242,7 +240,7 @@ class VolumesTableBase(tables.DataTable): status_choices=STATUS_CHOICES) def get_object_display(self, obj): - return obj.display_name + return obj.name class VolumesFilterAction(tables.FilterAction): @@ -251,11 +249,11 @@ class VolumesFilterAction(tables.FilterAction): """Naive case-insensitive search.""" q = filter_string.lower() return [volume for volume in volumes - if q in volume.display_name.lower()] + if q in volume.name.lower()] class VolumesTable(VolumesTableBase): - name = tables.Column("display_name", + name = tables.Column("name", verbose_name=_("Name"), link="horizon:project:volumes:volumes:detail") volume_type = tables.Column(get_volume_type, diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py index 03033f6273..195ae96fdd 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py @@ -47,12 +47,12 @@ class VolumeViewTests(test.TestCase): api.glance: ('image_list_detailed',), quotas: ('tenant_limit_usages',)}) def test_create_volume(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() volume_type = self.volume_types.first() az = self.cinder_availability_zones.first().zoneName usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -67,7 +67,7 @@ class VolumeViewTests(test.TestCase): quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -82,7 +82,7 @@ class VolumeViewTests(test.TestCase): cinder.extension_supported(IsA(http.HttpRequest), 'AvailabilityZones')\ .AndReturn(True) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) cinder.volume_create(IsA(http.HttpRequest), formData['size'], @@ -113,10 +113,10 @@ class VolumeViewTests(test.TestCase): api.glance: ('image_list_detailed',), quotas: ('tenant_limit_usages',)}) def test_create_volume_dropdown(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -124,13 +124,13 @@ class VolumeViewTests(test.TestCase): 'size': 50, 'type': '', 'volume_source_type': 'no_source_type', - 'snapshot_source': self.volume_snapshots.first().id, + 'snapshot_source': self.cinder_volume_snapshots.first().id, 'image_source': self.images.first().id} cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -140,7 +140,7 @@ class VolumeViewTests(test.TestCase): 'status': 'active'}) \ .AndReturn([[], False]) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) @@ -174,12 +174,12 @@ class VolumeViewTests(test.TestCase): 'volume_type_list'), quotas: ('tenant_limit_usages',)}) def test_create_volume_from_snapshot(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} - snapshot = self.volume_snapshots.first() + snapshot = self.cinder_volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', 'method': u'CreateForm', @@ -194,7 +194,7 @@ class VolumeViewTests(test.TestCase): cinder.volume_snapshot_get(IsA(http.HttpRequest), str(snapshot.id)).AndReturn(snapshot) cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\ - AndReturn(self.volumes.first()) + AndReturn(self.cinder_volumes.first()) cinder.volume_create(IsA(http.HttpRequest), formData['size'], @@ -228,10 +228,10 @@ class VolumeViewTests(test.TestCase): api.glance: ('image_list_detailed',), quotas: ('tenant_limit_usages',)}) def test_create_volume_from_volume(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} formData = {'name': u'A copy of a volume', @@ -243,16 +243,16 @@ class VolumeViewTests(test.TestCase): 'volume_source': volume.id} cinder.volume_list(IsA(http.HttpRequest)).\ - AndReturn(self.volumes.list()) + AndReturn(self.cinder_volumes.list()) cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) cinder.volume_get(IsA(http.HttpRequest), - volume.id).AndReturn(self.volumes.first()) + volume.id).AndReturn(self.cinder_volumes.first()) cinder.extension_supported(IsA(http.HttpRequest), 'AvailabilityZones').AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( @@ -296,12 +296,12 @@ class VolumeViewTests(test.TestCase): api.glance: ('image_list_detailed',), quotas: ('tenant_limit_usages',)}) def test_create_volume_from_snapshot_dropdown(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 250, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} - snapshot = self.volume_snapshots.first() + snapshot = self.cinder_volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', 'method': u'CreateForm', @@ -313,7 +313,7 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -323,7 +323,7 @@ class VolumeViewTests(test.TestCase): 'status': 'active'}) \ .AndReturn([[], False]) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) cinder.volume_snapshot_get(IsA(http.HttpRequest), @@ -362,9 +362,9 @@ class VolumeViewTests(test.TestCase): def test_create_volume_from_snapshot_invalid_size(self): usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} - snapshot = self.volume_snapshots.first() + snapshot = self.cinder_volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', 'method': u'CreateForm', @@ -377,7 +377,7 @@ class VolumeViewTests(test.TestCase): cinder.volume_snapshot_get(IsA(http.HttpRequest), str(snapshot.id)).AndReturn(snapshot) cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\ - AndReturn(self.volumes.first()) + AndReturn(self.cinder_volumes.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) @@ -400,10 +400,10 @@ class VolumeViewTests(test.TestCase): api.glance: ('image_get',), quotas: ('tenant_limit_usages',)}) def test_create_volume_from_image(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 200, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', @@ -457,10 +457,10 @@ class VolumeViewTests(test.TestCase): 'image_list_detailed'), quotas: ('tenant_limit_usages',)}) def test_create_volume_from_image_dropdown(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 200, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', @@ -469,13 +469,13 @@ class VolumeViewTests(test.TestCase): 'size': 30, 'type': '', 'volume_source_type': 'image_source', - 'snapshot_source': self.volume_snapshots.first().id, + 'snapshot_source': self.cinder_volume_snapshots.first().id, 'image_source': image.id} cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -485,7 +485,7 @@ class VolumeViewTests(test.TestCase): 'status': 'active'}) \ .AndReturn([[], False]) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) quotas.tenant_limit_usages(IsA(http.HttpRequest)) \ .AndReturn(usage_limit) api.glance.image_get(IsA(http.HttpRequest), @@ -525,7 +525,7 @@ class VolumeViewTests(test.TestCase): def test_create_volume_from_image_under_image_size(self): usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', @@ -574,7 +574,7 @@ class VolumeViewTests(test.TestCase): def test_create_volume_from_image_under_image_min_disk_size(self): usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} image = self.images.get(name="protected_images") image.min_disk = 30 @@ -617,7 +617,7 @@ class VolumeViewTests(test.TestCase): def test_create_volume_gb_used_over_alloted_quota(self): usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 80, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} formData = {'name': u'This Volume Is Huge!', 'description': u'This is a volume that is just too big!', @@ -629,7 +629,7 @@ class VolumeViewTests(test.TestCase): quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -639,7 +639,7 @@ class VolumeViewTests(test.TestCase): 'status': 'active'}) \ .AndReturn([[], False]) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) cinder.extension_supported(IsA(http.HttpRequest), 'AvailabilityZones')\ .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( @@ -666,8 +666,8 @@ class VolumeViewTests(test.TestCase): def test_create_volume_number_over_alloted_quota(self): usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), - 'maxTotalVolumes': len(self.volumes.list())} + 'volumesUsed': len(self.cinder_volumes.list()), + 'maxTotalVolumes': len(self.cinder_volumes.list())} formData = {'name': u'Too Many...', 'description': u'We have no volumes left!', 'method': u'CreateForm', @@ -678,7 +678,7 @@ class VolumeViewTests(test.TestCase): quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -688,7 +688,7 @@ class VolumeViewTests(test.TestCase): 'status': 'active'}) \ .AndReturn([[], False]) cinder.volume_list(IsA( - http.HttpRequest)).AndReturn(self.volumes.list()) + http.HttpRequest)).AndReturn(self.cinder_volumes.list()) cinder.extension_supported(IsA(http.HttpRequest), 'AvailabilityZones')\ .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( @@ -711,25 +711,25 @@ class VolumeViewTests(test.TestCase): api.nova: ('server_list',), quotas: ('tenant_quota_usages',)}) def test_delete_volume(self): - volumes = self.volumes.list() - volume = self.volumes.first() + volumes = self.cinder_volumes.list() + volume = self.cinder_volumes.first() formData = {'action': 'volumes__delete__%s' % volume.id} cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\ - AndReturn(volumes) + AndReturn(volumes) cinder.volume_delete(IsA(http.HttpRequest), volume.id) api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\ - AndReturn([self.servers.list(), False]) + AndReturn([self.servers.list(), False]) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_snapshots.list()) + AndReturn(self.cinder_volume_snapshots.list()) cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\ - AndReturn(volumes) + AndReturn(volumes) api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\ - AndReturn([self.servers.list(), False]) + AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes().\ - AndReturn(self.quota_usages.first()) + AndReturn(self.quota_usages.first()) self.mox.ReplayAll() @@ -744,8 +744,8 @@ class VolumeViewTests(test.TestCase): api.nova: ('server_list',), quotas: ('tenant_quota_usages',)}) def test_delete_volume_error_existing_snapshot(self): - volume = self.volumes.first() - volumes = self.volumes.list() + volume = self.cinder_volumes.first() + volumes = self.cinder_volumes.list() formData = {'action': 'volumes__delete__%s' % volume.id} exc = self.exceptions.cinder.__class__(400, @@ -762,7 +762,7 @@ class VolumeViewTests(test.TestCase): api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\ AndReturn([self.servers.list(), False]) cinder.volume_snapshot_list(IsA(http.HttpRequest))\ - .AndReturn(self.volume_snapshots.list()) + .AndReturn(self.cinder_volume_snapshots.list()) cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes().\ AndReturn(self.quota_usages.first()) @@ -774,14 +774,14 @@ class VolumeViewTests(test.TestCase): self.assertEqual(list(res.context['messages'])[0].message, u'Unable to delete volume "%s". ' u'One or more snapshots depend on it.' % - volume.display_name) + volume.name) @test.create_stubs({cinder: ('volume_get',), api.nova: ('server_list',)}) def test_edit_attachments(self): PREV = settings.OPENSTACK_HYPERVISOR_FEATURES['can_set_mount_point'] settings.OPENSTACK_HYPERVISOR_FEATURES['can_set_mount_point'] = True - volume = self.volumes.first() + volume = self.cinder_volumes.first() servers = [s for s in self.servers.list() if s.tenant_id == self.request.user.tenant_id] @@ -805,7 +805,7 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), api.nova: ('server_list',)}) def test_edit_attachments_cannot_set_mount_point(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() servers = [s for s in self.servers.list() if s.tenant_id == self.request.user.tenant_id] @@ -828,7 +828,7 @@ class VolumeViewTests(test.TestCase): servers = [s for s in self.servers.list() if s.tenant_id == self.request.user.tenant_id] server = servers[0] - volume = self.volumes.list()[0] + volume = self.cinder_volumes.list()[0] cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) @@ -856,14 +856,14 @@ class VolumeViewTests(test.TestCase): def test_create_button_disabled_when_quota_exceeded(self): quota_usages = self.quota_usages.first() quota_usages['volumes']['available'] = 0 - volumes = self.volumes.list() + volumes = self.cinder_volumes.list() cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\ .AndReturn(volumes) api.nova.server_list(IsA(http.HttpRequest), search_opts=None)\ .AndReturn([self.servers.list(), False]) cinder.volume_snapshot_list(IsA(http.HttpRequest))\ - .AndReturn(self.volume_snapshots.list()) + .AndReturn(self.cinder_volume_snapshots.list()) cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) quotas.tenant_quota_usages(IsA(http.HttpRequest))\ .MultipleTimes().AndReturn(quota_usages) @@ -874,7 +874,7 @@ class VolumeViewTests(test.TestCase): self.assertTemplateUsed(res, 'project/volumes/index.html') volumes = res.context['volumes_table'].data - self.assertItemsEqual(volumes, self.volumes.list()) + self.assertItemsEqual(volumes, self.cinder_volumes.list()) create_link = tables.CreateVolume() url = create_link.get_link_url() @@ -891,7 +891,7 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), api.nova: ('server_get',)}) def test_detail_view(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() server = self.servers.first() volume.attachments = [{"server_id": server.id}] @@ -908,10 +908,7 @@ class VolumeViewTests(test.TestCase): self.assertContains(res, "

Volume Details: Volume name

", 1, 200) self.assertContains(res, "
Volume name
", 1, 200) - self.assertContains(res, - "
41023e92-8008-4c8b-8059-7f2293ff3775
", - 1, - 200) + self.assertContains(res, "
%s
" % volume.id, 1, 200) self.assertContains(res, "
Available
", 1, 200) self.assertContains(res, "
40 GB
", 1, 200) self.assertContains(res, @@ -924,8 +921,8 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',)}) def test_get_data(self): - volume = self.volumes.first() - volume.display_name = '' + volume = self.cinder_volumes.get(name='v2_volume') + volume._apiresource.name = "" cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) @@ -938,11 +935,11 @@ class VolumeViewTests(test.TestCase): HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(res.status_code, 200) - self.assertEqual(volume.display_name, volume.id) + self.assertEqual(volume.name, volume.id) @test.create_stubs({cinder: ('volume_get',)}) def test_detail_view_with_exception(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() server = self.servers.first() volume.attachments = [{"server_id": server.id}] @@ -961,19 +958,19 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_update', 'volume_get',)}) def test_update_volume(self): - volume = self.volumes.get(name="my_volume") + volume = self.cinder_volumes.get(name="my_volume") cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) cinder.volume_update(IsA(http.HttpRequest), volume.id, - volume.display_name, - volume.display_description) + volume.name, + volume.description) self.mox.ReplayAll() formData = {'method': 'UpdateForm', - 'name': volume.display_name, - 'description': volume.display_description} + 'name': volume.name, + 'description': volume.description} url = reverse('horizon:project:volumes:volumes:update', args=[volume.id]) @@ -983,13 +980,13 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get', 'volume_extend')}) def test_extend_volume(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() formData = {'name': u'A Volume I Am Making', 'orig_size': volume.size, 'new_size': 100} cinder.volume_get(IsA(http.HttpRequest), volume.id).\ - AndReturn(self.volumes.first()) + AndReturn(self.cinder_volumes.first()) cinder.volume_extend(IsA(http.HttpRequest), volume.id, @@ -1007,17 +1004,17 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), quotas: ('tenant_limit_usages',)}) def test_extend_volume_with_wrong_size(self): - volume = self.volumes.first() + volume = self.cinder_volumes.first() usage_limit = {'maxTotalVolumeGigabytes': 100, 'gigabytesUsed': 20, - 'volumesUsed': len(self.volumes.list()), + 'volumesUsed': len(self.cinder_volumes.list()), 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'orig_size': volume.size, 'new_size': 10} cinder.volume_get(IsA(http.HttpRequest), volume.id).\ - AndReturn(self.volumes.first()) + AndReturn(self.cinder_volumes.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ AndReturn(usage_limit) diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/volumes/views.py index 7ab620a290..967399fccb 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/views.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/views.py @@ -115,7 +115,7 @@ class ExtendView(forms.ModalFormView): def get_initial(self): volume = self.get_object() return {'id': self.kwargs['volume_id'], - 'name': volume.display_name, + 'name': volume.name, 'orig_size': volume.size} @@ -171,8 +171,8 @@ class UpdateView(forms.ModalFormView): def get_initial(self): volume = self.get_object() return {'volume_id': self.kwargs["volume_id"], - 'name': volume.display_name, - 'description': volume.display_description} + 'name': volume.name, + 'description': volume.description} class EditAttachmentsView(tables.DataTableView, forms.ModalFormView): diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index 3cb6d82055..a9c96c182e 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -32,7 +32,8 @@ TEMPLATE_DEBUG = DEBUG # service API. For example, The identity service APIs have inconsistent # use of the decimal point, so valid options would be "2.0" or "3". # OPENSTACK_API_VERSIONS = { -# "identity": 3 +# "identity": 3, +# "volume": 2 # } # Set this to True if running on multi-domain model. When this is enabled, it diff --git a/openstack_dashboard/test/api_tests/cinder_tests.py b/openstack_dashboard/test/api_tests/cinder_tests.py index d395a54f30..54c0c20d06 100644 --- a/openstack_dashboard/test/api_tests/cinder_tests.py +++ b/openstack_dashboard/test/api_tests/cinder_tests.py @@ -14,6 +14,9 @@ # License for the specific language governing permissions and limitations # under the License. +from django.test.utils import override_settings + +import cinderclient as cinder_client from openstack_dashboard import api from openstack_dashboard.test import helpers as test @@ -22,7 +25,7 @@ from openstack_dashboard.test import helpers as test class CinderApiTests(test.APITestCase): def test_volume_list(self): search_opts = {'all_tenants': 1} - volumes = self.volumes.list() + volumes = self.cinder_volumes.list() cinderclient = self.stub_cinderclient() cinderclient.volumes = self.mox.CreateMockAnything() cinderclient.volumes.list(search_opts=search_opts,).AndReturn(volumes) @@ -32,7 +35,7 @@ class CinderApiTests(test.APITestCase): api.cinder.volume_list(self.request, search_opts=search_opts) def test_volume_snapshot_list(self): - volume_snapshots = self.volume_snapshots.list() + volume_snapshots = self.cinder_volume_snapshots.list() cinderclient = self.stub_cinderclient() cinderclient.volume_snapshots = self.mox.CreateMockAnything() cinderclient.volume_snapshots.list().AndReturn(volume_snapshots) @@ -46,7 +49,7 @@ class CinderApiTests(test.APITestCase): for service in catalog: if service["type"] == "volume": self.service_catalog.remove(service) - volume_snapshots = self.volume_snapshots.list() + volume_snapshots = self.cinder_volume_snapshots.list() cinderclient = self.stub_cinderclient() cinderclient.volume_snapshots = self.mox.CreateMockAnything() @@ -54,3 +57,109 @@ class CinderApiTests(test.APITestCase): self.mox.ReplayAll() api.cinder.volume_snapshot_list(self.request) + + +class CinderApiVersionTests(test.TestCase): + + def setUp(self): + super(CinderApiVersionTests, self).setUp() + # The version is set when the module is loaded. Reset the + # active version each time so that we can test with different + # versions. + api.cinder.VERSIONS._active = None + + def test_default_client_is_v1(self): + client = api.cinder.cinderclient(self.request) + self.assertIsInstance(client, cinder_client.v1.client.Client) + + @override_settings(OPENSTACK_API_VERSIONS={'volume': 1}) + def test_v1_setting_returns_v1_client(self): + client = api.cinder.cinderclient(self.request) + self.assertIsInstance(client, cinder_client.v1.client.Client) + + @override_settings(OPENSTACK_API_VERSIONS={'volume': 2}) + def test_v2_setting_returns_v2_client(self): + client = api.cinder.cinderclient(self.request) + self.assertIsInstance(client, cinder_client.v2.client.Client) + + def test_get_v1_volume_attributes(self): + # Get a v1 volume + volume = self.cinder_volumes.first() + self.assertTrue(hasattr(volume._apiresource, 'display_name')) + self.assertFalse(hasattr(volume._apiresource, 'name')) + + name = "A test volume name" + description = "A volume description" + setattr(volume._apiresource, 'display_name', name) + setattr(volume._apiresource, 'display_description', description) + self.assertEqual(volume.name, name) + self.assertEqual(volume.description, description) + + def test_get_v2_volume_attributes(self): + # Get a v2 volume + volume = self.cinder_volumes.get(name="v2_volume") + self.assertTrue(hasattr(volume._apiresource, 'name')) + self.assertFalse(hasattr(volume._apiresource, 'display_name')) + + name = "A v2 test volume name" + description = "A v2 volume description" + setattr(volume._apiresource, 'name', name) + setattr(volume._apiresource, 'description', description) + self.assertEqual(volume.name, name) + self.assertEqual(volume.description, description) + + def test_get_v1_snapshot_attributes(self): + # Get a v1 snapshot + snapshot = self.cinder_volume_snapshots.first() + self.assertFalse(hasattr(snapshot._apiresource, 'name')) + + name = "A test snapshot name" + description = "A snapshot description" + setattr(snapshot._apiresource, 'display_name', name) + setattr(snapshot._apiresource, 'display_description', description) + self.assertEqual(snapshot.name, name) + self.assertEqual(snapshot.description, description) + + def test_get_v2_snapshot_attributes(self): + # Get a v2 snapshot + snapshot = self.cinder_volume_snapshots.get( + description="v2 volume snapshot description") + self.assertFalse(hasattr(snapshot._apiresource, 'display_name')) + + name = "A v2 test snapshot name" + description = "A v2 snapshot description" + setattr(snapshot._apiresource, 'name', name) + setattr(snapshot._apiresource, 'description', description) + self.assertEqual(snapshot.name, name) + self.assertEqual(snapshot.description, description) + + def test_get_id_for_nameless_volume(self): + volume = self.cinder_volumes.first() + setattr(volume._apiresource, 'display_name', "") + self.assertEqual(volume.name, volume.id) + + @override_settings(OPENSTACK_API_VERSIONS={'volume': 1}) + def test_adapt_dictionary_to_v1(self): + volume = self.cinder_volumes.first() + data = {'name': volume.name, + 'description': volume.description, + 'size': volume.size} + + ret_data = api.cinder._replace_v2_parameters(data) + self.assertIn('display_name', ret_data.keys()) + self.assertIn('display_description', ret_data.keys()) + self.assertNotIn('name', ret_data.keys()) + self.assertNotIn('description', ret_data.keys()) + + @override_settings(OPENSTACK_API_VERSIONS={'volume': 2}) + def test_adapt_dictionary_to_v2(self): + volume = self.cinder_volumes.first() + data = {'name': volume.name, + 'description': volume.description, + 'size': volume.size} + + ret_data = api.cinder._replace_v2_parameters(data) + self.assertIn('name', ret_data.keys()) + self.assertIn('description', ret_data.keys()) + self.assertNotIn('display_name', ret_data.keys()) + self.assertNotIn('display_description', ret_data.keys()) diff --git a/openstack_dashboard/test/test_data/cinder_data.py b/openstack_dashboard/test/test_data/cinder_data.py index 18c5f6eeb9..9deb2e0d17 100644 --- a/openstack_dashboard/test/test_data/cinder_data.py +++ b/openstack_dashboard/test/test_data/cinder_data.py @@ -14,25 +14,94 @@ from cinderclient.v1 import availability_zones from cinderclient.v1 import quotas +from cinderclient.v1 import volume_snapshots as vol_snaps +from cinderclient.v1 import volumes +from cinderclient.v2 import volume_snapshots as vol_snaps_v2 +from cinderclient.v2 import volumes as volumes_v2 -from openstack_dashboard.api import base +from openstack_dashboard import api from openstack_dashboard.usage import quotas as usage_quotas from openstack_dashboard.test.test_data import utils def data(TEST): + TEST.cinder_volumes = utils.TestDataContainer() + TEST.cinder_volume_snapshots = utils.TestDataContainer() TEST.cinder_quotas = utils.TestDataContainer() TEST.cinder_quota_usages = utils.TestDataContainer() TEST.cinder_availability_zones = utils.TestDataContainer() + # Volumes - Cinder v1 + volume = volumes.Volume(volumes.VolumeManager(None), + {'id': "11023e92-8008-4c8b-8059-7f2293ff3887", + 'status': 'available', + 'size': 40, + 'display_name': 'Volume name', + 'display_description': 'Volume description', + 'created_at': '2014-01-27 10:30:00', + 'volume_type': None, + 'attachments': []}) + nameless_volume = volumes.Volume(volumes.VolumeManager(None), + dict(id="4b069dd0-6eaa-4272-8abc-5448a68f1cce", + status='available', + size=10, + display_name='', + display_description='', + device="/dev/hda", + created_at='2010-11-21 18:34:25', + volume_type='vol_type_1', + attachments=[])) + other_volume = volumes.Volume(volumes.VolumeManager(None), + {'id': "21023e92-8008-1234-8059-7f2293ff3889", + 'status': 'in-use', + 'size': 10, + 'display_name': u'my_volume', + 'display_description': '', + 'created_at': '2013-04-01 10:30:00', + 'volume_type': None, + 'attachments': [{"id": "1", "server_id": '1', + "device": "/dev/hda"}]}) + TEST.cinder_volumes.add(api.cinder.Volume(volume)) + TEST.cinder_volumes.add(api.cinder.Volume(nameless_volume)) + TEST.cinder_volumes.add(api.cinder.Volume(other_volume)) + + # Volumes - Cinder v2 + volume_v2 = volumes_v2.Volume(volumes_v2.VolumeManager(None), + {'id': "31023e92-8008-4c8b-8059-7f2293ff1234", + 'name': 'v2_volume', + 'description': "v2 Volume Description", + 'status': 'available', + 'size': 20, + 'created_at': '2014-01-27 10:30:00', + 'volume_type': None, + 'attachments': []}) + TEST.cinder_volumes.add(api.cinder.Volume(volume_v2)) + + snapshot = vol_snaps.Snapshot(vol_snaps.SnapshotManager(None), + {'id': '5f3d1c33-7d00-4511-99df-a2def31f3b5d', + 'display_name': 'test snapshot', + 'display_description': 'volume snapshot', + 'size': 40, + 'status': 'available', + 'volume_id': '11023e92-8008-4c8b-8059-7f2293ff3887'}) + snapshot2 = vol_snaps_v2.Snapshot(vol_snaps_v2.SnapshotManager(None), + {'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0f', + 'name': '', + 'description': 'v2 volume snapshot description', + 'size': 80, + 'status': 'available', + 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) + + TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot)) + TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot2)) + # Quota Sets quota_data = dict(volumes='1', snapshots='1', gigabytes='1000') quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data) - #TEST.quotas.cinder = QuotaSetWrapper(quota) - TEST.cinder_quotas.add(base.QuotaSet(quota)) + TEST.cinder_quotas.add(api.base.QuotaSet(quota)) # Quota Usages quota_usage_data = {'gigabytes': {'used': 0, @@ -43,7 +112,7 @@ def data(TEST): 'quota': 10}} quota_usage = usage_quotas.QuotaUsage() for k, v in quota_usage_data.items(): - quota_usage.add_quota(base.Quota(k, v['quota'])) + quota_usage.add_quota(api.base.Quota(k, v['quota'])) quota_usage.tally(k, v['used']) TEST.cinder_quota_usages.add(quota_usage) diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index d190a076ab..210454b4b2 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy from datetime import timedelta # noqa from django.conf import settings @@ -47,7 +48,7 @@ SERVICE_CATALOG = [ "internalURL": "http://int.nova2.example.com:8774/v2", "publicURL": "http://public.nova2.example.com:8774/v2"}]}, {"type": "volume", - "name": "nova", + "name": "cinder", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", @@ -126,7 +127,9 @@ SERVICE_CATALOG = [ def data(TEST): - TEST.service_catalog = SERVICE_CATALOG + # Make a deep copy of the catalog to avoid persisting side-effects + # when tests modify the catalog. + TEST.service_catalog = copy.deepcopy(SERVICE_CATALOG) TEST.tokens = utils.TestDataContainer() TEST.domains = utils.TestDataContainer() TEST.users = utils.TestDataContainer() @@ -205,7 +208,7 @@ def data(TEST): user5 = users.User(users.UserManager(None), user_dict) TEST.users.add(user, user2, user3, user4, user5) TEST.user = user # Your "current" user - TEST.user.service_catalog = SERVICE_CATALOG + TEST.user.service_catalog = copy.deepcopy(SERVICE_CATALOG) group_dict = {'id': "1", 'name': 'group_one',