Ejemplo n.º 1
0
class UpdatePort(project_forms.UpdatePort):
    # tenant_id = forms.CharField(widget=forms.HiddenInput())
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(max_length=100,
                                   label=_("Device Owner"),
                                   help_text=_("Device owner attached to the "
                                               "port"),
                                   required=False)
    binding__host_id = forms.CharField(
        label=_("Binding: Host"),
        help_text=_("The ID of the host where the port is allocated. In some "
                    "cases, different implementations can run on different "
                    "hosts."),
        required=False)
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("Specify a new MAC address for the port"))

    failure_url = 'horizon:admin:networks:detail'

    def handle(self, request, data):
        try:
            LOG.debug('params = %s', data)
            extension_kwargs = {}
            data['admin_state'] = (data['admin_state'] == 'True')
            if 'binding__vnic_type' in data:
                extension_kwargs['binding__vnic_type'] = \
                    data['binding__vnic_type']

            if 'mac_state' in data:
                extension_kwargs['mac_learning_enabled'] = data['mac_state']

            if 'port_security_enabled' in data:
                extension_kwargs['port_security_enabled'] = \
                    data['port_security_enabled']

            port = api.neutron.port_update(
                request,
                data['port_id'],
                name=data['name'],
                admin_state_up=data['admin_state'],
                device_id=data['device_id'],
                device_owner=data['device_owner'],
                binding__host_id=data['binding__host_id'],
                mac_address=data['mac_address'],
                **extension_kwargs)
            msg = _('Port %s was successfully updated.') % data['port_id']
            LOG.debug(msg)
            messages.success(request, msg)
            return port
        except Exception:
            msg = _('Failed to update port %s') % data['port_id']
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=[data['network_id']])
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 2
0
class UpdatePortInfoAction(project_workflow.UpdatePortInfoAction):
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                required=False)
    device_owner = forms.CharField(max_length=100,
                                   label=_("Device Owner"),
                                   required=False)
    binding__host_id = forms.CharField(label=_("Binding: Host"),
                                       required=False)
    mac_address = forms.MACAddressField(label=_("MAC Address"), required=False)

    class Meta(object):
        name = _("Info")
        help_text_template = 'admin/networks/ports/_edit_port_help.html'
Ejemplo n.º 3
0
    def test_mac_address_validator(self):
        GOOD_MAC_ADDRESSES = (
            "00:11:88:99:Aa:Ff",
            "00-11-88-99-Aa-Ff",
            "0011.8899.AaFf",
            "00118899AaFf",
        )
        BAD_MAC_ADDRESSES = (
            "not a mac",
            "11:22:33:44:55",
            "zz:11:22:33:44:55",
        )

        field = forms.MACAddressField()
        for input in GOOD_MAC_ADDRESSES:
            self.assertIsNone(field.validate(input))
        for input in BAD_MAC_ADDRESSES:
            self.assertRaises(ValidationError, field.validate, input)
Ejemplo n.º 4
0
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.MACAddressField(label=_("MAC Address"),
                                help_text=_("A valid MAC Address"),
                                required=False)
    failure_url = 'horizon:project:networks: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
Ejemplo n.º 5
0
class UpdatePortInfoAction(project_workflow.UpdatePortInfoAction):
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(
        max_length=100,
        label=_("Device Owner"),
        help_text=_("Device owner attached to the port"),
        required=False)
    binding__host_id = forms.CharField(
        label=_("Binding: Host"),
        help_text=_("The ID of the host where the port is allocated. In some "
                    "cases, different implementations can run on different "
                    "hosts."),
        required=False)
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("MAC address for the port"))

    class Meta(object):
        name = _("Info")
