Ejemplo n.º 1
0
class UpdateProviderNetworkRange(forms.SelfHandlingForm):
    failure_url = 'horizon:admin:datanets:datanets:detail'
    providernet_id = forms.CharField(widget=forms.HiddenInput())
    providernet_range_id = forms.CharField(widget=forms.HiddenInput())
    name = forms.CharField(
        max_length=255,
        label=_("Name"),
        required=False,
        widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    description = forms.CharField(max_length=255,
                                  label=_("Description"),
                                  required=False)
    minimum = forms.IntegerField(label=_("Minimum"), min_value=1)
    maximum = forms.IntegerField(label=_("Maximum"), min_value=1)
    shared = forms.BooleanField(widget=forms.HiddenInput(), required=False)
    tenant_id = forms.CharField(widget=forms.HiddenInput(), required=False)

    # VXLAN specific fields
    mode_widget = forms.TextInput(attrs={'readonly': 'readonly'})
    mode = forms.CharField(label=_("mode"), required=False, widget=mode_widget)
    group_widget = forms.TextInput(attrs={'readonly': 'readonly'})
    group = forms.CharField(max_length=255,
                            label=_("Multicast Group Address"),
                            required=False,
                            widget=group_widget)
    port_widget = forms.RadioSelect(attrs={'disabled': 'disabled'})
    port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')),
                    ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')),
                    ('8472', _('Legacy VXLAN UDP port (8472)'))]
    port = forms.ChoiceField(label=_("UDP Port"),
                             required=False,
                             widget=port_widget,
                             choices=port_choices)
    ttl_widget = forms.TextInput(attrs={'readonly': 'readonly'})
    ttl = forms.IntegerField(label=_("TTL"), required=False, widget=ttl_widget)

    def __init__(self, request, *args, **kwargs):
        super(UpdateProviderNetworkRange,
              self).__init__(request, *args, **kwargs)
        initial = kwargs['initial']
        if 'mode' not in initial:
            del self.fields["mode"]
        if 'group' not in initial or initial.get('mode') == 'static':
            del self.fields["group"]
        if 'port' not in initial:
            del self.fields["port"]
        if 'ttl' not in initial:
            del self.fields["ttl"]

    def handle(self, request, data):
        try:
            params = {
                'description': data['description'],
                'minimum': data['minimum'],
                'maximum': data['maximum']
            }

            providernet_range = stx_api.neutron.provider_network_range_modify(
                request, data['providernet_range_id'], **params)
            msg = (_('Provider network range %s was successfully updated.') %
                   data['providernet_range_id'])
            LOG.debug(msg)
            messages.success(request, msg)
            return providernet_range
        except neutron_exceptions.NeutronClientException as e:
            LOG.info(str(e))
            redirect = reverse('horizon:admin:datanets:datanets:'
                               'detail',
                               args=(data['providernet_id'], ))
            exceptions.handle(request, str(e), redirect=redirect)
        except Exception:
            msg = (_('Failed to update provider network range %s') %
                   data['providernet_range_id'])
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=[data['providernet_id']])
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 2
0
class CreateImageForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Name"), required=True)
    copy_from = forms.CharField(max_length="255",
                                label=_("Image Location"),
                                help_text=_("An external (HTTP) URL to load "
                                            "the image from."),
                                required=True)
    disk_format = forms.ChoiceField(
        label=_('Format'),
        required=True,
        choices=[('', ''), ('aki', _('AKI - Amazon Kernel '
                                     'Image')),
                 ('ami', _('AMI - Amazon Machine '
                           'Image')),
                 ('ari', _('ARI - Amazon Ramdisk '
                           'Image')), ('iso', _('ISO - Optical Disk Image')),
                 ('qcow2', _('QCOW2 - QEMU Emulator')), ('raw', 'Raw'),
                 ('vdi', 'VDI'), ('vhd', 'VHD'), ('vmdk', 'VMDK')],
        widget=forms.Select(attrs={'class': 'switchable'}))
    minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"),
                                      help_text=_(
                                          'The minimum disk size'
                                          ' required to boot the'
                                          ' image. If unspecified, this'
                                          ' value defaults to 0'
                                          ' (no minimum).'),
                                      required=False)
    minimum_ram = forms.IntegerField(label=_("Minimum Ram (MB)"),
                                     help_text=_('The minimum disk size'
                                                 ' required to boot the'
                                                 ' image. If unspecified, this'
                                                 ' value defaults to 0 (no'
                                                 ' minimum).'),
                                     required=False)
    is_public = forms.BooleanField(label=_("Public"), required=False)

    def handle(self, request, data):
        # Glance does not really do anything with container_format at the
        # moment. It requires it is set to the same disk_format for the three
        # Amazon image types, otherwise it just treats them as 'bare.' As such
        # we will just set that to be that here instead of bothering the user
        # with asking them for information we can already determine.
        if data['disk_format'] in (
                'ami',
                'aki',
                'ari',
        ):
            container_format = data['disk_format']
        else:
            container_format = 'bare'

        meta = {
            'is_public': data['is_public'],
            'disk_format': data['disk_format'],
            'container_format': container_format,
            'copy_from': data['copy_from'],
            'min_disk': (data['minimum_disk'] or 0),
            'min_ram': (data['minimum_ram'] or 0),
            'name': data['name']
        }

        try:
            image = api.glance.image_create(request, **meta)
            messages.success(
                request,
                _('Your image %s has been queued for creation.' %
                  data['name']))
            return image
        except:
            exceptions.handle(request, _('Unable to create new image.'))
Ejemplo n.º 3
0
class GeneralConfigAction(workflows.Action):
    nodegroup_name = forms.CharField(label=_("Template Name"))

    description = forms.CharField(label=_("Description"),
                                  required=False,
                                  widget=forms.Textarea(attrs={'rows': 4}))

    flavor = forms.ChoiceField(label=_("OpenStack Flavor"))

    availability_zone = forms.ChoiceField(
        label=_("Availability Zone"),
        help_text=_("Launch instances in this availability zone."),
        required=False,
        widget=forms.Select(attrs={"class": "availability_zone_field"}))

    storage = forms.ChoiceField(
        label=_("Storage location"),
        help_text=_("Choose a storage location"),
        choices=[],
        widget=forms.Select(attrs={
            "class": "storage_field switchable",
            'data-slug': 'storage_loc'
        }))

    volumes_per_node = forms.IntegerField(
        label=_("Volumes per node"),
        required=False,
        initial=1,
        widget=forms.TextInput(
            attrs={
                "class": "volume_per_node_field switched",
                "data-switch-on": "storage_loc",
                "data-storage_loc-cinder_volume": _('Volumes per node')
            }))

    volumes_size = forms.IntegerField(
        label=_("Volumes size (GB)"),
        required=False,
        initial=10,
        widget=forms.TextInput(
            attrs={
                "class": "volume_size_field switched",
                "data-switch-on": "storage_loc",
                "data-storage_loc-cinder_volume": _('Volumes size (GB)')
            }))

    volume_type = forms.ChoiceField(
        label=_("Volumes type"),
        required=False,
        widget=forms.Select(
            attrs={
                "class": "volume_type_field switched",
                "data-switch-on": "storage_loc",
                "data-storage_loc-cinder_volume": _('Volumes type')
            }))

    volume_local_to_instance = forms.BooleanField(
        label=_("Volume local to instance"),
        required=False,
        help_text=_("Instance and attached volumes will be created on the "
                    "same physical host"),
        widget=forms.CheckboxInput(
            attrs={
                "class": "volume_local_to_instance_field switched",
                "data-switch-on": "storage_loc",
                "data-storage_loc-cinder_volume": _('Volume local to instance')
            }))

    volumes_availability_zone = forms.ChoiceField(
        label=_("Volumes Availability Zone"),
        help_text=_("Create volumes in this availability zone."),
        required=False,
        widget=forms.Select(
            attrs={
                "class": "volumes_availability_zone_field switched",
                "data-switch-on": "storage_loc",
                "data-storage_loc-cinder_volume": _(
                    'Volumes Availability Zone')
            }))

    image = forms.DynamicChoiceField(label=_("Base Image"),
                                     required=False,
                                     add_item_link=BASE_IMAGE_URL)

    hidden_configure_field = forms.CharField(
        required=False,
        widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))

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

        hlps = helpers.Helpers(request)

        plugin, hadoop_version = (
            workflow_helpers.get_plugin_and_hadoop_version(request))

        if not saharaclient.SAHARA_AUTO_IP_ALLOCATION_ENABLED:
            pools = neutron.floating_ip_pools_list(request)
            pool_choices = [(pool.id, pool.name) for pool in pools]
            pool_choices.insert(0, (None, "Do not assign floating IPs"))

            self.fields['floating_ip_pool'] = forms.ChoiceField(
                label=_("Floating IP Pool"),
                choices=pool_choices,
                required=False)

        self.fields["use_autoconfig"] = forms.BooleanField(
            label=_("Auto-configure"),
            help_text=_("If selected, instances of a node group will be "
                        "automatically configured during cluster "
                        "creation. Otherwise you should manually specify "
                        "configuration values."),
            required=False,
            widget=forms.CheckboxInput(),
            initial=True,
        )

        self.fields["proxygateway"] = forms.BooleanField(
            label=_("Proxy Gateway"),
            widget=forms.CheckboxInput(),
            help_text=_("Sahara will use instances of this node group to "
                        "access other cluster instances."),
            required=False)

        self.fields['is_public'] = acl_utils.get_is_public_form(
            _("node group template"))
        self.fields['is_protected'] = acl_utils.get_is_protected_form(
            _("node group template"))

        self.fields["plugin_name"] = forms.CharField(
            widget=forms.HiddenInput(), initial=plugin)
        self.fields["hadoop_version"] = forms.CharField(
            widget=forms.HiddenInput(), initial=hadoop_version)

        self.fields["storage"].choices = storage_choices(request)

        node_parameters = hlps.get_general_node_group_configs(
            plugin, hadoop_version)
        for param in node_parameters:
            self.fields[param.name] = workflow_helpers.build_control(param)

        # when we copy or edit a node group template then
        # request contains valuable info in both GET and POST methods
        req = request.GET.copy()
        req.update(request.POST)
        if req.get("guide_template_type"):
            self.fields["guide_template_type"] = forms.CharField(
                required=False,
                widget=forms.HiddenInput(),
                initial=req.get("guide_template_type"))

        if is_cinder_enabled(request):
            volume_types = cinder.volume_type_list(request)
        else:
            volume_types = []

        self.fields['volume_type'].choices = [(None, _("No volume type"))] + \
                                             [(type.name, type.name)
                                              for type in volume_types]

    def populate_flavor_choices(self, request, context):
        flavors = nova_utils.flavor_list(request)
        if flavors:
            return nova_utils.sort_flavor_list(request, flavors)
        return []

    def populate_availability_zone_choices(self, request, context):
        # The default is None, i.e. not specifying any availability zone
        az_list = [(None, _('No availability zone specified'))]
        az_list.extend([(az.zoneName, az.zoneName)
                        for az in nova_utils.availability_zone_list(request)
                        if az.zoneState['available']])
        return az_list

    def populate_volumes_availability_zone_choices(self, request, context):
        az_list = [(None, _('No availability zone specified'))]
        if is_cinder_enabled(request):
            az_list.extend([
                (az.zoneName, az.zoneName)
                for az in cinder_utils.availability_zone_list(request)
                if az.zoneState['available']
            ])
        return az_list

    def populate_image_choices(self, request, context):
        return workflow_helpers.populate_image_choices(self,
                                                       request,
                                                       context,
                                                       empty_choice=True)

    def get_help_text(self):
        extra = dict()
        plugin_name, hadoop_version = (
            workflow_helpers.get_plugin_and_hadoop_version(self.request))
        extra["plugin_name"] = plugin_name
        extra["hadoop_version"] = hadoop_version
        plugin = saharaclient.plugin_get_version_details(
            self.request, plugin_name, hadoop_version)
        extra["deprecated"] = workflow_helpers.is_version_of_plugin_deprecated(
            plugin, hadoop_version)
        return super(GeneralConfigAction, self).get_help_text(extra)

    class Meta(object):
        name = _("Configure Node Group Template")
        help_text_template = "nodegroup_templates/_configure_general_help.html"
Ejemplo n.º 4
0
class CreateStackForm(forms.SelfHandlingForm):

    param_prefix = '__param_'

    class Meta:
        name = _('Create Stack')

    template_data = forms.CharField(
        widget=forms.widgets.HiddenInput,
        required=False)
    template_url = forms.CharField(
        widget=forms.widgets.HiddenInput,
        required=False)
    environment_data = forms.CharField(
        widget=forms.widgets.HiddenInput,
        required=False)
    parameters = forms.CharField(
        widget=forms.widgets.HiddenInput)
    stack_name = forms.RegexField(
        max_length='255',
        label=_('Stack Name'),
        help_text=_('Name of the stack to create.'),
        regex=r"^[a-zA-Z][a-zA-Z0-9_.-]*$",
        error_messages={'invalid': _('Name must start with a letter and may '
                            'only contain letters, numbers, underscores, '
                            'periods and hyphens.')})
    timeout_mins = forms.IntegerField(
        initial=60,
        label=_('Creation Timeout (minutes)'),
        help_text=_('Stack creation timeout in minutes.'))
    enable_rollback = forms.BooleanField(
        label=_('Rollback On Failure'),
        help_text=_('Enable rollback on create/update failure.'),
        required=False)

    def __init__(self, *args, **kwargs):
        parameters = kwargs.pop('parameters')
        # special case: load template data from API, not passed in params
        if(kwargs.get('validate_me')):
            parameters = kwargs.pop('validate_me')
        super(CreateStackForm, self).__init__(*args, **kwargs)
        self._build_parameter_fields(parameters)

    def _build_parameter_fields(self, template_validate):
        self.fields['password'] = forms.CharField(
            label=_('Password for user "%s"') % self.request.user.username,
            help_text=_('This is required for operations to be performed '
                        'throughout the lifecycle of the stack'),
            widget=forms.PasswordInput())

        self.help_text = template_validate['Description']

        params = template_validate.get('Parameters', {})

        for param_key, param in params.items():
            field_key = self.param_prefix + param_key
            field_args = {
                'initial': param.get('Default', None),
                'label': param.get('Label', param_key),
                'help_text': param.get('Description', ''),
                'required': param.get('Default', None) is None
            }

            param_type = param.get('Type', None)
            hidden = strutils.bool_from_string(param.get('NoEcho', 'false'))

            if 'AllowedValues' in param:
                choices = map(lambda x: (x, x), param['AllowedValues'])
                field_args['choices'] = choices
                field = forms.ChoiceField(**field_args)

            elif param_type in ('CommaDelimitedList', 'String'):
                if 'MinLength' in param:
                    field_args['min_length'] = int(param['MinLength'])
                    field_args['required'] = param.get('MinLength', 0) > 0
                if 'MaxLength' in param:
                    field_args['max_length'] = int(param['MaxLength'])
                if hidden:
                    field_args['widget'] = forms.PasswordInput()
                field = forms.CharField(**field_args)

            elif param_type == 'Number':
                if 'MinValue' in param:
                    field_args['min_value'] = int(param['MinValue'])
                if 'MaxValue' in param:
                    field_args['max_value'] = int(param['MaxValue'])
                field = forms.IntegerField(**field_args)

            self.fields[field_key] = field

    @sensitive_variables('password')
    def handle(self, request, data):
        prefix_length = len(self.param_prefix)
        params_list = [(k[prefix_length:], v) for (k, v) in six.iteritems(data)
                       if k.startswith(self.param_prefix)]
        fields = {
            'stack_name': data.get('stack_name'),
            'timeout_mins': data.get('timeout_mins'),
            'disable_rollback': not(data.get('enable_rollback')),
            'parameters': dict(params_list),
            'password': data.get('password')
        }

        if data.get('template_data'):
            fields['template'] = data.get('template_data')
        else:
            fields['template_url'] = data.get('template_url')

        if data.get('environment_data'):
            fields['environment'] = data.get('environment_data')

        try:
            api.heat.stack_create(self.request, **fields)
            messages.success(request, _("Stack creation started."))
            return True
        except Exception:
            exceptions.handle(request)
