class AddRouterRoute(forms.SelfHandlingForm): destination = forms.IPField(label=_("Destination CIDR"), mask=True) nexthop = forms.IPField(label=_("Next Hop")) failure_url = 'horizon:project:routers:detail' def handle(self, request, data, **kwargs): router_id = self.initial['router_id'] try: route = {'nexthop': data['nexthop'], 'destination': data['destination']} api.router_static_route_add(request, router_id, route) msg = _('Static route added') messages.success(request, msg) return True except neutron_exc.BadRequest as e: LOG.info('Invalid format for routes %(route)s: %(exc)s', {'route': route, 'exc': e}) msg = _('Invalid format for routes: %s') % e redirect = reverse(self.failure_url, args=[router_id]) exceptions.handle(request, msg, redirect=redirect) except Exception as e: LOG.info('Failed to add route: %s', e) msg = _('Failed to add route: %s') % e redirect = reverse(self.failure_url, args=[router_id]) exceptions.handle(request, msg, redirect=redirect)
class UpdateSubnetInfoAction(CreateSubnetInfoAction): use_required_attribute = False address_source = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) subnetpool = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) prefixlen = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) cidr = forms.IPField( label=_("Network Address"), required=False, initial="", widget=forms.TextInput(attrs={'readonly': 'readonly'}), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) # NOTE(amotoki): When 'disabled' attribute is set for the ChoiceField # and ValidationError is raised for POST request, the initial value of # the ip_version ChoiceField is not set in the re-displayed form # As a result, 'IPv4' is displayed even when IPv6 is used if # ValidationError is detected. In addition 'required=True' check complains # when re-POST since the value of the ChoiceField is not set. # Thus now I use HiddenInput for the ip_version ChoiceField as a work # around. ip_version = forms.ThemableChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.HiddenInput(), label=_("IP Version")) gateway_ip = forms.IPField( label=_("Gateway IP"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'gateway_ip', 'data-source-manual': _("Gateway IP") }), initial="", error_messages={ 'required': _('Specify IP address of gateway or ' 'check "Disable Gateway" checkbox.') }, help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) class Meta(object): name = _("Subnet") help_text = _('Update a subnet associated with the network. ' 'Advanced configuration are available at ' '"Subnet Details" tab.') def clean(self): cleaned_data = workflows.Action.clean(self) self._check_subnet_data(cleaned_data) return cleaned_data
class CreateExternalRouteParamForm(forms.SelfHandlingForm): destination = forms.IPField(label=_("Destination"), initial="", help_text=_("(e.g. 192.168.0.0/24," "2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) next_hop = forms.IPField(label=_("Next hop")) def handle(self, request, context): return ExternalRouteParam(context)
def test_validate_IPs(self): GOOD_IPS_V4 = ("0.0.0.0", "10.144.11.107", "169.144.11.107", "172.100.11.107", "255.255.255.255", "0.1.2.3") GOOD_IPS_V6 = ("", "::ffff:0:0", "2001:0db8::1428:57ab", "FEC0::", "fe80::204:61ff:254.157.241.86", "fe80::204:61ff:254.157.241.86", "2001:0DB8::CD30:0:0:0:0") BAD_IPS_V4 = ("1111:2222:3333:4444:::", "::2222:3333:4444:5555:6666:7777:8888:", ":1111:2222:3333:4444::6666:1.2.3.4", "1111:2222::4444:5555:6666::8888", "1111:2222::4444:5555:6666:8888/", "1111:2222::4444:5555:6666::8888/130", "127.0.0.1/", "127.0.0.1/33", "127.0.0.1/-1") BAD_IPS_V6 = ("1111:2222:3333:4444:::", "::2222:3333:4444:5555:6666:7777:8888:", ":1111:2222:3333:4444::6666:1.2.3.4", "1111:2222::4444:5555:6666::8888", "1111:2222::4444:5555:6666:8888/", "1111:2222::4444:5555:6666::8888/130") ipv4 = forms.IPField(required=True, version=forms.IPv4) ipv6 = forms.IPField(required=False, version=forms.IPv6) ipmixed = forms.IPField(required=False, version=forms.IPv4 | forms.IPv6) for ip_addr in GOOD_IPS_V4: self.assertIsNone(ipv4.validate(ip_addr)) self.assertIsNone(ipmixed.validate(ip_addr)) for ip_addr in GOOD_IPS_V6: self.assertIsNone(ipv6.validate(ip_addr)) self.assertIsNone(ipmixed.validate(ip_addr)) for ip_addr in BAD_IPS_V4: self.assertRaises(ValidationError, ipv4.validate, ip_addr) self.assertRaises(ValidationError, ipmixed.validate, ip_addr) for ip_addr in BAD_IPS_V6: self.assertRaises(ValidationError, ipv6.validate, ip_addr) self.assertRaises(ValidationError, ipmixed.validate, ip_addr) self.assertRaises(ValidationError, ipv4.validate, "") # required=True iprange = forms.IPField(required=False, mask=True, mask_range_from=10, version=forms.IPv4 | forms.IPv6) self.assertRaises(ValidationError, iprange.validate, "fe80::204:61ff:254.157.241.86/6") self.assertRaises(ValidationError, iprange.validate, "169.144.11.107/8") self.assertIsNone(iprange.validate("fe80::204:61ff:254.157.241.86/36")) self.assertIsNone(iprange.validate("169.144.11.107/18"))
class CreateRoute(forms.SelfHandlingForm): host_id = forms.CharField(widget=forms.HiddenInput()) interface_id = forms.CharField(widget=forms.HiddenInput()) success_url = 'horizon:admin:inventory:viewinterface' failure_url = 'horizon:admin:inventory:viewinterface' network = forms.IPField(label=_("Network Address"), required=True, initial="", help_text=_("IP network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48"), version=forms.IPv4 | forms.IPv6, mask=True) gateway = forms.IPField(label=_("Gateway Address"), required=True, initial="", help_text=_( "Gateway IP address " "(e.g. 192.168.0.1/24, 2001:DB8::1/48"), version=forms.IPv4 | forms.IPv6, mask=False) metric = forms.IntegerField(label=_("Route Metric"), initial="1", required=True) def handle(self, request, data): try: ip_network = netaddr.IPNetwork(data['network']) body = { 'interface_uuid': data['interface_id'], 'network': str(ip_network.ip), 'prefix': ip_network.prefixlen, 'gateway': data['gateway'], 'metric': data['metric'] } route = stx_api.sysinv.route_create(request, **body) msg = (_('Route to %(network)s/%(prefix)s via %(gateway)s was ' 'successfully created') % body) messages.success(request, msg) return route except Exception as e: # Allow REST API error message to appear on UI messages.error(request, e) LOG.error(e) # Redirect to failure page redirect = reverse(self.failure_url, args=(data['host_id'], data['interface_id'])) return shortcuts.redirect(redirect)
class AddRuleAction(workflows.Action): name = forms.CharField( max_length=80, label=_("Name"), required=False) description = forms.CharField( max_length=80, label=_("Description"), required=False) protocol = forms.ChoiceField( label=_("Protocol"), choices=[('tcp', _('TCP')), ('udp', _('UDP')), ('icmp', _('ICMP')), ('any', _('ANY'))],) action = forms.ChoiceField( label=_("Action"), choices=[('allow', _('ALLOW')), ('deny', _('DENY'))],) source_ip_address = forms.IPField( label=_("Source IP Address/Subnet"), version=forms.IPv4 | forms.IPv6, required=False, mask=True) destination_ip_address = forms.IPField( label=_("Destination IP Address/Subnet"), version=forms.IPv4 | forms.IPv6, required=False, mask=True) source_port = forms.CharField( max_length=80, label=_("Source Port/Port Range"), required=False, validators=[port_validator]) destination_port = forms.CharField( max_length=80, label=_("Destination Port/Port Range"), required=False, validators=[port_validator]) shared = forms.BooleanField( label=_("Shared"), initial=False, required=False) enabled = forms.BooleanField( label=_("Enabled"), initial=True, required=False) def __init__(self, request, *args, **kwargs): super(AddRuleAction, self).__init__(request, *args, **kwargs) class Meta(object): name = _("AddRule") permissions = ('openstack.services.network',) help_text = _("Create a firewall rule.\n\n" "Protocol and action must be specified. " "Other fields are optional.")
class UpdateSubnetInfoAction(CreateSubnetInfoAction): cidr = forms.IPField( label=_("Network Address"), required=False, initial="", widget=forms.TextInput(attrs={'readonly': 'readonly'}), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) # NOTE(amotoki): When 'disabled' attribute is set for the ChoiceField # and ValidationError is raised for POST request, the initial value of # the ip_version ChoiceField is not set in the re-displayed form # As a result, 'IPv4' is displayed even when IPv6 is used if # ValidationError is detected. In addition 'required=True' check complains # when re-POST since the value of the ChoiceField is not set. # Thus now I use HiddenInput for the ip_version ChoiceField as a work # around. ip_version = forms.ChoiceField( choices=[(4, 'IPv4'), (6, 'IPv6')], #widget=forms.Select( # attrs={'disabled': 'disabled'}), widget=forms.HiddenInput(), label=_("IP Version")) gateway_ip = forms.IPField( label=_("Gateway IP (optional)"), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254). " "You need to specify an explicit address " "to set the gateway. " "If you want to use no gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), initial=False, required=False) class Meta: name = _("Subnet") help_text = _('You can update a subnet associated with the ' 'network. Advanced configuration are available ' 'at "Subnet Detail" tab.') def clean(self): cleaned_data = workflows.Action.clean(self) self._check_subnet_data(cleaned_data, is_create=False) return cleaned_data
def test_validate_mixed_cidr(self): GOOD_CIDRS = ( "::ffff:0:0/56", "2001:0db8::1428:57ab/17", "FEC0::/10", "fe80::204:61ff:254.157.241.86/4", "fe80::204:61ff:254.157.241.86/0", "2001:0DB8::CD30:0:0:0:0/60", "0.0.0.0/16", "10.144.11.107/4", "255.255.255.255/0", "0.1.2.3/16", # short form "128.0/16", "10/4") BAD_CIDRS = ("1111:2222:3333:4444::://", "::2222:3333:4444:5555:6666:7777:8888:", ":1111:2222:3333:4444::6666:1.2.3.4/1/1", "1111:2222::4444:5555:6666::8888\\2", "1111:2222::4444:5555:6666:8888/", "1111:2222::4444:5555:6666::8888/130", "127.0.0.1/", "127.0.0.1/33", "127.0.0.1/-1") ip = forms.IPField(mask=True, version=forms.IPv4 | forms.IPv6) for cidr in GOOD_CIDRS: self.assertIsNone(ip.validate(cidr)) for cidr in BAD_CIDRS: self.assertRaises(ValidationError, ip.validate, cidr)
def test_validate_ipv4_cidr(self): GOOD_CIDRS = ( "192.168.1.1/16", "192.0.0.1/17", "0.0.0.0/16", "10.144.11.107/4", "255.255.255.255/0", "0.1.2.3/16", "0.0.0.0/32", # short form "128.0/16", "128/4") BAD_CIDRS = ( "255.255.255.256\\", "256.255.255.255$", "1.2.3.4.5/41", "0.0.0.0/99", "127.0.0.1/", "127.0.0.1/33", "127.0.0.1/-1", "127.0.0.1/100", # some valid IPv6 addresses "fe80::204:61ff:254.157.241.86/4", "fe80::204:61ff:254.157.241.86/0", "2001:0DB8::CD30:0:0:0:0/60", "2001:0DB8::CD30:0/90") ip = forms.IPField(mask=True, version=forms.IPv4) for cidr in GOOD_CIDRS: self.assertIsNone(ip.validate(cidr)) for cidr in BAD_CIDRS: self.assertRaises(ValidationError, ip.validate, cidr)
def test_validate_ipv6_cidr(self): GOOD_CIDRS = ("::ffff:0:0/56", "2001:0db8::1428:57ab/17", "FEC0::/10", "fe80::204:61ff:254.157.241.86/4", "fe80::204:61ff:254.157.241.86/0", "2001:0DB8::CD30:0:0:0:0/60", "2001:0DB8::CD30:0/90", "::1/128") BAD_CIDRS = ("1111:2222:3333:4444:::/", "::2222:3333:4444:5555:6666:7777:8888:\\", ":1111:2222:3333:4444::6666:1.2.3.4/1000", "1111:2222::4444:5555:6666::8888@", "1111:2222::4444:5555:6666:8888/", "::ffff:0:0/129", "1.2.3.4:1111:2222::5555//22", "fe80::204:61ff:254.157.241.86/200", # some valid IPv4 addresses "10.144.11.107/4", "255.255.255.255/0", "0.1.2.3/16") ip = forms.IPField(mask=True, version=forms.IPv6) for cidr in GOOD_CIDRS: self.assertIsNone(ip.validate(cidr)) for cidr in BAD_CIDRS: self.assertRaises(ValidationError, ip.validate, cidr)
class UpdateAddressPool(CreateAddressPool): id = forms.CharField(widget=forms.HiddenInput) network = forms.IPField( label=_("Network Address"), required=True, version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput(attrs={'readonly': 'readonly'})) def handle(self, request, data): try: updates = {} if 'name' in data: updates['name'] = data['name'] if 'order' in data: updates['order'] = data['order'] if data.get('ranges'): updates['ranges'] = data['ranges'] pool = api.sysinv.address_pool_update(request, data['id'], **updates) msg = (_('Address pool was successfully updated')) messages.success(request, msg) return pool except Exception as e: # Allow REST API error message to appear on UI messages.error(request, e) LOG.error(e) # Redirect to failure page redirect = reverse(self.failure_url) return shortcuts.redirect(redirect)
class UpdateNATPoolForm(BaseUpdateForm): name = forms.CharField(max_length=80, label=_("Name")) description = forms.CharField(max_length=80, label=_("Description"), required=False) ip_version = forms.ChoiceField( choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'ipversion', }), label=_("IP Version")) ip_pool = forms.IPField(label=_("CIDR"), initial="", required=True, help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24," "2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) external_segment_id = forms.ChoiceField(label=_("External Segment"), required=True) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) def __init__(self, request, *args, **kwargs): super(UpdateNATPoolForm, self).__init__(request, *args, **kwargs) nat_pool_id = self.initial['nat_pool_id'] ec_list = client.externalconnectivity_list( request, tenant_id=request.user.tenant_id) external_segments_options = [(ec.id, ec.name) for ec in ec_list] self.fields['external_segment_id'].choices = external_segments_options nat_pool = client.get_natpool(request, nat_pool_id) attributes = [ 'name', 'description', 'ip_version', 'ip_pool', 'external_segment_id' ] for attr in attributes: self.fields[attr].initial = str(nat_pool[attr]) self.fields['shared'].initial = nat_pool['shared'] def handle(self, request, context): url = reverse("horizon:project:network_policy:index") try: if context.get('name'): context['name'] = html.escape(context['name']) if context.get('description'): context['description'] = html.escape(context['description']) nat_pool_id = self.initial['nat_pool_id'] client.update_natpool(request, nat_pool_id, **context) msg = _("NAT Pool updated successfully!") LOG.debug(msg) messages.success(request, msg) return http.HttpResponseRedirect(url) except Exception as e: msg = _("Failed to update NAT Pool.%s") % \ (str(e)) LOG.error(msg) exceptions.handle(request, msg, redirect=url)
class CreateSubnetInfoAction(network_workflows.CreateSubnetInfoAction): with_subnet = forms.BooleanField(initial=True, required=False, widget=forms.HiddenInput()) cidr = forms.IPField( label=_("Network Address"), initial="", error_messages={'required': _("Specify network address")}, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-manual': _("Network Address"), }), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) class Meta(object): name = _("Subnet") help_text = _('Create a subnet associated with the network. ' 'Advanced configuration is available by clicking on the ' '"Subnet Details" tab.') def clean(self): cleaned_data = workflows.Action.clean(self) self._check_subnet_data(cleaned_data) return cleaned_data
class UpdateL3PolicyForm(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name")) description = forms.CharField(max_length=80, label=_("Description"), required=False) ip_version = forms.ChoiceField( choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'ipversion', }), label=_("IP Version")) ip_pool = forms.IPField(label=_("IP Pool"), initial="", help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24," "2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) subnet_prefix_length = forms.CharField( max_length=80, label=_("Subnet Prefix Length"), help_text=_("Between 2-30 for IP4" "and 2-127 for IP6."), ) def __init__(self, request, *args, **kwargs): super(UpdateL3PolicyForm, self).__init__(request, *args, **kwargs) def clean(self): cleaned_data = super(UpdateL3PolicyForm, self).clean() if self.is_valid(): ipversion = int(cleaned_data['ip_version']) subnet_prefix_length = int(cleaned_data['subnet_prefix_length']) msg = _("Subnet prefix out of range.") if ipversion == 4 and subnet_prefix_length not in range(2, 31): raise forms.ValidationError(msg) if ipversion == 6 and subnet_prefix_length not in range(2, 128): raise forms.ValidationError(msg) return cleaned_data def handle(self, request, context): url = reverse("horizon:project:network_policy:index") try: client.l3policy_create(request, **context) msg = _("L3 Policy Updated Successfully!") LOG.debug(msg) return http.HttpResponseRedirect(url) except Exception: msg = _("Failed to update L3 policy") LOG.error(msg) exceptions.handle(request, msg, redirect=shortcuts.redirect)
class CreateExternalSegmentParamForm(forms.SelfHandlingForm): external_segment = forms.ChoiceField(label=_("External Segment"), required=False) segment_ip = forms.IPField(label=_("External Segment IP"), initial="", required=False) def __init__(self, request, *args, **kwargs): super(CreateExternalSegmentParamForm, self).__init__(request, *args, **kwargs) ec_list = client.externalconnectivity_list( request, tenant_id=request.user.tenant_id) external_segments_options = [(ec.id, ec.name) for ec in ec_list] self.fields['external_segment'].choices = external_segments_options def handle(self, request, context): return ExternalSegmentParam(context)
class AdminFloatingIpAllocate(forms.SelfHandlingForm): pool = forms.ThemableChoiceField(label=_("Pool")) tenant = forms.ThemableChoiceField(label=_("Project")) floating_ip_address = forms.IPField( label=_("Floating IP Address (optional)"), required=False, initial="", help_text=_("The IP address of the new floating IP (e.g. 202.2.3.4). " "You need to specify an explicit address which is under " "the public network CIDR (e.g. 202.2.3.0/24)."), mask=False) description = forms.CharField(max_length=255, label=_("Description"), required=False) def __init__(self, *args, **kwargs): super(AdminFloatingIpAllocate, self).__init__(*args, **kwargs) floating_pool_list = kwargs.get('initial', {}).get('pool_list', []) self.fields['pool'].choices = floating_pool_list tenant_list = kwargs.get('initial', {}).get('tenant_list', []) self.fields['tenant'].choices = tenant_list def handle(self, request, data): try: # Admin ignore quota param = {} if data['floating_ip_address']: param['floating_ip_address'] = data['floating_ip_address'] if data['description']: param['description'] = data['description'] subnet = api.neutron.subnet_get(request, data['pool']) param['subnet_id'] = subnet.id fip = api.neutron.tenant_floating_ip_allocate( request, pool=subnet.network_id, tenant_id=data['tenant'], **param) messages.success( request, _('Allocated floating IP %(ip)s.') % {"ip": fip.ip}) return fip except Exception: redirect = reverse('horizon:admin:floating_ips:index') msg = _('Unable to allocate floating IP.') exceptions.handle(request, msg, redirect=redirect)
class AddAllowedAddressPairForm(forms.SelfHandlingForm): ip = forms.IPField(label=_("IP Address or CIDR"), help_text=_("A single IP Address or CIDR"), version=forms.IPv4 | forms.IPv6, mask=True) mac = forms.CharField(label=_("MAC Address"), help_text=_("A valid MAC Address"), validators=[validate_mac], required=False) failure_url = 'horizon:network:networks2:ports:detail' def clean(self): cleaned_data = super(AddAllowedAddressPairForm, self).clean() if '/' not in self.data['ip']: cleaned_data['ip'] = self.data['ip'] return cleaned_data def handle(self, request, data): port_id = self.initial['port_id'] try: port = api.neutron.port_get(request, port_id) current = port.get('allowed_address_pairs', []) current = [pair.to_dict() for pair in current] pair = {'ip_address': data['ip']} if data['mac']: pair['mac_address'] = data['mac'] current.append(pair) port = api.neutron.port_update(request, port_id, allowed_address_pairs=current) msg = _('Port %s was successfully updated.') % port_id messages.success(request, msg) return port except Exception as e: LOG.error('Failed to update port %(port_id)s: %(reason)s', { 'port_id': port_id, 'reason': e }) msg = _('Failed to update port "%s".') % port_id args = (self.initial.get('port_id'), ) redirect = reverse(self.failure_url, args=args) exceptions.handle(request, msg, redirect=redirect) return False
class UpdateSubnetInfoAction(CreateSubnetInfoAction): address_source = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) subnetpool = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) prefixlen = forms.ThemableChoiceField(widget=forms.HiddenInput(), required=False) cidr = forms.IPField( label=_("Network Address"), required=False, initial="", widget=forms.TextInput(attrs={'readonly': 'readonly'}), help_text=_("Network address in CIDR format " "(e.g. IPv4 address mode 192.168.0.0/24, " "IPv6 address mode 2001:DB8::/48, " "2001:DB8::/64). Note: IPv6 subnet " "prefix is required to be /64 " "when DHCP is enabled."), version=forms.IPv4 | forms.IPv6, mask=True) # NOTE(amotoki): When 'disabled' attribute is set for the ChoiceField # and ValidationError is raised for POST request, the initial value of # the ip_version ChoiceField is not set in the re-displayed form # As a result, 'IPv4' is displayed even when IPv6 is used if # ValidationError is detected. In addition 'required=True' check complains # when re-POST since the value of the ChoiceField is not set. # Thus now I use HiddenInput for the ip_version ChoiceField as a work # around. ip_version = forms.ThemableChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.HiddenInput(), label=_("IP Version")) class Meta(object): name = _("Subnet") help_text = _('Update a subnet associated with the network. ' 'Advanced configuration are available at ' '"Subnet Details" tab.') def clean(self): cleaned_data = workflows.Action.clean(self) self._check_subnet_data(cleaned_data, is_create=False) return cleaned_data
class SetGatewayForm(project_forms.SetGatewayForm): network_id = forms.ChoiceField(label=_("External Network")) ip_address = forms.IPField( label=_("IP Address (optional)"), required=False, initial="", help_text=_("IP address of gateway interface (e.g. 192.168.0.254). " "Specify an explicit address to use when creating the " "gateway interface. If one is not specified an address " "will be allocated from the external subnet."), version=forms.IPv4 | forms.IPv6, mask=False) router_name = forms.CharField( label=_("Router Name"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) router_id = forms.CharField( label=_("Router ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) enable_snat = forms.BooleanField(label=_("Enable SNAT"), initial=True, required=False) failure_url = 'horizon:admin:routers:index' def handle(self, request, data): try: ip_address = data.get('ip_address') or None enable_snat = data.get('enable_snat', True) api.neutron.router_add_gateway(request, data['router_id'], data['network_id'], ip_address=ip_address, enable_snat=enable_snat) msg = _('Gateway interface is added') LOG.debug(msg) messages.success(request, msg) return True except Exception as e: msg = _('Failed to set gateway %s') % e LOG.info(msg) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class AddComputeHostForm(forms.SelfHandlingForm): compute_server = forms.IPField(label=_("Add Compute IP Addr")) compute_user = forms.CharField( label=_("Compute User"), initial="root", widget=forms.TextInput(attrs={'readonly': 'readonly'})) compute_root_pw = forms.CharField( label=_("password"), widget=forms.PasswordInput(render_value=False)) def clean(self): cleaned_data = super(AddComputeHostForm, self).clean() return cleaned_data def handle(self, request, data): try: compute = api.device.add_compute_node(request, data) messages.success(request, _('Compute node add successfully!')) return True except Exception: msg = _('Compute node add failed!') messages.error(request, msg) return HttpResponseRedirect('/dashboard/admin/aggregates')
class CreateAddress(forms.SelfHandlingForm): host_id = forms.CharField(widget=forms.HiddenInput()) interface_id = forms.CharField(widget=forms.HiddenInput()) success_url = 'horizon:admin:inventory:viewinterface' failure_url = 'horizon:admin:inventory:viewinterface' ip_address = forms.IPField(label=_("IP Address"), required=True, initial="", help_text=_( "IP interface address in CIDR format " "(e.g. 192.168.0.2/24, 2001:DB8::/48"), version=forms.IPv4 | forms.IPv6, mask=True) def handle(self, request, data): try: ip_address = netaddr.IPNetwork(data['ip_address']) body = { 'interface_uuid': data['interface_id'], 'address': str(ip_address.ip), 'prefix': ip_address.prefixlen } address = stx_api.sysinv.address_create(request, **body) msg = (_('Address %(address)s/%(prefix)s was ' 'successfully created') % body) messages.success(request, msg) return address except Exception as e: # Allow REST API error message to appear on UI messages.error(request, e) LOG.error(e) # Redirect to failure page redirect = reverse(self.failure_url, args=(data['host_id'], data['interface_id'])) return shortcuts.redirect(redirect)
class CreateSubnetInfoAction(workflows.Action): with_subnet = forms.BooleanField(label=_("Create Subnet"), initial=True, required=False) subnet_name = forms.CharField(max_length=255, label=_("Subnet Name"), required=False) cidr = forms.IPField(label=_("Network Address"), required=False, initial="", help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) ip_version = forms.ChoiceField( choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'ipversion', }), label=_("IP Version")) gateway_ip = forms.IPField( label=_("Gateway IP"), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address " "(e.g. 192.168.0.1 for 192.168.0.0/24, " "2001:DB8::1 for 2001:DB8::/48). " "If you use the default, leave blank. " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), initial=False, required=False) msg = _('Specify "Network Address" or ' 'clear "Create Subnet" checkbox.') class Meta: name = _("Subnet") help_text = _('Create a subnet associated with the new network, ' 'in which case "Network Address" must be specified. ' 'If you wish to create a network without a subnet, ' 'uncheck the "Create Subnet" checkbox.') def __init__(self, request, context, *args, **kwargs): super(CreateSubnetInfoAction, self).__init__(request, context, *args, **kwargs) if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get( 'enable_ipv6', True): self.fields['ip_version'].widget = forms.HiddenInput() self.fields['ip_version'].initial = 4 def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') ip_version = int(cleaned_data.get('ip_version')) gateway_ip = cleaned_data.get('gateway_ip') no_gateway = cleaned_data.get('no_gateway') if not cidr: raise forms.ValidationError(self.msg) if cidr: subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if (ip_version == 4 and subnet.prefixlen == 32) or \ (ip_version == 6 and subnet.prefixlen == 128): msg = _("The subnet in the Network Address is " "too small (/%s).") % subnet.prefixlen raise forms.ValidationError(msg) if not no_gateway and gateway_ip: if netaddr.IPAddress(gateway_ip).version is not ip_version: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) if not is_create and not no_gateway and not gateway_ip: msg = _('Specify IP address of gateway or ' 'check "Disable Gateway".') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateSubnetInfoAction, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField( label=_('Rule'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'rule_menu' })) # "direction" field is enabled only when custom mode. # It is because most common rules in local_settings.py is meaningful # when its direction is 'ingress'. direction = forms.ChoiceField( label=_('Direction'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Direction'), 'data-rule_menu-udp': _('Direction'), 'data-rule_menu-icmp': _('Direction'), 'data-rule_menu-custom': _('Direction'), 'data-rule_menu-all_tcp': _('Direction'), 'data-rule_menu-all_udp': _('Direction'), 'data-rule_menu-all_icmp': _('Direction'), })) ip_protocol = forms.IntegerField( label=_('IP Protocol'), required=False, help_text=_("Enter an integer value between 0 and 255 " "(or -1 which means wildcard)."), validators=[utils_validators.validate_ip_protocol], widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-custom': _('IP Protocol') })) port_or_range = forms.ChoiceField( label=_('Open Port'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.Select( attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Open Port'), 'data-rule_menu-udp': _('Open Port') })) port = forms.IntegerField( label=_("Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-port': _('Port') }), validators=[utils_validators.validate_port_range]) from_port = forms.IntegerField( label=_("From Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('From Port') }), validators=[utils_validators.validate_port_range]) to_port = forms.IntegerField( label=_("To Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('To Port') }), validators=[utils_validators.validate_port_range]) icmp_type = forms.IntegerField( label=_("Type"), required=False, help_text=_("Enter a value for ICMP type " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Type') }), validators=[utils_validators.validate_port_range]) icmp_code = forms.IntegerField( label=_("Code"), required=False, help_text=_("Enter a value for ICMP code " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Code') }), validators=[utils_validators.validate_port_range]) remote = forms.ChoiceField(label=_('Remote'), choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))], help_text=_('To specify an allowed IP ' 'range, select "CIDR". To ' 'allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'remote' })) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-cidr': _('CIDR') })) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-sg': _('Security ' 'Group') })) # When cidr is used ethertype is determined from IP version of cidr. # When source group, ethertype needs to be specified explicitly. ethertype = forms.ChoiceField(label=_('Ether Type'), required=False, choices=[('IPv4', _('IPv4')), ('IPv6', _('IPv6'))], widget=forms.Select( attrs={ 'class': 'switched', 'data-slug': 'ethertype', 'data-switch-on': 'remote', 'data-remote-sg': _('Ether Type') })) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # remote group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices backend = api.network.security_group_backend(self.request) rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', []) common_rules = [(k, _(rules_dict[k]['name'])) for k in rules_dict if rules_dict[k].get('backend', backend) == backend] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule'))] if backend == 'neutron': custom_rules.append(('custom', _('Other Protocol'))) self.fields['rule_menu'].choices = custom_rules + common_rules self.rules = rules_dict if backend == 'neutron': self.fields['direction'].choices = [('ingress', _('Ingress')), ('egress', _('Egress'))] else: # direction and ethertype are not supported in Nova secgroup. self.fields['direction'].widget = forms.HiddenInput() self.fields['ethertype'].widget = forms.HiddenInput() # ip_protocol field is to specify arbitrary protocol number # and it is available only for neutron security group. self.fields['ip_protocol'].widget = forms.HiddenInput() def clean(self): cleaned_data = super(AddRule, self).clean() def update_cleaned_data(key, value): cleaned_data[key] = value self.errors.pop(key, None) rule_menu = cleaned_data.get('rule_menu') port_or_range = cleaned_data.get("port_or_range") remote = cleaned_data.get("remote") icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) if rule_menu == 'icmp': update_cleaned_data('ip_protocol', rule_menu) if icmp_type is None: msg = _('The ICMP type is invalid.') raise ValidationError(msg) if icmp_code is None: msg = _('The ICMP code is invalid.') raise ValidationError(msg) if icmp_type not in range(-1, 256): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if icmp_code not in range(-1, 256): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) update_cleaned_data('from_port', icmp_type) update_cleaned_data('to_port', icmp_code) update_cleaned_data('port', None) elif rule_menu == 'tcp' or rule_menu == 'udp': update_cleaned_data('ip_protocol', rule_menu) update_cleaned_data('icmp_code', None) update_cleaned_data('icmp_type', None) if port_or_range == "port": update_cleaned_data('from_port', port) update_cleaned_data('to_port', port) if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: update_cleaned_data('port', None) if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) elif rule_menu == 'custom': pass else: cleaned_data['ip_protocol'] = self.rules[rule_menu]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[rule_menu]['from_port']) cleaned_data['to_port'] = int(self.rules[rule_menu]['to_port']) if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']: direction = self.rules[rule_menu].get('direction') cleaned_data['direction'] = direction # NOTE(amotoki): There are two cases where cleaned_data['direction'] # is empty: (1) Nova Security Group is used. Since "direction" is # HiddenInput, direction field exists but its value is ''. # (2) Template except all_* is used. In this case, the default value # is None. To make sure 'direction' field has 'ingress' or 'egress', # fill this field here if it is not specified. if not cleaned_data['direction']: cleaned_data['direction'] = 'ingress' if remote == "cidr": update_cleaned_data('security_group', None) else: update_cleaned_data('cidr', None) # If cleaned_data does not contain cidr, cidr is already marked # as invalid, so skip the further validation for cidr. # In addition cleaned_data['cidr'] is None means source_group is used. if 'cidr' in cleaned_data and cleaned_data['cidr'] is not None: cidr = cleaned_data['cidr'] if not cidr: msg = _('CIDR must be specified.') self._errors['cidr'] = self.error_class([msg]) else: # If cidr is specified, ethertype is determined from IP address # version. It is used only when Neutron is enabled. ip_ver = netaddr.IPNetwork(cidr).version cleaned_data['ethertype'] = 'IPv6' if ip_ver == 6 else 'IPv4' return cleaned_data def handle(self, request, data): try: rule = api.network.security_group_rule_create( request, filters.get_int_or_uuid(data['id']), data['direction'], data['ethertype'], data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group']) messages.success(request, _('Successfully added rule: %s') % unicode(rule)) return rule except Exception: redirect = reverse( "horizon:project:access_and_security:" "security_groups:detail", args=[data['id']]) exceptions.handle(request, _('Unable to add rule to security group.'), redirect=redirect)
class AddVipAction(workflows.Action): name = forms.CharField(max_length=80, label=_("Name")) description = forms.CharField(initial="", required=False, max_length=80, label=_("Description")) floatip_address = forms.ChoiceField( label=_("VIP Address from Floating IPs"), widget=forms.Select(attrs={'disabled': 'disabled'}), required=False) other_address = forms.IPField(required=False, initial="", version=forms.IPv4, mask=False) protocol_port = forms.IntegerField( label=_("Protocol Port"), min_value=1, help_text=_("Enter an integer value " "between 1 and 65535."), validators=[validators.validate_port_range]) protocol = forms.ChoiceField(label=_("Protocol")) session_persistence = forms.ChoiceField( required=False, initial={}, label=_("Session Persistence"), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'persistence' })) cookie_name = forms.CharField( initial="", required=False, max_length=80, label=_("Cookie Name"), help_text=_("Required for APP_COOKIE persistence;" " Ignored otherwise."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'persistence', 'data-persistence-app_cookie': 'APP_COOKIE', })) connection_limit = forms.IntegerField( required=False, min_value=-1, label=_("Connection Limit"), help_text=_("Maximum number of connections allowed " "for the VIP or '-1' if the limit is not set")) admin_state_up = forms.BooleanField(label=_("Admin State"), initial=True, required=False) def __init__(self, request, *args, **kwargs): super(AddVipAction, self).__init__(request, *args, **kwargs) self.fields['other_address'].label = _("Specify a free IP address" " from %s") % args[0]['subnet'] protocol_choices = [('', _("Select a Protocol"))] [protocol_choices.append((p, p)) for p in AVAILABLE_PROTOCOLS] self.fields['protocol'].choices = protocol_choices session_persistence_choices = [('', _("No Session Persistence"))] for mode in ('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'): session_persistence_choices.append((mode.lower(), mode)) self.fields[ 'session_persistence'].choices = session_persistence_choices floatip_address_choices = [('', _("Currently Not Supported"))] self.fields['floatip_address'].choices = floatip_address_choices def clean(self): cleaned_data = super(AddVipAction, self).clean() persistence = cleaned_data.get('session_persistence') if persistence: cleaned_data['session_persistence'] = persistence.upper() if (cleaned_data.get('session_persistence') == 'APP_COOKIE' and not cleaned_data.get('cookie_name')): msg = _('Cookie name is required for APP_COOKIE persistence.') self._errors['cookie_name'] = self.error_class([msg]) return cleaned_data class Meta: name = _("Specify VIP") permissions = ('openstack.services.network', ) help_text = _("Create a VIP for this pool. " "Assign a name and description for the VIP. " "Specify an IP address and port for the VIP. " "Choose the protocol and session persistence " "method for the VIP." "Specify the max connections allowed. " "Admin State is UP (checked) by default.")
class AddRuleAction(workflows.Action): name = forms.CharField(max_length=80, label=_("Name"), required=False) description = forms.CharField(max_length=80, label=_("Description"), required=False) protocol = forms.ThemableChoiceField( label=_("Protocol"), choices=[('tcp', _('TCP')), ('udp', _('UDP')), ('icmp', _('ICMP')), ('any', _('ANY'))], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'protocol', })) action = forms.ThemableChoiceField( label=_("Action"), choices=[('allow', _('ALLOW')), ('deny', _('DENY')), ('reject', _('REJECT'))], ) source_ip_address = forms.IPField(label=_("Source IP Address/Subnet"), version=forms.IPv4 | forms.IPv6, required=False, mask=True) destination_ip_address = forms.IPField( label=_("Destination IP Address/Subnet"), version=forms.IPv4 | forms.IPv6, required=False, mask=True) source_port = forms.CharField( max_length=80, label=_("Source Port/Port Range"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'protocol', 'data-protocol-tcp': _("Source Port/Port Range"), 'data-protocol-udp': _("Source Port/Port Range"), }), required=False, validators=[port_validator]) destination_port = forms.CharField( max_length=80, label=_("Destination Port/Port Range"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'protocol', 'data-protocol-tcp': _("Destination Port/Port Range"), 'data-protocol-udp': _("Destination Port/Port Range"), }), required=False, validators=[port_validator]) ip_version = forms.ThemableChoiceField(label=_("IP Version"), required=False, choices=[('4', '4'), ('6', '6')]) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) enabled = forms.BooleanField(label=_("Enabled"), initial=True, required=False) def __init__(self, request, *args, **kwargs): super(AddRuleAction, self).__init__(request, *args, **kwargs) def _check_ip_addr_and_ip_version(self, cleaned_data): ip_version = int(str(cleaned_data.get('ip_version'))) src_ip = cleaned_data.get('source_ip_address') dst_ip = cleaned_data.get('destination_ip_address') msg = _('Source/Destination Network Address and IP version ' 'are inconsistent. Please make them consistent.') if (src_ip and netaddr.IPNetwork(src_ip).version != ip_version): self._errors['ip_version'] = self.error_class([msg]) elif (dst_ip and netaddr.IPNetwork(dst_ip).version != ip_version): self._errors['ip_version'] = self.error_class([msg]) def clean(self): cleaned_data = super(AddRuleAction, self).clean() self._check_ip_addr_and_ip_version(cleaned_data) class Meta(object): name = _("Rule") permissions = ('openstack.services.network', ) help_text = _("Create a firewall rule.\n\n" "A Firewall rule is an association of the following " "attributes:\n\n" "<li>IP Addresses: The addresses from/to which the " "traffic filtration needs to be applied.</li>" "<li>IP Version: The type of IP packets (IP V4/V6) " "that needs to be filtered.</li>" "<li>Protocol: Type of packets (UDP, ICMP, TCP, Any) " "that needs to be checked.</li>" "<li>Action: Action is the type of filtration " "required, it can be Reject/Deny/Allow data " "packets.</li>\n" "The protocol and action fields are required, all " "others are optional.")
class UpdateIPSecSiteConnection(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ipsecsiteconnection_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(required=False, max_length=80, label=_("Description")) peer_address = forms.IPField( label=_("Peer gateway public IPv4/IPv6 Address or FQDN"), help_text=_("Peer gateway public IPv4/IPv6 address or FQDN for " "the VPN Connection"), version=forms.IPv4 | forms.IPv6, mask=False) peer_id = forms.IPField( label=_("Peer router identity for authentication (Peer ID)"), help_text=_("Peer router identity for authentication. " "Can be IPv4/IPv6 address, e-mail, key ID, or FQDN"), version=forms.IPv4 | forms.IPv6, mask=False) peer_cidrs = forms.MultiIPField(label=_("Remote peer subnet(s)"), help_text=_( "Remote peer subnet(s) address(es) " "with mask(s) in CIDR format " "separated with commas if needed " "(e.g. 20.1.0.0/24, 21.1.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) psk = forms.CharField(widget=forms.PasswordInput(render_value=True), max_length=80, label=_("Pre-Shared Key (PSK) string")) mtu = forms.IntegerField( min_value=68, required=False, label=_("Maximum Transmission Unit size for the connection"), help_text=_("Equal to or greater than 68 if the local subnet is IPv4. " "Equal to or greater than 1280 if the local subnet " "is IPv6.")) dpd_action = forms.ChoiceField(label=_("Dead peer detection actions"), required=False, choices=[('hold', _('hold')), ('clear', _('clear')), ('disabled', _('disabled')), ('restart', _('restart')), ('restart-by-peer', _('restart-by-peer'))]) dpd_interval = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection interval"), help_text=_("Valid integer lesser than the DPD timeout")) dpd_timeout = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection timeout"), help_text=_("Valid integer greater than the DPD interval")) initiator = forms.ChoiceField(label=_("Initiator state"), required=False, choices=[ ('bi-directional', _('bi-directional')), ('response-only', _('response-only')) ]) admin_state_up = forms.ChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State"), required=False) failure_url = 'horizon:project:vpn:index' def clean(self): cleaned_data = super(UpdateIPSecSiteConnection, self).clean() interval = cleaned_data.get('dpd_interval') timeout = cleaned_data.get('dpd_timeout') if not interval < timeout: msg = _("DPD Timeout must be greater than DPD Interval") self._errors['dpd_timeout'] = self.error_class([msg]) return cleaned_data def handle(self, request, context): context['admin_state_up'] = (context['admin_state_up'] == 'True') try: data = { 'ipsec_site_connection': { 'name': context['name'], 'description': context['description'], 'peer_address': context['peer_address'], 'peer_id': context['peer_id'], 'peer_cidrs': context['peer_cidrs'].replace(" ", "").split(","), 'psk': context['psk'], 'mtu': context['mtu'], 'dpd': { 'action': context['dpd_action'], 'interval': context['dpd_interval'], 'timeout': context['dpd_timeout'] }, 'initiator': context['initiator'], 'admin_state_up': context['admin_state_up'], } } ipsecsiteconnection = api.vpn.ipsecsiteconnection_update( request, context['ipsecsiteconnection_id'], **data) msg = (_('IPSec Site Connection %s was successfully updated.') % context['name']) LOG.debug(msg) messages.success(request, msg) return ipsecsiteconnection except Exception as e: msg = (_('Failed to update IPSec Site Connection %s') % context['name']) LOG.info('%(msg)s: %(exception)s', {'msg': msg, 'exception': e}) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class CreateNetworkForm(forms.SelfHandlingForm): network_name = forms.CharField(max_length=255, label=_("Network Name"), required=False) admin_state = forms.ChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State"), required=False, help_text=_("The state to start" " the network in.")) network_shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) with_subnet = forms.BooleanField(label=_("Create Subnet"), widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-slug': 'with_subnet', 'data-hide-on-checked': 'false' }), initial=False, required=False) subnet_name = forms.CharField( max_length=255, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', 'data-source-manual': _("Subnet Name"), }), label=_("Subnet Name"), required=False) cidr = forms.IPField(label=_("Network Address"), required=False, initial="", widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', 'data-source-manual': _("Network Address") }), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) gateway_ip = forms.IPField( label=_("Gateway IP"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', 'data-source-manual': _("Gateway IP") }), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address " "(e.g. 192.168.0.1 for 192.168.0.0/24, " "2001:DB8::1 for 2001:DB8::/48). " "If you use the default, leave blank. " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) enable_dhcp = forms.BooleanField( label=_("Enable DHCP"), widget=forms.CheckboxInput( attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', # 'data-with_subnet-True': _('Network Address'), 'data-source-manual': _("Enable DHCP"), }), initial=True, required=False) dns_nameservers = forms.CharField( widget=forms.widgets.Textarea( attrs={ 'rows': 4, 'class': 'switched', 'data-switch-on': 'with_subnet', # 'data-with_subnet-True': _('Network Address'), 'data-source-manual': _("Enable DHCP"), }), label=_("DNS Name Servers"), help_text=_("IP address list of DNS name servers for this subnet. " "One entry per line."), required=False) # network_type = forms.ChoiceField( # label=_("Provider Network Type"), # help_text=_("The physical mechanism by which the virtual " # "network is implemented."), # widget=forms.Select(attrs={ # 'class': 'switchable', # 'data-slug': 'network_type' # })) def __init__(self, request, *args, **kwargs): super(CreateNetworkForm, self).__init__(request, *args, **kwargs) # self.fields['network_type'].choices = PROVIDER_TYPES if not policy.check((("network", "create_network:shared"), ), request): self.fields['shared'].widget = forms.CheckboxInput( attrs={'disabled': True}) self.fields['shared'].help_text = _( 'Non admin users are not allowed to set shared option.') def _convert_ip_address(self, ip, field_name): try: return netaddr.IPAddress(ip) except (netaddr.AddrFormatError, ValueError): msg = (_('%(field_name)s: Invalid IP address (value=%(ip)s)') % { 'field_name': field_name, 'ip': ip }) raise forms.ValidationError(msg) def _convert_ip_network(self, network, field_name): try: return netaddr.IPNetwork(network) except (netaddr.AddrFormatError, ValueError): msg = ( _('%(field_name)s: Invalid IP address (value=%(network)s)') % { 'field_name': field_name, 'network': network }) raise forms.ValidationError(msg) def _check_dns_nameservers(self, dns_nameservers): for ns in dns_nameservers.split('\n'): ns = ns.strip() if not ns: continue self._convert_ip_address(ns, "dns_nameservers") def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') gateway_ip = cleaned_data.get('gateway_ip') self._check_dns_nameservers(cleaned_data.get('dns_nameservers')) if not cidr: msg = _( 'If you choose create subnet, you must specify network address' ) raise forms.ValidationError(msg) if cidr: ip_version = 4 subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if ip_version == 4 and subnet.prefixlen == 32: msg = _("The subnet in the Network Address is " "too small (/%s).") % subnet.prefixlen self._errors['cidr'] = self.error_class([msg]) if gateway_ip: if netaddr.IPAddress(gateway_ip).version is not 4: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateNetworkForm, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data def format_status_message(self, message): # name = self.context.get('net_name') or self.context.get('net_id', '') return message def _create_network(self, request, data): try: params = { 'name': data['network_name'], 'admin_state_up': (data['admin_state'] == 'True'), 'shared': data['network_shared'] } network = api.neutron.network_create(request, **params) # self.context['net_id'] = network.id messages.success( request, _('Network "%s" was successfully created.') % network.name_or_id) msg = (_('Network "%s" was successfully created.') % network.name_or_id) LOG.debug(msg) return network except Exception as e: msg = (_('Failed to create network "%(network)s": %(reason)s') % { "network": data['network_name'], "reason": e }) LOG.info(msg) redirect = self.get_failure_url() exceptions.handle(request, msg, redirect=redirect) return False def _setup_subnet_parameters(self, params, data, is_create=True): if 'cidr' in data and data['cidr']: params['cidr'] = data['cidr'] params['ip_version'] = 4 if data['gateway_ip']: params['gateway_ip'] = data['gateway_ip'] params['enable_dhcp'] = data['enable_dhcp'] if data['dns_nameservers']: nameservers = [ ns.strip() for ns in data['dns_nameservers'].split('\n') if ns.strip() ] params['dns_nameservers'] = nameservers def _create_subnet(self, request, data, network, no_redirect=False): network_id = network.id network_name = network.name try: params = {'network_id': network_id, 'name': data['subnet_name']} self._setup_subnet_parameters(params, data) subnet = api.neutron.subnet_create(request, **params) # self.context['subnet_id'] = subnet.id msg = _('Subnet "%s" was successfully created.') % data['cidr'] messages.success( request, _('Subnet "%s" was successfully created.') % data['cidr']) LOG.debug(msg) return subnet except Exception as e: msg = _('Failed to create subnet "%(sub)s" for network "%(net)s": ' ' %(reason)s') if no_redirect: redirect = None else: redirect = self.get_failure_url() exceptions.handle(request, msg % { "sub": data['cidr'], "net": network_name, "reason": e }, redirect=redirect) return False def _delete_network(self, request, network): """Delete the created network when subnet creation failed.""" try: api.neutron.network_delete(request, network.id) msg = _('Delete the created network "%s" ' 'due to subnet creation failure.') % network.name LOG.debug(msg) redirect = self.get_failure_url() messages.info(request, msg) raise exceptions.Http302(redirect) except Exception: msg = _('Failed to delete network "%s"') % network.name LOG.info(msg) redirect = self.get_failure_url() exceptions.handle(request, msg, redirect=redirect) def handle(self, request, data): network = self._create_network(request, data) if not network: return False # If we do not need to create a subnet, return here. if not data['with_subnet']: return True subnet = self._create_subnet(request, data, network, no_redirect=True) if subnet: return True else: self._delete_network(request, network) return False def get_success_url(self): return reverse("horizon:sks:network:index") def get_failure_url(self): return reverse("horizon:sks:network:index")
class AddMemberAction(workflows.Action): pool_id = forms.ChoiceField(label=_("Pool")) member_type = forms.ChoiceField( label=_("Member Source"), choices=[('server_list', _("Select from active instances")), ('member_address', _("Specify member IP address"))], required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'membertype' })) members = forms.MultipleChoiceField( label=_("Member(s)"), required=False, initial=["default"], widget=forms.SelectMultiple( attrs={ 'class': 'switched', 'data-switch-on': 'membertype', 'data-membertype-server_list': _("Member(s)"), }), help_text=_("Select members for this pool ")) address = forms.IPField( required=False, label=_("Member address"), help_text=_("Specify member IP address"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'membertype', 'data-membertype-member_address': _("Member address"), }), initial="", version=forms.IPv4 | forms.IPv6, mask=False) weight = forms.IntegerField( max_value=256, min_value=1, label=_("Weight"), required=False, help_text=_("Relative part of requests this pool member serves " "compared to others. \nThe same weight will be applied to " "all the selected members and can be modified later. " "Weight must be in the range 1 to 256.")) protocol_port = forms.IntegerField( label=_("Protocol Port"), min_value=1, help_text=_("Enter an integer value between 1 and 65535. " "The same port will be used for all the selected " "members and can be modified later."), validators=[validators.validate_port_range]) admin_state_up = forms.ChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State")) def __init__(self, request, *args, **kwargs): super(AddMemberAction, self).__init__(request, *args, **kwargs) pool_id_choices = [('', _("Select a Pool"))] try: tenant_id = self.request.user.tenant_id pools = api.lbaas.pool_list(request, tenant_id=tenant_id) except Exception: pools = [] exceptions.handle(request, _('Unable to retrieve pools list.')) pools = sorted(pools, key=lambda pool: pool.name) for p in pools: pool_id_choices.append((p.id, p.name)) self.fields['pool_id'].choices = pool_id_choices members_choices = [] try: servers, has_more = api.nova.server_list(request) except Exception: servers = [] exceptions.handle(request, _('Unable to retrieve instances list.')) if len(servers) == 0: self.fields['members'].label = _( "No servers available. To add a member, you " "need at least one running instance.") self.fields['pool_id'].required = False self.fields['protocol_port'].required = False return for m in servers: members_choices.append((m.id, m.name)) self.fields['members'].choices = sorted(members_choices, key=lambda member: member[1]) def clean(self): cleaned_data = super(AddMemberAction, self).clean() if (cleaned_data.get('member_type') == 'server_list' and not cleaned_data.get('members')): msg = _('At least one member must be specified') self._errors['members'] = self.error_class([msg]) elif (cleaned_data.get('member_type') == 'member_address' and not cleaned_data.get('address')): msg = _('Member IP address must be specified') self._errors['address'] = self.error_class([msg]) return cleaned_data class Meta(object): name = _("Add New Member") permissions = ('openstack.services.network', ) help_text = _("Add member(s) to the selected pool.\n\n" "Choose one or more listed instances to be " "added to the pool as member(s). " "Assign a numeric weight and port number for the " "selected member(s) to operate(s) on; e.g., 80. \n\n" "Only one port can be associated with " "each instance.")
class AddVipAction(workflows.Action): name = forms.CharField(max_length=80, label=_("Name")) description = forms.CharField(initial="", required=False, max_length=80, label=_("Description")) subnet_id = forms.ChoiceField(label=_("VIP Subnet"), initial="", required=False) address = forms.IPField(label=_("IP address"), version=forms.IPv4, mask=False, required=False) protocol_port = forms.IntegerField( label=_("Protocol Port"), min_value=1, help_text=_("Enter an integer value " "between 1 and 65535."), validators=[validators.validate_port_range]) protocol = forms.ChoiceField(label=_("Protocol")) session_persistence = forms.ChoiceField( required=False, initial={}, label=_("Session Persistence"), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'persistence' })) cookie_name = forms.CharField( initial="", required=False, max_length=80, label=_("Cookie Name"), help_text=_("Required for APP_COOKIE persistence;" " Ignored otherwise."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'persistence', 'data-persistence-app_cookie': 'APP_COOKIE', })) connection_limit = forms.IntegerField( required=False, min_value=-1, label=_("Connection Limit"), help_text=_("Maximum number of connections allowed " "for the VIP or '-1' if the limit is not set")) admin_state_up = forms.ChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State")) def __init__(self, request, *args, **kwargs): super(AddVipAction, self).__init__(request, *args, **kwargs) tenant_id = request.user.tenant_id subnet_id_choices = [('', _("Select a Subnet"))] try: networks = api.neutron.network_list_for_tenant(request, tenant_id) except Exception: exceptions.handle(request, _('Unable to retrieve networks list.')) networks = [] for n in networks: for s in n['subnets']: name = "%s (%s)" % (s.name, s.cidr) subnet_id_choices.append((s.id, name)) self.fields['subnet_id'].choices = subnet_id_choices protocol_choices = [('', _("Select a Protocol"))] [protocol_choices.append((p, p)) for p in AVAILABLE_PROTOCOLS] self.fields['protocol'].choices = protocol_choices session_persistence_choices = [('', _("No Session Persistence"))] for mode in ('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'): session_persistence_choices.append((mode.lower(), mode)) self.fields[ 'session_persistence'].choices = session_persistence_choices def clean(self): cleaned_data = super(AddVipAction, self).clean() persistence = cleaned_data.get('session_persistence') if persistence: cleaned_data['session_persistence'] = persistence.upper() if (cleaned_data.get('session_persistence') == 'APP_COOKIE' and not cleaned_data.get('cookie_name')): msg = _('Cookie name is required for APP_COOKIE persistence.') self._errors['cookie_name'] = self.error_class([msg]) return cleaned_data class Meta(object): name = _("Specify VIP") permissions = ('openstack.services.network', ) help_text_template = 'project/loadbalancers/_create_vip_help.html'
class AddIPSecSiteConnectionAction(workflows.Action): name = forms.CharField(max_length=80, label=_("Name")) description = forms.CharField( initial="", required=False, max_length=80, label=_("Description")) vpnservice_id = forms.ChoiceField( label=_("VPN Service associated with this connection")) ikepolicy_id = forms.ChoiceField( label=_("IKE Policy associated with this connection")) ipsecpolicy_id = forms.ChoiceField( label=_("IPSec Policy associated with this connection")) peer_address = forms.IPField( label=_("Peer gateway public IPv4/IPv6 Address or FQDN"), help_text=_("Peer gateway public IPv4/IPv6 address or FQDN for " "the VPN Connection"), version=forms.IPv4 | forms.IPv6, mask=False) peer_id = forms.IPField( label=_("Peer router identity for authentication (Peer ID)"), help_text=_("Peer router identity for authentication. " "Can be IPv4/IPv6 address, e-mail, key ID, or FQDN"), version=forms.IPv4 | forms.IPv6, mask=False) peer_cidrs = forms.MultiIPField( label=_("Remote peer subnet(s)"), help_text=_("Remote peer subnet(s) address(es) " "with mask(s) in CIDR format " "separated with commas if needed " "(e.g. 20.1.0.0/24, 21.1.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) psk = forms.CharField(max_length=80, label=_("Pre-Shared Key (PSK) string")) def populate_ikepolicy_id_choices(self, request, context): ikepolicy_id_choices = [('', _("Select IKE Policy"))] try: tenant_id = self.request.user.tenant_id ikepolicies = api.vpn.ikepolicy_list(request, tenant_id=tenant_id) except Exception: exceptions.handle(request, _('Unable to retrieve IKE Policies list.')) ikepolicies = [] for p in ikepolicies: ikepolicy_id_choices.append((p.id, p.name)) self.fields['ikepolicy_id'].choices = ikepolicy_id_choices return ikepolicy_id_choices def populate_ipsecpolicy_id_choices(self, request, context): ipsecpolicy_id_choices = [('', _("Select IPSec Policy"))] try: tenant_id = self.request.user.tenant_id ipsecpolicies = api.vpn.ipsecpolicy_list(request, tenant_id=tenant_id) except Exception: exceptions.handle(request, _('Unable to retrieve IPSec Policies list.')) ipsecpolicies = [] for p in ipsecpolicies: ipsecpolicy_id_choices.append((p.id, p.name)) self.fields['ipsecpolicy_id'].choices = ipsecpolicy_id_choices return ipsecpolicy_id_choices def populate_vpnservice_id_choices(self, request, context): vpnservice_id_choices = [('', _("Select VPN Service"))] try: tenant_id = self.request.user.tenant_id vpnservices = api.vpn.vpnservice_list(request, tenant_id=tenant_id) except Exception: exceptions.handle(request, _('Unable to retrieve VPN Services list.')) vpnservices = [] for s in vpnservices: vpnservice_id_choices.append((s.id, s.name)) self.fields['vpnservice_id'].choices = vpnservice_id_choices return vpnservice_id_choices class Meta: name = _("Add New IPSec Site Connection") permissions = ('openstack.services.network',) help_text = _("Create IPSec Site Connection for current project.\n\n" "Assign a name and description for the " "IPSec Site Connection. " "All fields in this tab are required." )