Ejemplo n.º 6
0
class AddHostInfoAction(workflows.Action):
    FIELD_LABEL_PERSONALITY = _("Personality")
    FIELD_LABEL_HOSTNAME = _("Host Name")
    FIELD_LABEL_MGMT_MAC = _("Management MAC Address")
    FIELD_LABEL_MGMT_IP = _("Management IP Address")

    personality = forms.ChoiceField(
        label=FIELD_LABEL_PERSONALITY,
        help_text=_("Host Personality"),
        choices=PERSONALITY_CHOICES,
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'personality'
        }))

    subfunctions = forms.ChoiceField(
        label=FIELD_LABEL_PERFORMANCE_PROFILE,
        choices=PERFORMANCE_CHOICES,
        widget=forms.Select(
            attrs={
                'class':
                'switched',
                'data-switch-on':
                'personality',
                'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER:
                _("Personality Sub-Type")
            }))

    hostname = forms.RegexField(
        label=FIELD_LABEL_HOSTNAME,
        max_length=255,
        required=False,
        regex=r'^[\w\.\-]+$',
        error_messages={
            'invalid':
            _('Name may only contain letters,'
              ' numbers, underscores, '
              'periods and hyphens.')
        },
        widget=forms.TextInput(
            attrs={
                'class':
                'switched',
                'data-switch-on':
                'personality',
                'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER:
                FIELD_LABEL_HOSTNAME,
            }))

    mgmt_mac = forms.MACAddressField(
        label=FIELD_LABEL_MGMT_MAC,
        widget=forms.TextInput(
            attrs={
                'class':
                'switched',
                'data-switch-on':
                'personality',
                'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER:
                FIELD_LABEL_MGMT_MAC,
                'data-personality-' + stx_api.sysinv.PERSONALITY_CONTROLLER:
                FIELD_LABEL_MGMT_MAC,
                'data-personality-' + stx_api.sysinv.PERSONALITY_STORAGE:
                FIELD_LABEL_MGMT_MAC,
            }))

    class Meta(object):
        name = _("Host Info")
        help_text = _(
            "From here you can add the configuration for a new host.")

    def __init__(self, request, *arg, **kwargs):
        super(AddHostInfoAction, self).__init__(request, *arg, **kwargs)

        # pesonality cannot be storage if ceph is not configured
        storage_backend = stx_api.sysinv.get_storage_backend(request)
        if stx_api.sysinv.STORAGE_BACKEND_CEPH not in storage_backend:
            self.fields['personality'].choices = \
                PERSONALITY_CHOICES_WITHOUT_STORAGE

        # All-in-one system, personality can be controller or worker.
        systems = stx_api.sysinv.system_list(request)
        system_type = systems[0].to_dict().get('system_type')
        if system_type == constants.TS_AIO:
            self.fields['personality'].choices = \
                PERSONALITY_CHOICES_WITHOUT_STORAGE

        # Remove worker personality if in DC mode and region
        if getattr(self.request.user, 'services_region', None) == 'RegionOne' \
                and getattr(settings, 'DC_MODE', False):
            self.fields['personality'].choices = \
                [choice for choice in self.fields['personality'].choices
                 if choice[0] != stx_api.sysinv.PERSONALITY_WORKER]

    def clean(self):
        cleaned_data = super(AddHostInfoAction, self).clean()
        return cleaned_data