Ejemplo n.º 5
0
class SetInstanceDetailsAction(workflows.Action):
    availability_zone = forms.ThemableChoiceField(label=_("Availability Zone"),
                                                  required=False)

    name = forms.CharField(label=_("Instance Name"), max_length=255)

    flavor = forms.ThemableChoiceField(label=_("Flavor"),
                                       help_text=_("Size of image to launch."))

    count = forms.IntegerField(label=_("Number of Instances"),
                               min_value=1,
                               initial=1)

    source_type = forms.ThemableChoiceField(label=_("Instance Boot Source"),
                                            help_text=_(
                                                "Choose Your Boot Source "
                                                "Type."))

    instance_snapshot_id = forms.ThemableChoiceField(
        label=_("Instance Snapshot"), required=False)

    volume_id = forms.ThemableChoiceField(label=_("Volume"), required=False)

    volume_snapshot_id = forms.ThemableChoiceField(label=_("Volume Snapshot"),
                                                   required=False)

    image_id = forms.ChoiceField(
        label=_("Image Name"),
        required=False,
        widget=forms.ThemableSelectWidget(data_attrs=('volume_size', ),
                                          transform=lambda x:
                                          ("%s (%s)" %
                                           (x.name, filesizeformat(x.bytes)))))

    volume_size = forms.IntegerField(label=_("Device size (GB)"),
                                     initial=1,
                                     min_value=0,
                                     required=False,
                                     help_text=_("Volume size in gigabytes "
                                                 "(integer value)."))

    device_name = forms.CharField(label=_("Device Name"),
                                  required=False,
                                  initial="vda",
                                  help_text=_("Volume mount point (e.g. 'vda' "
                                              "mounts at '/dev/vda'). Leave "
                                              "this field blank to let the "
                                              "system choose a device name "
                                              "for you."))

    vol_delete_on_instance_delete = forms.BooleanField(
        label=_("Delete Volume on Instance Delete"),
        initial=False,
        required=False,
        help_text=_("Delete volume when the instance is deleted"))

    class Meta(object):
        name = _("Details")
        help_text_template = ("project/instances/" "_launch_details_help.html")

    def __init__(self, request, context, *args, **kwargs):
        self._init_images_cache()
        self.request = request
        self.context = context
        super(SetInstanceDetailsAction, self).__init__(request, context, *args,
                                                       **kwargs)

        # Hide the device field if the hypervisor doesn't support it.
        if not nova.can_set_mount_point():
            self.fields['device_name'].widget = forms.widgets.HiddenInput()

        source_type_choices = [
            ('', _("Select source")),
            ("image_id", _("Boot from image")),
            ("instance_snapshot_id", _("Boot from snapshot")),
        ]
        if cinder.is_volume_service_enabled(request):
            source_type_choices.append(("volume_id", _("Boot from volume")))

            try:
                if api.nova.extension_supported("BlockDeviceMappingV2Boot",
                                                request):
                    source_type_choices.append(
                        ("volume_image_id",
                         _("Boot from image (creates a new volume)")))
            except Exception:
                exceptions.handle(
                    request, _('Unable to retrieve extensions '
                               'information.'))

            source_type_choices.append(
                ("volume_snapshot_id",
                 _("Boot from volume snapshot (creates a new volume)")))
        self.fields['source_type'].choices = source_type_choices

    @memoized.memoized_method
    def _get_flavor(self, flavor_id):
        try:
            # We want to retrieve details for a given flavor,
            # however flavor_list uses a memoized decorator
            # so it is used instead of flavor_get to reduce the number
            # of API calls.
            flavors = instance_utils.flavor_list(self.request)
            flavor = [x for x in flavors if x.id == flavor_id][0]
        except IndexError:
            flavor = None
        return flavor

    @memoized.memoized_method
    def _get_image(self, image_id):
        try:
            # We want to retrieve details for a given image,
            # however get_available_images uses a cache of image list,
            # so it is used instead of image_get to reduce the number
            # of API calls.
            images = image_utils.get_available_images(
                self.request, self.context.get('project_id'),
                self._images_cache)
            image = [x for x in images if x.id == image_id][0]
        except IndexError:
            image = None
        return image

    def _check_quotas(self, cleaned_data):
        count = cleaned_data.get('count', 1)

        # Prevent launching more instances than the quota allows
        usages = quotas.tenant_quota_usages(self.request)
        available_count = usages['instances']['available']
        if available_count < count:
            error_message = ungettext_lazy(
                'The requested instance cannot be launched as you only '
                'have %(avail)i of your quota available. ',
                'The requested %(req)i instances cannot be launched as you '
                'only have %(avail)i of your quota available.', count)
            params = {'req': count, 'avail': available_count}
            raise forms.ValidationError(error_message % params)

        source_type = cleaned_data.get('source_type')
        if source_type in ('volume_image_id', 'volume_snapshot_id'):
            available_volume = usages['volumes']['available']
            if available_volume < count:
                msg = (_('The requested instance cannot be launched. '
                         'Requested volume exceeds quota: Available: '
                         '%(avail)s, Requested: %(req)s.') % {
                             'avail': available_volume,
                             'req': count
                         })
                raise forms.ValidationError(msg)

        flavor_id = cleaned_data.get('flavor')
        flavor = self._get_flavor(flavor_id)

        count_error = []
        # Validate cores and ram.
        available_cores = usages['cores']['available']
        if flavor and available_cores < count * flavor.vcpus:
            count_error.append(
                _("Cores(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_cores,
                      'req': count * flavor.vcpus
                  })

        available_ram = usages['ram']['available']
        if flavor and available_ram < count * flavor.ram:
            count_error.append(
                _("RAM(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_ram,
                      'req': count * flavor.ram
                  })

        if count_error:
            value_str = ", ".join(count_error)
            msg = (_('The requested instance cannot be launched. '
                     'The following requested resource(s) exceed '
                     'quota(s): %s.') % value_str)
            if count == 1:
                self._errors['flavor'] = self.error_class([msg])
            else:
                self._errors['count'] = self.error_class([msg])

    def _check_flavor_for_image(self, cleaned_data):
        # Prevents trying to launch an image needing more resources.
        image_id = cleaned_data.get('image_id')
        image = self._get_image(image_id)
        flavor_id = cleaned_data.get('flavor')
        flavor = self._get_flavor(flavor_id)
        if not image or not flavor:
            return
        props_mapping = (("min_ram", "ram"), ("min_disk", "disk"))
        for iprop, fprop in props_mapping:
            if getattr(image, iprop) > 0 and \
                    getattr(image, iprop) > getattr(flavor, fprop):
                msg = (_("The flavor '%(flavor)s' is too small "
                         "for requested image.\n"
                         "Minimum requirements: "
                         "%(min_ram)s MB of RAM and "
                         "%(min_disk)s GB of Root Disk.") % {
                             'flavor': flavor.name,
                             'min_ram': image.min_ram,
                             'min_disk': image.min_disk
                         })
                self._errors['image_id'] = self.error_class([msg])
                break  # Not necessary to continue the tests.

    def _check_volume_for_image(self, cleaned_data):
        image_id = cleaned_data.get('image_id')
        image = self._get_image(image_id)
        volume_size = cleaned_data.get('volume_size')
        if not image or not volume_size:
            return
        volume_size = int(volume_size)
        img_gigs = functions.bytes_to_gigabytes(image.size)
        smallest_size = max(img_gigs, image.min_disk)
        if volume_size < smallest_size:
            msg = (_("The Volume size is too small for the"
                     " '%(image_name)s' image and has to be"
                     " greater than or equal to "
                     "'%(smallest_size)d' GB.") % {
                         'image_name': image.name,
                         'smallest_size': smallest_size
                     })
            self._errors['volume_size'] = self.error_class([msg])

    def _check_source_image(self, cleaned_data):
        if not cleaned_data.get('image_id'):
            msg = _("You must select an image.")
            self._errors['image_id'] = self.error_class([msg])
        else:
            self._check_flavor_for_image(cleaned_data)

    def _check_source_volume_image(self, cleaned_data):
        volume_size = self.data.get('volume_size', None)
        if not volume_size:
            msg = _("You must set volume size")
            self._errors['volume_size'] = self.error_class([msg])
        if float(volume_size) <= 0:
            msg = _("Volume size must be greater than 0")
            self._errors['volume_size'] = self.error_class([msg])
        if not cleaned_data.get('image_id'):
            msg = _("You must select an image.")
            self._errors['image_id'] = self.error_class([msg])
            return
        else:
            self._check_flavor_for_image(cleaned_data)
            self._check_volume_for_image(cleaned_data)

    def _check_source_instance_snapshot(self, cleaned_data):
        # using the array form of get blows up with KeyError
        # if instance_snapshot_id is nil
        if not cleaned_data.get('instance_snapshot_id'):
            msg = _("You must select a snapshot.")
            self._errors['instance_snapshot_id'] = self.error_class([msg])

    def _check_source_volume(self, cleaned_data):
        if not cleaned_data.get('volume_id'):
            msg = _("You must select a volume.")
            self._errors['volume_id'] = self.error_class([msg])
        # Prevent launching multiple instances with the same volume.
        # TODO(gabriel): is it safe to launch multiple instances with
        # a snapshot since it should be cloned to new volumes?
        count = cleaned_data.get('count', 1)
        if count > 1:
            msg = _('Launching multiple instances is only supported for '
                    'images and instance snapshots.')
            raise forms.ValidationError(msg)

    def _check_source_volume_snapshot(self, cleaned_data):
        if not cleaned_data.get('volume_snapshot_id'):
            msg = _("You must select a snapshot.")
            self._errors['volume_snapshot_id'] = self.error_class([msg])

    def _check_source(self, cleaned_data):
        # Validate our instance source.
        source_type = self.data.get('source_type', None)
        source_check_methods = {
            'image_id': self._check_source_image,
            'volume_image_id': self._check_source_volume_image,
            'instance_snapshot_id': self._check_source_instance_snapshot,
            'volume_id': self._check_source_volume,
            'volume_snapshot_id': self._check_source_volume_snapshot
        }
        check_method = source_check_methods.get(source_type)
        if check_method:
            check_method(cleaned_data)

    def clean(self):
        cleaned_data = super(SetInstanceDetailsAction, self).clean()

        self._check_quotas(cleaned_data)
        self._check_source(cleaned_data)

        return cleaned_data

    def populate_flavor_choices(self, request, context):
        return instance_utils.flavor_field_data(request, False)

    def populate_availability_zone_choices(self, request, context):
        try:
            zones = api.nova.availability_zone_list(request)
        except Exception:
            zones = []
            exceptions.handle(request,
                              _('Unable to retrieve availability zones.'))

        zone_list = [(zone.zoneName, zone.zoneName) for zone in zones
                     if zone.zoneState['available']]
        zone_list.sort()
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 1:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        return zone_list

    def get_help_text(self, extra_context=None):
        extra = {} if extra_context is None else dict(extra_context)
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request,
                                                              reserved=True)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps(
                [f._info for f in instance_utils.flavor_list(self.request)])
            extra['flavors'] = flavors
            images = image_utils.get_available_images(
                self.request, self.initial['project_id'], self._images_cache)
            if images is not None:
                attrs = [{
                    'id': i.id,
                    'min_disk': getattr(i, 'min_disk', 0),
                    'min_ram': getattr(i, 'min_ram', 0),
                    'size': functions.bytes_to_gigabytes(i.size)
                } for i in images]
                extra['images'] = json.dumps(attrs)

        except Exception:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(SetInstanceDetailsAction, self).get_help_text(extra)

    def _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    def _get_volume_display_name(self, volume):
        if hasattr(volume, "volume_id"):
            vol_type = "snap"
            visible_label = _("Snapshot")
        else:
            vol_type = "vol"
            visible_label = _("Volume")
        return (("%s:%s" % (volume.id, vol_type)),
                (_("%(name)s - %(size)s GB (%(label)s)") % {
                    'name': volume.name,
                    'size': volume.size,
                    'label': visible_label
                }))

    def populate_image_id_choices(self, request, context):
        choices = []
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        for image in images:
            image.bytes = getattr(image, 'virtual_size', None) or image.size
            image.volume_size = max(image.min_disk,
                                    functions.bytes_to_gigabytes(image.bytes))
            choices.append((image.id, image))
            if context.get('image_id') == image.id and \
                    'volume_size' not in context:
                context['volume_size'] = image.volume_size
        if choices:
            choices.sort(key=lambda c: c[1].name or '')
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        return choices

    def populate_instance_snapshot_id_choices(self, request, context):
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        choices = [(image.id, image.name) for image in images
                   if image.properties.get("image_type", '') == "snapshot"]
        if choices:
            choices.sort(key=operator.itemgetter(1))
            choices.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available")))
        return choices

    def populate_volume_id_choices(self, request, context):
        volumes = []
        try:
            if cinder.is_volume_service_enabled(request):
                available = api.cinder.VOLUME_STATE_AVAILABLE
                volumes = [
                    self._get_volume_display_name(v)
                    for v in cinder.volume_list(
                        self.request,
                        search_opts=dict(status=available, bootable=True))
                ]
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        if volumes:
            volumes.insert(0, ("", _("Select Volume")))
        else:
            volumes.insert(0, ("", _("No volumes available")))
        return volumes

    def populate_volume_snapshot_id_choices(self, request, context):
        snapshots = []
        try:
            if cinder.is_volume_service_enabled(request):
                available = api.cinder.VOLUME_STATE_AVAILABLE
                snapshots = [
                    self._get_volume_display_name(s)
                    for s in cinder.volume_snapshot_list(
                        self.request, search_opts=dict(status=available))
                ]
        except Exception:
            exceptions.handle(
                self.request,
                _('Unable to retrieve list of volume '
                  'snapshots.'))
        if snapshots:
            snapshots.insert(0, ("", _("Select Volume Snapshot")))
        else:
            snapshots.insert(0, ("", _("No volume snapshots available")))
        return snapshots
Ejemplo n.º 6
0
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Volume Name"))
    description = forms.CharField(widget=forms.Textarea,
                                  label=_("Description"),
                                  required=False)
    size = forms.IntegerField(min_value=1, label=_("Size (GB)"))
    snapshot_source = forms.ChoiceField(
        label=_("Use snapshot as a source"),
        widget=SelectWidget(attrs={'class': 'snapshot-selector'},
                            data_attrs=('size', 'display_name'),
                            transform=lambda x: ("%s (%sGB)" %
                                                 (x.display_name, x.size))),
        required=False)

    def __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        if ("snapshot_id" in request.GET):
            try:
                snapshot = self.get_snapshot(request,
                                             request.GET["snapshot_id"])
                self.fields['name'].initial = snapshot.display_name
                self.fields['size'].initial = snapshot.size
                self.fields['snapshot_source'].choices = ((snapshot.id,
                                                           snapshot), )
                self.fields['size'].help_text = _(
                    'Volume size must be equal '
                    'to or greater than the snapshot size (%sGB)' %
                    snapshot.size)
            except:
                exceptions.handle(request,
                                  _('Unable to load the specified snapshot.'))
        else:
            try:
                snapshots = api.volume_snapshot_list(request)
                if snapshots:
                    choices = [('', _("Choose a snapshot"))] + \
                              [(s.id, s) for s in snapshots]
                    self.fields['snapshot_source'].choices = choices
                else:
                    del self.fields['snapshot_source']
            except:
                exceptions.handle(request,
                                  _("Unable to retrieve "
                                    "volume snapshots."))

    def handle(self, request, data):
        try:
            # FIXME(johnp): cinderclient currently returns a useless
            # error message when the quota is exceeded when trying to create
            # a volume, so we need to check for that scenario here before we
            # send it off to try and create.
            usages = api.tenant_quota_usages(request)

            snapshot_id = None
            if (data.get("snapshot_source", None)):
                # Create from Snapshot
                snapshot = self.get_snapshot(request, data["snapshot_source"])
                snapshot_id = snapshot.id
                if (data['size'] < snapshot.size):
                    error_message = _('The volume size cannot be less than '
                                      'the snapshot size (%sGB)' %
                                      snapshot.size)
                    raise ValidationError(error_message)
            else:
                if type(data['size']) is str:
                    data['size'] = int(data['size'])

            if usages['gigabytes']['available'] < data['size']:
                error_message = _('A volume of %(req)iGB cannot be created as '
                                  'you only have %(avail)iGB of your quota '
                                  'available.')
                params = {
                    'req': data['size'],
                    'avail': usages['gigabytes']['available']
                }
                raise ValidationError(error_message % params)
            elif usages['volumes']['available'] <= 0:
                error_message = _('You are already using all of your available'
                                  ' volumes.')
                raise ValidationError(error_message)

            volume = api.volume_create(request,
                                       data['size'],
                                       data['name'],
                                       data['description'],
                                       snapshot_id=snapshot_id)
            message = 'Creating volume "%s"' % data['name']
            messages.info(request, message)
            return volume
        except ValidationError, e:
            return self.api_error(e.messages[0])
        except:
