Standardize display name on detail pages

* Standardized overview pages for volume, image and snapshot
as per the instance overview page.

Fixes bug #1207770

Change-Id: I7ae48542f4c8732c92dc4393dbaaea81158d34d6
This commit is contained in:
Abhijeet Malawade
2013-09-10 05:45:34 -07:00
parent 0df6b78422
commit 3686360878
16 changed files with 176 additions and 44 deletions

View File

@@ -3,7 +3,7 @@
{% block title %}{% trans "Volume Details" %}{% endblock %} {% block title %}{% trans "Volume Details" %}{% endblock %}
{% block page_header %} {% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %} {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.display_name|default:_("Volume Details:") %}
{% endblock page_header %} {% endblock page_header %}
{% block main %} {% block main %}

View File

@@ -17,11 +17,8 @@
from django.core.urlresolvers import reverse # noqa from django.core.urlresolvers import reverse # noqa
from django.utils.translation import ugettext_lazy as _ # noqa from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tabs from horizon import tabs
from openstack_dashboard import api
class OverviewTab(tabs.Tab): class OverviewTab(tabs.Tab):
name = _("Overview") name = _("Overview")
@@ -29,15 +26,7 @@ class OverviewTab(tabs.Tab):
template_name = "project/images_and_snapshots/images/_detail_overview.html" template_name = "project/images_and_snapshots/images/_detail_overview.html"
def get_context_data(self, request): def get_context_data(self, request):
image_id = self.tab_group.kwargs['image_id'] return {"image": self.tab_group.kwargs['image']}
try:
image = api.glance.image_get(self.request, image_id)
except Exception:
redirect = reverse('horizon:project:images_and_snapshots:index')
exceptions.handle(request,
_('Unable to retrieve image details.'),
redirect=redirect)
return {'image': image}
class ImageDetailTabs(tabs.TabGroup): class ImageDetailTabs(tabs.TabGroup):

View File

@@ -163,10 +163,13 @@ class ImageViewTests(test.TestCase):
res = self.client.get( res = self.client.get(
reverse('horizon:project:images_and_snapshots:images:detail', reverse('horizon:project:images_and_snapshots:images:detail',
args=[image.id])) args=[image.id]))
self.assertTemplateUsed(res, self.assertTemplateUsed(res,
'project/images_and_snapshots/images/detail.html') 'project/images_and_snapshots/images/detail.html')
self.assertEqual(res.context['image'].name, image.name) self.assertEqual(res.context['image'].name, image.name)
self.assertEqual(res.context['image'].protected, image.protected) self.assertEqual(res.context['image'].protected, image.protected)
self.assertContains(res, "<h2>Image Details: %s</h2>" % image.name,
1, 200)
@test.create_stubs({api.glance: ('image_get',)}) @test.create_stubs({api.glance: ('image_get',)})
def test_protected_image_detail_get(self): def test_protected_image_detail_get(self):

View File

@@ -82,3 +82,25 @@ class UpdateView(forms.ModalFormView):
class DetailView(tabs.TabView): class DetailView(tabs.TabView):
tab_group_class = project_tabs.ImageDetailTabs tab_group_class = project_tabs.ImageDetailTabs
template_name = 'project/images_and_snapshots/images/detail.html' template_name = 'project/images_and_snapshots/images/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["image"] = self.get_data()
return context
def get_data(self):
if not hasattr(self, "_image"):
try:
image_id = self.kwargs['image_id']
self._image = api.glance.image_get(self.request, image_id)
except Exception:
url = reverse('horizon:project:images_and_snapshots:index')
exceptions.handle(self.request,
_('Unable to retrieve image details.'),
redirect=url)
return self._image
def get_tabs(self, request, *args, **kwargs):
image = self.get_data()
return self.tab_group_class(request, image=image, **kwargs)

View File

