From 031aed50a3cb5e8344b36abf5fb88ba7947b9efd Mon Sep 17 00:00:00 2001 From: Itxaka Date: Fri, 15 Jan 2016 12:29:52 +0100 Subject: [PATCH] Translate status in network topology Enables the translation of the status on network topology. Provides a json decoder that will resolve lazy objects like translations. Moves STATUS_DISPLAY_CHOICES and ADMIN_STATE_DISPLAY_CHOICES out of the RoutersTable for easy access to their status from other parts of the code. All network objects have their status translated and a new item called original_status which provides the untranslated status so the javascript code is able to display the proper image (green/red) based on the original status instead of doing it on the translation. Change-Id: Ic8cebafe7fbc291fa8933e8d1c2a244be20128e3 Closes-Bug: #1323599 Closes-Bug: #1484113 --- .../horizon/js/horizon.networktopology.js | 4 +- horizon/utils/lazy_encoder.py | 25 +++++++++ .../project/network_topology/tests.py | 18 ++++-- .../project/network_topology/views.py | 55 +++++++++++++++++-- .../dashboards/project/routers/tables.py | 19 ++++--- .../dashboards/project/routers/views.py | 4 +- 6 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 horizon/utils/lazy_encoder.py diff --git a/horizon/static/horizon/js/horizon.networktopology.js b/horizon/static/horizon/js/horizon.networktopology.js index 52183ee6bb..e0c752ef4e 100644 --- a/horizon/static/horizon/js/horizon.networktopology.js +++ b/horizon/static/horizon/js/horizon.networktopology.js @@ -918,7 +918,7 @@ horizon.network_topology = { object.router_id = port.device_id; object.url = port.url; object.port_status = port.status; - object.port_status_css = (port.status === 'ACTIVE') ? 'active' : 'down'; + object.port_status_css = (port.original_status === 'ACTIVE') ? 'active' : 'down'; var ipAddress = ''; try { for (var ip in port.fixed_ips) { @@ -962,7 +962,7 @@ horizon.network_topology = { type:d.type, delete_label: gettext('Delete'), status:d.status, - status_class: (d.status === 'ACTIVE') ? 'active' : 'down', + status_class: (d.original_status === 'ACTIVE') ? 'active' : 'down', status_label: gettext('STATUS'), id_label: gettext('ID'), interfaces_label: gettext('Interfaces'), diff --git a/horizon/utils/lazy_encoder.py b/horizon/utils/lazy_encoder.py new file mode 100644 index 0000000000..320bc27ece --- /dev/null +++ b/horizon/utils/lazy_encoder.py @@ -0,0 +1,25 @@ +# Copyright 2016 Red Hat, 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. + +from django.core.serializers.json import DjangoJSONEncoder +from django.utils.encoding import force_text +from django.utils.functional import Promise + + +class LazyTranslationEncoder(DjangoJSONEncoder): + """JSON encoder that resolves lazy objects like translations""" + def default(self, obj): + if isinstance(obj, Promise): + return force_text(obj) + return super(LazyTranslationEncoder, self).default(obj) diff --git a/openstack_dashboard/dashboards/project/network_topology/tests.py b/openstack_dashboard/dashboards/project/network_topology/tests.py index daceb4f3f8..8be4a7e8d1 100644 --- a/openstack_dashboard/dashboards/project/network_topology/tests.py +++ b/openstack_dashboard/dashboards/project/network_topology/tests.py @@ -20,6 +20,8 @@ from mox3.mox import IsA # noqa from oslo_serialization import jsonutils from openstack_dashboard import api +from openstack_dashboard.dashboards.project.network_topology.views import \ + TranslationHelper from openstack_dashboard.test import helpers as test from openstack_dashboard.usage import quotas @@ -28,6 +30,7 @@ INDEX_URL = reverse('horizon:project:network_topology:index') class NetworkTopologyTests(test.TestCase): + trans = TranslationHelper() @test.create_stubs({api.nova: ('server_list',), api.neutron: ('network_list_for_tenant', @@ -83,7 +86,8 @@ class NetworkTopologyTests(test.TestCase): expect_server_urls = [ {'id': server.id, 'name': server.name, - 'status': server.status, + 'status': self.trans.instance[server.status], + 'original_status': server.status, 'task': None, 'url': '/project/instances/%s/' % server.id} for server in self.servers.list()] @@ -98,7 +102,8 @@ class NetworkTopologyTests(test.TestCase): 'external_gateway_info': router.external_gateway_info, 'name': router.name, - 'status': router.status, + 'status': self.trans.router[router.status], + 'original_status': router.status, 'url': '/project/routers/%s/' % router.id} for router in routers] self.assertEqual(expect_router_urls, data['routers']) @@ -112,14 +117,16 @@ class NetworkTopologyTests(test.TestCase): 'url': '/project/networks/%s/detail' % net.id, 'name': net.name, 'router:external': net.router__external, - 'status': net.status, + 'status': self.trans.network[net.status], + 'original_status': net.status, 'subnets': []} for net in external_networks] expect_net_urls += [{'id': net.id, 'url': '/project/networks/%s/detail' % net.id, 'name': net.name, 'router:external': net.router__external, - 'status': net.status, + 'status': self.trans.network[net.status], + 'original_status': net.status, 'subnets': [{'cidr': subnet.cidr, 'id': subnet.id, 'url': @@ -139,7 +146,8 @@ class NetworkTopologyTests(test.TestCase): 'device_owner': port.device_owner, 'fixed_ips': port.fixed_ips, 'network_id': port.network_id, - 'status': port.status, + 'status': self.trans.port[port.status], + 'original_status': port.status, 'url': '/project/networks/ports/%s/detail' % port.id} for port in self.ports.list()] if router_enable: diff --git a/openstack_dashboard/dashboards/project/network_topology/views.py b/openstack_dashboard/dashboards/project/network_topology/views.py index d4b56c7abc..4f5c4661b5 100644 --- a/openstack_dashboard/dashboards/project/network_topology/views.py +++ b/openstack_dashboard/dashboards/project/network_topology/views.py @@ -17,6 +17,7 @@ # under the License. import json +import six from django.conf import settings from django.core.urlresolvers import reverse @@ -26,6 +27,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic import View # noqa from horizon import exceptions +from horizon.utils.lazy_encoder import LazyTranslationEncoder from horizon import views from openstack_dashboard import api @@ -44,6 +46,8 @@ from openstack_dashboard.dashboards.project.network_topology.subnets \ from openstack_dashboard.dashboards.project.instances import\ console as i_console +from openstack_dashboard.dashboards.project.instances.tables import \ + STATUS_DISPLAY_CHOICES as instance_choices from openstack_dashboard.dashboards.project.instances import\ views as i_views from openstack_dashboard.dashboards.project.instances.workflows import\ @@ -52,16 +56,48 @@ from openstack_dashboard.dashboards.project.networks.subnets import\ views as s_views from openstack_dashboard.dashboards.project.networks.subnets import\ workflows as s_workflows +from openstack_dashboard.dashboards.project.networks.tables import \ + DISPLAY_CHOICES as network_display_choices +from openstack_dashboard.dashboards.project.networks.tables import \ + STATUS_DISPLAY_CHOICES as network_choices from openstack_dashboard.dashboards.project.networks import\ views as n_views from openstack_dashboard.dashboards.project.networks import\ workflows as n_workflows +from openstack_dashboard.dashboards.project.routers.ports.tables import \ + DISPLAY_CHOICES as ports_choices +from openstack_dashboard.dashboards.project.routers.ports.tables import \ + STATUS_DISPLAY_CHOICES as ports_status_choices from openstack_dashboard.dashboards.project.routers.ports import\ views as p_views +from openstack_dashboard.dashboards.project.routers.tables import \ + ADMIN_STATE_DISPLAY_CHOICES as routers_admin_choices +from openstack_dashboard.dashboards.project.routers.tables import \ + STATUS_DISPLAY_CHOICES as routers_status_choices from openstack_dashboard.dashboards.project.routers import\ views as r_views +class TranslationHelper(object): + """Helper class to provide the translations of instances, networks, + routers and ports from other parts of the code to the network topology + """ + def __init__(self): + # turn translation tuples into dicts for easy access + self.instance = dict(instance_choices) + self.network = dict(network_choices) + self.network.update(dict(network_display_choices)) + self.router = dict(routers_admin_choices) + self.router.update(dict(routers_status_choices)) + self.port = dict(ports_choices) + self.port.update(dict(ports_status_choices)) + # and turn all the keys into Uppercase for simple access + self.instance = {k.upper(): v for k, v in six.iteritems(self.instance)} + self.network = {k.upper(): v for k, v in six.iteritems(self.network)} + self.router = {k.upper(): v for k, v in six.iteritems(self.router)} + self.port = {k.upper(): v for k, v in six.iteritems(self.port)} + + class NTAddInterfaceView(p_views.AddInterfaceView): success_url = "horizon:project:network_topology:index" failure_url = "horizon:project:network_topology:index" @@ -183,6 +219,7 @@ class NetworkTopologyView(views.HorizonTemplateView): class JSONView(View): + trans = TranslationHelper() @property def is_router_enabled(self): @@ -221,7 +258,8 @@ class JSONView(View): console = None server_data = {'name': server.name, - 'status': server.status, + 'status': self.trans.instance[server.status], + 'original_status': server.status, 'task': getattr(server, 'OS-EXT-STS:task_state'), 'id': server.id} if console: @@ -249,7 +287,8 @@ class JSONView(View): 'subnets': [{'id': subnet.id, 'cidr': subnet.cidr} for subnet in network.subnets], - 'status': network.status, + 'status': self.trans.network[network.status], + 'original_status': network.status, 'router:external': network['router:external']} self.add_resource_url('horizon:project:networks:subnets:detail', obj['subnets']) @@ -281,7 +320,8 @@ class JSONView(View): 'name': publicnet.name_or_id, 'id': publicnet.id, 'subnets': subnets, - 'status': publicnet.status, + 'status': self.trans.network[publicnet.status], + 'original_status': publicnet.status, 'router:external': publicnet['router:external']}) self.add_resource_url('horizon:project:networks:detail', @@ -303,7 +343,8 @@ class JSONView(View): routers = [{'id': router.id, 'name': router.name_or_id, - 'status': router.status, + 'status': self.trans.router[router.status], + 'original_status': router.status, 'external_gateway_info': router.external_gateway_info} for router in neutron_routers] self.add_resource_url('horizon:project:routers:detail', routers) @@ -320,7 +361,8 @@ class JSONView(View): 'device_id': port.device_id, 'fixed_ips': port.fixed_ips, 'device_owner': port.device_owner, - 'status': port.status} + 'status': self.trans.port[port.status], + 'original_status': port.status} for port in neutron_ports if port.device_owner != 'network:router_ha_interface'] self.add_resource_url('horizon:project:networks:ports:detail', @@ -354,5 +396,6 @@ class JSONView(View): 'ports': self._get_ports(request), 'routers': self._get_routers(request)} self._prepare_gateway_ports(data['routers'], data['ports']) - json_string = json.dumps(data, ensure_ascii=False) + json_string = json.dumps(data, cls=LazyTranslationEncoder, + ensure_ascii=False) return HttpResponse(json_string, content_type='text/json') diff --git a/openstack_dashboard/dashboards/project/routers/tables.py b/openstack_dashboard/dashboards/project/routers/tables.py index 31ce2c5c5f..8eaeebb953 100644 --- a/openstack_dashboard/dashboards/project/routers/tables.py +++ b/openstack_dashboard/dashboards/project/routers/tables.py @@ -197,16 +197,17 @@ class RoutersFilterAction(tables.FilterAction): if query in router.name.lower()] -class RoutersTable(tables.DataTable): - STATUS_DISPLAY_CHOICES = ( - ("active", pgettext_lazy("current status of router", u"Active")), - ("error", pgettext_lazy("current status of router", u"Error")), - ) - ADMIN_STATE_DISPLAY_CHOICES = ( - ("up", pgettext_lazy("Admin state of a Router", u"UP")), - ("down", pgettext_lazy("Admin state of a Router", u"DOWN")), - ) +STATUS_DISPLAY_CHOICES = ( + ("active", pgettext_lazy("current status of router", u"Active")), + ("error", pgettext_lazy("current status of router", u"Error")), +) +ADMIN_STATE_DISPLAY_CHOICES = ( + ("up", pgettext_lazy("Admin state of a Router", u"UP")), + ("down", pgettext_lazy("Admin state of a Router", u"DOWN")), +) + +class RoutersTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name"), link="horizon:project:routers:detail") diff --git a/openstack_dashboard/dashboards/project/routers/views.py b/openstack_dashboard/dashboards/project/routers/views.py index a714b8c950..319be7da48 100644 --- a/openstack_dashboard/dashboards/project/routers/views.py +++ b/openstack_dashboard/dashboards/project/routers/views.py @@ -155,9 +155,9 @@ class DetailView(tabs.TabbedTableView): self.request, "dvr", "get") context['ha_supported'] = api.neutron.get_feature_permission( self.request, "l3-ha", "get") - choices = table.STATUS_DISPLAY_CHOICES + choices = rtables.STATUS_DISPLAY_CHOICES router.status_label = filters.get_display_label(choices, router.status) - choices = table.ADMIN_STATE_DISPLAY_CHOICES + choices = rtables.ADMIN_STATE_DISPLAY_CHOICES router.admin_state_label = ( filters.get_display_label(choices, router.admin_state)) return context