Ejemplo n.º 7
0
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(
        max_length=80, label=_("Pre-Shared Key (PSK) string"))
    mtu = forms.IntegerField(
        min_value=68,
        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"),
        choices=[('hold', _('hold')),
                 ('clear', _('clear')),
                 ('disabled', _('disabled')),
                 ('restart', _('restart')),
                 ('restart-by-peer', _('restart-by-peer'))])
    dpd_interval = forms.IntegerField(
        min_value=1,
        label=_("Dead peer detection interval"),
        help_text=_("Valid integer"))
    dpd_timeout = forms.IntegerField(
        min_value=1,
        label=_("Dead peer detection timeout"),
        help_text=_("Valid integer greater than the DPD interval"))
    initiator = forms.ChoiceField(
        label=_("Initiator state"),
        choices=[('bi-directional', _('bi-directional')),
                 ('response-only', _('response-only'))])
    admin_state_up = forms.BooleanField(label=_("Admin State"), required=False)

    failure_url = 'horizon:project:vpn:index'

    def handle(self, request, context):
        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('%s: %s' % (msg, e))
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 8
0
class UpdateIPSecPolicy(forms.SelfHandlingForm):
    name = forms.CharField(max_length=80, label=_("Name"), required=False)
    ipsecpolicy_id = forms.CharField(
        label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    description = forms.CharField(required=False,
                                  max_length=80,
                                  label=_("Description"))
    # Currently this field has only one choice, so mark it as readonly.
    auth_algorithm = forms.ChoiceField(
        label=_("Authorization algorithm"),
        choices=[('sha1', _('sha1'))],
        widget=forms.TextInput(attrs={'readonly': 'readonly'}),
        required=False)
    encapsulation_mode = forms.ChoiceField(label=_("Encapsulation mode"),
                                           choices=[('tunnel', _('tunnel')),
                                                    ('transport',
                                                     _('transport'))],
                                           required=False)
    encryption_algorithm = forms.ChoiceField(label=_("Encryption algorithm"),
                                             choices=[
                                                 ('3des', _('3des')),
                                                 ('aes-128', _('aes-128')),
                                                 ('aes-192', _('aes-192')),
                                                 ('aes-256', _('aes-256'))
                                             ],
                                             required=False)
    # Currently this field has only one choice, so mark it as readonly.
    lifetime_units = forms.ChoiceField(
        label=_("Lifetime units"),
        choices=[('seconds', _('seconds'))],
        widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}),
        required=False)
    lifetime_value = forms.IntegerField(
        min_value=60,
        label=_("Lifetime value"),
        help_text=_("Equal to or greater than 60"),
        required=False)
    pfs = forms.ChoiceField(label=_("Perfect Forward Secrecy"),
                            choices=[('group2', _('group2')),
                                     ('group5', _('group5')),
                                     ('group14', _('group14'))],
                            required=False)
    transform_protocol = forms.ChoiceField(label=_("Transform Protocol"),
                                           choices=[('esp', _('esp')),
                                                    ('ah', _('ah')),
                                                    ('ah-esp', _('ah-esp'))],
                                           required=False)

    failure_url = 'horizon:project:vpn:index'

    def handle(self, request, context):
        try:
            data = {
                'ipsecpolicy': {
                    'name': context['name'],
                    'description': context['description'],
                    'auth_algorithm': context['auth_algorithm'],
                    'encapsulation_mode': context['encapsulation_mode'],
                    'encryption_algorithm': context['encryption_algorithm'],
                    'lifetime': {
                        'units': context['lifetime_units'],
                        'value': context['lifetime_value']
                    },
                    'pfs': context['pfs'],
                    'transform_protocol': context['transform_protocol'],
                }
            }
            ipsecpolicy = api.vpn.ipsecpolicy_update(request,
                                                     context['ipsecpolicy_id'],
                                                     **data)
            msg = (_('IPSec Policy %s was successfully updated.') %
                   context['name'])
            messages.success(request, msg)
            return ipsecpolicy
        except Exception as e:
            LOG.info('Failed to update IPSec Policy %(id)s: %(exc)s', {
                'id': context['ipsecpolicy_id'],
                'exc': e
            })
            msg = _('Failed to update IPSec Policy %s') % context['name']
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 9
0
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Server Group Name"))
    policy = forms.ChoiceField(label=_("Policy"),
                               required=False,
                               widget=forms.Select(attrs={
                                   'class': 'switchable',
                                   'data-slug': 'policy_ht'
                               }))

    is_best_effort = forms.BooleanField(label=_("Best Effort"), required=False)

    group_size = forms.IntegerField(
        min_value=1,
        label=_("Max Group Size (Instances)"),
        required=False,
        widget=forms.TextInput(
            attrs={
                'class': 'switchable switched',
                'data-switch-on': 'policy_ht',
                'data-policy_ht-anti-affinity': 'Max Group Size (Instances)',
                'data-policy_ht-affinity': 'Max Group Size (Instances)'
            }))

    group_size_ht = forms.IntegerField(
        label=_("Max Group Size (Instances)"),
        required=False,
        widget=forms.TextInput(
            attrs={
                'readonly': 'readonly',
                'class': 'switchable switched',
                'data-switch-on': 'policy_ht',
                'data-policy_ht-affinity-hyperthread':
                'Max Group Size (Instances)'
            }))

    def __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        self.fields['policy'].choices = [("anti-affinity", "anti-affinity"),
                                         ("affinity", "affinity")]

    def handle(self, request, data):
        try:
            project_id = self.request.user.tenant_id
            policy = data['policy']
            policies = []
            if policy:
                policies.append(policy)
            metadata = {}
            if data['is_best_effort']:
                metadata['wrs-sg:best_effort'] = "true"
            group_size = data['group_size']
            group_size_ht = data['group_size_ht']
            if group_size:
                metadata['wrs-sg:group_size'] = str(group_size)
            elif group_size_ht:
                metadata['wrs-sg:group_size'] = str(group_size_ht)

            server_group = nova.server_group_create(request, data['name'],
                                                    project_id, metadata,
                                                    policies)

            return server_group

        except ValidationError as e:
            self.api_error(e.messages[0])
            return False
        except Exception:
            exceptions.handle(request, ignore=True)
            self.api_error(_("Unable to create server group."))
            return False
Ejemplo n.º 10
0
class UpdateIPsecSiteConnection(forms.SelfHandlingForm):
    name = forms.CharField(max_length=80, label=_("Name"), required=False)
    description = forms.CharField(required=False,
                                  max_length=80,
                                  label=_("Description"))
    peer_address = forms.CharField(
        label=_("Peer gateway public IPv4/IPv6 Address or FQDN"),
        help_text=_("Peer gateway public IPv4/IPv6 address or FQDN for "
                    "the VPN Connection"),
    )
    peer_id = forms.CharField(
        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"),
    )
    peer_cidrs = forms.MultiIPField(required=False,
                                    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)
    local_ep_group_id = forms.CharField(
        required=False,
        label=_("Local Endpoint Group(s)"),
        help_text=_("IPsec connection validation requires "
                    "that local endpoints are subnets"))
    peer_ep_group_id = forms.CharField(
        required=False,
        label=_("Peer Endpoint Group(s)"),
        help_text=_("IPsec connection validation requires "
                    "that peer endpoints are CIDRs"))
    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.ThemableChoiceField(
        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.ThemableChoiceField(label=_("Initiator state"),
                                          required=False,
                                          choices=[('bi-directional',
                                                    _('bi-directional')),
                                                   ('response-only',
                                                    _('response-only'))])
    admin_state_up = forms.BooleanField(label=_("Enable 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):
        try:
            data = {
                'name': context['name'],
                'description': context['description'],
                'peer_address': context['peer_address'],
                'peer_id': context['peer_id'],
                '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']
            }
            if not context['peer_cidrs']:
                data['local_ep_group_id'] = context['local_ep_group_id']
                data['peer_ep_group_id'] = context['peer_ep_group_id']
            else:
                cidrs = context['peer_cidrs']
                data['peer_cidrs'] = [
                    cidr.strip() for cidr in cidrs.split(',') if cidr.strip()
                ]
            ipsecsiteconnection = api_vpn.ipsecsiteconnection_update(
                request,
                self.initial['ipsecsiteconnection_id'],
                ipsec_site_connection=data)
            msg = (_('IPsec site connection %s was successfully updated.') %
                   context['name'])
            messages.success(request, msg)
            return ipsecsiteconnection
        except Exception as e:
            LOG.info('Failed to update IPsec site connection %(id)s: %(exc)s',
                     {
                         'id': self.initial['ipsecsiteconnection_id'],
                         'exc': e
                     })
            msg = (_('Failed to update IPsec site connection %s') %
                   context['name'])
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 11
0
class CreateDatasource(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255,
                           label=_("Data Source Name"),
                           help_text='Name of the data source')

    driver = forms.ThemableChoiceField(label=_("Driver"),
                                       help_text='Data Source driver')

    description = forms.CharField(label=_("Description"),
                                  required=False,
                                  help_text='Data Source Description')
    username = forms.CharField(
        max_length=255,
        label=_("UserName"),
        help_text='username to connect to the driver service')

    password = forms.CharField(widget=forms.PasswordInput(render_value=False),
                               label=_('Password'))

    tenant_name = forms.CharField(max_length=255, label=_("Project Name"))

    # TODO(ramineni): support adding lazy tables
    # lazy_tables = forms.CharField(max_length=255, label=_("Lazy Tables"))
    auth_url = forms.URLField(max_length=255, label=_("Keystone Auth URL"))
    poll_time = forms.IntegerField(
        label=_("Poll Interval (in seconds)"),
        help_text='periodic interval congress needs to poll data')

    failure_url = 'horizon:admin:datasources:index'

    @classmethod
    def _instantiate(cls, request, *args, **kwargs):
        return cls(request, *args, **kwargs)

    def __init__(self, request, *args, **kwargs):
        super(CreateDatasource, self).__init__(request, *args, **kwargs)
        driver_choices = [('', _("Select a Driver"))]
        drivers = congress.supported_driver_list(request)
        driver_choices.extend(drivers)
        self.fields['driver'].choices = driver_choices

    def handle(self, request, data):
        datasource_name = data['name']
        datasource_description = data.get('description')
        datasource_driver = data.pop('driver')
        config = {
            'username': data['username'],
            'password': data['password'],
            'tenant_name': data['tenant_name'],
            'auth_url': data['auth_url'],
            'poll_time': data['poll_time']
        }
        try:
            params = {
                'name': datasource_name,
                'driver': datasource_driver,
                'description': datasource_description,
                'config': config
            }
            datasource = congress.create_datasource(request, params)
            msg = _('Created Data Source "%s"') % datasource_name
            LOG.info(msg)
            messages.success(request, msg)
        except Exception as e:
            msg_args = {'datasource_name': datasource_name, 'error': str(e)}
            msg = _('Failed to create data source "%(datasource_name)s": '
                    '%(error)s') % msg_args
            LOG.error(msg)
            messages.error(self.request, msg)
            redirect = reverse(self.failure_url)
            raise exceptions.Http302(redirect)
        return datasource
Ejemplo n.º 12
0
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 &quot;CIDR&quot;. '
                                           'To allow access from all '
                                           'members of another security '
                                           'group select &quot;Security '
                                           'Group&quot;.'),
                               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 _update_and_pop_error(self, cleaned_data, key, value):
        cleaned_data[key] = value
        self.errors.pop(key, None)

    def _clean_rule_icmp(self, cleaned_data, rule_menu):
        icmp_type = cleaned_data.get("icmp_type", None)
        icmp_code = cleaned_data.get("icmp_code", None)

        self._update_and_pop_error(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)
        self._update_and_pop_error(cleaned_data, 'from_port', icmp_type)
        self._update_and_pop_error(cleaned_data, 'to_port', icmp_code)
        self._update_and_pop_error(cleaned_data, 'port', None)

    def _clean_rule_tcp_udp(self, cleaned_data, rule_menu):
        port_or_range = cleaned_data.get("port_or_range")
        from_port = cleaned_data.get("from_port", None)
        to_port = cleaned_data.get("to_port", None)
        port = cleaned_data.get("port", None)

        self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu)
        self._update_and_pop_error(cleaned_data, 'icmp_code', None)
        self._update_and_pop_error(cleaned_data, 'icmp_type', None)
        if port_or_range == "port":
            self._update_and_pop_error(cleaned_data, 'from_port', port)
            self._update_and_pop_error(cleaned_data, 'to_port', port)
            if port is None:
                msg = _('The specified port is invalid.')
                raise ValidationError(msg)
        else:
            self._update_and_pop_error(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)

    def _apply_rule_menu(self, cleaned_data, rule_menu):
        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

    def _clean_rule_menu(self, cleaned_data):
        rule_menu = cleaned_data.get('rule_menu')
        if rule_menu == 'icmp':
            self._clean_rule_icmp(cleaned_data, rule_menu)
        elif rule_menu == 'tcp' or rule_menu == 'udp':
            self._clean_rule_tcp_udp(cleaned_data, rule_menu)
        elif rule_menu == 'custom':
            pass
        else:
            self._apply_rule_menu(cleaned_data, rule_menu)

    def clean(self):
        cleaned_data = super(AddRule, self).clean()

        self._clean_rule_menu(cleaned_data)

        # 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'

        remote = cleaned_data.get("remote")
        if remote == "cidr":
            self._update_and_pop_error(cleaned_data, 'security_group', None)
        else:
            self._update_and_pop_error(cleaned_data, 'cidr', None)

        # If cleaned_data does not contain a non-empty value, IPField already
        # has validated it, 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') % six.text_type(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)
