1# Copyright 2012, Nachi Ueno, NTT MCL, Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# not use this file except in compliance with the License. You may obtain 5# a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15import logging 16 17from django.template import defaultfilters as filters 18from django.urls import reverse 19from django.utils.translation import pgettext_lazy 20from django.utils.translation import ugettext_lazy as _ 21from django.utils.translation import ungettext_lazy 22from neutronclient.common import exceptions as neutron_exceptions 23 24from horizon import exceptions 25from horizon import tables 26from horizon.tables import actions 27 28from openstack_dashboard import api 29from openstack_dashboard import policy 30from openstack_dashboard.usage import quotas 31 32 33LOG = logging.getLogger(__name__) 34 35 36class DeleteRouter(policy.PolicyTargetMixin, tables.DeleteAction): 37 @staticmethod 38 def action_present(count): 39 return ungettext_lazy( 40 "Delete Router", 41 "Delete Routers", 42 count 43 ) 44 45 @staticmethod 46 def action_past(count): 47 return ungettext_lazy( 48 "Deleted Router", 49 "Deleted Routers", 50 count 51 ) 52 53 redirect_url = "horizon:project:routers:index" 54 policy_rules = (("network", "delete_router"),) 55 56 @actions.handle_exception_with_detail_message( 57 # normal_log_message 58 'Failed to delete router %(id)s: %(exc)s', 59 # target_exception 60 neutron_exceptions.NeutronClientException, 61 # target_log_message 62 'Unable to delete router %(id)s: %(exc)s', 63 # target_user_message 64 _('Unable to delete router %(name)s: %(exc)s'), 65 # logger_name 66 __name__) 67 def delete(self, request, obj_id): 68 # detach all interfaces before attempting to delete the router 69 search_opts = {'device_owner': 'network:router_interface', 70 'device_id': obj_id} 71 ports = api.neutron.port_list(request, **search_opts) 72 for port in ports: 73 api.neutron.router_remove_interface(request, obj_id, 74 port_id=port.id) 75 api.neutron.router_delete(request, obj_id) 76 77 78class CreateRouter(tables.LinkAction): 79 name = "create" 80 verbose_name = _("Create Router") 81 url = "horizon:project:routers:create" 82 classes = ("ajax-modal",) 83 icon = "plus" 84 policy_rules = (("network", "create_router"),) 85 86 def allowed(self, request, datum=None): 87 usages = quotas.tenant_quota_usages(request, targets=('router', )) 88 # when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False 89 # usages['router'] is empty 90 if usages.get('router', {}).get('available', 1) <= 0: 91 if "disabled" not in self.classes: 92 self.classes = list(self.classes) + ['disabled'] 93 self.verbose_name = _("Create Router (Quota exceeded)") 94 else: 95 self.verbose_name = _("Create Router") 96 self.classes = [c for c in self.classes if c != "disabled"] 97 98 return True 99 100 101class EditRouter(policy.PolicyTargetMixin, tables.LinkAction): 102 name = "update" 103 verbose_name = _("Edit Router") 104 url = "horizon:project:routers:update" 105 classes = ("ajax-modal",) 106 icon = "pencil" 107 policy_rules = (("network", "update_router"),) 108 109 110class SetGateway(policy.PolicyTargetMixin, tables.LinkAction): 111 name = "setgateway" 112 verbose_name = _("Set Gateway") 113 url = "horizon:project:routers:setgateway" 114 classes = ("ajax-modal",) 115 icon = "camera" 116 policy_rules = (("network", "update_router"),) 117 118 def allowed(self, request, datum=None): 119 if datum.external_gateway_info: 120 return False 121 return True 122 123 124class ClearGateway(policy.PolicyTargetMixin, tables.BatchAction): 125 help_text = _("You may reset the gateway later by using the" 126 " set gateway action, but the gateway IP may change.") 127 128 @staticmethod 129 def action_present(count): 130 return ungettext_lazy( 131 "Clear Gateway", 132 "Clear Gateways", 133 count 134 ) 135 136 @staticmethod 137 def action_past(count): 138 return ungettext_lazy( 139 "Cleared Gateway", 140 "Cleared Gateways", 141 count 142 ) 143 144 name = "clear" 145 classes = ('btn-cleargateway',) 146 redirect_url = "horizon:project:routers:index" 147 policy_rules = (("network", "update_router"),) 148 action_type = "danger" 149 150 @actions.handle_exception_with_detail_message( 151 # normal_log_message 152 'Unable to clear gateway for router %(id)s: %(exc)s', 153 # target_exception 154 neutron_exceptions.Conflict, 155 # target_log_message 156 'Unable to clear gateway for router %(id)s: %(exc)s', 157 # target_user_message 158 _('Unable to clear gateway for router %(name)s. ' 159 'Most possible reason is because the gateway is required ' 160 'by one or more floating IPs'), 161 # logger_name 162 __name__) 163 def action(self, request, obj_id): 164 api.neutron.router_remove_gateway(request, obj_id) 165 166 def get_success_url(self, request): 167 return reverse(self.redirect_url) 168 169 def allowed(self, request, datum=None): 170 if datum.external_gateway_info: 171 return True 172 return False 173 174 175class UpdateRow(tables.Row): 176 ajax = True 177 178 def get_data(self, request, router_id): 179 router = api.neutron.router_get(request, router_id) 180 return router 181 182 183def get_external_network(router): 184 if router.external_gateway_info: 185 return router.external_gateway_info['network'] 186 return _("-") 187 188 189def get_availability_zones(router): 190 if 'availability_zones' in router and router.availability_zones: 191 return ', '.join(router.availability_zones) 192 return _("-") 193 194 195class RoutersFilterAction(tables.FilterAction): 196 name = 'filter_project_routers' 197 filter_type = 'server' 198 filter_choices = (('name', _("Router Name ="), True), 199 ('status', _("Status ="), True), 200 ('admin_state_up', _("Admin State ="), True, 201 _("e.g. UP / DOWN"))) 202 203 204STATUS_DISPLAY_CHOICES = ( 205 ("active", pgettext_lazy("current status of router", "Active")), 206 ("error", pgettext_lazy("current status of router", "Error")), 207) 208ADMIN_STATE_DISPLAY_CHOICES = ( 209 ("up", pgettext_lazy("Admin state of a Router", "UP")), 210 ("down", pgettext_lazy("Admin state of a Router", "DOWN")), 211) 212 213 214class RoutersTable(tables.DataTable): 215 name = tables.WrappingColumn("name", 216 verbose_name=_("Name"), 217 link="horizon:project:routers:detail") 218 status = tables.Column("status", 219 verbose_name=_("Status"), 220 status=True, 221 display_choices=STATUS_DISPLAY_CHOICES) 222 distributed = tables.Column("distributed", 223 filters=(filters.yesno, filters.capfirst), 224 verbose_name=_("Distributed")) 225 ha = tables.Column("ha", 226 filters=(filters.yesno, filters.capfirst), 227 # Translators: High Availability mode of Neutron router 228 verbose_name=_("HA mode")) 229 ext_net = tables.Column(get_external_network, 230 verbose_name=_("External Network")) 231 admin_state = tables.Column("admin_state", 232 verbose_name=_("Admin State"), 233 display_choices=ADMIN_STATE_DISPLAY_CHOICES) 234 availability_zones = tables.Column(get_availability_zones, 235 verbose_name=_("Availability Zones")) 236 237 def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs): 238 super().__init__(request, data=data, 239 needs_form_wrapper=needs_form_wrapper, **kwargs) 240 if not api.neutron.get_feature_permission(request, "dvr", "get"): 241 del self.columns["distributed"] 242 if not api.neutron.get_feature_permission(request, "l3-ha", "get"): 243 del self.columns["ha"] 244 try: 245 if not api.neutron.is_extension_supported( 246 request, "router_availability_zone"): 247 del self.columns["availability_zones"] 248 except Exception: 249 msg = _("Unable to check if router availability zone extension " 250 "is supported") 251 exceptions.handle(self.request, msg) 252 del self.columns['availability_zones'] 253 254 def get_object_display(self, obj): 255 return obj.name 256 257 class Meta(object): 258 name = "routers" 259 verbose_name = _("Routers") 260 status_columns = ["status"] 261 row_class = UpdateRow 262 table_actions = (CreateRouter, DeleteRouter, 263 RoutersFilterAction) 264 row_actions = (SetGateway, ClearGateway, EditRouter, DeleteRouter) 265