@@ -1,10 +1,10 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Image Detail "%}{% endblock %} {% block title %}{% trans "Image Details"%}{% endblock %}
{% block page_header %} {% block page_header %}
{% include "horizon/common/_page_header.html" with title="Image Detail" %} {% include "horizon/common/_page_header.html" with title=_("Image Details: ")|add:image.name|default:_("Image Details:") %}
{% endblock page_header %} {% endblock page_header %}
{% block main %} {% block main %}

View File

@@ -1,7 +1,7 @@
{% load i18n sizeformat parse_date %} {% load i18n sizeformat parse_date %}
{% load url from future %} {% load url from future %}
<h3>{{snapshot.display_name }}</h3> <h3>{% trans "Volume Snapshot Overview" %}</h3>
<div class="info row-fluid detail"> <div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4> <h4>{% trans "Info" %}</h4>

View File

@@ -3,9 +3,8 @@
{% block title %}{% trans "Volume Snapshot Details" %}{% endblock %} {% block title %}{% trans "Volume Snapshot Details" %}{% endblock %}
{% block page_header %} {% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Volume Snapshot Detail") %} {% include "horizon/common/_page_header.html" with title=_("Volume Snapshot Details: ")|add:snapshot.display_name|default:_("Volume Snapshot Details:") %}
{% endblock page_header %} {% endblock page_header %}
{% block main %} {% block main %}
<div class="row-fluid"> <div class="row-fluid">
<div class="span12"> <div class="span12">

View File

@@ -22,6 +22,8 @@
""" """
Views for managing Images and Snapshots. Views for managing Images and Snapshots.
""" """
from django.core.urlresolvers import reverse # noqa
from django.utils.translation import ugettext_lazy as _ # noqa from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions from horizon import exceptions
@@ -87,3 +89,26 @@ class IndexView(tables.MultiTableView):
class DetailView(tabs.TabView): class DetailView(tabs.TabView):
tab_group_class = vol_snsh_tabs.SnapshotDetailTabs tab_group_class = vol_snsh_tabs.SnapshotDetailTabs
template_name = 'project/images_and_snapshots/snapshots/detail.html' template_name = 'project/images_and_snapshots/snapshots/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["snapshot"] = self.get_data()
return context
def get_data(self):
if not hasattr(self, "_snapshot"):
try:
snapshot_id = self.kwargs['snapshot_id']
self._snapshot = api.cinder.volume_snapshot_get(self.request,
snapshot_id)
except Exception:
url = reverse('horizon:project:images_and_snapshots:index')
exceptions.handle(self.request,
_('Unable to retrieve snapshot details.'),
redirect=url)
return self._snapshot
def get_tabs(self, request, *args, **kwargs):
snapshot = self.get_data()
return self.tab_group_class(request, snapshot=snapshot, **kwargs)

View File

@@ -30,18 +30,16 @@ class OverviewTab(tabs.Tab):
"_detail_overview.html") "_detail_overview.html")
def get_context_data(self, request): def get_context_data(self, request):
snapshot_id = self.tab_group.kwargs['snapshot_id']
try: try:
snapshot = cinder.volume_snapshot_get(request, snapshot_id) snapshot = self.tab_group.kwargs['snapshot']
volume = cinder.volume_get(request, snapshot.volume_id) volume = cinder.volume_get(request, snapshot.volume_id)
volume.display_name = None
except Exception: except Exception:
redirect = reverse('horizon:project:images_and_snapshots:index') redirect = reverse('horizon:project:images_and_snapshots:index')
exceptions.handle(self.request, exceptions.handle(self.request,
_('Unable to retrieve snapshot details.'), _('Unable to retrieve snapshot details.'),
redirect=redirect) redirect=redirect)
return {'snapshot': snapshot, return {"snapshot": snapshot,
'volume': volume} "volume": volume}
class SnapshotDetailTabs(tabs.TabGroup): class SnapshotDetailTabs(tabs.TabGroup):

View File