Ejemplo n.º 13
0
class AddRule(forms.SelfHandlingForm):
    id = forms.CharField(widget=forms.HiddenInput())
    rule_menu = forms.ChoiceField(
        label=_('Rule'),
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'rule_menu'
        }))
    description = forms.CharField(
        label=_('Description'),
        required=False,
        max_length=255,
        widget=forms.Textarea(attrs={'rows': 2}),
        help_text=_('A brief description of the security group rule '
                    'you are adding'))

    # "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.ThemableSelectWidget(
            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."),
        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.ThemableSelectWidget(
            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-required-when-shown': 'true',
                '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-required-when-shown': 'true',
                '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-required-when-shown': 'true',
                '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_icmp_type_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_icmp_code_range])

    remote = forms.ChoiceField(
        label=_('Remote'),
        choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))],
        help_text=_('To specify an allowed IP '
                    'range, select &quot;CIDR&quot;. '
                    'To allow access from all '
                    'members of another security '
                    'group select &quot;Security '
                    'Group&quot;.'),
        widget=forms.ThemableSelectWidget(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, or "
                                     "2001:db8::/128)"),
                         version=forms.IPv4 | forms.IPv6,
                         mask=True,
                         widget=forms.TextInput(
                             attrs={
                                 'class': 'switched',
                                 'data-required-when-shown': 'true',
                                 'data-switch-on': 'remote',
                                 'data-remote-cidr': _('CIDR')
                             }))

    security_group = forms.ChoiceField(
        label=_('Security Group'),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={
                'class': 'switched',
                'data-required-when-shown': 'true',
                '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.ThemableSelectWidget(
                                      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

        # TODO(amotoki): settings.SECURITY_GROUP_RULES may contains 'backend'
        # parameter. If 'backend' is used, error message should be emitted.
        backend = 'neutron'

        rules_dict = 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')),
                        ('custom', _('Other Protocol'))]
        self.fields['rule_menu'].choices = custom_rules + common_rules
        self.rules = rules_dict

        self.fields['direction'].choices = [('ingress', _('Ingress')),
                                            ('egress', _('Egress'))]
        self.fields['ip_protocol'].help_text = _(
            "Enter an integer value between -1 and 255 "
            "(-1 means wild card).")

        self.fields['port_or_range'].choices = [
            ('port', _('Port')),
            ('range', _('Port Range')),
            ('all', _('All ports')),
        ]

        if not setting_utils.get_dict_config('OPENSTACK_NEUTRON_NETWORK',
                                             'enable_ipv6'):
            self.fields['cidr'].version = forms.IPv4
            self.fields['ethertype'].widget = forms.TextInput(
                attrs={'readonly': 'readonly'})
            self.fields['ethertype'].initial = 'IPv4'

        try:
            is_desc_supported = api.neutron.is_extension_supported(
                self.request, 'standard-attr-description')
        except Exception:
            exceptions.handle(
                self.request,
                _('Failed to check if description field is supported.'))
            is_desc_supported = False
        if not is_desc_supported:
            del self.fields['description']

    def _update_and_pop_error(self, cleaned_data, key, value):
        cleaned_data[key] = value
        self.errors.pop(key, None)

    def _clean_rule_icmp(self, cleaned_data, rule_menu):
        icmp_type = cleaned_data.get("icmp_type", None)
        icmp_code = cleaned_data.get("icmp_code", None)

        self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu)
        if icmp_type == -1 and icmp_code != -1:
            msg = _('ICMP code is provided but ICMP type is missing.')
            raise ValidationError(msg)
        if self.errors.get('icmp_type'):
            msg = _('The ICMP type not in range (-1, 255)')
            raise ValidationError(msg)
        if self.errors.get('icmp_code'):
            msg = _('The ICMP code not in range (-1, 255)')
            raise ValidationError(msg)

        self._update_and_pop_error(cleaned_data, 'from_port', icmp_type)
        self._update_and_pop_error(cleaned_data, 'to_port', icmp_code)
        self._update_and_pop_error(cleaned_data, 'port', None)

    def _clean_rule_tcp_udp(self, cleaned_data, rule_menu):
        port_or_range = cleaned_data.get("port_or_range")
        from_port = cleaned_data.get("from_port", None)
        to_port = cleaned_data.get("to_port", None)
        port = cleaned_data.get("port", None)

        self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu)
        self._update_and_pop_error(cleaned_data, 'icmp_code', None)
        self._update_and_pop_error(cleaned_data, 'icmp_type', None)
        if port_or_range == 'all':
            self._update_and_pop_error(cleaned_data, 'port', None)
            self._update_and_pop_error(cleaned_data, 'from_port', None)
            self._update_and_pop_error(cleaned_data, 'to_port', None)
        elif port_or_range == "port":
            self._update_and_pop_error(cleaned_data, 'from_port', port)
            self._update_and_pop_error(cleaned_data, 'to_port', port)
            if port is None:
                msg = _('The specified port is invalid.')
                raise ValidationError(msg)
        else:
            self._update_and_pop_error(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)

    def _clean_rule_custom(self, cleaned_data, rule_menu):
        # custom IP protocol rule so we need to fill unused fields so
        # the validation works
        unused_fields = [
            'icmp_code', 'icmp_type', 'from_port', 'to_port', 'port'
        ]
        for unused_field in unused_fields:
            self._update_and_pop_error(cleaned_data, unused_field, None)

    def _apply_rule_menu(self, cleaned_data, rule_menu):
        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'])
        self._update_and_pop_error(cleaned_data, 'icmp_code', None)
        self._update_and_pop_error(cleaned_data, 'icmp_type', None)
        if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']:
            direction = self.rules[rule_menu].get('direction')
            cleaned_data['direction'] = direction

    def _clean_rule_menu(self, cleaned_data):
        rule_menu = cleaned_data.get('rule_menu')
        if rule_menu == 'icmp':
            self._clean_rule_icmp(cleaned_data, rule_menu)
        elif rule_menu in ('tcp', 'udp'):
            self._clean_rule_tcp_udp(cleaned_data, rule_menu)
        elif rule_menu == 'custom':
            self._clean_rule_custom(cleaned_data, rule_menu)
        else:
            self._apply_rule_menu(cleaned_data, rule_menu)

    def _adjust_ip_protocol_of_icmp(self, data):
        # Note that this needs to be called after IPv4/IPv6 is determined.
        try:
            ip_protocol = int(data['ip_protocol'])
        except ValueError:
            # string representation of IP protocol
            ip_protocol = data['ip_protocol']
        is_ipv6 = data['ethertype'] == 'IPv6'

        if isinstance(ip_protocol, int):
            # When IP protocol number is specified, we assume a user
            # knows more detail on IP protocol number,
            # so a warning message on a mismatch between IP proto number
            # and IP version is displayed.
            if is_ipv6 and ip_protocol == 1:
                msg = _('58 (ipv6-icmp) should be specified for IPv6 '
                        'instead of 1.')
                self._errors['ip_protocol'] = self.error_class([msg])
            elif not is_ipv6 and ip_protocol == 58:
                msg = _('1 (icmp) should be specified for IPv4 '
                        'instead of 58.')
                self._errors['ip_protocol'] = self.error_class([msg])
        else:
            # ICMPv6 uses different an IP protocol name and number.
            # To allow 'icmp' for both IPv4 and IPv6 in the form,
            # we need to replace 'icmp' with 'ipv6-icmp' based on IP version.
            if is_ipv6 and ip_protocol == 'icmp':
                data['ip_protocol'] = 'ipv6-icmp'

    def clean(self):
        cleaned_data = super(AddRule, self).clean()

        self._clean_rule_menu(cleaned_data)

        # 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'

        remote = cleaned_data.get("remote")
        if remote == "cidr":
            self._update_and_pop_error(cleaned_data, 'security_group', None)
        else:
            self._update_and_pop_error(cleaned_data, 'cidr', None)

        # If cleaned_data does not contain a non-empty value, IPField already
        # has validated it, 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'

        self._adjust_ip_protocol_of_icmp(cleaned_data)

        return cleaned_data

    def handle(self, request, data):
        redirect = reverse("horizon:project:security_groups:detail",
                           args=[data['id']])
        params = {}
        if 'description' in data:
            params['description'] = data['description']
        try:
            rule = api.neutron.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'], **params)
            messages.success(request, _('Successfully added rule: %s') % rule)
            return rule
        except exceptions.Conflict as error:
            exceptions.handle(request, error, redirect=redirect)
        except Exception:
            exceptions.handle(request,
                              _('Unable to add rule to security group.'),
                              redirect=redirect)
Ejemplo n.º 14
0
class CreateProviderNetworkRange(forms.SelfHandlingForm):
    providernet_id = forms.CharField(widget=forms.HiddenInput())
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    description = forms.CharField(max_length=255,
                                  label=_("Description"),
                                  required=False)
    shared = forms.BooleanField(label=_("Shared"),
                                initial=False,
                                required=False,
                                widget=forms.CheckboxInput(
                                    attrs={
                                        'class': 'switchable',
                                        'data-hide-on-checked': 'true',
                                        'data-slug': 'is_shared'
                                    }))

    tenant_id = forms.ChoiceField(
        label=_("Project"),
        required=False,
        widget=forms.Select(attrs={
            'class': 'switched',
            'data-switch-on': 'is_shared'
        }))

    minimum = forms.IntegerField(label=_("Minimum"), min_value=1)
    maximum = forms.IntegerField(label=_("Maximum"), min_value=1)
    # VXLAN specific fields
    mode_choices = [('dynamic', _('Multicast VXLAN')),
                    ('static', _('Static VXLAN'))]
    mode = forms.ChoiceField(label=_("Mode"),
                             initial='dynamic',
                             required=False,
                             choices=mode_choices,
                             widget=forms.Select(attrs={
                                 'class': 'switchable',
                                 'data-slug': 'vxlan_mode'
                             }))
    group_help = (_("Specify the IPv4 or IPv6 multicast address for these "
                    "VXLAN instances"))
    group = forms.CharField(
        max_length=255,
        label=_("Multicast Group Address"),
        initial="239.0.0.1",
        required=False,
        help_text=group_help,
        widget=forms.TextInput(
            attrs={
                'class': 'switchable switched',
                'data-slug': 'vxlan_group',
                'data-switch-on': 'vxlan_mode',
                'data-vxlan_mode-dynamic': 'Multicast Group Address'
            }))
    port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')),
                    ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')),
                    ('8472', _('Legacy VXLAN UDP port (8472)'))]
    port = forms.ChoiceField(label=_("UDP Port"),
                             required=True,
                             widget=forms.RadioSelect(),
                             choices=port_choices)
    ttl = forms.IntegerField(
        label=_("TTL"),
        required=False,
        initial=1,
        min_value=1,
        max_value=255,
        help_text=(
            _("Specify the time-to-live value for these VXLAN instances")))

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

        tenant_choices = [('', _("Select a project"))]
        tenants, has_more = api.keystone.tenant_list(request)  # noqa pylint: disable=unused-variable
        for tenant in tenants:
            if tenant.enabled:
                tenant_choices.append((tenant.id, tenant.name))
        self.fields['tenant_id'].choices = tenant_choices
        initial = kwargs['initial']
        if 'providernet_type' in initial:
            providernet_type = initial['providernet_type']
            if providernet_type != "vxlan":
                del self.fields["mode"]
                del self.fields["group"]
                del self.fields["port"]
                del self.fields["ttl"]

    def handle(self, request, data):
        try:
            params = {
                'providernet_id': data['providernet_id'],
                'name': data['name'],
                'description': data['description'],
                'minimum': data['minimum'],
                'maximum': data['maximum'],
                'shared': data['shared'],
                'tenant_id': data['tenant_id']
            }

            if not data['tenant_id']:
                params['shared'] = True

            if self.initial['providernet_type'] == "vxlan":
                params['mode'] = data['mode']
                if params['mode'] == 'dynamic':
                    params['group'] = data['group']
                params['port'] = int(data['port'])
                params['ttl'] = int(data['ttl'])

            providernet_range = stx_api.neutron.provider_network_range_create(
                request, **params)
            msg = (_('Provider network range %s was successfully created.') %
                   providernet_range['id'])
            LOG.debug(msg)
            messages.success(request, msg)
            return providernet_range
        except neutron_exceptions.NeutronClientException as e:
            LOG.info(str(e))
            redirect = reverse('horizon:admin:datanets:datanets:'
                               'detail',
                               args=(data['providernet_id'], ))
            exceptions.handle(request, str(e), redirect=redirect)
        except Exception:
            msg = _('Failed to create a provider'
                    ' network range for network %s') \
                % data['providernet_id']
            LOG.info(msg)
            redirect = reverse('horizon:admin:datanets:datanets:'
                               'detail',
                               args=(data['providernet_id'], ))
            exceptions.handle(request, msg, redirect=redirect)

    def clean(self):
        cleaned_data = super(CreateProviderNetworkRange, self).clean()
        if not cleaned_data["shared"] and not cleaned_data["tenant_id"]:
            msg = "Project must be specified for non-shared Segmentation Range"
            raise forms.ValidationError(msg)
        if cleaned_data["shared"]:
            cleaned_data["tenant_id"] = ""
Ejemplo n.º 15
0
class LaunchForm(forms.SelfHandlingForm):
    name = forms.CharField(label=_("Cluster Name"), max_length=80)
    datastore = forms.ChoiceField(
        label=_("Datastore"),
        help_text=_("Type and version of datastore."),
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'datastore'
        }))
    network = forms.ChoiceField(label=_("Network"),
                                help_text=_("Network attached to instance."),
                                required=False)
    volume = forms.IntegerField(label=_("Volume Size"),
                                min_value=0,
                                initial=1,
                                help_text=_("Size of the volume in GB."))
    locality = forms.ChoiceField(
        label=_("Locality"),
        choices=[("", "None"), ("affinity", "affinity"),
                 ("anti-affinity", "anti-affinity")],
        required=False,
        help_text=_("Specify whether instances in the cluster will "
                    "be created on the same hypervisor (affinity) or on "
                    "different hypervisors (anti-affinity)."))
    root_password = forms.CharField(
        label=_("Root Password"),
        required=False,
        help_text=_("Password for root user."),
        widget=forms.PasswordInput(attrs={
            'class': 'switched',
            'data-switch-on': 'datastore',
        }))
    num_instances_vertica = forms.IntegerField(
        label=_("Number of Instances"),
        min_value=3,
        initial=3,
        required=False,
        help_text=_("Number of instances in the cluster. (Read only)"),
        widget=forms.TextInput(
            attrs={
                'readonly': 'readonly',
                'class': 'switched',
                'data-switch-on': 'datastore',
            }))
    num_shards = forms.IntegerField(
        label=_("Number of Shards"),
        min_value=1,
        initial=1,
        required=False,
        help_text=_("Number of shards. (Read only)"),
        widget=forms.TextInput(
            attrs={
                'readonly': 'readonly',
                'class': 'switched',
                'data-switch-on': 'datastore',
            }))
    num_instances = forms.IntegerField(
        label=_("Number of Instances"),
        initial=3,
        required=False,
        help_text=_("Number of instances in the cluster."),
        widget=forms.TextInput(attrs={
            'class': 'switched',
            'data-switch-on': 'datastore',
        }))

    # (name of field variable, label)
    default_fields = [('num_instances', _('Number of Instances'))]
    mongodb_fields = default_fields + [
        ('num_shards', _('Number of Shards')),
    ]
    vertica_fields = [
        ('num_instances_vertica', ('Number of Instances')),
        ('root_password', _('Root Password')),
    ]

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

        self.fields['datastore'].choices = self.populate_datastore_choices(
            request)
        self.fields['network'].choices = self.populate_network_choices(request)

    def clean(self):
        datastore_field_value = self.data.get("datastore", None)
        if datastore_field_value:
            datastore, datastore_version = (
                create_instance.parse_datastore_and_version_text(
                    common_utils.unhexlify(datastore_field_value)))

            flavor_field_name = self._build_widget_field_name(
                datastore, datastore_version)
            if not self.data.get(flavor_field_name, None):
                msg = _("The flavor must be specified.")
                self._errors[flavor_field_name] = self.error_class([msg])

            if db_capability.is_vertica_datastore(datastore):
                if not self.data.get("root_password", None):
                    msg = _("Password for root user must be specified.")
                    self._errors["root_password"] = self.error_class([msg])
            else:
                if int(self.data.get("num_instances", 0)) < 1:
                    msg = _("The number of instances must be greater than 1.")
                    self._errors["num_instances"] = self.error_class([msg])

                if db_capability.is_mongodb_datastore(datastore):
                    if int(self.data.get("num_shards", 0)) < 1:
                        msg = _("The number of shards must be greater than 1.")
                        self._errors["num_shards"] = self.error_class([msg])

        if not self.data.get("locality", None):
            self.cleaned_data["locality"] = None

        return self.cleaned_data

    @memoized.memoized_method
    def datastore_flavors(self, request, datastore_name, datastore_version):
        try:
            return trove_api.trove.datastore_flavors(request, datastore_name,
                                                     datastore_version)
        except Exception:
            LOG.exception("Exception while obtaining flavors list")
            self._flavors = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to obtain flavors.'),
                              redirect=redirect)

    @memoized.memoized_method
    def populate_network_choices(self, request):
        network_list = []
        try:
            if api.base.is_service_enabled(request, 'network'):
                tenant_id = self.request.user.tenant_id
                networks = api.neutron.network_list_for_tenant(
                    request, tenant_id)
                network_list = [(network.id, network.name_or_id)
                                for network in networks]
            else:
                self.fields['network'].widget = forms.HiddenInput()
        except exceptions.ServiceCatalogException:
            network_list = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to retrieve networks.'),
                              redirect=redirect)
        return network_list

    @memoized.memoized_method
    def datastores(self, request):
        try:
            return trove_api.trove.datastore_list(request)
        except Exception:
            LOG.exception("Exception while obtaining datastores list")
            self._datastores = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to obtain datastores.'),
                              redirect=redirect)

    def filter_cluster_datastores(self, request):
        datastores = []
        for ds in self.datastores(request):
            # TODO(michayu): until capabilities lands
            if db_capability.is_cluster_capable_datastore(ds.name):
                datastores.append(ds)
        return datastores

    @memoized.memoized_method
    def datastore_versions(self, request, datastore):
        try:
            return trove_api.trove.datastore_version_list(request, datastore)
        except Exception:
            LOG.exception("Exception while obtaining datastore version list")
            self._datastore_versions = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to obtain datastore versions.'),
                              redirect=redirect)

    def populate_datastore_choices(self, request):
        choices = ()
        datastores = self.filter_cluster_datastores(request)
        if datastores is not None:
            datastore_flavor_fields = {}
            for ds in datastores:
                versions = self.datastore_versions(request, ds.name)
                if versions:
                    # only add to choices if datastore has at least one version
                    version_choices = ()
                    for v in versions:
                        # NOTE(zhaochao): troveclient API resources are lazy
                        # loading objects. When an attribute is not found, the
                        # get() method of the Manager object will be called
                        # with the ID of the resource. However for
                        # datastore_versions, the get() method is expecting two
                        # arguments: datastore and datastore_version(name), so
                        # TypeError will be raised as not enough arguments are
                        # passed. In Python 2.x, hasattr() won't reraise the
                        # exception(which is not correct), but reraise under
                        # Python 3(which should be correct).
                        # Use v.to_dict() to verify the 'active' info instead.
                        if not v.to_dict().get('active', True):
                            continue
                        selection_text = self._build_datastore_display_text(
                            ds.name, v.name)
                        widget_text = self._build_widget_field_name(
                            ds.name, v.name)
                        version_choices = (version_choices +
                                           ((widget_text, selection_text), ))
                        k, v = self._add_datastore_flavor_field(
                            request, ds.name, v.name)
                        datastore_flavor_fields[k] = v
                        self._add_attr_to_optional_fields(ds.name, widget_text)

                    choices = choices + version_choices
            self._insert_datastore_version_fields(datastore_flavor_fields)
        return choices

    def _add_datastore_flavor_field(self, request, datastore,
                                    datastore_version):
        name = self._build_widget_field_name(datastore, datastore_version)
        attr_key = 'data-datastore-' + name
        field = forms.ChoiceField(label=_("Flavor"),
                                  help_text=_("Size of image to launch."),
                                  required=False,
                                  widget=forms.Select(
                                      attrs={
                                          'class': 'switched',
                                          'data-switch-on': 'datastore',
                                          attr_key: _("Flavor")
                                      }))
        valid_flavors = self.datastore_flavors(request, datastore,
                                               datastore_version)
        if valid_flavors:
            field.choices = instance_utils.sort_flavor_list(
                request, valid_flavors)

        return name, field

    def _build_datastore_display_text(self, datastore, datastore_version):
        return datastore + ' - ' + datastore_version

    def _build_widget_field_name(self, datastore, datastore_version):
        # Since the fieldnames cannot contain an uppercase character
        # we generate a hex encoded string representation of the
        # datastore and version as the fieldname
        return common_utils.hexlify(
            self._build_datastore_display_text(datastore, datastore_version))

    def _insert_datastore_version_fields(self, datastore_flavor_fields):
        fields_to_restore_at_the_end = collections.OrderedDict()
        while True:
            k, v = self.fields.popitem()
            if k == 'datastore':
                self.fields[k] = v
                break
            else:
                fields_to_restore_at_the_end[k] = v

        for k, v in datastore_flavor_fields.items():
            self.fields[k] = v

        for k in reversed(list(fields_to_restore_at_the_end.keys())):
            self.fields[k] = fields_to_restore_at_the_end[k]

    def _add_attr_to_optional_fields(self, datastore, selection_text):
        if db_capability.is_mongodb_datastore(datastore):
            fields = self.mongodb_fields
        elif db_capability.is_vertica_datastore(datastore):
            fields = self.vertica_fields
        else:
            fields = self.default_fields

        for field in fields:
            attr_key = 'data-datastore-' + selection_text
            widget = self.fields[field[0]].widget
            if attr_key not in widget.attrs:
                widget.attrs[attr_key] = field[1]

    def _get_locality(self, data):
        locality = None
        if data.get('locality'):
            locality = data['locality']
        return locality

    @sensitive_variables('data')
    def handle(self, request, data):
        try:
            datastore, datastore_version = (
                create_instance.parse_datastore_and_version_text(
                    common_utils.unhexlify(data['datastore'])))

            flavor_field_name = self._build_widget_field_name(
                datastore, datastore_version)
            flavor = data[flavor_field_name]
            num_instances = data['num_instances']
            root_password = None
            if db_capability.is_vertica_datastore(datastore):
                root_password = data['root_password']
                num_instances = data['num_instances_vertica']
            LOG.info(
                "Launching cluster with parameters "
                "{name=%s, volume=%s, flavor=%s, "
                "datastore=%s, datastore_version=%s", "locality=%s",
                data['name'], data['volume'], flavor, datastore,
                datastore_version, self._get_locality(data))

            trove_api.trove.cluster_create(request,
                                           data['name'],
                                           data['volume'],
                                           flavor,
                                           num_instances,
                                           datastore=datastore,
                                           datastore_version=datastore_version,
                                           nics=data['network'],
                                           root_password=root_password,
                                           locality=self._get_locality(data))
            messages.success(request,
                             _('Launched cluster "%s"') % data['name'])
            return True
        except Exception:
            redirect = reverse("horizon:project:database_clusters:index")
            exceptions.handle(request,
                              _('Unable to launch cluster.'),
                              redirect=redirect)
