1# Copyright 2012, Nachi Ueno, NTT MCL, Inc. 2# All rights reserved. 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""" 19import logging 20 21from django.urls import reverse 22from django.urls import reverse_lazy 23from django.utils.translation import ugettext_lazy as _ 24 25from horizon import exceptions 26from horizon import forms 27from horizon import messages 28 29from openstack_dashboard import api 30 31LOG = logging.getLogger(__name__) 32 33 34class CreateForm(forms.SelfHandlingForm): 35 name = forms.CharField(max_length=255, label=_("Router Name"), 36 required=False) 37 admin_state_up = forms.BooleanField( 38 label=_("Enable Admin State"), 39 initial=True, 40 required=False, 41 help_text=_("If checked, the router will be enabled.")) 42 external_network = forms.ThemableChoiceField(label=_("External Network"), 43 required=False) 44 enable_snat = forms.BooleanField(label=_("Enable SNAT"), 45 initial=True, 46 required=False) 47 mode = forms.ThemableChoiceField(label=_("Router Type")) 48 ha = forms.ThemableChoiceField(label=_("High Availability Mode")) 49 az_hints = forms.MultipleChoiceField( 50 label=_("Availability Zone Hints"), 51 required=False, 52 help_text=_("Availability Zones where the router may be scheduled. " 53 "Leaving this unset is equivalent to selecting all " 54 "Availability Zones")) 55 failure_url = 'horizon:project:routers:index' 56 57 def __init__(self, request, *args, **kwargs): 58 super().__init__(request, *args, **kwargs) 59 self.dvr_allowed = api.neutron.get_feature_permission(self.request, 60 "dvr", "create") 61 if self.dvr_allowed: 62 mode_choices = [('server_default', _('Use Server Default')), 63 ('centralized', _('Centralized')), 64 ('distributed', _('Distributed'))] 65 self.fields['mode'].choices = mode_choices 66 else: 67 del self.fields['mode'] 68 69 self.ha_allowed = api.neutron.get_feature_permission(self.request, 70 "l3-ha", "create") 71 if self.ha_allowed: 72 ha_choices = [('server_default', _('Use Server Default')), 73 ('enabled', _('Enable HA mode')), 74 ('disabled', _('Disable HA mode'))] 75 self.fields['ha'].choices = ha_choices 76 else: 77 del self.fields['ha'] 78 networks = self._get_network_list(request) 79 if networks: 80 self.fields['external_network'].choices = networks 81 else: 82 del self.fields['external_network'] 83 84 self.enable_snat_allowed = self.initial['enable_snat_allowed'] 85 if (not networks or not self.enable_snat_allowed): 86 del self.fields['enable_snat'] 87 88 try: 89 az_supported = api.neutron.is_extension_supported( 90 self.request, 'router_availability_zone') 91 92 if az_supported: 93 zones = api.neutron.list_availability_zones( 94 self.request, 'router', 'available') 95 self.fields['az_hints'].choices = [(zone['name'], zone['name']) 96 for zone in zones] 97 else: 98 del self.fields['az_hints'] 99 except Exception: 100 msg = _("Failed to get availability zone list.") 101 exceptions.handle(self.request, msg) 102 del self.fields['az_hints'] 103 104 def _get_network_list(self, request): 105 search_opts = {'router:external': True} 106 try: 107 networks = api.neutron.network_list(request, **search_opts) 108 except Exception as e: 109 LOG.info('Failed to get network list: %s', e) 110 msg = _('Failed to get network list.') 111 messages.warning(request, msg) 112 networks = [] 113 114 choices = [(network.id, network.name or network.id) 115 for network in networks] 116 if choices: 117 choices.insert(0, ("", _("Select network"))) 118 return choices 119 120 def handle(self, request, data): 121 try: 122 params = {'name': data['name'], 123 'admin_state_up': data['admin_state_up']} 124 # NOTE: admin form allows to specify tenant_id. 125 # We have the logic here to simplify the logic. 126 if 'tenant_id' in data and data['tenant_id']: 127 params['tenant_id'] = data['tenant_id'] 128 if 'external_network' in data and data['external_network']: 129 params['external_gateway_info'] = {'network_id': 130 data['external_network']} 131 if self.enable_snat_allowed: 132 params['external_gateway_info']['enable_snat'] = \ 133 data['enable_snat'] 134 if 'az_hints' in data and data['az_hints']: 135 params['availability_zone_hints'] = data['az_hints'] 136 if (self.dvr_allowed and data['mode'] != 'server_default'): 137 params['distributed'] = (data['mode'] == 'distributed') 138 if (self.ha_allowed and data['ha'] != 'server_default'): 139 params['ha'] = (data['ha'] == 'enabled') 140 router = api.neutron.router_create(request, **params) 141 message = (_('Router %s was successfully created.') % 142 router.name_or_id) 143 messages.success(request, message) 144 return router 145 except Exception as exc: 146 LOG.info('Failed to create router: %s', exc) 147 if exc.status_code == 409: 148 msg = _('Quota exceeded for resource router.') 149 else: 150 if data["name"]: 151 msg = _('Failed to create router "%s".') % data['name'] 152 else: 153 msg = _('Failed to create router.') 154 redirect = reverse(self.failure_url) 155 exceptions.handle(request, msg, redirect=redirect) 156 return False 157 158 159class UpdateForm(forms.SelfHandlingForm): 160 name = forms.CharField(label=_("Name"), required=False) 161 admin_state = forms.BooleanField( 162 label=_("Enable Admin State"), 163 required=False, 164 help_text=_("If checked, the router will be enabled.")) 165 mode = forms.ThemableChoiceField(label=_("Router Type")) 166 ha = forms.BooleanField(label=_("High Availability Mode"), required=False) 167 168 redirect_url = reverse_lazy('horizon:project:routers:index') 169 170 def __init__(self, request, *args, **kwargs): 171 super().__init__(request, *args, **kwargs) 172 self.dvr_allowed = api.neutron.get_feature_permission(self.request, 173 "dvr", "update") 174 if not self.dvr_allowed: 175 del self.fields['mode'] 176 elif self.initial.get('mode') == 'distributed': 177 # Neutron supports only changing from centralized to 178 # distributed now. 179 mode_choices = [('distributed', _('Distributed'))] 180 self.fields['mode'].widget = forms.TextInput(attrs={'readonly': 181 'readonly'}) 182 self.fields['mode'].choices = mode_choices 183 else: 184 mode_choices = [('centralized', _('Centralized')), 185 ('distributed', _('Distributed'))] 186 self.fields['mode'].choices = mode_choices 187 188 # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables 189 # PUT operation. It will be fixed in Kilo cycle. 190 # self.ha_allowed = api.neutron.get_feature_permission( 191 # self.request, "l3-ha", "update") 192 self.ha_allowed = False 193 if not self.ha_allowed: 194 del self.fields['ha'] 195 196 def handle(self, request, data): 197 try: 198 params = {'admin_state_up': data['admin_state'], 199 'name': data['name']} 200 if self.dvr_allowed: 201 params['distributed'] = (data['mode'] == 'distributed') 202 if self.ha_allowed: 203 params['ha'] = data['ha'] 204 router = api.neutron.router_update(request, 205 self.initial['router_id'], 206 **params) 207 msg = _('Router %s was successfully updated.') % router.name_or_id 208 messages.success(request, msg) 209 return router 210 except Exception as exc: 211 LOG.info('Failed to update router %(id)s: %(exc)s', 212 {'id': self.initial['router_id'], 'exc': exc}) 213 name_or_id = data['name'] or self.initial['router_id'] 214 msg = _('Failed to update router %s') % name_or_id 215 exceptions.handle(request, msg, redirect=self.redirect_url) 216