Ejemplo n.º 7
0
class CreatePort(forms.SelfHandlingForm):
    network_name = forms.CharField(
        label=_("Network Name"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}),
        required=False)
    network_id = forms.CharField(
        label=_("Network ID"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.BooleanField(label=_("Enable Admin State"),
                                     initial=True,
                                     required=False)
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(
        max_length=100,
        label=_("Device Owner"),
        help_text=_("Owner of the device attached to the port"),
        required=False)
    specify_ip = forms.ThemableChoiceField(
        label=_("Specify IP address or subnet"),
        help_text=_("To specify a subnet or a fixed IP, select any options."),
        initial=False,
        required=False,
        choices=[('', _("Unspecified")), ('subnet_id', _("Subnet")),
                 ('fixed_ip', _("Fixed IP Address"))],
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'specify_ip',
        }))
    subnet_id = forms.ThemableChoiceField(
        label=_("Subnet"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-subnet_id': _('Subnet'),
            }))
    fixed_ip = forms.IPField(
        label=_("Fixed IP Address"),
        required=False,
        help_text=_("Specify the subnet IP address for the new port"),
        version=forms.IPv4 | forms.IPv6,
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-fixed_ip': _('Fixed IP Address'),
            }))
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("Specify the MAC address for the new port"))
    failure_url = 'horizon:project:networks:detail'

    def __init__(self, request, *args, **kwargs):
        super(CreatePort, self).__init__(request, *args, **kwargs)

        # prepare subnet choices and input area for each subnet
        subnet_choices = self._get_subnet_choices(kwargs['initial'])
        if subnet_choices:
            subnet_choices.insert(0, ('', _("Select a subnet")))
            self.fields['subnet_id'].choices = subnet_choices
        else:
            self.fields['specify_ip'].widget = forms.HiddenInput()
            self.fields['subnet_id'].widget = forms.HiddenInput()
            self.fields['fixed_ip'].widget = forms.HiddenInput()

        if api.neutron.is_extension_supported(request, 'mac-learning'):
            self.fields['mac_state'] = forms.BooleanField(
                label=_("MAC Learning State"), initial=False, required=False)

        try:
            if api.neutron.is_extension_supported(request, 'port-security'):
                self.fields['port_security_enabled'] = forms.BooleanField(
                    label=_("Port Security"),
                    help_text=_("Enable anti-spoofing rules for the port"),
                    initial=True,
                    required=False)
        except Exception:
            msg = _("Unable to retrieve port security state")
            exceptions.handle(self.request, msg)

    def _get_subnet_choices(self, kwargs):
        try:
            network_id = kwargs['network_id']
            network = api.neutron.network_get(self.request, network_id)
        except Exception:
            return []

        return [(subnet.id, '%s %s' % (subnet.name_or_id, subnet.cidr))
                for subnet in network.subnets]

    def handle(self, request, data):
        try:
            params = {
                'network_id': data['network_id'],
                'admin_state_up': data['admin_state'],
                'name': data['name'],
                'device_id': data['device_id'],
                'device_owner': data['device_owner']
            }

            if data.get('specify_ip') == 'subnet_id':
                if data.get('subnet_id'):
                    params['fixed_ips'] = [{"subnet_id": data['subnet_id']}]
            elif data.get('specify_ip') == 'fixed_ip':
                if data.get('fixed_ip'):
                    params['fixed_ips'] = [{"ip_address": data['fixed_ip']}]

            if data.get('mac_state'):
                params['mac_learning_enabled'] = data['mac_state']
            if 'port_security_enabled' in data:
                params['port_security_enabled'] = data['port_security_enabled']

            # Send mac_address only when it is specified.
            if data['mac_address']:
                params['mac_address'] = data['mac_address']

            port = api.neutron.port_create(request, **params)
            if port['name']:
                msg = _('Port %s was successfully created.') % port['name']
            else:
                msg = _('Port %s was successfully created.') % port['id']
            LOG.debug(msg)
            messages.success(request, msg)
            return port
        except Exception:
            msg = _('Failed to create a port for network %s') \
                % data['network_id']
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=(data['network_id'], ))
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 8
0
 def test_mac_address_normal_form(self):
     field = forms.MACAddressField()
     field.validate("00-11-88-99-Aa-Ff")
     self.assertEqual(field.mac_address, "00:11:88:99:aa:ff")