Ejemplo n.º 16
0
class CreateNetwork(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255,
                           label=_("Name"),
                           required=False)
    tenant_id = forms.ChoiceField(label=_("Project"))
    if api.neutron.is_port_profiles_supported():
        widget = None
    else:
        widget = forms.HiddenInput()
    net_profile_id = forms.ChoiceField(label=_("Network Profile"),
                                       required=False,
                                       widget=widget)
    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'
        }))
    physical_network = forms.CharField(
        max_length=255,
        label=_("Physical Network"),
        help_text=_("The name of the physical network over which the "
                    "virtual network is implemented."),
        initial='default',
        widget=forms.TextInput(attrs={
            'class': 'switched',
            'data-switch-on': 'network_type',
        }))
    segmentation_id = forms.IntegerField(
        label=_("Segmentation ID"),
        widget=forms.TextInput(attrs={
            'class': 'switched',
            'data-switch-on': 'network_type',
        }))
    admin_state = forms.ChoiceField(choices=[(True, _('UP')),
                                             (False, _('DOWN'))],
                                    label=_("Admin State"))
    shared = forms.BooleanField(label=_("Shared"),
                                initial=False, required=False)
    external = forms.BooleanField(label=_("External Network"),
                                  initial=False, required=False)

    @classmethod
    def _instantiate(cls, request, *args, **kwargs):
        return cls(request, *args, **kwargs)

    def __init__(self, request, *args, **kwargs):
        super(CreateNetwork, self).__init__(request, *args, **kwargs)
        tenant_choices = [('', _("Select a project"))]
        tenants, has_more = api.keystone.tenant_list(request)
        for tenant in tenants:
            if tenant.enabled:
                tenant_choices.append((tenant.id, tenant.name))
        self.fields['tenant_id'].choices = tenant_choices

        if api.neutron.is_port_profiles_supported():
            self.fields['net_profile_id'].choices = (
                self.get_network_profile_choices(request))

        if api.neutron.is_extension_supported(request, 'provider'):
            neutron_settings = getattr(settings,
                                       'OPENSTACK_NEUTRON_NETWORK', {})
            self.seg_id_range = SEGMENTATION_ID_RANGE.copy()
            seg_id_range = neutron_settings.get('segmentation_id_range')
            if seg_id_range:
                self.seg_id_range.update(seg_id_range)

            self.provider_types = PROVIDER_TYPES.copy()
            extra_provider_types = neutron_settings.get('extra_provider_types')
            if extra_provider_types:
                self.provider_types.update(extra_provider_types)

            self.nettypes_with_seg_id = [
                net_type for net_type in self.provider_types
                if self.provider_types[net_type]['require_segmentation_id']]
            self.nettypes_with_physnet = [
                net_type for net_type in self.provider_types
                if self.provider_types[net_type]['require_physical_network']]

            supported_provider_types = neutron_settings.get(
                'supported_provider_types', DEFAULT_PROVIDER_TYPES)
            if supported_provider_types == ['*']:
                supported_provider_types = DEFAULT_PROVIDER_TYPES

            undefined_provider_types = [
                net_type for net_type in supported_provider_types
                if net_type not in self.provider_types]
            if undefined_provider_types:
                LOG.error('Undefined provider network types are found: %s',
                          undefined_provider_types)

            seg_id_help = [
                _("For %(type)s networks, valid IDs are %(min)s to %(max)s.")
                % {'type': net_type,
                   'min': self.seg_id_range[net_type][0],
                   'max': self.seg_id_range[net_type][1]}
                for net_type in self.nettypes_with_seg_id]
            self.fields['segmentation_id'].help_text = ' '.join(seg_id_help)

            # Register network types which require segmentation ID
            attrs = dict(('data-network_type-%s' % network_type,
                          _('Segmentation ID'))
                         for network_type in self.nettypes_with_seg_id)
            self.fields['segmentation_id'].widget.attrs.update(attrs)

            # Register network types which require physical network
            attrs = dict(('data-network_type-%s' % network_type,
                          _('Physical Network'))
                         for network_type in self.nettypes_with_physnet)
            self.fields['physical_network'].widget.attrs.update(attrs)

            network_type_choices = [
                (net_type, self.provider_types[net_type]['display_name'])
                for net_type in supported_provider_types]
            if len(network_type_choices) == 0:
                self._hide_provider_network_type()
            else:
                self.fields['network_type'].choices = network_type_choices
        else:
            self._hide_provider_network_type()

    def get_network_profile_choices(self, request):
        profile_choices = [('', _("Select a profile"))]
        for profile in self._get_profiles(request, 'network'):
            profile_choices.append((profile.id, profile.name))
        return profile_choices

    def _get_profiles(self, request, type_p):
        profiles = []
        try:
            profiles = api.neutron.profile_list(request, type_p)
        except Exception:
            msg = _('Network Profiles could not be retrieved.')
            exceptions.handle(request, msg)
        return profiles

    def _hide_provider_network_type(self):
        self.fields['network_type'].widget = forms.HiddenInput()
        self.fields['physical_network'].widget = forms.HiddenInput()
        self.fields['segmentation_id'].widget = forms.HiddenInput()
        self.fields['network_type'].required = False
        self.fields['physical_network'].required = False
        self.fields['segmentation_id'].required = False

    def handle(self, request, data):
        try:
            params = {'name': data['name'],
                      'tenant_id': data['tenant_id'],
                      'admin_state_up': (data['admin_state'] == 'True'),
                      'shared': data['shared'],
                      'router:external': data['external']}
            if api.neutron.is_port_profiles_supported():
                params['net_profile_id'] = data['net_profile_id']
            if api.neutron.is_extension_supported(request, 'provider'):
                network_type = data['network_type']
                params['provider:network_type'] = network_type
                if network_type in self.nettypes_with_physnet:
                    params['provider:physical_network'] = (
                        data['physical_network'])
                if network_type in self.nettypes_with_seg_id:
                    params['provider:segmentation_id'] = (
                        data['segmentation_id'])
            network = api.neutron.network_create(request, **params)
            msg = _('Network %s was successfully created.') % data['name']
            LOG.debug(msg)
            messages.success(request, msg)
            return network
        except Exception:
            redirect = reverse('horizon:admin:networks:index')
            msg = _('Failed to create network %s') % data['name']
            exceptions.handle(request, msg, redirect=redirect)

    def clean(self):
        cleaned_data = super(CreateNetwork, self).clean()
        if api.neutron.is_extension_supported(self.request, 'provider'):
            self._clean_physical_network(cleaned_data)
            self._clean_segmentation_id(cleaned_data)
        return cleaned_data

    def _clean_physical_network(self, data):
        network_type = data.get('network_type')
        if ('physical_network' in self._errors and
                network_type not in self.nettypes_with_physnet):
            # In this case the physical network is not required, so we can
            # ignore any errors.
            del self._errors['physical_network']

    def _clean_segmentation_id(self, data):
        network_type = data.get('network_type')
        if 'segmentation_id' in self._errors:
            if network_type not in self.nettypes_with_seg_id:
                # In this case the segmentation ID is not required, so we can
                # ignore any errors.
                del self._errors['segmentation_id']
        elif network_type in self.nettypes_with_seg_id:
            seg_id = data.get('segmentation_id')
            seg_id_range = {'min': self.seg_id_range[network_type][0],
                            'max': self.seg_id_range[network_type][1]}
            if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']:
                msg = (_('For a %(network_type)s network, valid segmentation '
                         'IDs are %(min)s through %(max)s.')
                       % {'network_type': network_type,
                          'min': seg_id_range['min'],
                          'max': seg_id_range['max']})
                self._errors['segmentation_id'] = self.error_class([msg])
Ejemplo n.º 17
0
class StopPluginForm(forms.SelfHandlingForm):

    uuid = forms.CharField(label=_("Plugin ID"), widget=forms.HiddenInput)

    name = forms.CharField(
        label=_('Plugin Name'),
        widget=forms.TextInput(attrs={'readonly': 'readonly'})
    )

    delay = forms.IntegerField(
        label=_("Delay in secs"),
        required=False,
        help_text=_("OPTIONAL: seconds to wait before stopping the plugin")
    )

    board_list = forms.MultipleChoiceField(
        label=_("Boards List"),
        widget=forms.SelectMultiple(
            attrs={'class': 'switchable', 'data-slug': 'slug-stop-boards'}),
        help_text=_("Select boards in this pool ")
    )

    def __init__(self, *args, **kwargs):

        super(StopPluginForm, self).__init__(*args, **kwargs)
        # input=kwargs.get('initial',{})

        boardslist_length = len(kwargs["initial"]["board_list"])

        self.fields["board_list"].choices = kwargs["initial"]["board_list"]
        self.fields["board_list"].max_length = boardslist_length

    def handle(self, request, data):

        counter = 0

        if not data["delay"]:
            data["delay"] = {}
        else:
            data["delay"] = {"delay": data["delay"]}

        for board in data["board_list"]:
            for key, value in self.fields["board_list"].choices:
                if key == board:

                    try:
                        plugin = None
                        plugin = iotronic.plugin_action(request, key,
                                                        data["uuid"],
                                                        "PluginStop",
                                                        data["delay"])
                        # LOG.debug("API: %s %s", plugin, request)
                        message_text = "Plugin stopped successfully on board "\
                                       + str(value) + "."
                        messages.success(request, _(message_text))

                        if counter != len(data["board_list"]) - 1:
                            counter += 1
                        else:
                            return plugin
                    except Exception:
                        message_text = "Unable to stop plugin on board " \
                                       + str(value) + "."
                        exceptions.handle(request, _(message_text))

                    break
Ejemplo n.º 18
0
class SetInstanceDetailsAction(workflows.Action):
    SOURCE_TYPE_CHOICES = (
        ("image_id", _("Image")),
        ("instance_snapshot_id", _("Snapshot")),
    )
    source_type = forms.ChoiceField(label=_("Instance Source"),
                                    choices=SOURCE_TYPE_CHOICES)
    image_id = forms.ChoiceField(label=_("Image"), required=False)
    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
                                             required=False)
    name = forms.CharField(max_length=80, label=_("Instance Name"))
    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))
    count = forms.IntegerField(label=_("Instance Count"),
                               min_value=1,
                               initial=1,
                               help_text=_("Number of instances to launch."))

    class Meta:
        name = _("Details")
        help_text_template = ("nova/instances/"
                              "_launch_details_help.html")

    def clean(self):
        cleaned_data = super(SetInstanceDetailsAction, self).clean()

        # Validate our instance source.
        source = cleaned_data['source_type']
        # There should always be at least one image_id choice, telling the user
        # that there are "No Images Available" so we check for 2 here...
        if source == 'image_id' and not \
                filter(lambda x: x[0] != '', self.fields['image_id'].choices):
            raise forms.ValidationError(_("There are no image sources "
                                          "available; you must first create "
                                          "an image before attempting to "
                                          "launch an instance."))
        if not cleaned_data[source]:
            raise forms.ValidationError(_("Please select an option for the "
                                          "instance source."))

        # Prevent launching multiple instances with the same volume.
        # TODO(gabriel): is it safe to launch multiple instances with
        # a snapshot since it should be cloned to new volumes?
        count = cleaned_data.get('count', 1)
        volume_type = self.data.get('volume_type', None)
        if volume_type and count > 1:
            msg = _('Launching multiple instances is only supported for '
                    'images and instance snapshots.')
            raise forms.ValidationError(msg)

        return cleaned_data

    def _get_available_images(self, request, context):
        project_id = context.get('project_id', None)
        if not hasattr(self, "_public_images"):
            public = {"is_public": True,
                      "status": "active"}
            try:
                public_images, _more = api.glance.image_list_detailed(request,
                                                           filters=public)
            except:
                public_images = []
                exceptions.handle(request,
                                  _("Unable to retrieve public images."))
            self._public_images = public_images

        # Preempt if we don't have a project_id yet.
        if project_id is None:
            setattr(self, "_images_for_%s" % project_id, [])

        if not hasattr(self, "_images_for_%s" % project_id):
            owner = {"property-owner_id": project_id,
                     "status": "active"}
            try:
                owned_images, _more = api.glance.image_list_detailed(request,
                                                          filters=owner)
            except:
                exceptions.handle(request,
                                  _("Unable to retrieve images for "
                                    "the current project."))
            setattr(self, "_images_for_%s" % project_id, owned_images)

        owned_images = getattr(self, "_images_for_%s" % project_id)
        images = owned_images + self._public_images

        # Remove duplicate images
        image_ids = []
        final_images = []
        for image in images:
            if image.id not in image_ids:
                image_ids.append(image.id)
                final_images.append(image)
        return [image for image in final_images
                if image.container_format not in ('aki', 'ari')]

    def populate_image_id_choices(self, request, context):
        images = self._get_available_images(request, context)
        choices = [(image.id, image.name)
                   for image in images
                   if image.properties.get("image_type", '') != "snapshot"]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available.")))
        return choices

    def populate_instance_snapshot_id_choices(self, request, context):
        images = self._get_available_images(request, context)
        choices = [(image.id, image.name)
                   for image in images
                   if image.properties.get("image_type", '') == "snapshot"]
        if choices:
            choices.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available.")))
        return choices

    def populate_flavor_choices(self, request, context):
        try:
            flavors = api.nova.flavor_list(request)
            flavor_list = [(flavor.id, "%s" % flavor.name)
                           for flavor in flavors]
        except:
            flavor_list = []
            exceptions.handle(request,
                              _('Unable to retrieve instance flavors.'))
        return sorted(flavor_list)

    def get_help_text(self):
        extra = {}
        try:
            extra['usages'] = api.nova.tenant_quota_usages(self.request)
            extra['usages_json'] = jsonutils.dumps(extra['usages'])
            flavors = jsonutils.dumps([f._info for f in
                                       api.nova.flavor_list(self.request)])
            extra['flavors'] = flavors
        except:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(SetInstanceDetailsAction, self).get_help_text(extra)
