diff --git a/openstack_dashboard/dashboards/admin/snapshots/views.py b/openstack_dashboard/dashboards/admin/snapshots/views.py
index 22954609b8..de0852ffde 100644
--- a/openstack_dashboard/dashboards/admin/snapshots/views.py
+++ b/openstack_dashboard/dashboards/admin/snapshots/views.py
@@ -115,7 +115,7 @@ class UpdateStatusView(forms.ModalFormView):
class DetailView(views.DetailView):
tab_group_class = vol_snapshot_tabs.SnapshotDetailsTabs
- volume_url = 'horizon:admin:volumes:volumes:detail'
+ volume_url = 'horizon:admin:volumes:detail'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py b/openstack_dashboard/dashboards/admin/volumes/forms.py
similarity index 99%
rename from openstack_dashboard/dashboards/admin/volumes/volumes/forms.py
rename to openstack_dashboard/dashboards/admin/volumes/forms.py
index ec7a16bcef..06708a9bc2 100644
--- a/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py
+++ b/openstack_dashboard/dashboards/admin/volumes/forms.py
@@ -210,7 +210,7 @@ class MigrateVolume(forms.SelfHandlingForm):
% data['name'])
return True
except Exception:
- redirect = reverse("horizon:admin:volumes:volumes_tab")
+ redirect = reverse("horizon:admin:volumes:index")
exceptions.handle(request, _("Failed to migrate volume."),
redirect=redirect)
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/tables.py b/openstack_dashboard/dashboards/admin/volumes/tables.py
similarity index 93%
rename from openstack_dashboard/dashboards/admin/volumes/volumes/tables.py
rename to openstack_dashboard/dashboards/admin/volumes/tables.py
index 5b5350c03c..ae88850fa5 100644
--- a/openstack_dashboard/dashboards/admin/volumes/volumes/tables.py
+++ b/openstack_dashboard/dashboards/admin/volumes/tables.py
@@ -37,7 +37,7 @@ class VolumesFilterAction(tables.FilterAction):
class ManageVolumeAction(tables.LinkAction):
name = "manage"
verbose_name = _("Manage Volume")
- url = "horizon:admin:volumes:volumes:manage"
+ url = "horizon:admin:volumes:manage"
classes = ("ajax-modal",)
icon = "plus"
policy_rules = (("volume", "volume_extension:volume_manage"),)
@@ -47,7 +47,7 @@ class ManageVolumeAction(tables.LinkAction):
class UnmanageVolumeAction(tables.LinkAction):
name = "unmanage"
verbose_name = _("Unmanage Volume")
- url = "horizon:admin:volumes:volumes:unmanage"
+ url = "horizon:admin:volumes:unmanage"
classes = ("ajax-modal",)
icon = "pencil"
policy_rules = (("volume", "volume_extension:volume_unmanage"),)
@@ -73,7 +73,7 @@ class UnmanageVolumeAction(tables.LinkAction):
class MigrateVolume(tables.LinkAction):
name = "migrate"
verbose_name = _("Migrate Volume")
- url = "horizon:admin:volumes:volumes:migrate"
+ url = "horizon:admin:volumes:migrate"
classes = ("ajax-modal", "btn-migrate")
policy_rules = (
("volume", "volume_extension:volume_admin_actions:migrate_volume"),)
@@ -85,7 +85,7 @@ class MigrateVolume(tables.LinkAction):
class UpdateVolumeStatusAction(tables.LinkAction):
name = "update_status"
verbose_name = _("Update Volume Status")
- url = "horizon:admin:volumes:volumes:update_status"
+ url = "horizon:admin:volumes:update_status"
classes = ("ajax-modal",)
icon = "pencil"
policy_rules = (("volume",
@@ -95,7 +95,7 @@ class UpdateVolumeStatusAction(tables.LinkAction):
class VolumesTable(volumes_tables.VolumesTable):
name = tables.WrappingColumn("name",
verbose_name=_("Name"),
- link="horizon:admin:volumes:volumes:detail")
+ link="horizon:admin:volumes:detail")
host = tables.Column("os-vol-host-attr:host", verbose_name=_("Host"))
tenant = tables.Column(lambda obj: getattr(obj, 'tenant_name', None),
verbose_name=_("Project"))
diff --git a/openstack_dashboard/dashboards/admin/volumes/tabs.py b/openstack_dashboard/dashboards/admin/volumes/tabs.py
deleted file mode 100644
index 3358f9a115..0000000000
--- a/openstack_dashboard/dashboards/admin/volumes/tabs.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from collections import OrderedDict
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-
-from horizon import exceptions
-from horizon import tables
-from horizon import tabs
-
-from openstack_dashboard.api import keystone
-
-from openstack_dashboard.dashboards.admin.volumes.volumes \
- import tables as volumes_tables
-from openstack_dashboard.dashboards.project.volumes \
- import views as volumes_views
-
-
-class VolumeTab(tables.PagedTableMixin, tabs.TableTab,
- volumes_views.VolumeTableMixIn, tables.DataTableView):
- table_classes = (volumes_tables.VolumesTable,)
- name = _("Volumes")
- slug = "volumes_tab"
- template_name = "admin/volumes/volumes/volumes_tables.html"
- preload = False
- FILTERS_MAPPING = {'bootable': {_('yes'): 'true', _('no'): 'false'},
- 'encrypted': {_('yes'): True, _('no'): False}}
-
- def get_volumes_data(self):
- default_filters = {'all_tenants': True}
-
- filters = self.get_filters(default_filters.copy())
- filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
- volumes = []
-
- self.table.needs_filter_first = False
-
- if filter_first.get('admin.volumes', False) and \
- len(filters) == len(default_filters):
- self.table.needs_filter_first = True
- return volumes
-
- if 'project' in filters:
- # Keystone returns a tuple ([],false) where the first element is
- # tenant list that's why the 0 is hardcoded below
- tenants = keystone.tenant_list(self.request)[0]
- tenant_ids = [t.id for t in tenants
- if t.name == filters['project']]
- if not tenant_ids:
- return []
- del filters['project']
- for id in tenant_ids:
- filters['project_id'] = id
- volumes += self._get_volumes(search_opts=filters)
- else:
- volumes = self._get_volumes(search_opts=filters)
-
- attached_instance_ids = self._get_attached_instance_ids(volumes)
- instances = self._get_instances(search_opts={'all_tenants': True},
- instance_ids=attached_instance_ids)
- volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots(
- search_opts={'all_tenants': True})
- self._set_volume_attributes(
- volumes, instances, volume_ids_with_snapshots)
-
- # Gather our tenants to correlate against IDs
- try:
- tenants, has_more = keystone.tenant_list(self.request)
- except Exception:
- tenants = []
- msg = _('Unable to retrieve volume project information.')
- exceptions.handle(self.request, msg)
-
- tenant_dict = OrderedDict([(t.id, t) for t in tenants])
- for volume in volumes:
- tenant_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None)
- tenant = tenant_dict.get(tenant_id, None)
- volume.tenant_name = getattr(tenant, "name", None)
-
- return volumes
-
- def get_filters(self, filters):
- self.table = self._tables['volumes']
- self.handle_server_filter(self.request, table=self.table)
- self.update_server_filter_action(self.request, table=self.table)
- filters = super(VolumeTab, self).get_filters(filters,
- self.FILTERS_MAPPING)
- return filters
-
-
-class VolumesGroupTabs(tabs.TabGroup):
- slug = "volumes_group_tabs"
- tabs = (VolumeTab, )
- sticky = True
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_manage_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/_manage_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_manage_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/_manage_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_migrate_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/_migrate_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_migrate_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/_migrate_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_unmanage_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/_unmanage_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_unmanage_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/_unmanage_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/_update_status.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/_update_status.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html
deleted file mode 100644
index 4b518c863d..0000000000
--- a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Volumes" %}{% endblock %}
-
-{% block main %}
-
-
- {{ tab_group.render }}
-
-
-{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/manage_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/manage_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/manage_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/manage_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/migrate_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/migrate_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/migrate_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/migrate_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/unmanage_volume.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/unmanage_volume.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/unmanage_volume.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/unmanage_volume.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/update_status.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/update_status.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/volumes_tables.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes_tables.html
similarity index 100%
rename from openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/volumes_tables.html
rename to openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes_tables.html
diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py
index bb8883512e..14618600b0 100644
--- a/openstack_dashboard/dashboards/admin/volumes/tests.py
+++ b/openstack_dashboard/dashboards/admin/volumes/tests.py
@@ -28,6 +28,8 @@ from openstack_dashboard.dashboards.project.volumes \
import tables as volume_tables
from openstack_dashboard.test import helpers as test
+from openstack_dashboard.dashboards.admin.snapshots import forms
+
INDEX_URL = reverse('horizon:admin:volumes:index')
@@ -72,7 +74,7 @@ class VolumeTests(test.BaseAdminViewTests):
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
- self.assertTemplateUsed(res, 'admin/volumes/index.html')
+ self.assertTemplateUsed(res, 'horizon/common/_data_table_view.html')
volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, self.cinder_volumes.list())
@@ -108,7 +110,7 @@ class VolumeTests(test.BaseAdminViewTests):
res = self.client.get(urlunquote(url))
- self.assertTemplateUsed(res, 'admin/volumes/index.html')
+ self.assertTemplateUsed(res, 'horizon/common/_data_table_view.html')
self.assertEqual(res.status_code, 200)
self.mox.UnsetStubs()
@@ -117,7 +119,7 @@ class VolumeTests(test.BaseAdminViewTests):
@override_settings(FILTER_DATA_FIRST={'admin.volumes': True})
def test_volumes_tab_with_admin_filter_first(self):
res = self.client.get(INDEX_URL)
- self.assertTemplateUsed(res, 'admin/volumes/index.html')
+ self.assertTemplateUsed(res, 'horizon/common/_data_table_view.html')
volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, [])
@@ -147,7 +149,7 @@ class VolumeTests(test.BaseAdminViewTests):
expected_volumes = mox_volumes[size:2 * size]
marker = expected_volumes[0].id
next = volume_tables.VolumesTable._meta.pagination_param
- url = "?".join([INDEX_URL, "=".join([next, marker])])
+ url = INDEX_URL + "?%s=%s" % (next, marker)
res = self._test_index_paginated(marker=marker, sort_dir="desc",
volumes=expected_volumes, url=url,
has_more=True, has_prev=True)
@@ -158,7 +160,7 @@ class VolumeTests(test.BaseAdminViewTests):
expected_volumes = mox_volumes[-size:]
marker = expected_volumes[0].id
next = volume_tables.VolumesTable._meta.pagination_param
- url = "?".join([INDEX_URL, "=".join([next, marker])])
+ url = INDEX_URL + "?%s=%s" % (next, marker)
res = self._test_index_paginated(marker=marker, sort_dir="desc",
volumes=expected_volumes, url=url,
has_more=False, has_prev=True)
@@ -174,7 +176,7 @@ class VolumeTests(test.BaseAdminViewTests):
expected_volumes = mox_volumes[size:2 * size]
marker = mox_volumes[0].id
prev = volume_tables.VolumesTable._meta.prev_pagination_param
- url = "?".join([INDEX_URL, "=".join([prev, marker])])
+ url = INDEX_URL + "?%s=%s" % (prev, marker)
res = self._test_index_paginated(marker=marker, sort_dir="asc",
volumes=expected_volumes, url=url,
has_more=False, has_prev=True)
@@ -185,9 +187,210 @@ class VolumeTests(test.BaseAdminViewTests):
expected_volumes = mox_volumes[:size]
marker = mox_volumes[0].id
prev = volume_tables.VolumesTable._meta.prev_pagination_param
- url = "?".join([INDEX_URL, "=".join([prev, marker])])
+ url = INDEX_URL + "?%s=%s" % (prev, marker)
res = self._test_index_paginated(marker=marker, sort_dir="asc",
volumes=expected_volumes, url=url,
has_more=True, has_prev=False)
volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, expected_volumes)
+
+ @test.create_stubs({cinder: ('volume_reset_state',
+ 'volume_get')})
+ def test_update_volume_status(self):
+ volume = self.volumes.first()
+ formData = {'status': 'error'}
+
+ cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
+ cinder.volume_reset_state(IsA(http.HttpRequest),
+ volume.id,
+ formData['status'])
+ self.mox.ReplayAll()
+
+ res = self.client.post(
+ reverse('horizon:admin:volumes:update_status',
+ args=(volume.id,)),
+ formData)
+ self.assertNoFormErrors(res)
+
+ @test.create_stubs({cinder: ('volume_manage',
+ 'volume_type_list',
+ 'availability_zone_list',
+ 'extension_supported')})
+ def test_manage_volume(self):
+ metadata = {'key': u'k1',
+ 'value': u'v1'}
+ formData = {'host': 'host-1',
+ 'identifier': 'vol-1',
+ 'id_type': u'source-name',
+ 'name': 'name-1',
+ 'description': 'manage a volume',
+ 'volume_type': 'vol_type_1',
+ 'availability_zone': 'nova',
+ 'metadata': metadata['key'] + '=' + metadata['value'],
+ 'bootable': False}
+ cinder.volume_type_list(
+ IsA(http.HttpRequest)). \
+ AndReturn(self.cinder_volume_types.list())
+ cinder.availability_zone_list(
+ IsA(http.HttpRequest)). \
+ AndReturn(self.availability_zones.list())
+ cinder.extension_supported(
+ IsA(http.HttpRequest),
+ 'AvailabilityZones'). \
+ AndReturn(True)
+ cinder.volume_manage(
+ IsA(http.HttpRequest),
+ host=formData['host'],
+ identifier=formData['identifier'],
+ id_type=formData['id_type'],
+ name=formData['name'],
+ description=formData['description'],
+ volume_type=formData['volume_type'],
+ availability_zone=formData['availability_zone'],
+ metadata={metadata['key']: metadata['value']},
+ bootable=formData['bootable'])
+ self.mox.ReplayAll()
+ res = self.client.post(
+ reverse('horizon:admin:volumes:manage'),
+ formData)
+ self.assertNoFormErrors(res)
+
+ @test.create_stubs({cinder: ('volume_unmanage',
+ 'volume_get')})
+ def test_unmanage_volume(self):
+ # important - need to get the v2 cinder volume which has host data
+ volume_list = [x for x in self.cinder_volumes.list()
+ if x.name == 'v2_volume']
+ volume = volume_list[0]
+ formData = {'volume_name': volume.name,
+ 'host_name': 'host@backend-name#pool',
+ 'volume_id': volume.id}
+
+ cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
+ cinder.volume_unmanage(IsA(http.HttpRequest), volume.id). \
+ AndReturn(volume)
+ self.mox.ReplayAll()
+ res = self.client.post(
+ reverse('horizon:admin:volumes:unmanage',
+ args=(volume.id,)),
+ formData)
+ self.assertNoFormErrors(res)
+
+ @test.create_stubs({cinder: ('pool_list',
+ 'volume_get',)})
+ def test_volume_migrate_get(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndReturn(volume)
+ cinder.pool_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.cinder_pools.list())
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:volumes:migrate',
+ args=[volume.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res,
+ 'admin/volumes/migrate_volume.html')
+
+ @test.create_stubs({cinder: ('volume_get',)})
+ def test_volume_migrate_get_volume_get_exception(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndRaise(self.exceptions.cinder)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:volumes:migrate',
+ args=[volume.id])
+ res = self.client.get(url)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({cinder: ('pool_list',
+ 'volume_get',)})
+ def test_volume_migrate_list_pool_get_exception(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndReturn(volume)
+ cinder.pool_list(IsA(http.HttpRequest)) \
+ .AndRaise(self.exceptions.cinder)
+
+ self.mox.ReplayAll()
+ url = reverse('horizon:admin:volumes:migrate',
+ args=[volume.id])
+ res = self.client.get(url)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({cinder: ('pool_list',
+ 'volume_get',
+ 'volume_migrate',)})
+ def test_volume_migrate_post(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ host = self.cinder_pools.first().name
+
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndReturn(volume)
+ cinder.pool_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.cinder_pools.list())
+ cinder.volume_migrate(IsA(http.HttpRequest),
+ volume.id,
+ host,
+ False) \
+ .AndReturn(None)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:volumes:migrate',
+ args=[volume.id])
+ res = self.client.post(url, {'host': host, 'volume_id': volume.id})
+ self.assertNoFormErrors(res)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({cinder: ('pool_list',
+ 'volume_get',
+ 'volume_migrate',)})
+ def test_volume_migrate_post_api_exception(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ host = self.cinder_pools.first().name
+
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndReturn(volume)
+ cinder.pool_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.cinder_pools.list())
+ cinder.volume_migrate(IsA(http.HttpRequest),
+ volume.id,
+ host,
+ False) \
+ .AndRaise(self.exceptions.cinder)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:volumes:migrate',
+ args=[volume.id])
+ res = self.client.post(url, {'host': host, 'volume_id': volume.id})
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ def test_get_volume_status_choices_without_current(self):
+ current_status = {'status': 'available'}
+ status_choices = forms.populate_status_choices(current_status,
+ forms.STATUS_CHOICES)
+ self.assertEqual(len(status_choices), len(forms.STATUS_CHOICES))
+ self.assertNotIn(current_status['status'],
+ [status[0] for status in status_choices])
+
+ @test.create_stubs({cinder: ('volume_get',)})
+ def test_update_volume_status_get(self):
+ volume = self.cinder_volumes.get(name='v2_volume')
+ cinder.volume_get(IsA(http.HttpRequest), volume.id) \
+ .AndReturn(volume)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:volumes:update_status',
+ args=[volume.id])
+ res = self.client.get(url)
+ status_option = "" % volume.status
+ self.assertNotContains(res, status_option)
diff --git a/openstack_dashboard/dashboards/admin/volumes/urls.py b/openstack_dashboard/dashboards/admin/volumes/urls.py
index a9cf84977e..74ee26e819 100644
--- a/openstack_dashboard/dashboards/admin/volumes/urls.py
+++ b/openstack_dashboard/dashboards/admin/volumes/urls.py
@@ -10,20 +10,28 @@
# License for the specific language governing permissions and limitations
# under the License.
-from django.conf.urls import include
from django.conf.urls import url
from openstack_dashboard.dashboards.admin.volumes import views
-from openstack_dashboard.dashboards.admin.volumes.volumes \
- import urls as volumes_urls
+
urlpatterns = [
url(r'^$',
- views.IndexView.as_view(),
+ views.VolumesView.as_view(),
name='index'),
- url(r'^\?tab=volumes_group_tabs__volumes_tab$',
- views.IndexView.as_view(),
- name='volumes_tab'),
- url(r'',
- include(volumes_urls, namespace='volumes')),
+ url(r'^manage/$',
+ views.ManageVolumeView.as_view(),
+ name='manage'),
+ url(r'^(?P[^/]+)/$',
+ views.DetailView.as_view(),
+ name='detail'),
+ url(r'^(?P[^/]+)/update_status$',
+ views.UpdateStatusView.as_view(),
+ name='update_status'),
+ url(r'^(?P[^/]+)/unmanage$',
+ views.UnmanageVolumeView.as_view(),
+ name='unmanage'),
+ url(r'^(?P[^/]+)/migrate$',
+ views.MigrateVolumeView.as_view(),
+ name='migrate'),
]
diff --git a/openstack_dashboard/dashboards/admin/volumes/views.py b/openstack_dashboard/dashboards/admin/volumes/views.py
index 9f872cb53d..b30b7cf0fb 100644
--- a/openstack_dashboard/dashboards/admin/volumes/views.py
+++ b/openstack_dashboard/dashboards/admin/volumes/views.py
@@ -15,15 +15,229 @@
"""
Admin views for managing volumes and snapshots.
"""
+from collections import OrderedDict
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
-from horizon import tabs
+from horizon import exceptions
+from horizon import forms
+from horizon import tables
+from horizon.utils import memoized
+from openstack_dashboard.api import cinder
+from openstack_dashboard.api import keystone
from openstack_dashboard.dashboards.admin.volumes \
- import tabs as volumes_tabs
+ import forms as volumes_forms
+from openstack_dashboard.dashboards.admin.volumes \
+ import tables as volumes_tables
+from openstack_dashboard.dashboards.project.volumes \
+ import views as volumes_views
-class IndexView(tabs.TabbedTableView):
- tab_group_class = volumes_tabs.VolumesGroupTabs
- template_name = 'admin/volumes/index.html'
+class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
+ tables.DataTableView):
+ table_class = volumes_tables.VolumesTable
page_title = _("Volumes")
+
+ FILTERS_MAPPING = {'bootable': {_('yes'): 'true', _('no'): 'false'},
+ 'encrypted': {_('yes'): True, _('no'): False}}
+
+ def get_data(self):
+ default_filters = {'all_tenants': True}
+
+ filters = self.get_filters(default_filters.copy())
+ filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
+ volumes = []
+
+ self.table.needs_filter_first = False
+
+ if filter_first.get('admin.volumes', False) and \
+ len(filters) == len(default_filters):
+ self.table.needs_filter_first = True
+ return volumes
+
+ if 'project' in filters:
+ # Keystone returns a tuple ([],false) where the first element is
+ # tenant list that's why the 0 is hardcoded below
+ tenants = keystone.tenant_list(self.request)[0]
+ tenant_ids = [t.id for t in tenants
+ if t.name == filters['project']]
+ if not tenant_ids:
+ return []
+ del filters['project']
+ for id in tenant_ids:
+ filters['project_id'] = id
+ volumes += self._get_volumes(search_opts=filters)
+ else:
+ volumes = self._get_volumes(search_opts=filters)
+
+ attached_instance_ids = self._get_attached_instance_ids(volumes)
+ instances = self._get_instances(search_opts={'all_tenants': True},
+ instance_ids=attached_instance_ids)
+ volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots(
+ search_opts={'all_tenants': True})
+ self._set_volume_attributes(
+ volumes, instances, volume_ids_with_snapshots)
+
+ # Gather our tenants to correlate against IDs
+ try:
+ tenants, has_more = keystone.tenant_list(self.request)
+ except Exception:
+ tenants = []
+ msg = _('Unable to retrieve volume project information.')
+ exceptions.handle(self.request, msg)
+
+ tenant_dict = OrderedDict([(t.id, t) for t in tenants])
+ for volume in volumes:
+ tenant_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None)
+ tenant = tenant_dict.get(tenant_id, None)
+ volume.tenant_name = getattr(tenant, "name", None)
+
+ return volumes
+
+ def get_filters(self, filters):
+ self.table = self._tables['volumes']
+ self.handle_server_filter(self.request, table=self.table)
+ self.update_server_filter_action(self.request, table=self.table)
+ filters = super(VolumesView, self).get_filters(filters,
+ self.FILTERS_MAPPING)
+ return filters
+
+
+class DetailView(volumes_views.DetailView):
+ def get_context_data(self, **kwargs):
+ context = super(DetailView, self).get_context_data(**kwargs)
+ table = volumes_tables.VolumesTable(self.request)
+ context["actions"] = table.render_row_actions(context["volume"])
+ return context
+
+ def get_redirect_url(self):
+ return reverse('horizon:admin:volumes:index')
+
+
+class ManageVolumeView(forms.ModalFormView):
+ form_class = volumes_forms.ManageVolume
+ template_name = 'admin/volumes/manage_volume.html'
+ form_id = "manage_volume_modal"
+ submit_label = _("Manage")
+ success_url = reverse_lazy('horizon:admin:volumes:index')
+ submit_url = reverse_lazy('horizon:admin:volumes:manage')
+ cancel_url = reverse_lazy("horizon:admin:volumes:index")
+ page_title = _("Manage Volume")
+
+ def get_context_data(self, **kwargs):
+ context = super(ManageVolumeView, self).get_context_data(**kwargs)
+ return context
+
+
+class UnmanageVolumeView(forms.ModalFormView):
+ form_class = volumes_forms.UnmanageVolume
+ template_name = 'admin/volumes/unmanage_volume.html'
+ form_id = "unmanage_volume_modal"
+ submit_label = _("Unmanage")
+ success_url = reverse_lazy('horizon:admin:volumes:index')
+ submit_url = 'horizon:admin:volumes:unmanage'
+ cancel_url = reverse_lazy("horizon:admin:volumes:index")
+ page_title = _("Unmanage Volume")
+
+ def get_context_data(self, **kwargs):
+ context = super(UnmanageVolumeView, self).get_context_data(**kwargs)
+ args = (self.kwargs['volume_id'],)
+ context['submit_url'] = reverse(self.submit_url, args=args)
+ return context
+
+ @memoized.memoized_method
+ def get_data(self):
+ try:
+ volume_id = self.kwargs['volume_id']
+ volume = cinder.volume_get(self.request, volume_id)
+ except Exception:
+ exceptions.handle(self.request,
+ _('Unable to retrieve volume details.'),
+ redirect=self.success_url)
+ return volume
+
+ def get_initial(self):
+ volume = self.get_data()
+ return {'volume_id': self.kwargs["volume_id"],
+ 'name': volume.name,
+ 'host': getattr(volume, "os-vol-host-attr:host")}
+
+
+class MigrateVolumeView(forms.ModalFormView):
+ form_class = volumes_forms.MigrateVolume
+ template_name = 'admin/volumes/migrate_volume.html'
+ form_id = "migrate_volume_modal"
+ submit_label = _("Migrate")
+ success_url = reverse_lazy('horizon:admin:volumes:index')
+ submit_url = 'horizon:admin:volumes:migrate'
+ cancel_url = reverse_lazy("horizon:admin:volumes:index")
+ page_title = _("Migrate Volume")
+
+ def get_context_data(self, **kwargs):
+ context = super(MigrateVolumeView, self).get_context_data(**kwargs)
+ args = (self.kwargs['volume_id'],)
+ context['submit_url'] = reverse(self.submit_url, args=args)
+ return context
+
+ @memoized.memoized_method
+ def get_data(self):
+ try:
+ volume_id = self.kwargs['volume_id']
+ volume = cinder.volume_get(self.request, volume_id)
+ except Exception:
+ exceptions.handle(self.request,
+ _('Unable to retrieve volume details.'),
+ redirect=self.success_url)
+ return volume
+
+ @memoized.memoized_method
+ def get_hosts(self):
+ try:
+ return cinder.pool_list(self.request)
+ except Exception:
+ exceptions.handle(self.request,
+ _('Unable to retrieve pools information.'),
+ redirect=self.success_url)
+
+ def get_initial(self):
+ volume = self.get_data()
+ return {'volume_id': self.kwargs["volume_id"],
+ 'name': volume.name,
+ 'current_host': getattr(volume, "os-vol-host-attr:host"),
+ 'hosts': self.get_hosts()}
+
+
+class UpdateStatusView(forms.ModalFormView):
+ form_class = volumes_forms.UpdateStatus
+ modal_id = "update_volume_status_modal"
+ template_name = 'admin/volumes/update_status.html'
+ submit_label = _("Update Status")
+ submit_url = "horizon:admin:volumes:update_status"
+ success_url = reverse_lazy('horizon:admin:volumes:index')
+ page_title = _("Update Volume Status")
+
+ def get_context_data(self, **kwargs):
+ context = super(UpdateStatusView, self).get_context_data(**kwargs)
+ context["volume_id"] = self.kwargs['volume_id']
+ args = (self.kwargs['volume_id'],)
+ context['submit_url'] = reverse(self.submit_url, args=args)
+ return context
+
+ @memoized.memoized_method
+ def get_data(self):
+ try:
+ volume_id = self.kwargs['volume_id']
+ volume = cinder.volume_get(self.request, volume_id)
+ except Exception:
+ exceptions.handle(self.request,
+ _('Unable to retrieve volume details.'),
+ redirect=self.success_url)
+ return volume
+
+ def get_initial(self):
+ volume = self.get_data()
+ return {'volume_id': self.kwargs["volume_id"],
+ 'status': volume.status}
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/__init__.py b/openstack_dashboard/dashboards/admin/volumes/volumes/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/volumes/tests.py
deleted file mode 100644
index 7ba3cd03be..0000000000
--- a/openstack_dashboard/dashboards/admin/volumes/volumes/tests.py
+++ /dev/null
@@ -1,235 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from django.core.urlresolvers import reverse
-from django import http
-from mox3.mox import IsA # noqa
-
-from openstack_dashboard.api import cinder
-from openstack_dashboard.test import helpers as test
-
-from openstack_dashboard.dashboards.admin.snapshots import forms
-
-INDEX_URL = reverse('horizon:admin:volumes:volumes_tab')
-
-
-class VolumeViewTests(test.BaseAdminViewTests):
- def tearDown(self):
- for volume in self.cinder_volumes.list():
- # VolumeTableMixIn._set_volume_attributes mutates data
- # and cinder_volumes.list() doesn't deep copy
- for att in volume.attachments:
- if 'instance' in att:
- del att['instance']
- super(VolumeViewTests, self).tearDown()
-
- @test.create_stubs({cinder: ('volume_reset_state',
- 'volume_get')})
- def test_update_volume_status(self):
- volume = self.volumes.first()
- formData = {'status': 'error'}
-
- cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
- cinder.volume_reset_state(IsA(http.HttpRequest),
- volume.id,
- formData['status'])
- self.mox.ReplayAll()
-
- res = self.client.post(
- reverse('horizon:admin:volumes:volumes:update_status',
- args=(volume.id,)),
- formData)
- self.assertNoFormErrors(res)
-
- @test.create_stubs({cinder: ('volume_manage',
- 'volume_type_list',
- 'availability_zone_list',
- 'extension_supported')})
- def test_manage_volume(self):
- metadata = {'key': u'k1',
- 'value': u'v1'}
- formData = {'host': 'host-1',
- 'identifier': 'vol-1',
- 'id_type': u'source-name',
- 'name': 'name-1',
- 'description': 'manage a volume',
- 'volume_type': 'vol_type_1',
- 'availability_zone': 'nova',
- 'metadata': metadata['key'] + '=' + metadata['value'],
- 'bootable': False}
- cinder.volume_type_list(
- IsA(http.HttpRequest)).\
- AndReturn(self.cinder_volume_types.list())
- cinder.availability_zone_list(
- IsA(http.HttpRequest)).\
- AndReturn(self.availability_zones.list())
- cinder.extension_supported(
- IsA(http.HttpRequest),
- 'AvailabilityZones').\
- AndReturn(True)
- cinder.volume_manage(
- IsA(http.HttpRequest),
- host=formData['host'],
- identifier=formData['identifier'],
- id_type=formData['id_type'],
- name=formData['name'],
- description=formData['description'],
- volume_type=formData['volume_type'],
- availability_zone=formData['availability_zone'],
- metadata={metadata['key']: metadata['value']},
- bootable=formData['bootable'])
- self.mox.ReplayAll()
- res = self.client.post(
- reverse('horizon:admin:volumes:volumes:manage'),
- formData)
- self.assertNoFormErrors(res)
-
- @test.create_stubs({cinder: ('volume_unmanage',
- 'volume_get')})
- def test_unmanage_volume(self):
- # important - need to get the v2 cinder volume which has host data
- volume_list = [x for x in self.cinder_volumes.list()
- if x.name == 'v2_volume']
- volume = volume_list[0]
- formData = {'volume_name': volume.name,
- 'host_name': 'host@backend-name#pool',
- 'volume_id': volume.id}
-
- cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
- cinder.volume_unmanage(IsA(http.HttpRequest), volume.id).\
- AndReturn(volume)
- self.mox.ReplayAll()
- res = self.client.post(
- reverse('horizon:admin:volumes:volumes:unmanage',
- args=(volume.id,)),
- formData)
- self.assertNoFormErrors(res)
-
- @test.create_stubs({cinder: ('pool_list',
- 'volume_get',)})
- def test_volume_migrate_get(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndReturn(volume)
- cinder.pool_list(IsA(http.HttpRequest)) \
- .AndReturn(self.cinder_pools.list())
-
- self.mox.ReplayAll()
-
- url = reverse('horizon:admin:volumes:volumes:migrate',
- args=[volume.id])
- res = self.client.get(url)
-
- self.assertTemplateUsed(res,
- 'admin/volumes/volumes/migrate_volume.html')
-
- @test.create_stubs({cinder: ('volume_get',)})
- def test_volume_migrate_get_volume_get_exception(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndRaise(self.exceptions.cinder)
-
- self.mox.ReplayAll()
-
- url = reverse('horizon:admin:volumes:volumes:migrate',
- args=[volume.id])
- res = self.client.get(url)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- @test.create_stubs({cinder: ('pool_list',
- 'volume_get',)})
- def test_volume_migrate_list_pool_get_exception(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndReturn(volume)
- cinder.pool_list(IsA(http.HttpRequest)) \
- .AndRaise(self.exceptions.cinder)
-
- self.mox.ReplayAll()
- url = reverse('horizon:admin:volumes:volumes:migrate',
- args=[volume.id])
- res = self.client.get(url)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- @test.create_stubs({cinder: ('pool_list',
- 'volume_get',
- 'volume_migrate',)})
- def test_volume_migrate_post(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- host = self.cinder_pools.first().name
-
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndReturn(volume)
- cinder.pool_list(IsA(http.HttpRequest)) \
- .AndReturn(self.cinder_pools.list())
- cinder.volume_migrate(IsA(http.HttpRequest),
- volume.id,
- host,
- False) \
- .AndReturn(None)
-
- self.mox.ReplayAll()
-
- url = reverse('horizon:admin:volumes:volumes:migrate',
- args=[volume.id])
- res = self.client.post(url, {'host': host, 'volume_id': volume.id})
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- @test.create_stubs({cinder: ('pool_list',
- 'volume_get',
- 'volume_migrate',)})
- def test_volume_migrate_post_api_exception(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- host = self.cinder_pools.first().name
-
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndReturn(volume)
- cinder.pool_list(IsA(http.HttpRequest)) \
- .AndReturn(self.cinder_pools.list())
- cinder.volume_migrate(IsA(http.HttpRequest),
- volume.id,
- host,
- False) \
- .AndRaise(self.exceptions.cinder)
-
- self.mox.ReplayAll()
-
- url = reverse('horizon:admin:volumes:volumes:migrate',
- args=[volume.id])
- res = self.client.post(url, {'host': host, 'volume_id': volume.id})
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_get_volume_status_choices_without_current(self):
- current_status = {'status': 'available'}
- status_choices = forms.populate_status_choices(current_status,
- forms.STATUS_CHOICES)
- self.assertEqual(len(status_choices), len(forms.STATUS_CHOICES))
- self.assertNotIn(current_status['status'],
- [status[0] for status in status_choices])
-
- @test.create_stubs({cinder: ('volume_get',)})
- def test_update_volume_status_get(self):
- volume = self.cinder_volumes.get(name='v2_volume')
- cinder.volume_get(IsA(http.HttpRequest), volume.id) \
- .AndReturn(volume)
-
- self.mox.ReplayAll()
-
- url = reverse('horizon:admin:volumes:volumes:update_status',
- args=[volume.id])
- res = self.client.get(url)
- status_option = "" % volume.status
- self.assertNotContains(res, status_option)
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/urls.py b/openstack_dashboard/dashboards/admin/volumes/volumes/urls.py
deleted file mode 100644
index 279c4f3902..0000000000
--- a/openstack_dashboard/dashboards/admin/volumes/volumes/urls.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from django.conf.urls import url
-
-from openstack_dashboard.dashboards.admin.volumes.volumes \
- import views
-
-urlpatterns = [
- url(r'^manage/$',
- views.ManageVolumeView.as_view(),
- name='manage'),
- url(r'^(?P[^/]+)/$',
- views.DetailView.as_view(),
- name='detail'),
- url(r'^(?P[^/]+)/update_status$',
- views.UpdateStatusView.as_view(),
- name='update_status'),
- url(r'^(?P[^/]+)/unmanage$',
- views.UnmanageVolumeView.as_view(),
- name='unmanage'),
- url(r'^(?P[^/]+)/migrate$',
- views.MigrateVolumeView.as_view(),
- name='migrate'),
-]
diff --git a/openstack_dashboard/dashboards/admin/volumes/volumes/views.py b/openstack_dashboard/dashboards/admin/volumes/volumes/views.py
deleted file mode 100644
index 490b938242..0000000000
--- a/openstack_dashboard/dashboards/admin/volumes/volumes/views.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
-from django.utils.translation import ugettext_lazy as _
-
-from horizon import exceptions
-from horizon import forms
-from horizon.utils import memoized
-
-from openstack_dashboard.api import cinder
-from openstack_dashboard.dashboards.admin.volumes.volumes \
- import forms as volumes_forms
-from openstack_dashboard.dashboards.admin.volumes.volumes \
- import tables as volumes_tables
-from openstack_dashboard.dashboards.project.volumes \
- import views as volumes_views
-
-
-class DetailView(volumes_views.DetailView):
- def get_context_data(self, **kwargs):
- context = super(DetailView, self).get_context_data(**kwargs)
- table = volumes_tables.VolumesTable(self.request)
- context["actions"] = table.render_row_actions(context["volume"])
- return context
-
- def get_redirect_url(self):
- return reverse('horizon:admin:volumes:index')
-
-
-class ManageVolumeView(forms.ModalFormView):
- form_class = volumes_forms.ManageVolume
- template_name = 'admin/volumes/volumes/manage_volume.html'
- form_id = "manage_volume_modal"
- submit_label = _("Manage")
- success_url = reverse_lazy('horizon:admin:volumes:volumes_tab')
- submit_url = reverse_lazy('horizon:admin:volumes:volumes:manage')
- cancel_url = reverse_lazy("horizon:admin:volumes:index")
- page_title = _("Manage Volume")
-
- def get_context_data(self, **kwargs):
- context = super(ManageVolumeView, self).get_context_data(**kwargs)
- return context
-
-
-class UnmanageVolumeView(forms.ModalFormView):
- form_class = volumes_forms.UnmanageVolume
- template_name = 'admin/volumes/volumes/unmanage_volume.html'
- form_id = "unmanage_volume_modal"
- submit_label = _("Unmanage")
- success_url = reverse_lazy('horizon:admin:volumes:volumes_tab')
- submit_url = 'horizon:admin:volumes:volumes:unmanage'
- cancel_url = reverse_lazy("horizon:admin:volumes:index")
- page_title = _("Unmanage Volume")
-
- def get_context_data(self, **kwargs):
- context = super(UnmanageVolumeView, self).get_context_data(**kwargs)
- args = (self.kwargs['volume_id'],)
- context['submit_url'] = reverse(self.submit_url, args=args)
- return context
-
- @memoized.memoized_method
- def get_data(self):
- try:
- volume_id = self.kwargs['volume_id']
- volume = cinder.volume_get(self.request, volume_id)
- except Exception:
- exceptions.handle(self.request,
- _('Unable to retrieve volume details.'),
- redirect=self.success_url)
- return volume
-
- def get_initial(self):
- volume = self.get_data()
- return {'volume_id': self.kwargs["volume_id"],
- 'name': volume.name,
- 'host': getattr(volume, "os-vol-host-attr:host")}
-
-
-class MigrateVolumeView(forms.ModalFormView):
- form_class = volumes_forms.MigrateVolume
- template_name = 'admin/volumes/volumes/migrate_volume.html'
- form_id = "migrate_volume_modal"
- submit_label = _("Migrate")
- success_url = reverse_lazy('horizon:admin:volumes:volumes_tab')
- submit_url = 'horizon:admin:volumes:volumes:migrate'
- cancel_url = reverse_lazy("horizon:admin:volumes:index")
- page_title = _("Migrate Volume")
-
- def get_context_data(self, **kwargs):
- context = super(MigrateVolumeView, self).get_context_data(**kwargs)
- args = (self.kwargs['volume_id'],)
- context['submit_url'] = reverse(self.submit_url, args=args)
- return context
-
- @memoized.memoized_method
- def get_data(self):
- try:
- volume_id = self.kwargs['volume_id']
- volume = cinder.volume_get(self.request, volume_id)
- except Exception:
- exceptions.handle(self.request,
- _('Unable to retrieve volume details.'),
- redirect=self.success_url)
- return volume
-
- @memoized.memoized_method
- def get_hosts(self):
- try:
- return cinder.pool_list(self.request)
- except Exception:
- exceptions.handle(self.request,
- _('Unable to retrieve pools information.'),
- redirect=self.success_url)
-
- def get_initial(self):
- volume = self.get_data()
- return {'volume_id': self.kwargs["volume_id"],
- 'name': volume.name,
- 'current_host': getattr(volume, "os-vol-host-attr:host"),
- 'hosts': self.get_hosts()}
-
-
-class UpdateStatusView(forms.ModalFormView):
- form_class = volumes_forms.UpdateStatus
- modal_id = "update_volume_status_modal"
- template_name = 'admin/volumes/volumes/update_status.html'
- submit_label = _("Update Status")
- submit_url = "horizon:admin:volumes:volumes:update_status"
- success_url = reverse_lazy('horizon:admin:volumes:index')
- page_title = _("Update Volume Status")
-
- def get_context_data(self, **kwargs):
- context = super(UpdateStatusView, self).get_context_data(**kwargs)
- context["volume_id"] = self.kwargs['volume_id']
- args = (self.kwargs['volume_id'],)
- context['submit_url'] = reverse(self.submit_url, args=args)
- return context
-
- @memoized.memoized_method
- def get_data(self):
- try:
- volume_id = self.kwargs['volume_id']
- volume = cinder.volume_get(self.request, volume_id)
- except Exception:
- exceptions.handle(self.request,
- _('Unable to retrieve volume details.'),
- redirect=self.success_url)
- return volume
-
- def get_initial(self):
- volume = self.get_data()
- return {'volume_id': self.kwargs["volume_id"],
- 'status': volume.status}
diff --git a/openstack_dashboard/enabled/_2070_admin_volumes_panel.py b/openstack_dashboard/enabled/_2320_admin_volumes_panel.py
similarity index 93%
rename from openstack_dashboard/enabled/_2070_admin_volumes_panel.py
rename to openstack_dashboard/enabled/_2320_admin_volumes_panel.py
index 204762076c..ebc979db08 100644
--- a/openstack_dashboard/enabled/_2070_admin_volumes_panel.py
+++ b/openstack_dashboard/enabled/_2320_admin_volumes_panel.py
@@ -3,7 +3,7 @@ PANEL = 'volumes'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# The slug of the panel group the PANEL is associated with.
-PANEL_GROUP = 'admin'
+PANEL_GROUP = 'volumes'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.admin.volumes.panel.Volumes'