Ejemplo n.º 9
0
class CreatePortInfoAction(workflows.Action):
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.BooleanField(label=_("Enable Admin State"),
                                     initial=True,
                                     required=False)
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(
        max_length=100,
        label=_("Device Owner"),
        help_text=_("Owner of the device attached to the port"),
        required=False)
    specify_ip = forms.ThemableChoiceField(
        label=_("Specify IP address or subnet"),
        help_text=_("To specify a subnet or a fixed IP, select any options."),
        required=False,
        choices=[('', _("Unspecified")), ('subnet_id', _("Subnet")),
                 ('fixed_ip', _("Fixed IP Address"))],
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'specify_ip',
        }))
    subnet_id = forms.ThemableChoiceField(
        label=_("Subnet"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-subnet_id': _('Subnet'),
            }))
    fixed_ip = forms.IPField(
        label=_("Fixed IP Address"),
        required=False,
        help_text=_("Specify the subnet IP address for the new port"),
        version=forms.IPv4 | forms.IPv6,
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-fixed_ip': _('Fixed IP Address'),
            }))
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("Specify the MAC address for the new port"))
    mac_state = forms.BooleanField(label=_("MAC Learning State"),
                                   initial=False,
                                   required=False)
    port_security_enabled = forms.BooleanField(
        label=_("Port Security"),
        help_text=_("Enable anti-spoofing rules for the port"),
        initial=True,
        required=False,
        widget=forms.CheckboxInput(
            attrs={
                'class': 'switchable',
                'data-slug': 'port_security_enabled',
                'data-hide-tab': 'create_port__create_security_groups',
                'data-hide-on-checked': 'false'
            }))
    binding__vnic_type = forms.ThemableChoiceField(
        label=_("VNIC Type"),
        help_text=_("The VNIC type that is bound to the network port"),
        required=False)

    def __init__(self, request, context, *args, **kwargs):
        super(CreatePortInfoAction, self).__init__(request, context, *args,
                                                   **kwargs)

        # prepare subnet choices and input area for each subnet
        subnet_choices = self._get_subnet_choices(context)
        if subnet_choices:
            subnet_choices.insert(0, ('', _("Select a subnet")))
            self.fields['subnet_id'].choices = subnet_choices
        else:
            self.fields['specify_ip'].widget = forms.HiddenInput()
            self.fields['subnet_id'].widget = forms.HiddenInput()
            self.fields['fixed_ip'].widget = forms.HiddenInput()

        self._hide_field_if_not_supported(
            request, 'mac_state', 'mac-learning',
            _("Unable to retrieve MAC learning state"))
        self._hide_field_if_not_supported(
            request, 'port_security_enabled', 'port-security',
            _("Unable to retrieve port security state"))

        self._populate_vnic_type_choices(request)

    def _hide_field_if_not_supported(self, request, field, extension_alias,
                                     failure_message):
        is_supproted = False
        try:
            is_supproted = api.neutron.is_extension_supported(
                request, extension_alias)
        except Exception:
            exceptions.handle(self.request, failure_message)
        if not is_supproted:
            del self.fields[field]
        return is_supproted

    def _populate_vnic_type_choices(self, request):
        neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
        supported_vnic_types = neutron_settings.get('supported_vnic_types',
                                                    ['*'])
        # When a list of VNIC types is empty, hide the corresponding field.
        if not supported_vnic_types:
            del self.fields['binding__vnic_type']
            return

        binding_supported = self._hide_field_if_not_supported(
            request, 'binding__vnic_type', 'binding',
            _("Unable to verify the VNIC types extension in Neutron"))
        if not binding_supported:
            # binding__vnic_type field is already deleted, so return here
            return

        if supported_vnic_types == ['*']:
            vnic_type_choices = api.neutron.VNIC_TYPES
        else:
            vnic_type_choices = [
                vnic_type for vnic_type in api.neutron.VNIC_TYPES
                if vnic_type[0] in supported_vnic_types
            ]
        self.fields['binding__vnic_type'].choices = vnic_type_choices

    def _get_subnet_choices(self, context):
        try:
            network_id = context['network_id']
            network = api.neutron.network_get(self.request, network_id)
        except Exception:
            return []

        # NOTE(amotoki): When a user cannot retrieve a subnet info,
        # subnet ID is stored in network.subnets field.
        # If so, we skip such subnet as subnet choices.
        # This happens usually for external networks.
        # TODO(amotoki): Ideally it is better to disable/hide
        # Create Port button in the port table, but as of Pike
        # the default neutron policy.json for "create_port" is empty
        # and there seems no appropriate policy. This is a dirty hack.
        return [(subnet.id, '%s %s' % (subnet.name_or_id, subnet.cidr))
                for subnet in network.subnets
                if isinstance(subnet, api.neutron.Subnet)]

    class Meta(object):
        name = _("Info")
        slug = 'create_info'
        help_text_template = 'project/networks/ports/_create_port_help.html'