Ejemplo n.º 19
0
class ProjectQuotaAction(workflows.Action):
    ifcb_label = _("Injected File Content (Bytes)")
    metadata_items = forms.IntegerField(min_value=-1,
                                        label=_("Metadata Items"))
    cores = forms.IntegerField(min_value=-1, label=_("VCPUs"))
    instances = forms.IntegerField(min_value=-1, label=_("Instances"))
    injected_files = forms.IntegerField(min_value=-1,
                                        label=_("Injected Files"))
    injected_file_content_bytes = forms.IntegerField(min_value=-1,
                                                     label=ifcb_label)
    volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
    snapshots = forms.IntegerField(min_value=-1, label=_("Volume Snapshots"))
    gigabytes = forms.IntegerField(
        min_value=-1, label=_("Total Size of Volumes and Snapshots (GB)"))
    ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"))
    floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
    fixed_ips = forms.IntegerField(min_value=-1, label=_("Fixed IPs"))
    security_groups = forms.IntegerField(min_value=-1,
                                         label=_("Security Groups"))
    security_group_rules = forms.IntegerField(min_value=-1,
                                              label=_("Security Group Rules"))

    # Neutron
    security_group = forms.IntegerField(min_value=-1,
                                        label=_("Security Groups"))
    security_group_rule = forms.IntegerField(min_value=-1,
                                             label=_("Security Group Rules"))
    floatingip = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
    network = forms.IntegerField(min_value=-1, label=_("Networks"))
    port = forms.IntegerField(min_value=-1, label=_("Ports"))
    router = forms.IntegerField(min_value=-1, label=_("Routers"))
    subnet = forms.IntegerField(min_value=-1, label=_("Subnets"))

    def __init__(self, request, *args, **kwargs):
        super(ProjectQuotaAction, self).__init__(request, *args, **kwargs)
        disabled_quotas = quotas.get_disabled_quotas(request)
        for field in disabled_quotas:
            if field in self.fields:
                self.fields[field].required = False
                self.fields[field].widget = forms.HiddenInput()
Ejemplo n.º 20
0
class CreateFlavorInfoAction(workflows.Action):
    _flavor_id_regex = (r'^[a-zA-Z0-9. _-]+$')
    _flavor_id_help_text = _("flavor id can only contain alphanumeric "
                             "characters, underscores, periods, hyphens, "
                             "spaces.")
    name = forms.CharField(label=_("Name"), max_length=255)
    flavor_id = forms.RegexField(label=_("ID"),
                                 regex=_flavor_id_regex,
                                 required=False,
                                 initial='auto',
                                 max_length=255,
                                 help_text=_flavor_id_help_text)
    vcpus = forms.IntegerField(label=_("VCPUs"),
                               min_value=1,
                               max_value=2147483647)
    memory_mb = forms.IntegerField(label=_("RAM (MB)"),
                                   min_value=1,
                                   max_value=2147483647)
    disk_gb = forms.IntegerField(label=_("Root Disk (GB)"),
                                 min_value=0,
                                 max_value=2147483647)
    eph_gb = forms.IntegerField(label=_("Ephemeral Disk (GB)"),
                                required=False,
                                initial=0,
                                min_value=0)
    swap_mb = forms.IntegerField(label=_("Swap Disk (MB)"),
                                 required=False,
                                 initial=0,
                                 min_value=0)
    rxtx_factor = forms.FloatField(label=_("RX/TX Factor"),
                                   required=False,
                                   initial=1,
                                   min_value=1)

    class Meta(object):
        name = _("Flavor Information")
        help_text = _("Flavors define the sizes for RAM, disk, number of "
                      "cores, and other resources and can be selected when "
                      "users deploy instances.")

    def clean_name(self):
        name = self.cleaned_data.get('name').strip()
        if not name:
            msg = _('Flavor name cannot be empty.')
            self._errors['name'] = self.error_class([msg])
        return name

    def clean(self):
        cleaned_data = super().clean()
        name = cleaned_data.get('name')
        flavor_id = cleaned_data.get('flavor_id')

        try:
            flavors = api.nova.flavor_list(self.request, None)
        except Exception:
            flavors = []
            msg = _('Unable to get flavor list')
            exceptions.handle(self.request, msg)
            raise
        if flavors is not None and name is not None:
            for flavor in flavors:
                if flavor.name.lower() == name.lower():
                    error_msg = _('The name "%s" is already used by '
                                  'another flavor.') % name
                    self._errors['name'] = self.error_class([error_msg])
                if flavor.id == flavor_id:
                    error_msg = _('The ID "%s" is already used by '
                                  'another flavor.') % flavor_id
                    self._errors['flavor_id'] = self.error_class([error_msg])
        return cleaned_data
Ejemplo n.º 21
0
class UpdateIKEPolicy(forms.SelfHandlingForm):
    name = forms.CharField(max_length=80, label=_("Name"), required=False)
    ikepolicy_id = forms.CharField(
        label=_("ID"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    description = forms.CharField(
        required=False, max_length=80, label=_("Description"))
    # Currently this field has only one choice, so mark it as readonly.
    auth_algorithm = forms.ChoiceField(
        label=_("Authorization algorithm"),
        choices=[('sha1', _('sha1'))],
        widget=forms.Select(attrs={'readonly': 'readonly'}))
    encryption_algorithm = forms.ChoiceField(
        label=_("Encryption algorithm"),
        choices=[('3des', _('3des')),
                 ('aes-128', _('aes-128')),
                 ('aes-192', _('aes-192')),
                 ('aes-256', _('aes-256'))])
    ike_version = forms.ChoiceField(
        label=_("IKE version"),
        choices=[('v1', _('v1')),
                 ('v2', _('v2'))])
    # Currently this field has only one choice, so mark it as readonly.
    lifetime_units = forms.ChoiceField(
        label=_("Lifetime units for IKE keys"),
        choices=[('seconds', _('seconds'))],
        widget=forms.Select(attrs={'readonly': 'readonly'}))
    lifetime_value = forms.IntegerField(
        min_value=60,
        label=_("Lifetime value for IKE keys"),
        help_text=_("Equal to or greater than 60"))
    pfs = forms.ChoiceField(
        label=_("Perfect Forward Secrecy"),
        choices=[('group2', _('group2')),
                 ('group5', _('group5')),
                 ('group14', _('group14'))])
    # Currently this field has only one choice, so mark it as readonly.
    phase1_negotiation_mode = forms.ChoiceField(
        label=_("IKE Phase1 negotiation mode"),
        choices=[('main', 'main')],
        widget=forms.Select(attrs={'readonly': 'readonly'}))

    failure_url = 'horizon:project:vpn:index'

    def handle(self, request, context):
        try:
            data = {'ikepolicy':
                {'name': context['name'],
                 'description': context['description'],
                 'auth_algorithm': context['auth_algorithm'],
                 'encryption_algorithm': context['encryption_algorithm'],
                 'ike_version': context['ike_version'],
                 'lifetime': {'units': context['lifetime_units'],
                              'value': context['lifetime_value']},
                 'pfs': context['pfs'],
                 'phase1_negotiation_mode': context['phase1_negotiation_mode'],
                 }}
            ikepolicy = api.vpn.ikepolicy_update(
                request, context['ikepolicy_id'], **data)
            msg = (_('IKE Policy %s was successfully updated.')
                   % context['name'])
            LOG.debug(msg)
            messages.success(request, msg)
            return ikepolicy
        except Exception as e:
            msg = _('Failed to update IKE Policy %s') % context['name']
            LOG.info('%s: %s' % (msg, e))
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
Ejemplo n.º 22
0
class CreateNetwork(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    tenant_id = forms.ThemableChoiceField(label=_("Project"))
    network_type = forms.ChoiceField(
        label=_("Provider Network Type"),
        help_text=_("The physical mechanism by which the virtual "
                    "network is implemented."),
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'network_type'
        }))
    physical_network = forms.CharField(
        max_length=255,
        label=_("Physical Network"),
        help_text=_("The name of the physical network over which the "
                    "virtual network is implemented. Specify one of the "
                    "physical networks defined in your neutron deployment."),
        widget=forms.TextInput(attrs={
            'class': 'switched',
            'data-switch-on': 'network_type',
        }))
    segmentation_id = forms.IntegerField(
        label=_("Segmentation ID"),
        widget=forms.TextInput(attrs={
            'class': 'switched',
            'data-switch-on': 'network_type',
        }))
    admin_state = forms.BooleanField(label=_("Enable Admin State"),
                                     initial=True,
                                     required=False)
    shared = forms.BooleanField(label=_("Shared"),
                                initial=False,
                                required=False)
    external = forms.BooleanField(label=_("External Network"),
                                  initial=False,
                                  required=False)
    with_subnet = forms.BooleanField(label=_("Create Subnet"),
                                     widget=forms.CheckboxInput(
                                         attrs={
                                             'class':
                                             'switchable',
                                             'data-slug':
                                             'with_subnet',
                                             'data-hide-tab':
                                             'create_network__'
                                             'createsubnetinfo'
                                             'action,'
                                             'create_network__'
                                             'createsubnetdetail'
                                             'action',
                                             'data-hide-on-checked':
                                             'false'
                                         }),
                                     initial=True,
                                     required=False)
    az_hints = forms.MultipleChoiceField(
        label=_("Availability Zone Hints"),
        required=False,
        help_text=_("Availability zones where the DHCP agents may be "
                    "scheduled. Leaving this unset is equivalent to "
                    "selecting all availability zones"))

    @classmethod
    def _instantiate(cls, request, *args, **kwargs):
        return cls(request, *args, **kwargs)

    def __init__(self, request, *args, **kwargs):
        super(CreateNetwork, self).__init__(request, *args, **kwargs)
        tenant_choices = [('', _("Select a project"))]
        tenants, has_more = api.keystone.tenant_list(request)
        for tenant in tenants:
            if tenant.enabled:
                tenant_choices.append((tenant.id, tenant.name))
        self.fields['tenant_id'].choices = tenant_choices

        try:
            is_extension_supported = \
                api.neutron.is_extension_supported(request, 'provider')
        except Exception:
            msg = _("Unable to verify Neutron service providers")
            exceptions.handle(self.request, msg)
            self._hide_provider_network_type()
            is_extension_supported = False

        if is_extension_supported:
            neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK',
                                       {})
            self.seg_id_range = SEGMENTATION_ID_RANGE.copy()
            seg_id_range = neutron_settings.get('segmentation_id_range')
            if seg_id_range:
                self.seg_id_range.update(seg_id_range)

            self.provider_types = PROVIDER_TYPES.copy()
            extra_provider_types = neutron_settings.get('extra_provider_types')
            if extra_provider_types:
                self.provider_types.update(extra_provider_types)

            self.nettypes_with_seg_id = [
                net_type for net_type in self.provider_types
                if self.provider_types[net_type]['require_segmentation_id']
            ]
            self.nettypes_with_physnet = [
                net_type for net_type in self.provider_types
                if self.provider_types[net_type]['require_physical_network']
            ]

            supported_provider_types = neutron_settings.get(
                'supported_provider_types', DEFAULT_PROVIDER_TYPES)
            if supported_provider_types == ['*']:
                supported_provider_types = DEFAULT_PROVIDER_TYPES

            undefined_provider_types = [
                net_type for net_type in supported_provider_types
                if net_type not in self.provider_types
            ]
            if undefined_provider_types:
                LOG.error('Undefined provider network types are found: %s',
                          undefined_provider_types)

            seg_id_help = [
                _("For %(type)s networks, valid IDs are %(min)s to %(max)s.") %
                {
                    'type': net_type,
                    'min': self.seg_id_range[net_type][0],
                    'max': self.seg_id_range[net_type][1]
                } for net_type in self.nettypes_with_seg_id
            ]
            self.fields['segmentation_id'].help_text = ' '.join(seg_id_help)

            # Register network types which require segmentation ID
            attrs = dict(
                ('data-network_type-%s' % network_type, _('Segmentation ID'))
                for network_type in self.nettypes_with_seg_id)
            self.fields['segmentation_id'].widget.attrs.update(attrs)

            physical_networks = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK',
                                        {}).get('physical_networks', [])

            if physical_networks:
                self.fields['physical_network'] = forms.ThemableChoiceField(
                    label=_("Physical Network"),
                    choices=[(net, net) for net in physical_networks],
                    widget=forms.ThemableSelectWidget(
                        attrs={
                            'class': 'switched',
                            'data-switch-on': 'network_type',
                        }),
                    help_text=_("The name of the physical network over "
                                "which the virtual network is implemented."),
                )

            # Register network types which require physical network
            attrs = dict(
                ('data-network_type-%s' % network_type, _('Physical Network'))
                for network_type in self.nettypes_with_physnet)
            self.fields['physical_network'].widget.attrs.update(attrs)

            network_type_choices = [
                (net_type, self.provider_types[net_type]['display_name'])
                for net_type in supported_provider_types
            ]
            if not network_type_choices:
                self._hide_provider_network_type()
            else:
                self.fields['network_type'].choices = network_type_choices

        try:
            if api.neutron.is_extension_supported(request,
                                                  'network_availability_zone'):
                zones = api.neutron.list_availability_zones(
                    self.request, 'network', 'available')
                self.fields['az_hints'].choices = [(zone['name'], zone['name'])
                                                   for zone in zones]
            else:
                del self.fields['az_hints']
        except Exception:
            msg = _('Failed to get availability zone list.')
            messages.warning(request, msg)
            del self.fields['az_hints']

    def _hide_provider_network_type(self):
        self.fields['network_type'].widget = forms.HiddenInput()
        self.fields['physical_network'].widget = forms.HiddenInput()
        self.fields['segmentation_id'].widget = forms.HiddenInput()
        self.fields['network_type'].required = False
        self.fields['physical_network'].required = False
        self.fields['segmentation_id'].required = False

    def handle(self, request, data):
        try:
            params = {
                'name': data['name'],
                'tenant_id': data['tenant_id'],
                'admin_state_up': data['admin_state'],
                'shared': data['shared'],
                'router:external': data['external']
            }
            if api.neutron.is_extension_supported(request, 'provider'):
                network_type = data['network_type']
                params['provider:network_type'] = network_type
                if network_type in self.nettypes_with_physnet:
                    params['provider:physical_network'] = (
                        data['physical_network'])
                if network_type in self.nettypes_with_seg_id:
                    params['provider:segmentation_id'] = (
                        data['segmentation_id'])
            if 'az_hints' in data and data['az_hints']:
                params['availability_zone_hints'] = data['az_hints']
            network = api.neutron.network_create(request, **params)
            LOG.debug('Network %s was successfully created.', data['name'])
            return network
        except Exception:
            redirect = reverse('horizon:admin:networks:index')
            msg = _('Failed to create network %s') % data['name']
            exceptions.handle(request, msg, redirect=redirect)

    def clean(self):
        cleaned_data = super(CreateNetwork, self).clean()
        if api.neutron.is_extension_supported(self.request, 'provider'):
            self._clean_physical_network(cleaned_data)
            self._clean_segmentation_id(cleaned_data)
        return cleaned_data

    def _clean_physical_network(self, data):
        network_type = data.get('network_type')
        if ('physical_network' in self._errors
                and network_type not in self.nettypes_with_physnet):
            # In this case the physical network is not required, so we can
            # ignore any errors.
            del self._errors['physical_network']

    def _clean_segmentation_id(self, data):
        network_type = data.get('network_type')
        if 'segmentation_id' in self._errors:
            if (network_type not in self.nettypes_with_seg_id
                    and not self.data.get("segmentation_id")):
                # In this case the segmentation ID is not required, so we can
                # ignore the field is required error.
                del self._errors['segmentation_id']
        elif network_type in self.nettypes_with_seg_id:
            seg_id = data.get('segmentation_id')
            seg_id_range = {
                'min': self.seg_id_range[network_type][0],
                'max': self.seg_id_range[network_type][1]
            }
            if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']:
                msg = (_('For a %(network_type)s network, valid segmentation '
                         'IDs are %(min)s through %(max)s.') % {
                             'network_type': network_type,
                             'min': seg_id_range['min'],
                             'max': seg_id_range['max']
                         })
                self._errors['segmentation_id'] = self.error_class([msg])