@@ -40,7 +40,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
'volumesUsed': len(self.volumes.list()), 'volumesUsed': len(self.volumes.list()),
'maxTotalVolumes': 6} 'maxTotalVolumes': 6}
quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
AndReturn(usage_limit) AndReturn(usage_limit)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:create_snapshot', url = reverse('horizon:project:volumes:create_snapshot',
@@ -58,7 +58,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
volume.id, volume.id,
snapshot.display_name, snapshot.display_name,
snapshot.display_description) \ snapshot.display_description) \
.AndReturn(snapshot) .AndReturn(snapshot)
self.mox.ReplayAll() self.mox.ReplayAll()
formData = {'method': 'CreateSnapshotForm', formData = {'method': 'CreateSnapshotForm',
@@ -102,3 +102,64 @@ class VolumeSnapshotsViewTests(test.TestCase):
self.assertIn("Scheduled deletion of Volume Snapshot: test snapshot", self.assertIn("Scheduled deletion of Volume Snapshot: test snapshot",
[m.message for m in res.context['messages']]) [m.message for m in res.context['messages']])
@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()
api.cinder.volume_get(IsA(http.HttpRequest), volume.id). \
AndReturn(volume)
api.cinder.volume_snapshot_get(IsA(http.HttpRequest), snapshot.id). \
AndReturn(snapshot)
self.mox.ReplayAll()
url = reverse('horizon:project:images_and_snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
self.assertContains(res,
"<h2>Volume Snapshot Details: %s</h2>" %
snapshot.display_name,
1, 200)
self.assertContains(res, "<dd>test snapshot</dd>", 1, 200)
self.assertContains(res,
"<dd>40f3fabf-3613-4f5e-90e5-6c9a08333fc3</dd>",
1,
200)
self.assertContains(res, "<dd>Available</dd>", 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()
api.cinder.volume_snapshot_get(IsA(http.HttpRequest), snapshot.id).\
AndRaise(self.exceptions.cinder)
self.mox.ReplayAll()
url = reverse('horizon:project:images_and_snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
@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()
api.cinder.volume_get(IsA(http.HttpRequest), volume.id). \
AndRaise(self.exceptions.cinder)
api.cinder.volume_snapshot_get(IsA(http.HttpRequest), snapshot.id). \
AndReturn(snapshot)
self.mox.ReplayAll()
url = reverse('horizon:project:images_and_snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@@ -1,9 +1,9 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n sizeformat %} {% load i18n sizeformat %}
{% block title %}{% trans "Instance Detail" %}{% endblock %} {% block title %}{% trans "Instance Details" %}{% endblock %}
{% block page_header %} {% block page_header %}
{% include "horizon/common/_page_header.html" with title="Instance Detail: "|add:instance.name %} {% include "horizon/common/_page_header.html" with title=_("Instance Details: ")|add:instance.name %}
{% endblock page_header %} {% endblock page_header %}
{% block main %} {% block main %}

View File

@@ -17,12 +17,8 @@
from django.core.urlresolvers import reverse # noqa from django.core.urlresolvers import reverse # noqa
from django.utils.translation import ugettext_lazy as _ # noqa from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tabs from horizon import tabs
from openstack_dashboard.api import cinder
from openstack_dashboard.api import nova
class OverviewTab(tabs.Tab): class OverviewTab(tabs.Tab):
name = _("Overview") name = _("Overview")
@@ -31,17 +27,7 @@ class OverviewTab(tabs.Tab):
"_detail_overview.html") "_detail_overview.html")
def get_context_data(self, request): def get_context_data(self, request):
volume_id = self.tab_group.kwargs['volume_id'] return {"volume": self.tab_group.kwargs['volume']}
try:
volume = cinder.volume_get(request, volume_id)
for att in volume.attachments:
att['instance'] = nova.server_get(request, att['server_id'])
except Exception:
redirect = reverse('horizon:project:volumes:index')
exceptions.handle(self.request,
_('Unable to retrieve volume details.'),
redirect=redirect)
return {'volume': volume}
class VolumeDetailTabs(tabs.TabGroup): class VolumeDetailTabs(tabs.TabGroup):

View File

@@ -1,7 +1,7 @@
{% load i18n sizeformat parse_date %} {% load i18n sizeformat parse_date %}
{% load url from future %} {% load url from future %}
<h3>{% trans "Volume Overview" %}: {{volume.display_name }}</h3> <h3>{% trans "Volume Overview" %}</h3>
<div class="info row-fluid detail"> <div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4> <h4>{% trans "Info" %}</h4>

View File

@@ -3,7 +3,7 @@
{% block title %}{% trans "Volume Details" %}{% endblock %} {% block title %}{% trans "Volume Details" %}{% endblock %}
{% block page_header %} {% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %} {% include "horizon/common/_page_header.html" with title=_("Volume Details: ")|add:volume.display_name|default:_("Volume Details:") %}
{% endblock page_header %} {% endblock page_header %}
{% block main %} {% block main %}

View File

@@ -32,6 +32,9 @@ from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas from openstack_dashboard.usage import quotas
VOLUME_INDEX_URL = reverse('horizon:project:volumes:index')
class VolumeViewTests(test.TestCase): class VolumeViewTests(test.TestCase):
@test.create_stubs({cinder: ('volume_create', @test.create_stubs({cinder: ('volume_create',
'volume_snapshot_list', 'volume_snapshot_list',
@@ -674,6 +677,8 @@ class VolumeViewTests(test.TestCase):
args=[volume.id]) args=[volume.id])
res = self.client.get(url) res = self.client.get(url)
self.assertContains(res, "<h2>Volume Details: Volume name</h2>",
1, 200)
self.assertContains(res, "<dd>Volume name</dd>", 1, 200) self.assertContains(res, "<dd>Volume name</dd>", 1, 200)
self.assertContains(res, self.assertContains(res,
"<dd>41023e92-8008-4c8b-8059-7f2293ff3775</dd>", "<dd>41023e92-8008-4c8b-8059-7f2293ff3775</dd>",
@@ -706,3 +711,21 @@ class VolumeViewTests(test.TestCase):
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertEqual(volume.display_name, volume.id) self.assertEqual(volume.display_name, volume.id)
@test.create_stubs({cinder: ('volume_get',)})
def test_detail_view_with_exception(self):
volume = self.volumes.first()
server = self.servers.first()
volume.attachments = [{"server_id": server.id}]
cinder.volume_get(IsA(http.HttpRequest), volume.id).\
AndRaise(self.exceptions.cinder)
self.mox.ReplayAll()
url = reverse('horizon:project:volumes:detail',
args=[volume.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, VOLUME_INDEX_URL)

View File

@@ -18,6 +18,7 @@
Views for managing volumes. Views for managing volumes.
""" """
from django.core.urlresolvers import reverse # noqa
from django.core.urlresolvers import reverse_lazy # noqa from django.core.urlresolvers import reverse_lazy # noqa
from django.utils.datastructures import SortedDict # noqa from django.utils.datastructures import SortedDict # noqa
from django.utils.translation import ugettext_lazy as _ # noqa from django.utils.translation import ugettext_lazy as _ # noqa
@@ -91,6 +92,31 @@ class DetailView(tabs.TabView):
tab_group_class = project_tabs.VolumeDetailTabs tab_group_class = project_tabs.VolumeDetailTabs
template_name = 'project/volumes/detail.html' template_name = 'project/volumes/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["volume"] = self.get_data()
return context
def get_data(self):
if not hasattr(self, "_volume"):
try:
volume_id = self.kwargs['volume_id']
self._volume = cinder.volume_get(self.request, volume_id)
for att in self._volume.attachments:
att['instance'] = api.nova.server_get(self.request,
att['server_id'])
except Exception:
redirect = reverse('horizon:project:volumes:index')
exceptions.handle(self.request,
_('Unable to retrieve volume details.'),
redirect=redirect)
return self._volume
def get_tabs(self, request, *args, **kwargs):
volume = self.get_data()
return self.tab_group_class(request, volume=volume, **kwargs)
class CreateView(forms.ModalFormView): class CreateView(forms.ModalFormView):
form_class = project_forms.CreateForm form_class = project_forms.CreateForm