1# Copyright 2012,  Nachi Ueno,  NTT MCL,  Inc.
2# Copyright 2013,  Big Switch Networks, Inc.
3#
4#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5#    not use this file except in compliance with the License. You may obtain
6#    a copy of the License at
7#
8#         http://www.apache.org/licenses/LICENSE-2.0
9#
10#    Unless required by applicable law or agreed to in writing, software
11#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13#    License for the specific language governing permissions and limitations
14#    under the License.
15
16"""
17Views for managing Neutron Routers.
18"""
19
20from collections import OrderedDict
21
22from django.urls import reverse
23from django.urls import reverse_lazy
24from django.utils.translation import pgettext_lazy
25from django.utils.translation import ugettext_lazy as _
26
27from horizon import exceptions
28from horizon import forms
29from horizon import messages
30from horizon import tables
31from horizon import tabs
32from horizon.utils import memoized
33
34from openstack_dashboard import api
35from openstack_dashboard.utils import filters
36
37from openstack_dashboard.dashboards.project.routers\
38    import forms as project_forms
39from openstack_dashboard.dashboards.project.routers import tables as rtables
40from openstack_dashboard.dashboards.project.routers import tabs as rdtabs
41
42
43class IndexView(tables.DataTableView):
44    table_class = rtables.RoutersTable
45    page_title = _("Routers")
46    FILTERS_MAPPING = {'admin_state_up': {_("up"): True, _("down"): False}}
47
48    def _get_routers(self, search_opts=None):
49        try:
50            search_opts = self.get_filters(
51                filters=search_opts, filters_map=self.FILTERS_MAPPING)
52            tenant_id = self.request.user.tenant_id
53            routers = api.neutron.router_list(self.request,
54                                              tenant_id=tenant_id,
55                                              **search_opts)
56        except Exception:
57            routers = []
58            exceptions.handle(self.request,
59                              _('Unable to retrieve router list.'))
60
61        ext_net_dict = self._list_external_networks()
62
63        for r in routers:
64            r.name = r.name_or_id
65            self._set_external_network(r, ext_net_dict)
66        return routers
67
68    def get_data(self):
69        routers = self._get_routers()
70        return routers
71
72    def _list_external_networks(self):
73        try:
74            search_opts = {'router:external': True}
75            ext_nets = api.neutron.network_list(self.request,
76                                                **search_opts)
77            ext_net_dict = OrderedDict((n['id'], n.name_or_id)
78                                       for n in ext_nets)
79        except Exception:
80            msg = _('Unable to retrieve a list of external networks.')
81            exceptions.handle(self.request, msg)
82            ext_net_dict = {}
83        return ext_net_dict
84
85    def _set_external_network(self, router, ext_net_dict):
86        gateway_info = router.external_gateway_info
87        if gateway_info:
88            ext_net_id = gateway_info['network_id']
89            if ext_net_id in ext_net_dict:
90                gateway_info['network'] = ext_net_dict[ext_net_id]
91            else:
92                msg_params = {'ext_net_id': ext_net_id, 'router_id': router.id}
93                msg = _('External network "%(ext_net_id)s" expected but not '
94                        'found for router "%(router_id)s".') % msg_params
95                messages.error(self.request, msg)
96                # gateway_info['network'] is just the network name, so putting
97                # in a smallish error message in the table is reasonable.
98                # Translators: The usage is "<UUID of ext_net> (Not Found)"
99                gateway_info['network'] = pgettext_lazy(
100                    'External network not found',
101                    '%s (Not Found)') % ext_net_id
102
103
104class DetailView(tabs.TabbedTableView):
105    tab_group_class = rdtabs.RouterDetailTabs
106    template_name = 'horizon/common/_detail.html'
107    failure_url = reverse_lazy('horizon:project:routers:index')
108    network_url = 'horizon:project:networks:detail'
109    page_title = "{{ router.name|default:router.id }}"
110
111    @memoized.memoized_method
112    def _get_data(self):
113        try:
114            router_id = self.kwargs['router_id']
115            router = api.neutron.router_get(self.request, router_id)
116            router.set_id_as_name_if_empty(length=0)
117        except Exception:
118            msg = _('Unable to retrieve details for router "%s".') \
119                % router_id
120            exceptions.handle(self.request, msg, redirect=self.failure_url)
121        if router.external_gateway_info:
122            ext_net_id = router.external_gateway_info['network_id']
123            router.external_gateway_info['network_url'] = reverse(
124                self.network_url, args=[ext_net_id])
125            try:
126                ext_net = api.neutron.network_get(self.request, ext_net_id,
127                                                  expand_subnet=False)
128                ext_net.set_id_as_name_if_empty(length=0)
129                router.external_gateway_info['network'] = ext_net.name
130            except Exception:
131                msg = _('Unable to retrieve an external network "%s".') \
132                    % ext_net_id
133                exceptions.handle(self.request, msg)
134                router.external_gateway_info['network'] = ext_net_id
135        return router
136
137    @memoized.memoized_method
138    def _get_ports(self):
139        try:
140            ports = api.neutron.port_list(self.request,
141                                          device_id=self.kwargs['router_id'])
142        except Exception:
143            ports = []
144            msg = _('Unable to retrieve port details.')
145            exceptions.handle(self.request, msg)
146        return ports
147
148    def get_context_data(self, **kwargs):
149        context = super().get_context_data(**kwargs)
150        router = self._get_data()
151        table = rtables.RoutersTable(self.request)
152
153        context["router"] = router
154        context["url"] = self.failure_url
155        context["actions"] = table.render_row_actions(router)
156        context['dvr_supported'] = api.neutron.get_feature_permission(
157            self.request, "dvr", "get")
158        context['ha_supported'] = api.neutron.get_feature_permission(
159            self.request, "l3-ha", "get")
160        choices = rtables.STATUS_DISPLAY_CHOICES
161        router.status_label = filters.get_display_label(choices, router.status)
162        choices = rtables.ADMIN_STATE_DISPLAY_CHOICES
163        router.admin_state_label = (
164            filters.get_display_label(choices, router.admin_state))
165        return context
166
167    def get_tabs(self, request, *args, **kwargs):
168        router = self._get_data()
169        ports = self._get_ports()
170        return self.tab_group_class(request, router=router,
171                                    ports=ports, **kwargs)
172
173
174class CreateView(forms.ModalFormView):
175    form_class = project_forms.CreateForm
176    form_id = "create_router_form"
177    template_name = 'project/routers/create.html'
178    success_url = reverse_lazy("horizon:project:routers:index")
179    page_title = _("Create Router")
180    submit_label = _("Create Router")
181    submit_url = reverse_lazy("horizon:project:routers:create")
182
183    def get_context_data(self, **kwargs):
184        context = super().get_context_data(**kwargs)
185        context['enable_snat_allowed'] = self.initial['enable_snat_allowed']
186        return context
187
188    def get_initial(self):
189        enable_snat_allowed = api.neutron.get_feature_permission(
190            self.request, 'ext-gw-mode', 'create_router_enable_snat')
191        self.initial['enable_snat_allowed'] = enable_snat_allowed
192        return super().get_initial()
193
194
195class UpdateView(forms.ModalFormView):
196    form_class = project_forms.UpdateForm
197    form_id = "update_router_form"
198    template_name = 'project/routers/update.html'
199    success_url = reverse_lazy("horizon:project:routers:index")
200    page_title = _("Edit Router")
201    submit_label = _("Save Changes")
202    submit_url = "horizon:project:routers:update"
203
204    def get_context_data(self, **kwargs):
205        context = super().get_context_data(**kwargs)
206        args = (self.kwargs['router_id'],)
207        context["router_id"] = self.kwargs['router_id']
208        context['submit_url'] = reverse(self.submit_url, args=args)
209        return context
210
211    def _get_object(self, *args, **kwargs):
212        router_id = self.kwargs['router_id']
213        try:
214            return api.neutron.router_get(self.request, router_id)
215        except Exception:
216            redirect = self.success_url
217            msg = _('Unable to retrieve router details.')
218            exceptions.handle(self.request, msg, redirect=redirect)
219
220    def get_initial(self):
221        router = self._get_object()
222        initial = {'router_id': router['id'],
223                   'tenant_id': router['tenant_id'],
224                   'name': router['name'],
225                   'admin_state': router['admin_state_up']}
226        if hasattr(router, 'distributed'):
227            initial['mode'] = ('distributed' if router.distributed
228                               else 'centralized')
229        if hasattr(router, 'ha'):
230            initial['ha'] = router.ha
231        return initial
232