Ejemplo n.º 23
0
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Volume Name"),
                           required=False)
    description = forms.CharField(max_length=255, widget=forms.Textarea(
        attrs={'rows': 4}),
        label=_("Description"), required=False)
    volume_source_type = forms.ChoiceField(
        label=_("Volume Source"),
        required=False,
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'source'}))
    snapshot_source = forms.ChoiceField(
        label=_("Use snapshot as a source"),
        widget=forms.ThemableSelectWidget(
            attrs={'class': 'snapshot-selector'},
            data_attrs=('size', 'name'),
            transform=lambda x: "%s (%s GiB)" % (x.name, x.size)),
        required=False)
    image_source = forms.ChoiceField(
        label=_("Use image as a source"),
        widget=forms.ThemableSelectWidget(
            attrs={'class': 'image-selector'},
            data_attrs=('size', 'name', 'min_disk'),
            transform=lambda x: "%s (%s)" % (x.name, filesizeformat(x.bytes))),
        required=False)
    volume_source = forms.ChoiceField(
        label=_("Use a volume as source"),
        widget=forms.ThemableSelectWidget(
            attrs={'class': 'image-selector'},
            data_attrs=('size', 'name'),
            transform=lambda x: "%s (%s GiB)" % (x.name, x.size)),
        required=False)
    type = forms.ChoiceField(
        label=_("Type"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={'class': 'switched',
                   'data-switch-on': 'source',
                   'data-source-no_source_type': _('Type'),
                   'data-source-image_source': _('Type')}))
    size = forms.IntegerField(min_value=1, initial=1, label=_("Size (GiB)"))
    availability_zone = forms.ChoiceField(
        label=_("Availability Zone"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={'class': 'switched',
                   'data-switch-on': 'source',
                   'data-source-no_source_type': _('Availability Zone'),
                   'data-source-image_source': _('Availability Zone')}))

    def prepare_source_fields_if_snapshot_specified(self, request):
        try:
            snapshot = self.get_snapshot(request,
                                         request.GET["snapshot_id"])
            self.fields['name'].initial = snapshot.name
            self.fields['size'].initial = snapshot.size
            self.fields['snapshot_source'].choices = ((snapshot.id,
                                                       snapshot),)
            try:
                # Set the volume type from the original volume
                orig_volume = cinder.volume_get(request,
                                                snapshot.volume_id)
                self.fields['type'].initial = orig_volume.volume_type
            except Exception:
                pass
            self.fields['size'].help_text = (
                _('Volume size must be equal to or greater than the '
                  'snapshot size (%sGiB)') % snapshot.size)
            del self.fields['image_source']
            del self.fields['volume_source']
            del self.fields['volume_source_type']
            del self.fields['availability_zone']
        except Exception:
            exceptions.handle(request,
                              _('Unable to load the specified snapshot.'))

    def prepare_source_fields_if_image_specified(self, request):
        self.fields['availability_zone'].choices = \
            availability_zones(request)
        try:
            image = self.get_image(request,
                                   request.GET["image_id"])
            image.bytes = image.size
            self.fields['name'].initial = image.name
            min_vol_size = functions.bytes_to_gigabytes(
                image.size)
            size_help_text = (_('Volume size must be equal to or greater '
                                'than the image size (%s)')
                              % filesizeformat(image.size))
            properties = getattr(image, 'properties', {})
            min_disk_size = (getattr(image, 'min_disk', 0) or
                             properties.get('min_disk', 0))
            if (min_disk_size > min_vol_size):
                min_vol_size = min_disk_size
                size_help_text = (_('Volume size must be equal to or '
                                    'greater than the image minimum '
                                    'disk size (%sGiB)')
                                  % min_disk_size)
            self.fields['size'].initial = min_vol_size
            self.fields['size'].help_text = size_help_text
            self.fields['image_source'].choices = ((image.id, image),)
            del self.fields['snapshot_source']
            del self.fields['volume_source']
            del self.fields['volume_source_type']
        except Exception:
            msg = _('Unable to load the specified image. %s')
            exceptions.handle(request, msg % request.GET['image_id'])

    def prepare_source_fields_if_volume_specified(self, request):
        self.fields['availability_zone'].choices = \
            availability_zones(request)
        volume = None
        try:
            volume = self.get_volume(request, request.GET["volume_id"])
        except Exception:
            msg = _('Unable to load the specified volume. %s')
            exceptions.handle(request, msg % request.GET['volume_id'])

        if volume is not None:
            self.fields['name'].initial = volume.name
            self.fields['description'].initial = volume.description
            min_vol_size = volume.size
            size_help_text = (_('Volume size must be equal to or greater '
                                'than the origin volume size (%sGiB)')
                              % volume.size)
            self.fields['size'].initial = min_vol_size
            self.fields['size'].help_text = size_help_text
            self.fields['volume_source'].choices = ((volume.id, volume),)
            self.fields['type'].initial = volume.type
            del self.fields['snapshot_source']
            del self.fields['image_source']
            del self.fields['volume_source_type']

    def prepare_source_fields_default(self, request):
        source_type_choices = []
        self.fields['availability_zone'].choices = \
            availability_zones(request)

        try:
            available = api.cinder.VOLUME_STATE_AVAILABLE
            snapshots = cinder.volume_snapshot_list(
                request, search_opts=dict(status=available))
            if snapshots:
                source_type_choices.append(("snapshot_source",
                                            _("Snapshot")))
                choices = [('', _("Choose a snapshot"))] + \
                          [(s.id, s) for s in snapshots]
                self.fields['snapshot_source'].choices = choices
            else:
                del self.fields['snapshot_source']
        except Exception:
            exceptions.handle(request,
                              _("Unable to retrieve volume snapshots."))

        images = utils.get_available_images(request,
                                            request.user.tenant_id)
        if images:
            source_type_choices.append(("image_source", _("Image")))
            choices = [('', _("Choose an image"))]
            for image in images:
                image.bytes = image.size
                image.size = functions.bytes_to_gigabytes(image.bytes)
                choices.append((image.id, image))
            self.fields['image_source'].choices = choices
        else:
            del self.fields['image_source']

        volumes = self.get_volumes(request)
        if volumes:
            source_type_choices.append(("volume_source", _("Volume")))
            choices = [('', _("Choose a volume"))]
            for volume in volumes:
                choices.append((volume.id, volume))
            self.fields['volume_source'].choices = choices
        else:
            del self.fields['volume_source']

        if source_type_choices:
            choices = ([('no_source_type',
                         _("No source, empty volume"))] +
                       source_type_choices)
            self.fields['volume_source_type'].choices = choices
        else:
            del self.fields['volume_source_type']

    def __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        volume_types = cinder.volume_type_list(request)
        self.fields['type'].choices = [("no_type", _("No volume type"))] + \
                                      [(type.name, type.name)
                                       for type in volume_types]
        if 'initial' in kwargs and 'type' in kwargs['initial']:
            # if there is a default volume type to select, then remove
            # the first ""No volume type" entry
            self.fields['type'].choices.pop(0)

        if "snapshot_id" in request.GET:
            self.prepare_source_fields_if_snapshot_specified(request)
        elif 'image_id' in request.GET:
            self.prepare_source_fields_if_image_specified(request)
        elif 'volume_id' in request.GET:
            self.prepare_source_fields_if_volume_specified(request)
        else:
            self.prepare_source_fields_default(request)

    def clean(self):
        cleaned_data = super(CreateForm, self).clean()
        source_type = self.cleaned_data.get('volume_source_type')
        if (source_type == 'image_source' and
                not cleaned_data.get('image_source')):
            msg = _('Image source must be specified')
            self._errors['image_source'] = self.error_class([msg])
        elif (source_type == 'snapshot_source' and
                not cleaned_data.get('snapshot_source')):
            msg = _('Snapshot source must be specified')
            self._errors['snapshot_source'] = self.error_class([msg])
        elif (source_type == 'volume_source' and
                not cleaned_data.get('volume_source')):
            msg = _('Volume source must be specified')
            self._errors['volume_source'] = self.error_class([msg])
        return cleaned_data

    def get_volumes(self, request):
        volumes = []
        try:
            available = api.cinder.VOLUME_STATE_AVAILABLE
            volumes = cinder.volume_list(self.request,
                                         search_opts=dict(status=available))
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve list of volumes.'))
        return volumes

    def handle(self, request, data):
        try:
            usages = quotas.tenant_limit_usages(self.request)
            availableGB = usages['maxTotalVolumeGigabytes'] - \
                usages['gigabytesUsed']
            availableVol = usages['maxTotalVolumes'] - usages['volumesUsed']

            snapshot_id = None
            image_id = None
            volume_id = None
            source_type = data.get('volume_source_type', None)
            az = data.get('availability_zone', None) or None
            if (data.get("snapshot_source", None) and
                    source_type in ['', None, 'snapshot_source']):
                # Create from Snapshot
                snapshot = self.get_snapshot(request,
                                             data["snapshot_source"])
                snapshot_id = snapshot.id
                if (data['size'] < snapshot.size):
                    error_message = (_('The volume size cannot be less than '
                                       'the snapshot size (%sGiB)')
                                     % snapshot.size)
                    raise ValidationError(error_message)
                az = None
            elif (data.get("image_source", None) and
                  source_type in ['', None, 'image_source']):
                # Create from Snapshot
                image = self.get_image(request,
                                       data["image_source"])
                image_id = image.id
                image_size = functions.bytes_to_gigabytes(image.size)
                if (data['size'] < image_size):
                    error_message = (_('The volume size cannot be less than '
                                       'the image size (%s)')
                                     % filesizeformat(image.size))
                    raise ValidationError(error_message)
                properties = getattr(image, 'properties', {})
                min_disk_size = (getattr(image, 'min_disk', 0) or
                                 properties.get('min_disk', 0))
                if (min_disk_size > 0 and data['size'] < min_disk_size):
                    error_message = (_('The volume size cannot be less than '
                                       'the image minimum disk size (%sGiB)')
                                     % min_disk_size)
                    raise ValidationError(error_message)
            elif (data.get("volume_source", None) and
                  source_type in ['', None, 'volume_source']):
                # Create from volume
                volume = self.get_volume(request, data["volume_source"])
                volume_id = volume.id

                if data['size'] < volume.size:
                    error_message = (_('The volume size cannot be less than '
                                       'the source volume size (%sGiB)')
                                     % volume.size)
                    raise ValidationError(error_message)
            else:
                if type(data['size']) is str:
                    data['size'] = int(data['size'])

            if availableGB < data['size']:
                error_message = _('A volume of %(req)iGiB cannot be created '
                                  'as you only have %(avail)iGiB of your '
                                  'quota available.')
                params = {'req': data['size'],
                          'avail': availableGB}
                raise ValidationError(error_message % params)
            elif availableVol <= 0:
                error_message = _('You are already using all of your available'
                                  ' volumes.')
                raise ValidationError(error_message)

            metadata = {}

            if data['type'] == 'no_type':
                data['type'] = ''

            volume = cinder.volume_create(request,
                                          data['size'],
                                          data['name'],
                                          data['description'],
                                          data['type'],
                                          snapshot_id=snapshot_id,
                                          image_id=image_id,
                                          metadata=metadata,
                                          availability_zone=az,
                                          source_volid=volume_id)
            message = _('Creating volume "%s"') % data['name']
            messages.info(request, message)
            return volume
        except ValidationError as e:
            self.api_error(e.messages[0])
            return False
        except Exception:
            redirect = reverse("horizon:project:volumes:index")
            exceptions.handle(request,
                              _("Unable to create volume."),
                              redirect=redirect)

    @memoized
    def get_snapshot(self, request, id):
        return cinder.volume_snapshot_get(request, id)

    @memoized
    def get_image(self, request, id):
        return glance.image_get(request, id)

    @memoized
    def get_volume(self, request, id):
        return cinder.volume_get(request, id)
Ejemplo n.º 24
0
class CreateStackForm(forms.SelfHandlingForm):

    param_prefix = '__param_'

    class Meta(object):
        name = _('Create Stack')

    environment_data = forms.CharField(widget=forms.widgets.HiddenInput,
                                       required=False)
    if django.VERSION >= (1, 9):
        # Note(Itxaka): On django>=1.9 Charfield has an strip option that
        # we need to set to False as to not hit
        # https://bugs.launchpad.net/python-heatclient/+bug/1546166
        environment_data.strip = False

    parameters = forms.CharField(widget=forms.widgets.HiddenInput)
    stack_name = forms.RegexField(
        max_length=255,
        label=_('Stack Name'),
        help_text=_('Name of the stack to create.'),
        regex=r"^[a-zA-Z][a-zA-Z0-9_.-]*$",
        error_messages={
            'invalid':
            _('Name must start with a letter and may '
              'only contain letters, numbers, underscores, '
              'periods and hyphens.')
        })
    timeout_mins = forms.IntegerField(
        initial=60,
        label=_('Creation Timeout (minutes)'),
        help_text=_('Stack creation timeout in minutes.'))
    enable_rollback = forms.BooleanField(
        label=_('Rollback On Failure'),
        help_text=_('Enable rollback on create/update failure.'),
        required=False)

    def __init__(self, *args, **kwargs):
        parameters = kwargs.pop('parameters')
        # special case: load template data from API, not passed in params
        if kwargs.get('validate_me'):
            parameters = kwargs.pop('validate_me')
        super(CreateStackForm, self).__init__(*args, **kwargs)

        if self._stack_password_enabled():
            self.fields['password'] = forms.CharField(
                label=_('Password for user "%s"') % self.request.user.username,
                help_text=_('This is required for operations to be performed '
                            'throughout the lifecycle of the stack'),
                widget=forms.PasswordInput())

        self._build_parameter_fields(parameters)

    def _stack_password_enabled(self):
        stack_settings = getattr(settings, 'OPENSTACK_HEAT_STACK', {})
        return stack_settings.get('enable_user_pass', True)

    def _build_parameter_fields(self, template_validate):
        self.help_text = template_validate['Description']

        params = template_validate.get('Parameters', {})
        if template_validate.get('ParameterGroups'):
            params_in_order = []
            for group in template_validate['ParameterGroups']:
                for param in group.get('parameters', []):
                    if param in params:
                        params_in_order.append((param, params[param]))
        else:
            # no parameter groups, simply sorted to make the order fixed
            params_in_order = sorted(params.items())
        for param_key, param in params_in_order:
            field = None
            field_key = self.param_prefix + param_key
            initial = param.get(
                'Value', param.get('DefaultValue', param.get('Default')))
            field_args = {
                'initial': initial,
                'label': param.get('Label', param_key),
                'help_text': html.escape(param.get('Description', '')),
                'required': initial is None,
            }

            param_type = param.get('Type', None)
            hidden = strutils.bool_from_string(param.get('NoEcho', 'false'))
            if 'CustomConstraint' in param:
                choices = self._populate_custom_choices(
                    param['CustomConstraint'])
                field_args['choices'] = choices
                field = forms.ChoiceField(**field_args)

            elif 'AllowedValues' in param:
                choices = map(lambda x: (x, x), param['AllowedValues'])
                field_args['choices'] = choices
                field = forms.ChoiceField(**field_args)

            elif param_type == 'Json' and 'Default' in param:
                field_args['initial'] = json.dumps(param['Default'])
                field = forms.CharField(**field_args)

            elif param_type in ('CommaDelimitedList', 'String', 'Json'):
                if 'MinLength' in param:
                    field_args['min_length'] = int(param['MinLength'])
                    field_args['required'] = field_args['min_length'] > 0
                if 'MaxLength' in param:
                    field_args['max_length'] = int(param['MaxLength'])
                if hidden:
                    field_args['widget'] = forms.PasswordInput(
                        render_value=True)
                field = forms.CharField(**field_args)

            elif param_type == 'Number':
                if 'MinValue' in param:
                    field_args['min_value'] = int(param['MinValue'])
                if 'MaxValue' in param:
                    field_args['max_value'] = int(param['MaxValue'])
                field = forms.IntegerField(**field_args)

            elif param_type == 'Boolean':
                field_args['required'] = False
                field = forms.BooleanField(**field_args)

            if field:
                self.fields[field_key] = field

    @sensitive_variables('password')
    def handle(self, request, data):
        prefix_length = len(self.param_prefix)
        params_list = [(k[prefix_length:], v) for (k, v) in data.items()
                       if k.startswith(self.param_prefix)]
        fields = {
            'stack_name': data.get('stack_name'),
            'timeout_mins': data.get('timeout_mins'),
            'disable_rollback': not (data.get('enable_rollback')),
            'parameters': dict(params_list),
            'files': json.loads(data.get('parameters')).get('files'),
            'template': json.loads(data.get('parameters')).get('template')
        }
        if data.get('password'):
            fields['password'] = data.get('password')

        if data.get('environment_data'):
            fields['environment'] = data.get('environment_data')

        try:
            api.heat.stack_create(self.request, **fields)
            messages.info(request, _("Stack creation started."))
            return True
        except Exception:
            exceptions.handle(request)

    def _populate_custom_choices(self, custom_type):
        if custom_type == 'neutron.network':
            return instance_utils.network_field_data(self.request, True)
        if custom_type == 'nova.keypair':
            return instance_utils.keypair_field_data(self.request, True)
        if custom_type == 'glance.image':
            return image_utils.image_field_data(self.request, True)
        if custom_type == 'nova.flavor':
            return instance_utils.flavor_field_data(self.request, True)
        return []
