diff --git a/horizon/dashboards/nova/images_and_snapshots/images/tables.py b/horizon/dashboards/nova/images_and_snapshots/images/tables.py index 452ead6296..fb239340ef 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/tables.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/tables.py @@ -66,7 +66,8 @@ def get_container_format(image): class ImagesTable(tables.DataTable): - name = tables.Column("name") + name = tables.Column("name", link="horizon:nova:images_and_snapshots:" \ + "images:detail") image_type = tables.Column(get_image_type, verbose_name=_("Type"), filters=(filters.title,)) diff --git a/horizon/dashboards/nova/images_and_snapshots/images/tests.py b/horizon/dashboards/nova/images_and_snapshots/images/tests.py index 78d00938cc..49c39d4153 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/tests.py @@ -210,3 +210,17 @@ class ImageViewTests(test.TestCase): args=[image.id]) res = self.client.post(url, form_data) self.assertRedirectsNoFollow(res, IMAGES_INDEX_URL) + + def test_image_detail_get(self): + image = self.images.first() + self.mox.StubOutWithMock(api.glance, 'image_get_meta') + api.glance.image_get_meta(IsA(http.HttpRequest), str(image.id)) \ + .AndReturn(self.images.first()) + self.mox.ReplayAll() + + res = self.client.get( + reverse('horizon:nova:images_and_snapshots:images:detail', + args=[image.id])) + self.assertTemplateUsed(res, + 'nova/images_and_snapshots/images/detail.html') + self.assertEqual(res.context['image'].name, image.name) diff --git a/horizon/dashboards/nova/images_and_snapshots/images/urls.py b/horizon/dashboards/nova/images_and_snapshots/images/urls.py index 43004d797a..cf9fb58b31 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/urls.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/urls.py @@ -20,12 +20,13 @@ from django.conf.urls.defaults import patterns, url -from .views import UpdateView, LaunchView +from .views import UpdateView, LaunchView, DetailView VIEWS_MOD = 'horizon.dashboards.nova.images_and_snapshots.images.views' urlpatterns = patterns(VIEWS_MOD, url(r'^(?P[^/]+)/launch/$', LaunchView.as_view(), name='launch'), - url(r'^(?P[^/]+)/update/$', UpdateView.as_view(), name='update') + url(r'^(?P[^/]+)/update/$', UpdateView.as_view(), name='update'), + url(r'^(?P[^/]+)/$', DetailView.as_view(), name='detail'), ) diff --git a/horizon/dashboards/nova/images_and_snapshots/images/views.py b/horizon/dashboards/nova/images_and_snapshots/images/views.py index bd7ce2b571..e3ccaab862 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/views.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/views.py @@ -24,12 +24,14 @@ Views for managing Nova images. import logging +from django import shortcuts from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from horizon import api from horizon import exceptions from horizon import forms +from horizon import views from .forms import UpdateImageForm, LaunchForm @@ -168,3 +170,18 @@ class UpdateView(forms.ModalFormView): 'architecture': properties.get('architecture', ''), 'container_format': self.object.get('container_format', ''), 'disk_format': self.object.get('disk_format', ''), } + + +class DetailView(views.APIView): + template_name = 'nova/images_and_snapshots/images/detail.html' + + def get_data(self, request, context, *args, **kwargs): + image_id = kwargs['image_id'] + try: + image = api.glance.image_get_meta(self.request, kwargs['image_id']) + except: + exceptions.handle(request, _('Unable to retrieve details for ' + 'instance "%s".') % image_id, + redirect=redirect) + shortcuts.redirect('horizon:nova:images_and_snapshots:index') + return {'image': image} diff --git a/horizon/dashboards/nova/templates/nova/images_and_snapshots/images/detail.html b/horizon/dashboards/nova/templates/nova/images_and_snapshots/images/detail.html new file mode 100644 index 0000000000..485131aba4 --- /dev/null +++ b/horizon/dashboards/nova/templates/nova/images_and_snapshots/images/detail.html @@ -0,0 +1,55 @@ +{% extends 'nova/base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Image Detail "%}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title="Image Detail: "|add:image.name %} +{% endblock page_header %} + +{% block dash_main %} +
    +
  • +

    {% trans "Status" %}

    +
      +
    • {{ image.id|default:"None" }}
    • +
    • {{ image.status|default:"None" }}
    • +
    • {{ image.name|default:"None" }}
    • +
    • {{ image.is_public }}
    • +
    • {{ image.checksum|default:"None" }}
    • +
    • {{ image.created_at|default:"None" }}
    • +
    • {{ image.updated_app|default:"None" }}
    • +
    +
  • +
  • +

    {% trans "Specs" %}

    +
      +
    • {{ image.size|filesizeformat }}
    • +
    • {{ image.container_format|default:"None" }}
    • +
    • {{ image.disk_format|default:"None" }}
    • +
    +
  • +
  • +

    {% trans "Custom Properties" %}

    +
      + {% if image.properties.architecture %} +
    • {{ image.properties.architecture }}
    • + {% endif %} + {% if image.properties.kernel_id %} +
    • {{ image.properties.kernel_id }}
    • + {% endif %} + {% if image.properties.ramdisk_id %} +
    • {{ image.properties.ramdisk_id }}
    • + {% endif %} + {% if image.properties.image_state %} +
    • {{ image.properties.image_state }}
    • + {% endif %} + {% if image.properties.project_id %} +
    • {{ image.properties.project_id }}
    • + {% endif %} + {% if image.properties.image_type %} +
    • {{ image.properties.image_type }}
    • + {% endif %} +
    +
  • +
+{% endblock %} \ No newline at end of file diff --git a/openstack_dashboard/static/dashboard/css/style.css b/openstack_dashboard/static/dashboard/css/style.css index aedf2b7211..a8f85fe286 100644 --- a/openstack_dashboard/static/dashboard/css/style.css +++ b/openstack_dashboard/static/dashboard/css/style.css @@ -852,10 +852,11 @@ iframe { border: none; } -form .error { - background: #fce6e6; - color: #b94a48; - border: 1px solid #E9B1B0; +.item_detail ul li label { + color: #000; + font-weight: bold; + display: block; + margin-top: 5px; } .progress_bar { @@ -902,3 +903,9 @@ form .error { .header_rule { margin: 0 0 10px; } + +.item_detail .detail_section { + margin-bottom: 25px; + float: left; + margin-right: 50px; +}