Ejemplo n.º 10
0
class CreatePort(forms.SelfHandlingForm):
    network_name = forms.CharField(
        label=_("Network Name"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}),
        required=False)
    network_id = forms.CharField(
        label=_("Network ID"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.ChoiceField(choices=[(True, _('UP')),
                                             (False, _('DOWN'))],
                                    label=_("Admin State"))
    device_id = forms.ChoiceField(
        label=_("Device ID"),
        help_text=_("Device ID attached to the port"),
        required=False)
    device_owner = forms.CharField(max_length=100,
                                   label=_("Device Owner"),
                                   help_text=_("Device owner attached to the "
                                               "port"),
                                   required=False)
    tenant_id = forms.ChoiceField(
        label=_("Tenant ID"),
        required=True,
        help_text=_("Tenant ID"),
    )
    """add by hades 2018-8-21"""
    """Aim t attach QoS policy ID or name to the port"""
    qos_policy_id = forms.ChoiceField(
        label=_("Qos Policy"),
        required=False,
        help_text=_("Qos Policy"),
    )
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("Specify the MAC address for the new port"))
    fixed_ip = forms.IPField(
        label=_("Fixed IP Address"),
        required=False,
        help_text=_("Specify the subnet IP address for the new port"),
        version=forms.IPv4 | forms.IPv6,
    )
    binding__host_id = forms.CharField(
        label=_("Binding: Host"),
        help_text=_("The ID of the host where the port is allocated. In some "
                    "cases, different implementations can run on different "
                    "hosts."),
        required=False)

    failure_url = 'horizon:admin:networks:detail'

    def __init__(self, request, *args, **kwargs):
        super(CreatePort, self).__init__(request, *args, **kwargs)
        if api.neutron.is_extension_supported(request, 'binding'):
            neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK',
                                       {})
            supported_vnic_types = neutron_settings.get(
                'supported_vnic_types', ['*'])
            if supported_vnic_types:
                if supported_vnic_types == ['*']:
                    vnic_type_choices = VNIC_TYPES
                else:
                    vnic_type_choices = [
                        vnic_type for vnic_type in VNIC_TYPES
                        if vnic_type[0] in supported_vnic_types
                    ]

                self.fields['binding__vnic_type'] = forms.ChoiceField(
                    choices=vnic_type_choices,
                    label=_("Binding: VNIC Type"),
                    help_text=_(
                        "The VNIC type that is bound to the neutron port"),
                    required=False)
        if api.neutron.is_extension_supported(request, 'mac-learning'):
            self.fields['mac_state'] = forms.BooleanField(
                label=_("MAC Learning State"), initial=False, required=False)
        tenants = instance_utils.tenant_field_data(request)
        self.fields['tenant_id'].choices = tenants
        qos_choices = [('', _("Select a project"))]
        qos = api.neutron.policy_list(request)
        for q in qos:
            qos_choices.append((q.id, q.name))
        self.fields['qos_policy_id'].choices = qos_choices
        device_choices = [('', _("Select a project"))]
        devices = api.nova.server_list(request, all_tenants=True)[0]
        for dev in devices:
            device_choices.append((dev.id, dev.name))
        self.fields['device_id'].choices = device_choices

    def handle(self, request, data):
        try:
            # We must specify tenant_id of the network which a subnet is
            # created for if admin user does not belong to the tenant.
            network = api.neutron.network_get(request, data['network_id'])
            # data['tenant_id'] = network.tenant_id
            if data['tenant_id'] == 'null':
                data['tenant_id'] = network.tenant_id
            data['admin_state_up'] = (data['admin_state'] == 'True')
            del data['network_name']
            del data['admin_state']
            if data['qos_policy_id'] == '':
                del data['qos_policy_id']
            if data['fixed_ip']:
                data['fixed_ips'] = [{'ip_address': data['fixed_ip']}]
                del data['fixed_ip']
            else:
                del data['fixed_ip']
            if data['mac_address']:
                pass
            else:
                del data['mac_address']
            if 'mac_state' in data:
                data['mac_learning_enabled'] = data['mac_state']
                del data['mac_state']

            port = api.neutron.port_create(request, **data)
            msg = _('Port %s was successfully created.') % port['id']
            LOG.debug(msg)
            messages.success(request, msg)
            return port
        except Exception:
            msg = _('Failed to create a port for network %s') \
                % data['network_id']
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=(data['network_id'], ))
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 11
0
class CreatePort(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.BooleanField(label=_("Enable Admin State"),
                                     initial=True,
                                     required=False)
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(
        max_length=100,
        label=_("Device Owner"),
        help_text=_("Owner of the device attached to the port"),
        required=False)
    specify_ip = forms.ThemableChoiceField(
        label=_("Specify IP address or subnet"),
        help_text=_("To specify a subnet or a fixed IP, select any options."),
        required=False,
        choices=[('', _("Unspecified")), ('subnet_id', _("Subnet")),
                 ('fixed_ip', _("Fixed IP Address"))],
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'specify_ip',
        }))
    subnet_id = forms.ThemableChoiceField(
        label=_("Subnet"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-subnet_id': _('Subnet'),
            }))
    fixed_ip = forms.IPField(
        label=_("Fixed IP Address"),
        required=False,
        help_text=_("Specify the subnet IP address for the new port"),
        version=forms.IPv4 | forms.IPv6,
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-fixed_ip': _('Fixed IP Address'),
            }))
    mac_address = forms.MACAddressField(
        label=_("MAC Address"),
        required=False,
        help_text=_("Specify the MAC address for the new port"))
    failure_url = 'horizon:project:networks:detail'

    def __init__(self, request, *args, **kwargs):
        super(CreatePort, self).__init__(request, *args, **kwargs)

        # prepare subnet choices and input area for each subnet
        subnet_choices = self._get_subnet_choices(kwargs['initial'])
        if subnet_choices:
            subnet_choices.insert(0, ('', _("Select a subnet")))
            self.fields['subnet_id'].choices = subnet_choices
        else:
            self.fields['specify_ip'].widget = forms.HiddenInput()
            self.fields['subnet_id'].widget = forms.HiddenInput()
            self.fields['fixed_ip'].widget = forms.HiddenInput()

        if api.neutron.is_extension_supported(request, 'mac-learning'):
            self.fields['mac_state'] = forms.BooleanField(
                label=_("MAC Learning State"), initial=False, required=False)

        try:
            if api.neutron.is_extension_supported(request, 'port-security'):
                self.fields['port_security_enabled'] = forms.BooleanField(
                    label=_("Port Security"),
                    help_text=_("Enable anti-spoofing rules for the port"),
                    initial=True,
                    required=False)
        except Exception:
            msg = _("Unable to retrieve port security state")
            exceptions.handle(self.request, msg)

    def _get_subnet_choices(self, kwargs):
        try:
            network_id = kwargs['network_id']
            network = api.neutron.network_get(self.request, network_id)
        except Exception:
            return []

        # NOTE(amotoki): When a user cannot retrieve a subnet info,
        # subnet ID is stored in network.subnets field.
        # If so, we skip such subnet as subnet choices.
        # This happens usually for external networks.
        # TODO(amotoki): Ideally it is better to disable/hide
        # Create Port button in the port table, but as of Pike
        # the default neutron policy.json for "create_port" is empty
        # and there seems no appropriate policy. This is a dirty hack.
        return [(subnet.id, '%s %s' % (subnet.name_or_id, subnet.cidr))
                for subnet in network.subnets
                if isinstance(subnet, api.neutron.Subnet)]

    def handle(self, request, data):
        try:
            params = {
                'network_id': self.initial['network_id'],
                'admin_state_up': data['admin_state'],
                'name': data['name'],
                'device_id': data['device_id'],
                'device_owner': data['device_owner']
            }

            if data.get('specify_ip') == 'subnet_id':
                if data.get('subnet_id'):
                    params['fixed_ips'] = [{"subnet_id": data['subnet_id']}]
            elif data.get('specify_ip') == 'fixed_ip':
                if data.get('fixed_ip'):
                    params['fixed_ips'] = [{"ip_address": data['fixed_ip']}]

            if data.get('mac_state'):
                params['mac_learning_enabled'] = data['mac_state']
            if 'port_security_enabled' in data:
                params['port_security_enabled'] = data['port_security_enabled']

            # Send mac_address only when it is specified.
            if data['mac_address']:
                params['mac_address'] = data['mac_address']

            port = api.neutron.port_create(request, **params)
            if port['name']:
                msg = _('Port %s was successfully created.') % port['name']
            else:
                msg = _('Port %s was successfully created.') % port['id']
            messages.success(request, msg)
            return port
        except Exception as e:
            LOG.info('Failed to create a port for network %(id)s: %(exc)s', {
                'id': self.initial['network_id'],
                'exc': e
            })
            if isinstance(e, neutron_exc.Forbidden):
                msg = (_('You are not allowed to create a port '
                         'for network %s.') % self.initial['network_id'])
            else:
                msg = (_('Failed to create a port for network %s') %
                       self.initial['network_id'])
            redirect = reverse(self.failure_url,
                               args=(self.initial['network_id'], ))
            exceptions.handle(request, msg, redirect=redirect)