From 0d8273463dbc6d55d725e8f9404006927ec5a67b Mon Sep 17 00:00:00 2001 From: Tihomir Trifonov Date: Tue, 15 May 2012 19:56:02 +0300 Subject: [PATCH] Added a new attribute - display_choices to Column class, for substituting the display value of the statuses provided by Nova to some more meaningful ones in the instance table. Fixes bug 997374 Change-Id: I18560868435b4cbc42670e3fc9c0bc83ebf9fda4 --- .../instances_and_volumes/instances/tables.py | 11 ++--- .../instances_and_volumes/instances/tests.py | 42 +++++++++++++++++++ .../dashboards/syspanel/instances/tables.py | 14 ++++--- horizon/tables/base.py | 22 ++++++++-- horizon/utils/filters.py | 19 +++++++++ 5 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 horizon/utils/filters.py diff --git a/horizon/dashboards/nova/instances_and_volumes/instances/tables.py b/horizon/dashboards/nova/instances_and_volumes/instances/tables.py index b35366dd9..12c4bccf1 100644 --- a/horizon/dashboards/nova/instances_and_volumes/instances/tables.py +++ b/horizon/dashboards/nova/instances_and_volumes/instances/tables.py @@ -23,6 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from horizon import api from horizon import tables from horizon.templatetags import sizeformat +from horizon.utils.filters import replace_underscores from .tabs import InstanceDetailTabs, LogTab, VNCTab @@ -216,10 +217,6 @@ def get_power_state(instance): return POWER_STATES.get(getattr(instance, "OS-EXT-STS:power_state", 0), '') -def replace_underscores(string): - return string.replace("_", " ") - - class InstancesTable(tables.DataTable): TASK_STATUS_CHOICES = ( (None, True), @@ -231,6 +228,9 @@ class InstancesTable(tables.DataTable): ("paused", True), ("error", False), ) + TASK_DISPLAY_CHOICES = ( + ("image_snapshot", "Snapshotting"), + ) name = tables.Column("name", link="horizon:nova:instances_and_volumes:" \ "instances:detail", verbose_name=_("Instance Name")) @@ -245,7 +245,8 @@ class InstancesTable(tables.DataTable): verbose_name=_("Task"), filters=(title, replace_underscores), status=True, - status_choices=TASK_STATUS_CHOICES) + status_choices=TASK_STATUS_CHOICES, + display_choices=TASK_DISPLAY_CHOICES) state = tables.Column(get_power_state, filters=(title, replace_underscores), verbose_name=_("Power State")) diff --git a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py index fc98863a0..05b53f8d4 100644 --- a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py +++ b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py @@ -21,6 +21,7 @@ from django import http from django.core.urlresolvers import reverse from mox import IsA, IgnoreArg +from copy import deepcopy from horizon import api from horizon import test @@ -308,6 +309,47 @@ class InstanceViewTests(test.TestCase): res = self.client.get(url) self.assertRedirectsNoFollow(res, INDEX_URL) + def test_create_instance_snapshot(self): + server = self.servers.first() + snapshot_server = deepcopy(server) + setattr(snapshot_server, 'OS-EXT-STS:task_state', + "IMAGE_SNAPSHOT") + self.mox.StubOutWithMock(api, 'server_get') + self.mox.StubOutWithMock(api, 'snapshot_create') + self.mox.StubOutWithMock(api, 'snapshot_list_detailed') + self.mox.StubOutWithMock(api, 'image_list_detailed') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'flavor_list') + self.mox.StubOutWithMock(api, 'server_delete') + self.mox.StubOutWithMock(api, 'volume_list') + api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.snapshot_create(IsA(http.HttpRequest), + server.id, + "snapshot1") + api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn([]) + api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([]) + api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([]) + + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) + api.server_list(IsA(http.HttpRequest)).AndReturn([snapshot_server]) + api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) + self.mox.ReplayAll() + + formData = {'instance_id': server.id, + 'method': 'CreateSnapshot', + 'tenant_id': server.tenant_id, + 'name': 'snapshot1'} + url = reverse('horizon:nova:images_and_snapshots:snapshots:create', + args=[server.id]) + redir_url = reverse('horizon:nova:images_and_snapshots:index') + res = self.client.post(url, formData) + self.assertRedirects(res, redir_url) + res = self.client.get(INDEX_URL) + self.assertContains(res, "" + "Snapshotting", 1) + def test_instance_update_get(self): server = self.servers.first() diff --git a/horizon/dashboards/syspanel/instances/tables.py b/horizon/dashboards/syspanel/instances/tables.py index 84bcd1d59..6f05cd735 100644 --- a/horizon/dashboards/syspanel/instances/tables.py +++ b/horizon/dashboards/syspanel/instances/tables.py @@ -26,7 +26,7 @@ from horizon.dashboards.nova.instances_and_volumes.instances.tables import ( TerminateInstance, EditInstance, ConsoleLink, LogLink, SnapshotLink, TogglePause, ToggleSuspend, RebootInstance, get_size, UpdateRow, get_ips, get_power_state) - +from horizon.utils.filters import replace_underscores LOG = logging.getLogger(__name__) @@ -50,6 +50,9 @@ class SyspanelInstancesTable(tables.DataTable): ("active", True), ("error", False), ) + TASK_DISPLAY_CHOICES = ( + ("image_snapshot", "Snapshotting"), + ) tenant = tables.Column("tenant_name", verbose_name=_("Tenant")) # NOTE(gabriel): Commenting out the user column because all we have # is an ID, and correlating that at production scale using our current @@ -67,17 +70,18 @@ class SyspanelInstancesTable(tables.DataTable): verbose_name=_("Size"), classes=('nowrap-col',)) status = tables.Column("status", - filters=(title,), + filters=(title, replace_underscores), verbose_name=_("Status"), status=True, status_choices=STATUS_CHOICES) task = tables.Column("OS-EXT-STS:task_state", verbose_name=_("Task"), - filters=(title,), + filters=(title, replace_underscores), status=True, - status_choices=TASK_STATUS_CHOICES) + status_choices=TASK_STATUS_CHOICES, + display_choices=TASK_DISPLAY_CHOICES) state = tables.Column(get_power_state, - filters=(title,), + filters=(title, replace_underscores), verbose_name=_("Power State")) class Meta: diff --git a/horizon/tables/base.py b/horizon/tables/base.py index dad7bae6e..de3dc7891 100644 --- a/horizon/tables/base.py +++ b/horizon/tables/base.py @@ -110,6 +110,11 @@ class Column(html.HTMLElement): ('off', False), ) + .. attribute:: display_choices + + A tuple of tuples representing the possible values to substitute + the data when displayed in the column cell. + .. attribute:: empty_value A string or callable to be used for cells which have no data. @@ -157,8 +162,8 @@ class Column(html.HTMLElement): def __init__(self, transform, verbose_name=None, sortable=False, link=None, hidden=False, attrs=None, status=False, - status_choices=None, empty_value=None, filters=None, - classes=None): + status_choices=None, display_choices=None, + empty_value=None, filters=None, classes=None): self.classes = classes or getattr(self, "classes", []) super(Column, self).__init__() self.attrs.update(attrs or {}) @@ -183,6 +188,7 @@ class Column(html.HTMLElement): self.filters = filters or [] if status_choices: self.status_choices = status_choices + self.display_choices = display_choices self.creation_counter = Column.creation_counter Column.creation_counter += 1 @@ -227,8 +233,16 @@ class Column(html.HTMLElement): msg = termcolors.colorize(msg, **PALETTE['ERROR']) LOG.warning(msg) data = None - for filter_func in self.filters: - data = filter_func(data) + display_value = None + if self.display_choices: + display_value = [display for (value, display) in + self.display_choices + if value.lower() == (data or '').lower()] + if display_value: + data = display_value[0] + else: + for filter_func in self.filters: + data = filter_func(data) self.table._data_cache[self][datum_id] = data return self.table._data_cache[self][datum_id] diff --git a/horizon/utils/filters.py b/horizon/utils/filters.py new file mode 100644 index 000000000..2f84032de --- /dev/null +++ b/horizon/utils/filters.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Nebula, Inc. +# +# 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. + + +def replace_underscores(string): + return string.replace("_", " ")