Ejemplo n.º 25
0
class AddRule(forms.SelfHandlingForm):
    ip_protocol = forms.ChoiceField(
        label=_('IP Protocol'),
        choices=[('tcp', 'TCP'), ('udp', 'UDP'), ('icmp', 'ICMP')],
        help_text=_("The protocol which this "
                    "rule should be applied to."),
        widget=forms.Select(attrs={'class': 'switchable'}))
    from_port = forms.IntegerField(
        label=_("From Port"),
        help_text=_("TCP/UDP: Enter integer value "
                    "between 1 and 65535. ICMP: "
                    "enter a value for ICMP type "
                    "in the range (-1: 255)"),
        widget=forms.TextInput(attrs={
            'data': _('From Port'),
            'data-icmp': _('Type')
        }),
        validators=[validate_port_range])
    to_port = forms.IntegerField(label=_("To Port"),
                                 help_text=_("TCP/UDP: Enter integer value "
                                             "between 1 and 65535. ICMP: "
                                             "enter a value for ICMP code "
                                             "in the range (-1: 255)"),
                                 widget=forms.TextInput(attrs={
                                     'data': _('To Port'),
                                     'data-icmp': _('Code')
                                 }),
                                 validators=[validate_port_range])

    source_group = forms.ChoiceField(label=_('Source Group'),
                                     required=False,
                                     help_text=_("To specify an allowed IP "
                                                 "range, select CIDR. To "
                                                 "allow access from all "
                                                 "members of another security "
                                                 "group select Source Group."))
    cidr = fields.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=fields.IPv4 | fields.IPv6,
                          mask=True)

    security_group_id = forms.IntegerField(widget=forms.HiddenInput())

    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
        # source group option; add the choices and enable the option if so.
        security_groups_choices = [("", "CIDR")]
        if sg_list:
            security_groups_choices.append(('Security Group', sg_list))
        self.fields['source_group'].choices = security_groups_choices

    def clean(self):
        cleaned_data = super(AddRule, self).clean()
        from_port = cleaned_data.get("from_port", None)
        to_port = cleaned_data.get("to_port", None)
        cidr = cleaned_data.get("cidr", None)
        ip_proto = cleaned_data.get('ip_protocol', None)
        source_group = cleaned_data.get("source_group", None)

        if ip_proto == 'icmp':
            if from_port is None:
                msg = _('The ICMP type is invalid.')
                raise ValidationError(msg)
            if to_port is None:
                msg = _('The ICMP code is invalid.')
                raise ValidationError(msg)
            if from_port not in xrange(-1, 256):
                msg = _('The ICMP type not in range (-1, 255)')
                raise ValidationError(msg)
            if to_port not in xrange(-1, 256):
                msg = _('The ICMP code not in range (-1, 255)')
                raise ValidationError(msg)
        else:
            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)

        if source_group and cidr != self.fields['cidr'].initial:
            # Specifying a source group *and* a custom CIDR is invalid.
            msg = _('Either CIDR or Source Group may be specified, '
                    'but not both.')
            raise ValidationError(msg)
        elif source_group:
            # If a source group is specified, clear the CIDR from its default
            cleaned_data['cidr'] = None
        else:
            # If only cidr is specified, clear the source_group entirely
            cleaned_data['source_group'] = None

        return cleaned_data

    def handle(self, request, data):
        try:
            rule = api.security_group_rule_create(
                request, data['security_group_id'], data['ip_protocol'],
                data['from_port'], data['to_port'], data['cidr'],
                data['source_group'])
            messages.success(request,
                             _('Successfully added rule: %s') % unicode(rule))
            return rule
        except:
            redirect = reverse("horizon:project:access_and_security:index")
            exceptions.handle(request,
                              _('Unable to add rule to security group.'),
                              redirect=redirect)
Ejemplo n.º 26
0
    def _build_parameter_fields(self, template_validate):
        self.help_text = template_validate['Description']

        params = template_validate.get('Parameters', {})
        if template_validate.get('ParameterGroups'):
            params_in_order = []
            for group in template_validate['ParameterGroups']:
                for param in group.get('parameters', []):
                    if param in params:
                        params_in_order.append((param, params[param]))
        else:
            # no parameter groups, simply sorted to make the order fixed
            params_in_order = sorted(params.items())
        for param_key, param in params_in_order:
            field = None
            field_key = self.param_prefix + param_key
            initial = param.get(
                'Value', param.get('DefaultValue', param.get('Default')))
            field_args = {
                'initial': initial,
                'label': param.get('Label', param_key),
                'help_text': html.escape(param.get('Description', '')),
                'required': initial is None,
            }

            param_type = param.get('Type', None)
            hidden = strutils.bool_from_string(param.get('NoEcho', 'false'))
            if 'CustomConstraint' in param:
                choices = self._populate_custom_choices(
                    param['CustomConstraint'])
                field_args['choices'] = choices
                field = forms.ChoiceField(**field_args)

            elif 'AllowedValues' in param:
                choices = map(lambda x: (x, x), param['AllowedValues'])
                field_args['choices'] = choices
                field = forms.ChoiceField(**field_args)

            elif param_type == 'Json' and 'Default' in param:
                field_args['initial'] = json.dumps(param['Default'])
                field = forms.CharField(**field_args)

            elif param_type in ('CommaDelimitedList', 'String', 'Json'):
                if 'MinLength' in param:
                    field_args['min_length'] = int(param['MinLength'])
                    field_args['required'] = field_args['min_length'] > 0
                if 'MaxLength' in param:
                    field_args['max_length'] = int(param['MaxLength'])
                if hidden:
                    field_args['widget'] = forms.PasswordInput(
                        render_value=True)
                field = forms.CharField(**field_args)

            elif param_type == 'Number':
                if 'MinValue' in param:
                    field_args['min_value'] = int(param['MinValue'])
                if 'MaxValue' in param:
                    field_args['max_value'] = int(param['MaxValue'])
                field = forms.IntegerField(**field_args)

            elif param_type == 'Boolean':
                field_args['required'] = False
                field = forms.BooleanField(**field_args)

            if field:
                self.fields[field_key] = field
Ejemplo n.º 27
0
class CreateImageForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Name"), required=True)
    description = forms.CharField(widget=forms.widgets.Textarea(),
                                  label=_("Description"),
                                  required=False)

    source_type = forms.ChoiceField(
        label=_('Image Source'),
        choices=[('url', _('Image Location')), ('file', _('Image File'))],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'source'
        }))

    copy_from = forms.CharField(max_length="255",
                                label=_("Image Location"),
                                help_text=_("An external (HTTP) URL to load "
                                            "the image from."),
                                widget=forms.TextInput(
                                    attrs={
                                        'class': 'switched',
                                        'data-switch-on': 'source',
                                        'data-source-url': _('Image Location')
                                    }),
                                required=False)
    image_file = forms.FileField(label=_("Image File"),
                                 help_text=_("A local image to upload."),
                                 widget=forms.FileInput(
                                     attrs={
                                         'class': 'switched',
                                         'data-switch-on': 'source',
                                         'data-source-file': _('Image File')
                                     }),
                                 required=False)
    disk_format = forms.ChoiceField(
        label=_('Format'),
        required=True,
        choices=[('', ''), ('aki', _('AKI - Amazon Kernel '
                                     'Image')),
                 ('ami', _('AMI - Amazon Machine '
                           'Image')),
                 ('ari', _('ARI - Amazon Ramdisk '
                           'Image')), ('iso', _('ISO - Optical Disk Image')),
                 ('qcow2', _('QCOW2 - QEMU Emulator')), ('raw', 'Raw'),
                 ('vdi', 'VDI'), ('vhd', 'VHD'), ('vmdk', 'VMDK')],
        widget=forms.Select(attrs={'class': 'switchable'}))
    minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"),
                                      help_text=_(
                                          'The minimum disk size'
                                          ' required to boot the'
                                          ' image. If unspecified, this'
                                          ' value defaults to 0'
                                          ' (no minimum).'),
                                      required=False)
    minimum_ram = forms.IntegerField(label=_("Minimum Ram (MB)"),
                                     help_text=_('The minimum disk size'
                                                 ' required to boot the'
                                                 ' image. If unspecified, this'
                                                 ' value defaults to 0 (no'
                                                 ' minimum).'),
                                     required=False)
    is_public = forms.BooleanField(label=_("Public"), required=False)
    protected = forms.BooleanField(label=_("Protected"), required=False)

    def __init__(self, *args, **kwargs):
        super(CreateImageForm, self).__init__(*args, **kwargs)
        if not settings.HORIZON_IMAGES_ALLOW_UPLOAD:
            self.fields['image_file'].widget = HiddenInput()

    def clean(self):
        data = super(CreateImageForm, self).clean()
        if not data['copy_from'] and not data['image_file']:
            raise ValidationError(
                _("A image or external image location must be specified."))
        elif data['copy_from'] and data['image_file']:
            raise ValidationError(
                _("Can not specify both image and external image location."))
        else:
            return data

    def handle(self, request, data):
        # Glance does not really do anything with container_format at the
        # moment. It requires it is set to the same disk_format for the three
        # Amazon image types, otherwise it just treats them as 'bare.' As such
        # we will just set that to be that here instead of bothering the user
        # with asking them for information we can already determine.
        if data['disk_format'] in (
                'ami',
                'aki',
                'ari',
        ):
            container_format = data['disk_format']
        else:
            container_format = 'bare'

        meta = {
            'is_public': data['is_public'],
            'protected': data['protected'],
            'disk_format': data['disk_format'],
            'container_format': container_format,
            'min_disk': (data['minimum_disk'] or 0),
            'min_ram': (data['minimum_ram'] or 0),
            'name': data['name'],
            'properties': {}
        }

        if data['description']:
            meta['properties']['description'] = data['description']
        if settings.HORIZON_IMAGES_ALLOW_UPLOAD and data['image_file']:
            meta['data'] = self.files['image_file']
        else:
            meta['copy_from'] = data['copy_from']

        try:
            image = api.glance.image_create(request, **meta)
            messages.success(
                request,
                _('Your image %s has been queued for creation.') %
                data['name'])
            return image
        except Exception:
            exceptions.handle(request, _('Unable to create new image.'))
Ejemplo n.º 28
0
class ClusterAddInstanceForm(forms.SelfHandlingForm):
    cluster_id = forms.CharField(required=False, widget=forms.HiddenInput())
    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))
    volume = forms.IntegerField(label=_("Volume Size"),
                                min_value=0,
                                initial=1,
                                help_text=_("Size of the volume in GB."))
    name = forms.CharField(label=_("Name"),
                           required=False,
                           help_text=_("Optional name of the instance."))
    type = forms.CharField(
        label=_("Instance Type"),
        required=False,
        help_text=_("Optional datastore specific type of the instance."))
    related_to = forms.CharField(
        label=_("Related To"),
        required=False,
        help_text=_("Optional datastore specific value that defines the "
                    "relationship from one instance in the cluster to "
                    "another."))
    network = forms.ChoiceField(label=_("Network"),
                                help_text=_("Network attached to instance."),
                                required=False)

    def __init__(self, request, *args, **kwargs):
        super(ClusterAddInstanceForm, self).__init__(request, *args, **kwargs)
        self.fields['cluster_id'].initial = kwargs['initial']['cluster_id']
        self.fields['flavor'].choices = self.populate_flavor_choices(request)
        self.fields['network'].choices = self.populate_network_choices(request)

    @memoized.memoized_method
    def flavors(self, request):
        try:
            datastore = None
            datastore_version = None
            datastore_dict = self.initial.get('datastore', None)
            if datastore_dict:
                datastore = datastore_dict.get('type', None)
                datastore_version = datastore_dict.get('version', None)
            return trove_api.trove.datastore_flavors(
                request,
                datastore_name=datastore,
                datastore_version=datastore_version)
        except Exception:
            LOG.exception("Exception while obtaining flavors list")
            self._flavors = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to obtain flavors.'),
                              redirect=redirect)

    def populate_flavor_choices(self, request):
        flavor_list = [(f.id, "%s" % f.name) for f in self.flavors(request)]
        return sorted(flavor_list)

    @memoized.memoized_method
    def populate_network_choices(self, request):
        network_list = []
        try:
            if api.base.is_service_enabled(request, 'network'):
                tenant_id = self.request.user.tenant_id
                networks = api.neutron.network_list_for_tenant(
                    request, tenant_id)
                network_list = [(network.id, network.name_or_id)
                                for network in networks]
            else:
                self.fields['network'].widget = forms.HiddenInput()
        except exceptions.ServiceCatalogException:
            network_list = []
            redirect = reverse('horizon:project:database_clusters:index')
            exceptions.handle(request,
                              _('Unable to retrieve networks.'),
                              redirect=redirect)
        return network_list

    def handle(self, request, data):
        try:
            flavor = trove_api.trove.flavor_get(request, data['flavor'])
            manager = cluster_manager.get(data['cluster_id'])
            manager.add_instance(str(uuid.uuid4()), data.get('name', None),
                                 data['flavor'], flavor.name, data['volume'],
                                 data.get('type', None),
                                 data.get('related_to', None),
                                 data.get('network', None))
        except Exception:
            redirect = reverse("horizon:project:database_clusters:index")
            exceptions.handle(request,
                              _('Unable to grow cluster.'),
                              redirect=redirect)
        return True
Ejemplo n.º 29
0
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=_("Specify a free IP address "
                                    "from the selected subnet"),
                            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']:
                subnet_id_choices.append((s.id, s.cidr))
        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:
        name = _("Specify VIP")
        permissions = ('openstack.services.network', )
        help_text = _("Create a VIP for this pool. "
                      "Assign a name, description, IP address, port, "
                      "and maximum connections allowed for the VIP. "
                      "Choose the protocol and session persistence "
                      "method for the VIP. "
                      "Admin State is UP (checked) by default.")
Ejemplo n.º 30
0
class AddTaskDetailsAction(workflows.Action):

    scheduleId = forms.IntegerField(label=_("ScheduleId)"),
                                    required=True,
                                    min_value=1,
                                    max_value=9999999,
                                    help_text=_("ScheduleId"))

    name = forms.CharField(label=_("Name"),
                           required=True,
                           max_length=80,
                           help_text=_("Name"))

    description = forms.CharField(label=_("Description"),
                                  required=True,
                                  max_length=120,
                                  help_text=_("Description"))

    hour = forms.IntegerField(label=_("Hour)"),
                              required=True,
                              min_value=1,
                              max_value=12,
                              help_text=_("1-12"))

    min = forms.IntegerField(label=_("Minute"),
                             required=True,
                             min_value=0,
                             max_value=59,
                             help_text=_("0 - 59"))

    period = forms.ChoiceField(label=_("Period"),
                               choices=[('AM', 'AM'), ('PM', 'PM')],
                               required=True,
                               help_text=_("AM or PM"))

    start = forms.DateField(
        label=_("Start date"),
        required=False,
        input_formats=("%Y-%m-%d", ),
        help_text=_("YYYY-MM-DD"),
        widget=forms.DateInput(attrs={'data-date-format': 'yyyy-mm-dd'}))

    end = forms.DateField(
        label=_("End date"),
        required=True,
        input_formats=("%Y-%m-%d", ),
        help_text=_("YYYY-MM-DD"),
        widget=forms.DateInput(attrs={'data-date-format': 'yyyy-mm-dd'}))

    enabled = forms.BooleanField(label=_("Enabled"),
                                 required=False,
                                 help_text=_("Enabled"))

    class Meta:
        name = _("Details")

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