예제 #1
0
    def _init_fields(self, readOnly=False, create=False, initial=None):
        required = True
        textWidget = None
        choiceWidget = forms.Select
        if create:
            expressionWidget = ExpressionWidget(initial)
            matchByWidget = MatchByWidget(initial)
            notificationWidget = NotificationCreateWidget()
        else:
            expressionWidget = textWidget
            matchByWidget = forms.TextInput(attrs={'readonly': 'readonly'})
            notificationWidget = NotificationCreateWidget()

        self.fields['name'] = forms.CharField(label=_("Name"),
                                              required=required,
                                              max_length=250,
                                              widget=textWidget,
                                              help_text=_("An unique name of the alarm."))
        self.fields['expression'] = forms.CharField(label=_("Expression"),
                                                    required=required,
                                                    widget=expressionWidget,
                                                    help_text=_("An alarm expression."))
        self.fields['match_by'] = forms.CharField(label=_("Match by"),
                                                  required=False,
                                                  widget=matchByWidget,
                                                  help_text=_("The metric dimensions used "
                                                              "to create unique alarms."))
        self.fields['description'] = forms.CharField(label=_("Description"),
                                                     required=False,
                                                     widget=textWidget,
                                                     help_text=_("A description of an alarm."))
        sev_choices = [("LOW", _("Low")),
                       ("MEDIUM", _("Medium")),
                       ("HIGH", _("High")),
                       ("CRITICAL", _("Critical"))]
        self.fields['severity'] = forms.ChoiceField(label=_("Severity"),
                                                    choices=sev_choices,
                                                    initial=sev_choices[0],
                                                    widget=choiceWidget,
                                                    required=False,
                                                    help_text=_("Severity of an alarm. "
                                                                "Must be either LOW, MEDIUM, HIGH "
                                                                "or CRITICAL. Default is LOW."))
        if not create:
            self.fields['actions_enabled'] = \
                forms.BooleanField(label=_("Notifications Enabled"),
                                   required=False,
                                   initial=True)
        self.fields['notifications'] = NotificationField(
            label=_("Notifications"),
            required=False,
            widget=notificationWidget,
            help_text=_("Notification methods. "
                        "Notifications can be sent when an alarm state transition occurs."))
        self.fields['alarm_actions'] = NotificationField(
            label=_("Alarm Actions"),
            widget=forms.MultipleHiddenInput())
        self.fields['ok_actions'] = NotificationField(
            label=_("OK Actions"),
            widget=forms.MultipleHiddenInput())
        self.fields['undetermined_actions'] = NotificationField(
            label=_("Undetermined Actions"),
            widget=forms.MultipleHiddenInput())
예제 #2
0
class AddPoolAction(workflows.Action):
    name = forms.CharField(max_length=80, label=_("Name"))
    description = forms.CharField(initial="",
                                  required=False,
                                  max_length=80,
                                  label=_("Description"))
    # provider is optional because some LBaaS implementation does
    # not support service-type extension.
    provider = forms.ChoiceField(label=_("Provider"), required=False)
    subnet_id = forms.ChoiceField(label=_("Subnet"))
    protocol = forms.ChoiceField(label=_("Protocol"))
    lb_method = forms.ChoiceField(label=_("Load Balancing Method"))
    admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
                                                (False, _('DOWN'))],
                                       label=_("Admin State"))

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

        tenant_id = request.user.tenant_id

        subnet_id_choices = [('', _("Select a Subnet"))]
        try:
            networks = api.neutron.network_list_for_tenant(request, tenant_id)
        except Exception:
            exceptions.handle(request, _('Unable to retrieve networks list.'))
            networks = []
        for n in networks:
            for s in n['subnets']:
                name = "%s (%s)" % (s.name_or_id, s.cidr)
                subnet_id_choices.append((s.id, name))
        self.fields['subnet_id'].choices = subnet_id_choices

        protocol_choices = [('', _("Select a Protocol"))]
        [protocol_choices.append((p, p)) for p in AVAILABLE_PROTOCOLS]
        self.fields['protocol'].choices = protocol_choices

        lb_method_choices = [('', _("Select a Method"))]
        [lb_method_choices.append((m, m)) for m in AVAILABLE_METHODS]
        self.fields['lb_method'].choices = lb_method_choices

        # provider choice
        try:
            if api.neutron.is_extension_supported(request, 'service-type'):
                provider_list = api.neutron.provider_list(request)
                providers = [
                    p for p in provider_list
                    if p['service_type'] == 'LOADBALANCER'
                ]
            else:
                providers = None
        except Exception:
            exceptions.handle(request, _('Unable to retrieve providers list.'))
            providers = []

        if providers:
            default_providers = [p for p in providers if p.get('default')]
            if default_providers:
                default_provider = default_providers[0]['name']
            else:
                default_provider = None
            provider_choices = [(p['name'], p['name']) for p in providers
                                if p['name'] != default_provider]
            if default_provider:
                provider_choices.insert(
                    0,
                    (default_provider, _("%s (default)") % default_provider))
        else:
            if providers is None:
                msg = _("Provider for Load Balancer is not supported")
            else:
                msg = _("No provider is available")
            provider_choices = [('', msg)]
            self.fields['provider'].widget.attrs['readonly'] = True
        self.fields['provider'].choices = provider_choices

    class Meta(object):
        name = _("Add New Pool")
        permissions = ('openstack.services.network', )
        help_text_template = 'project/loadbalancers/_create_pool_help.html'
예제 #3
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)
예제 #4
0
class AttachForm(forms.SelfHandlingForm):
    instance = forms.ChoiceField(label=_("Attach to Instance"),
                                 help_text=_("Select an instance to "
                                             "attach to."))

    device = forms.CharField(
        label=_("Device Name"),
        widget=forms.TextInput(attrs={'placeholder': '/dev/vdc'}),
        required=False,
        help_text=_("Actual device name may differ due "
                    "to hypervisor settings. If not "
                    "specified, then hypervisor will "
                    "select a device name."))

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

        # Hide the device field if the hypervisor doesn't support it.
        hypervisor_features = getattr(settings,
                                      "OPENSTACK_HYPERVISOR_FEATURES", {})
        can_set_mount_point = hypervisor_features.get("can_set_mount_point",
                                                      False)
        if not can_set_mount_point:
            self.fields['device'].widget = forms.widgets.HiddenInput()

        # populate volume_id
        volume = kwargs.get('initial', {}).get("volume", None)
        if volume:
            volume_id = volume.id
        else:
            volume_id = None
        self.fields['volume_id'] = forms.CharField(widget=forms.HiddenInput(),
                                                   initial=volume_id)

        # Populate instance choices
        instance_list = kwargs.get('initial', {}).get('instances', [])
        instances = []
        for instance in instance_list:
            if instance.status in tables.VOLUME_ATTACH_READY_STATES and \
                    not any(instance.id == att["server_id"]
                            for att in volume.attachments):
                instances.append(
                    (instance.id, '%s (%s)' % (instance.name, instance.id)))
        if instances:
            instances.insert(0, ("", _("Select an instance")))
        else:
            instances = (("", _("No instances available")), )
        self.fields['instance'].choices = instances

    def handle(self, request, data):
        instance_choices = dict(self.fields['instance'].choices)
        instance_name = instance_choices.get(data['instance'],
                                             _("Unknown instance (None)"))
        # The name of the instance in the choices list has the ID appended to
        # it, so let's slice that off...
        instance_name = instance_name.rsplit(" (")[0]
        try:
            attach = api.nova.instance_volume_attach(request,
                                                     data['volume_id'],
                                                     data['instance'],
                                                     data.get('device', ''))
            volume = cinder.volume_get(request, data['volume_id'])
            message = _('Attaching volume %(vol)s to instance '
                        '%(inst)s on %(dev)s.') % {
                            "vol": volume.name,
                            "inst": instance_name,
                            "dev": attach.device
                        }
            messages.info(request, message)
            return True
        except Exception:
            redirect = reverse("horizon:project:volumes:index")
            exceptions.handle(request,
                              _('Unable to attach volume.'),
                              redirect=redirect)
예제 #5
0
파일: forms.py 프로젝트: raydeann/manila-ui
class CreateShareType(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Name"), required=True)
    spec_driver_handles_share_servers = forms.ChoiceField(
        label=_("Driver handles share servers"),
        required=True,
        choices=(('False', 'False'), ('True', 'True')))
    extra_specs = forms.CharField(
        required=False,
        label=_("Extra specs"),
        widget=forms.widgets.Textarea(attrs=ST_EXTRA_SPECS_FORM_ATTRS))
    is_public = forms.BooleanField(
        label=_("Public"),
        required=False,
        initial=True,
        help_text=("Defines whether this share type is available for all "
                   "or not. List of allowed tenants should be set "
                   "separately."))

    def __init__(self, *args, **kwargs):
        super(CreateShareType, self).__init__(*args, **kwargs)
        manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
        self.enable_public_share_type_creation = manila_features.get(
            'enable_public_share_type_creation', True)
        if not self.enable_public_share_type_creation:
            self.fields.pop('is_public')

    def handle(self, request, data):
        try:
            spec_dhss = data['spec_driver_handles_share_servers'].lower()
            allowed_dhss_values = ('true', 'false')
            if spec_dhss not in allowed_dhss_values:
                msg = _("Improper value set to required extra spec "
                        "'spec_driver_handles_share_servers'. "
                        "Allowed values are %s. "
                        "Case insensitive.") % allowed_dhss_values
                raise ValidationError(message=msg)

            set_dict, unset_list = utils.parse_str_meta(data['extra_specs'])
            if unset_list:
                msg = _("Expected only pairs of key=value.")
                raise ValidationError(message=msg)

            is_public = (self.enable_public_share_type_creation
                         and data["is_public"])
            share_type = manila.share_type_create(request,
                                                  data["name"],
                                                  spec_dhss,
                                                  is_public=is_public)
            if set_dict:
                manila.share_type_set_extra_specs(request, share_type.id,
                                                  set_dict)

            msg = _("Successfully created share type: %s") % share_type.name
            messages.success(request, msg)
            return True
        except ValidationError as e:
            # handle error without losing dialog
            self.api_error(e.messages[0])
            return False
        except Exception:
            exceptions.handle(request, _('Unable to create share type.'))
            return False
예제 #6
0
class LaunchMarketImage(forms.SelfHandlingForm):
    image_id = forms.CharField(label=_("image"),
                               widget=forms.HiddenInput,
                               required=True)
    image_name = forms.CharField(label=_("Image Name"), required=False)
    flavor = forms.ChoiceField(label=_("Choose flavor"), required=True)

    def __init__(self, *args, **kwargs):
        super(LaunchMarketImage, self).__init__(*args, **kwargs)
        self.avail_zone = 'nova'
        name = self.request.user.username.split('@')[0]
        self.instance_name = random_name(prefix=name)
        self.nics = []
        self.private_ip = ''
        self.floating_ip = None
        self.port = None
        self.vpn_username = None
        self.vpn_password = None
        self.success_message = _(
            'Created %(count)s named "%(name)s". '
            'Please check account and password for this virtual machine in %(email)s.'
        ) % {
            "count": _("instance"),
            "name": self.instance_name,
            "email": self.request.user.username
        }
        if get_language() == 'en':
            self.vpn_email_template = 'project/instances/vpn_email_en.html'
            self.instance_email_template = 'project/instances/email_instance_info_en.html'
        elif get_language() == 'zh-cn':
            self.vpn_email_template = 'project/instances/vpn_email.html'
            self.instance_email_template = 'project/instances/email_instance_info.html'
        image_id = kwargs.get('initial', {}).get('image_id', "")
        hypervisor_type = 'docker'
        try:
            image = api.glance.image_get(self.request, image_id)
            hypervisor_type = image.properties.get('hypervisor_type', '')
        except Exception:
            pass
        flavors = instance_utils.flavor_list(self.request)
        if flavors:
            self.fields['flavor'].choices = \
                instance_utils.sort_match_flavor_list(self.request, flavors, hypervisor_type)
        self.fields['image_name'].widget.attrs['readonly'] = True

    def create_network(self):
        internal_ip_id = ''
        external_ip_id = ''
        management_ip_id = ''
        for config in OPENSTACK_CONFIG_BACKEND['configs']:
            if config['region'] == self.request.user.services_region:
                internal_ip_id = config['internal_ip_id']
                external_ip_id = config['external_ip_id']
                management_ip_id = config['management_ip_id']

        self.port = api.neutron.port_create(self.request, internal_ip_id)
        for ips in self.port.fixed_ips:
            self.private_ip = ips['ip_address']
        if self.request.user.is_superuser and management_ip_id != '':
            self.nics = [{
                "port-id": self.port.id
            }, {
                "net-id": management_ip_id
            }]
        else:
            self.nics = [{"port-id": self.port.id}]

        self.floating_ip = api.network.tenant_floating_ip_allocate(
            self.request, external_ip_id)

    def pre_auth_user(self, flavor_id):
        need_points = 10
        check_result = True
        if self.request.user.is_superuser:
            return 0, check_result

        try:
            point_rate = PointRate.objects.get(flavor_id=flavor_id,
                                               service_type='Cloud')
            need_points = point_rate.points
        except Exception, ex:
            LOG.error('%s %s', Exception, ex)

        check_result, err_code = preauth(self.request.user.username,
                                         need_points)

        return need_points, check_result
예제 #7
0
class TestForm(forms.SelfHandlingForm):

    name = forms.CharField(max_length=255)

    def handle(self, request, data):
        return True
예제 #8
0
파일: forms.py 프로젝트: yashbathia/horizon
class EditTagsForm(ImageForm):
    image_id = forms.CharField(widget=forms.HiddenInput())
예제 #9
0
class CreateNamespaceForm(forms.SelfHandlingForm):
    source_type = forms.ChoiceField(
        label=_('Namespace Definition Source'),
        required=False,
        choices=[('file', _('Metadata Definition File')),
                 ('raw', _('Direct Input'))],
        widget=forms.Select(
            attrs={'class': 'switchable', 'data-slug': 'source'}))

    metadef_file = forms.FileField(
        label=_("Metadata Definition File"),
        help_text=_("A local metadata definition file to upload."),
        widget=forms.FileInput(
            attrs={'class': 'switched', 'data-switch-on': 'source',
                   'data-source-file': _('Metadata Definition File')}),
        required=False)

    direct_input = forms.CharField(
        label=_('Namespace JSON'),
        help_text=_('The JSON formatted contents of a namespace.'),
        widget=forms.widgets.Textarea(
            attrs={'class': 'switched', 'data-switch-on': 'source',
                   'data-source-raw': _('Namespace JSON')}),
        required=False)

    public = forms.BooleanField(label=_("Public"), required=False)
    protected = forms.BooleanField(label=_("Protected"), required=False)

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

    def clean(self):
        data = super(CreateNamespaceForm, self).clean()

        # The key can be missing based on particular upload
        # conditions. Code defensively for it here...
        metadef_file = data.get('metadef_file', None)
        metadata_raw = data.get('direct_input', None)

        if metadata_raw and metadef_file:
            raise ValidationError(
                _("Cannot specify both file and direct input."))
        if not metadata_raw and not metadef_file:
            raise ValidationError(
                _("No input was provided for the namespace content."))
        try:
            if metadef_file:
                ns_str = self.files['metadef_file'].read()
            else:
                ns_str = data['direct_input']
            namespace = json.loads(ns_str)

            if data['public']:
                namespace['visibility'] = 'public'
            else:
                namespace['visibility'] = 'private'

            namespace['protected'] = data['protected']

            for protected_prop in constants.METADEFS_PROTECTED_PROPS:
                namespace.pop(protected_prop, None)

            data['namespace'] = namespace
        except Exception as e:
            msg = _('There was a problem loading the namespace: %s.') % e
            raise forms.ValidationError(msg)

        return data

    def handle(self, request, data):
        try:
            namespace = glance.metadefs_namespace_create(request,
                                                         data['namespace'])
            messages.success(request,
                             _('Namespace %s has been created.') %
                             namespace['namespace'])
            return namespace
        except Exception as e:
            msg = _('Unable to create new namespace. %s')
            msg %= e.message.split('Failed validating', 1)[0]
            exceptions.handle(request, message=msg)
            return False
예제 #10
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)
    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
                                          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 = ("project/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...
        volume_type = self.data.get('volume_type', None)
        if volume_type:  # Boot from volume
            if cleaned_data[source]:
                raise forms.ValidationError(
                    _("You can't select an instance "
                      "source when booting from a "
                      "Volume. The Volume is your "
                      "source and should contain "
                      "the operating system."))
        else:  # Boot from image / image_snapshot
            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 "
                      "attemtping to launch an "
                      "instance."))
            elif 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)
        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 _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    def populate_image_id_choices(self, request, context):
        self._init_images_cache()
        images = 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.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available.")))
        return choices

    def populate_instance_snapshot_id_choices(self, request, context):
        self._init_images_cache()
        images = 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.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 populate_availability_zone_choices(self, request, context):
        try:
            zones = api.nova.availability_zone_list(request)
        except:
            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 zone_list:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        else:
            zone_list.insert(0, ("", _("No availability zones found.")))
        return zone_list

    def get_help_text(self):
        extra = {}
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.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)
예제 #11
0
class VolumeOptionsAction(workflows.Action):
    VOLUME_CHOICES = (
        ('', _("Don't boot from a volume.")),
        ("volume_id", _("Boot from volume.")),
        ("volume_snapshot_id",
         _("Boot from volume snapshot "
           "(creates a new volume).")),
    )
    # Boot from volume options
    volume_type = forms.ChoiceField(label=_("Volume Options"),
                                    choices=VOLUME_CHOICES,
                                    required=False)
    volume_id = forms.ChoiceField(label=_("Volume"), required=False)
    volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
                                           required=False)
    device_name = forms.CharField(label=_("Device Name"),
                                  required=False,
                                  initial="vda",
                                  help_text=_("Volume mount point (e.g. 'vda' "
                                              "mounts at '/dev/vda')."))
    delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
                                             initial=False,
                                             required=False,
                                             help_text=_("Delete volume on "
                                                         "instance terminate"))

    class Meta:
        name = _("Volume Options")
        permissions = ('openstack.services.volume', )
        help_text_template = ("project/instances/" "_launch_volumes_help.html")

    def clean(self):
        cleaned_data = super(VolumeOptionsAction, self).clean()
        volume_opt = cleaned_data.get('volume_type', None)

        if volume_opt and not cleaned_data[volume_opt]:
            raise forms.ValidationError(
                _('Please choose a volume, or select '
                  '%s.') % self.VOLUME_CHOICES[0][1])
        return cleaned_data

    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.display_name or volume.id,
                    'size': volume.size,
                    'label': visible_label
                }))

    def populate_volume_id_choices(self, request, context):
        volume_options = [("", _("Select Volume"))]
        try:
            volumes = [
                v for v in cinder.volume_list(self.request)
                if v.status == api.cinder.VOLUME_STATE_AVAILABLE
            ]
            volume_options.extend(
                [self._get_volume_display_name(vol) for vol in volumes])
        except:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        return volume_options

    def populate_volume_snapshot_id_choices(self, request, context):
        volume_options = [("", _("Select Volume Snapshot"))]
        try:
            snapshots = cinder.volume_snapshot_list(self.request)
            snapshots = [
                s for s in snapshots
                if s.status == api.cinder.VOLUME_STATE_AVAILABLE
            ]
            volume_options.extend(
                [self._get_volume_display_name(snap) for snap in snapshots])
        except:
            exceptions.handle(
                self.request,
                _('Unable to retrieve list of volume '
                  'snapshots.'))

        return volume_options
예제 #12
0
파일: forms.py 프로젝트: zhang-hua/horizon
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.'))
예제 #13
0
class JobConfigAction(workflows.Action):
    MAIN_CLASS = "edp.java.main_class"
    JAVA_OPTS = "edp.java.java_opts"
    EDP_MAPPER = "edp.streaming.mapper"
    EDP_REDUCER = "edp.streaming.reducer"
    EDP_PREFIX = "edp."
    EDP_HBASE_COMMON_LIB = "edp.hbase_common_lib"
    EDP_ADAPT_FOR_OOZIE = "edp.java.adapt_for_oozie"
    EDP_ADAPT_SPARK_SWIFT = "edp.spark.adapt_for_swift"
    EDP_SUBST_DATASOURCE_NAME = "edp.substitute_data_source_for_name"
    EDP_SUBST_DATASOURCE_UUID = "edp.substitute_data_source_for_uuid"

    property_name = forms.ChoiceField(required=False, )

    job_configs = forms.CharField(required=False, widget=forms.HiddenInput())

    job_params = forms.CharField(required=False, widget=forms.HiddenInput())

    job_args_array = forms.CharField(required=False,
                                     widget=forms.HiddenInput())

    job_type = forms.CharField(required=False, widget=forms.HiddenInput())

    main_class = forms.CharField(label=_("Main Class"), required=False)

    java_opts = forms.CharField(label=_("Java Opts"), required=False)

    streaming_mapper = forms.CharField(label=_("Mapper"))

    streaming_reducer = forms.CharField(label=_("Reducer"))

    hbase_common_lib = forms.BooleanField(
        label=_("Use HBase Common library"),
        help_text=_("Run HBase EDP Jobs with common HBase library on HDFS"),
        required=False,
        initial=True)

    adapt_oozie = forms.BooleanField(
        label=_("Adapt For Oozie"),
        help_text=_("Automatically modify the Hadoop configuration"
                    " so that job config values are set and so that"
                    " Oozie will handle exit codes correctly."),
        required=False,
        initial=True)

    adapt_spark_swift = forms.BooleanField(
        label=_("Enable Swift Paths"),
        help_text=_("Modify the configuration so that swift URLs can "
                    "be dereferenced through HDFS at runtime."),
        required=False,
        initial=True)

    datasource_substitute = forms.BooleanField(
        label=_("Use Data Source Substitution for Names and UUIDs"),
        help_text=_("Substitute data source objects for URLs of "
                    "the form datasource://name or uuid."),
        required=False,
        initial=True)

    def __init__(self, request, *args, **kwargs):
        super(JobConfigAction, self).__init__(request, *args, **kwargs)
        req = request.GET or request.POST
        job_ex_id = req.get("job_execution_id")
        if job_ex_id is not None:
            job_ex = saharaclient.job_execution_get(request, job_ex_id)
            job = saharaclient.job_get(request, job_ex.job_id)
            job_configs, interface_args = _merge_interface_with_configs(
                job.interface, job_ex.job_configs)
            edp_configs = {}

            if 'configs' in job_configs:
                configs, edp_configs = (self.clean_edp_configs(
                    job_configs['configs']))
                self.fields['job_configs'].initial = (json.dumps(configs))

            if 'params' in job_configs:
                self.fields['job_params'].initial = (json.dumps(
                    job_configs['params']))

            if 'args' in job_configs:
                self.fields['job_args_array'].initial = (json.dumps(
                    job_configs['args']))

            if self.MAIN_CLASS in edp_configs:
                self.fields['main_class'].initial = (
                    edp_configs[self.MAIN_CLASS])
            if self.JAVA_OPTS in edp_configs:
                self.fields['java_opts'].initial = (
                    edp_configs[self.JAVA_OPTS])

            if self.EDP_MAPPER in edp_configs:
                self.fields['streaming_mapper'].initial = (
                    edp_configs[self.EDP_MAPPER])
            if self.EDP_REDUCER in edp_configs:
                self.fields['streaming_reducer'].initial = (
                    edp_configs[self.EDP_REDUCER])
            if self.EDP_HBASE_COMMON_LIB in edp_configs:
                self.fields['hbase_common_lib'].initial = (
                    edp_configs[self.EDP_HBASE_COMMON_LIB])
            if self.EDP_ADAPT_FOR_OOZIE in edp_configs:
                self.fields['adapt_oozie'].initial = (
                    edp_configs[self.EDP_ADAPT_FOR_OOZIE])
            if self.EDP_ADAPT_SPARK_SWIFT in edp_configs:
                self.fields['adapt_spark_swift'].initial = (
                    edp_configs[self.EDP_ADAPT_SPARK_SWIFT])
            if (self.EDP_SUBST_DATASOURCE_NAME in edp_configs
                    or self.EDP_SUBST_DATASOURCE_UUID in edp_configs):
                self.fields['datasource_substitute'].initial = (
                    edp_configs.get(self.EDP_SUBST_DATASOURCE_UUID, True)
                    or edp_configs.get(self.EDP_SUBST_DATASOURCE_NAME, True))

    def clean(self):
        cleaned_data = super(workflows.Action, self).clean()
        job_type = cleaned_data.get("job_type", None)

        if job_type != "MapReduce.Streaming":
            if "streaming_mapper" in self._errors:
                del self._errors["streaming_mapper"]
            if "streaming_reducer" in self._errors:
                del self._errors["streaming_reducer"]

        return cleaned_data

    def populate_property_name_choices(self, request, context):
        req = request.GET or request.POST
        job_id = req.get("job_id") or req.get("job")
        job_type = saharaclient.job_get(request, job_id).type
        job_configs = (saharaclient.job_get_configs(request,
                                                    job_type).job_config)
        choices = [(param['value'], param['name'])
                   for param in job_configs['configs']]
        return choices

    def clean_edp_configs(self, configs):
        edp_configs = {}
        for key, value in six.iteritems(configs):
            if key.startswith(self.EDP_PREFIX):
                edp_configs[key] = value
        for rmkey in edp_configs.keys():
            # remove all configs handled via other controls
            # so they do not show up in the free entry inputs
            if rmkey in [
                    self.EDP_HBASE_COMMON_LIB,
                    self.EDP_MAPPER,
                    self.EDP_REDUCER,
                    self.MAIN_CLASS,
                    self.JAVA_OPTS,
                    self.EDP_ADAPT_FOR_OOZIE,
                    self.EDP_ADAPT_SPARK_SWIFT,
                    self.EDP_SUBST_DATASOURCE_UUID,
                    self.EDP_SUBST_DATASOURCE_NAME,
            ]:
                del configs[rmkey]
        return (configs, edp_configs)

    class Meta(object):
        name = _("Configure")
        help_text_template = "job_templates/_launch_job_configure_help.html"
예제 #14
0
class AddHostForm(forms.SelfHandlingForm):

    segment_id = forms.CharField(widget=forms.HiddenInput())
    segment_name = forms.CharField(
        label=_('Segment Name'),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}),
        required=False)
    name = forms.ChoiceField(label=_('Host Name'), choices=[])
    reserved = forms.ChoiceField(
        label=_('Reserved'),
        choices=[('False', 'False'), ('True', 'True')],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'available host'
        }),
        required=False,
        help_text=_("A boolean indicating whether this host is reserved or"
                    " not. Default value is set to False."))
    type = forms.CharField(label=_('Type'),
                           widget=forms.TextInput(attrs={'maxlength': 255}),
                           help_text=_("Type of host."))
    control_attributes = forms.CharField(
        label=_('Control Attribute'),
        widget=forms.TextInput(),
        help_text=_("Attributes to control host."))
    on_maintenance = forms.ChoiceField(
        label=_('On Maintenance'),
        choices=[('False', 'False'), ('True', 'True')],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'available host'
        }),
        required=False,
        help_text=_("A boolean indicating whether this host is on maintenance"
                    " or not. Default value is set to False."))

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

        # Populate hypervisor name choices
        hypervisor_list = kwargs.get('initial', {}).get("hypervisor_list", [])
        hypervisor_name_list = []
        for hypervisor in hypervisor_list:
            hypervisor_name_list.append(
                (hypervisor.hypervisor_hostname, '%(name)s (%(id)s)' % {
                    "name": hypervisor.hypervisor_hostname,
                    "id": hypervisor.id
                }))
        if hypervisor_name_list:
            hypervisor_name_list.insert(0, ("", _("Select a host")))
        else:
            hypervisor_name_list.insert(0, ("", _("No host available")))
        self.fields['name'].choices = hypervisor_name_list

    def handle(self, request, data):
        try:
            api.create_host(request, data)
            msg = _('Host created successfully.')
            messages.success(request, msg)
        except Exception:
            msg = _('Failed to create host.')
            redirect = reverse('horizon:masakaridashboard:segments:index')
            exceptions.handle(request, msg, redirect=redirect)

        return True
예제 #15
0
class UpdatePort(forms.SelfHandlingForm):
    network_id = forms.CharField(widget=forms.HiddenInput())
    port_id = forms.CharField(
        label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.ThemableChoiceField(choices=[('True', _('UP')),
                                                     ('False', _('DOWN'))],
                                            label=_("Admin State"))
    failure_url = 'horizon:project:networks:detail'

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

                    self.fields['binding__vnic_type'] = forms.ChoiceField(
                        choices=vnic_type_choices,
                        label=_("Binding: VNIC Type"),
                        help_text=_(
                            "The VNIC type that is bound to the neutron port"),
                        required=False)
        except Exception:
            msg = _("Unable to verify the VNIC types extension in Neutron")
            exceptions.handle(self.request, msg)

        try:
            if api.neutron.is_extension_supported(request, 'mac-learning'):
                self.fields['mac_state'] = forms.BooleanField(
                    label=_("MAC Learning State"),
                    initial=False,
                    required=False)
        except Exception:
            msg = _("Unable to retrieve MAC learning state")
            exceptions.handle(self.request, msg)

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

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

            port = api.neutron.port_update(request,
                                           data['port_id'],
                                           name=data['name'],
                                           admin_state_up=data['admin_state'],
                                           **extension_kwargs)
            msg = _('Port %s was successfully updated.') % data['port_id']
            LOG.debug(msg)
            messages.success(request, msg)
            return port
        except Exception:
            msg = _('Failed to update port %s') % data['port_id']
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=[data['network_id']])
            exceptions.handle(request, msg, redirect=redirect)
예제 #16
0
class DefinitionForm(forms.SelfHandlingForm):
    definition_source = forms.ChoiceField(
        label=_('Definition Source'),
        choices=[('file', _('File')), ('raw', _('Direct Input'))],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'definitionsource'
        }))
    definition_upload = forms.FileField(
        label=_('Definition File'),
        help_text=_('A local definition to upload.'),
        widget=forms.FileInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'definitionsource',
                'data-required-when-shown': 'true',
                'data-definitionsource-file': _('Definition File')
            }),
        required=False)
    definition_data = forms.CharField(
        label=_('Definition Data'),
        help_text=_('The raw contents of the definition.'),
        widget=forms.widgets.Textarea(
            attrs={
                'class': 'switched',
                'data-switch-on': 'definitionsource',
                'data-required-when-shown': 'true',
                'data-definitionsource-raw': _('Definition Data'),
                'rows': 4
            }),
        required=False)

    def __init__(self, *args, **kwargs):
        self.next_view = kwargs.pop('next_view')
        super(DefinitionForm, self).__init__(*args, **kwargs)

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

        if cleaned_data.get('definition_upload'):
            files = self.request.FILES
            cleaned_data['definition'] = files['definition_upload'].read()
        elif cleaned_data.get('definition_data'):
            cleaned_data['definition'] = cleaned_data['definition_data']
        else:
            raise forms.ValidationError(
                _('You must specify the definition source.'))
        try:
            validated = api.workflow_validate(self.request,
                                              cleaned_data['definition'])
        except Exception as e:
            raise forms.ValidationError(six.text_type(e))

        if not validated.get('valid'):
            raise forms.ValidationError(
                validated.get('error', _('Validated failed')))

        return cleaned_data

    def handle(self, request, data):
        kwargs = {'definition': data['definition']}
        request.method = 'GET'

        return self.next_view.as_view()(request, **kwargs)
예제 #17
0
class CreatePort(forms.SelfHandlingForm):
    network_name = forms.CharField(
        label=_("Network Name"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}),
        required=False)
    network_id = forms.CharField(
        label=_("Network ID"),
        widget=forms.TextInput(attrs={'readonly': 'readonly'}))
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    admin_state = forms.ChoiceField(choices=[('True', _('UP')),
                                             ('False', _('DOWN'))],
                                    label=_("Admin State"))
    device_id = forms.CharField(max_length=100,
                                label=_("Device ID"),
                                help_text=_("Device ID attached to the port"),
                                required=False)
    device_owner = forms.CharField(
        max_length=100,
        label=_("Device Owner"),
        help_text=_("Owner of the device attached to the port"),
        required=False)
    specify_ip = forms.ThemableChoiceField(
        label=_("Specify IP address or subnet"),
        help_text=_("To specify a subnet or a fixed IP, select any options."),
        initial=False,
        required=False,
        choices=[('', _("Unspecified")), ('subnet_id', _("Subnet")),
                 ('fixed_ip', _("Fixed IP Address"))],
        widget=forms.ThemableSelectWidget(attrs={
            'class': 'switchable',
            'data-slug': 'specify_ip',
        }))
    subnet_id = forms.ThemableChoiceField(
        label=_("Subnet"),
        required=False,
        widget=forms.ThemableSelectWidget(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-subnet_id': _('Subnet'),
            }))
    fixed_ip = forms.IPField(
        label=_("Fixed IP Address"),
        required=False,
        help_text=_("Specify the subnet IP address for the new port"),
        version=forms.IPv4 | forms.IPv6,
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'specify_ip',
                'data-specify_ip-fixed_ip': _('Fixed IP Address'),
            }))
    failure_url = 'horizon:project:networks:detail'

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

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

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

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

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

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

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

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

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

            port = api.neutron.port_create(request, **params)
            if port['name']:
                msg = _('Port %s was successfully created.') % port['name']
            else:
                msg = _('Port %s was successfully created.') % port['id']
            LOG.debug(msg)
            messages.success(request, msg)
            return port
        except Exception:
            msg = _('Failed to create a port for network %s') \
                % data['network_id']
            LOG.info(msg)
            redirect = reverse(self.failure_url, args=(data['network_id'], ))
            exceptions.handle(request, msg, redirect=redirect)
예제 #18
0
class SetAccessControlsAction(workflows.Action):
    keypair = forms.DynamicChoiceField(label=_("Keypair"),
                                       required=False,
                                       help_text=_("Which keypair to use for "
                                                   "authentication."),
                                       add_item_link=KEYPAIR_IMPORT_URL)
    admin_pass = forms.RegexField(
            label=_("Admin Pass"),
            required=False,
            widget=forms.PasswordInput(render_value=False),
            regex=validators.password_validator(),
            error_messages={'invalid': validators.password_validator_msg()})
    confirm_admin_pass = forms.CharField(
            label=_("Confirm Admin Pass"),
            required=False,
            widget=forms.PasswordInput(render_value=False))
    groups = forms.MultipleChoiceField(label=_("Security Groups"),
                                       required=True,
                                       initial=["default"],
                                       widget=forms.CheckboxSelectMultiple(),
                                       help_text=_("Launch instance in these "
                                                   "security groups."))

    class Meta:
        name = _("Access & Security")
        help_text = _("Control access to your instance via keypairs, "
                      "security groups, and other mechanisms.")

    def populate_keypair_choices(self, request, context):
        try:
            keypairs = api.nova.keypair_list(request)
            keypair_list = [(kp.name, kp.name) for kp in keypairs]
        except Exception:
            keypair_list = []
            exceptions.handle(request,
                              _('Unable to retrieve keypairs.'))
        if keypair_list:
            if len(keypair_list) == 1:
                self.fields['keypair'].initial = keypair_list[0][0]
            keypair_list.insert(0, ("", _("Select a keypair")))
        else:
            keypair_list = (("", _("No keypairs available.")),)
        return keypair_list

    def populate_groups_choices(self, request, context):
        try:
            groups = api.network.security_group_list(request)
            security_group_list = [(sg.name, sg.name) for sg in groups]
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve list of security groups'))
            security_group_list = []
        return security_group_list

    def clean(self):
        '''Check to make sure password fields match.'''
        cleaned_data = super(SetAccessControlsAction, self).clean()
        if 'admin_pass' in cleaned_data:
            if cleaned_data['admin_pass'] != cleaned_data.get(
                    'confirm_admin_pass', None):
                raise forms.ValidationError(_('Passwords do not match.'))
        return cleaned_data
예제 #19
0
class RebuildInstanceForm(forms.SelfHandlingForm):
    instance_id = forms.CharField(widget=forms.HiddenInput())

    image = forms.ChoiceField(label=_("Select Image"),
                              widget=forms.SelectWidget(
                                  attrs={'class': 'image-selector'},
                                  data_attrs=('size', 'display-name'),
                                  transform=_image_choice_title))
    password = forms.RegexField(
        label=_("Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False),
        regex=validators.password_validator(),
        error_messages={'invalid': validators.password_validator_msg()})
    confirm_password = forms.CharField(
        label=_("Confirm Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False))
    disk_config = forms.ChoiceField(label=_("Disk Partition"), required=False)

    def __init__(self, request, *args, **kwargs):
        super(RebuildInstanceForm, self).__init__(request, *args, **kwargs)
        instance_id = kwargs.get('initial', {}).get('instance_id')
        self.fields['instance_id'].initial = instance_id

        images = utils.get_available_images(request, request.user.tenant_id)
        choices = [(image.id, image) for image in images]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        self.fields['image'].choices = choices

        if not api.nova.can_set_server_password():
            del self.fields['password']
            del self.fields['confirm_password']

        try:
            if not api.nova.extension_supported("DiskConfig", request):
                del self.fields['disk_config']
            else:
                # Set our disk_config choices
                config_choices = [("AUTO", _("Automatic")),
                                  ("MANUAL", _("Manual"))]
                self.fields['disk_config'].choices = config_choices
        except Exception:
            exceptions.handle(
                request, _('Unable to retrieve extensions '
                           'information.'))

    def clean(self):
        cleaned_data = super(RebuildInstanceForm, self).clean()
        if 'password' in cleaned_data:
            passwd = cleaned_data.get('password')
            confirm = cleaned_data.get('confirm_password')
            if passwd is not None and confirm is not None:
                if passwd != confirm:
                    raise forms.ValidationError(_("Passwords do not match."))
        return cleaned_data

    # We have to protect the entire "data" dict because it contains the
    # password and confirm_password strings.
    @sensitive_variables('data', 'password')
    def handle(self, request, data):
        instance = data.get('instance_id')
        image = data.get('image')
        password = data.get('password') or None
        disk_config = data.get('disk_config', None)
        try:
            api.nova.server_rebuild(request, instance, image, password,
                                    disk_config)
            messages.success(request, _('Rebuilding instance %s.') % instance)
        except Exception:
            redirect = reverse('horizon:project:instances:index')
            exceptions.handle(request,
                              _("Unable to rebuild instance."),
                              redirect=redirect)
        return True
예제 #20
0
class SetInstanceDetailsAction(workflows.Action):
    SOURCE_TYPE_CHOICES = (
        ('', _("--- Select source ---")),
        ("image_id", _("Boot from image.")),
        ("instance_snapshot_id", _("Boot from snapshot.")),
        ("volume_id", _("Boot from volume.")),
        ("volume_image_id", _("Boot from image "
                                  "(creates a new volume).")),
        ("volume_snapshot_id", _("Boot from volume snapshot "
                                 "(creates a new volume).")),
    )

    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
                                          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."))

    source_type = forms.ChoiceField(label=_("Instance Boot Source"),
                                    required=True,
                                    choices=SOURCE_TYPE_CHOICES,
                                    help_text=_("Choose Your Boot Source "
                                                "Type."))

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

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

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

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

    volume_size = forms.CharField(label=_("Device size (GB)"),
                                  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')."))

    delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
                                             initial=False,
                                             required=False,
                                             help_text=_("Delete volume on "
                                                         "instance terminate"))

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

    def __init__(self, request, context, *args, **kwargs):
        super(SetInstanceDetailsAction, self).__init__(request,
                                                       context,
                                                       *args,
                                                       **kwargs)
        choices = [("", _("Select Image"))]
        try:
            images = utils.get_available_images(request,
                                                context.get('project_id'))
            for image in images:
                image.bytes = image.size
                image.volume_size = functions.bytes_to_gigabytes(image.bytes)
                choices.append((image.id, image))
            self.fields['image_id'].choices = choices
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of images .'))

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

        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)

        # Validate our instance source.
        source_type = self.data.get('source_type', None)

        if source_type == 'image_id':
            if not cleaned_data.get('image_id'):
                raise forms.ValidationError(_("There are no image sources "
                                              "available; you must first "
                                              "create an image before "
                                              "attemtping to launch an "
                                              "instance."))

        elif source_type == 'instance_snapshot_id':
            if not cleaned_data['instance_snapshot_id']:
                raise forms.ValidationError(_("There are no snapshot sources "
                                              "available; you must first "
                                              "create an snapshot before "
                                              "attemtping to launch an "
                                              "instance."))

        elif source_type == 'volume_id':
            if not cleaned_data.get('volume_id'):
                raise forms.ValidationError(_("You can't select an instance "
                                              "source when booting from a "
                                              "Volume. The Volume is your "
                                              "source and should contain "
                                              "the operating system."))
            # 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?
            if count > 1:
                msg = _('Launching multiple instances is only supported for '
                        'images and instance snapshots.')
                raise forms.ValidationError(msg)

        elif source_type == 'volume_image_id':
            if not cleaned_data['image_id']:
                self._errors[_('volume_image_id')] = [
                    u"You must select an image."]
            if not self.data.get('volume_size', None):
                self._errors['volume_size'] = [_(u"You must set volume size")]
            if not cleaned_data.get('device_name'):
                self._errors['device_name'] = [_(u"You must set device name")]

        elif source_type == 'volume_snapshot_id':
            if not cleaned_data.get('volume_snapshot_id'):
                self._errors['volume_snapshot_id'] = [
                    _(u"You must select a snapshot.")]
            if not cleaned_data.get('device_name'):
                self._errors['device_name'] = [_(u"You must set device name")]

        return cleaned_data

    def populate_flavor_choices(self, request, context):
        """By default, returns the available flavors, sorted by RAM
        usage (ascending).
        Override these behaviours with a CREATE_INSTANCE_FLAVOR_SORT dict
        in local_settings.py."""
        try:
            flavors = api.nova.flavor_list(request)
            flavor_sort = getattr(settings, 'CREATE_INSTANCE_FLAVOR_SORT', {})
            rev = flavor_sort.get('reverse', False)
            key = flavor_sort.get('key', lambda flavor: flavor.ram)

            flavor_list = [(flavor.id, "%s" % flavor.name)
                           for flavor in sorted(flavors, key=key, reverse=rev)]
        except Exception:
            flavor_list = []
            exceptions.handle(request,
                              _('Unable to retrieve instance flavors.'))
        return flavor_list

    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 zone_list:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        else:
            zone_list.insert(0, ("", _("No availability zones found.")))
        return zone_list

    def get_help_text(self):
        extra = {}
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps([f._info for f in
                                  api.nova.flavor_list(self.request)])
            extra['flavors'] = flavors
        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.display_name or volume.id,
                  'size': volume.size,
                  'label': visible_label}))

    def populate_instance_snapshot_id_choices(self, request, context):
        self._init_images_cache()
        images = 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.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available.")))
        return choices

    def populate_volume_id_choices(self, request, context):
        volume_options = [("", _("Select Volume"))]
        try:
            volumes = [v for v in cinder.volume_list(self.request)
                       if v.status == api.cinder.VOLUME_STATE_AVAILABLE]
            volume_options.extend([self._get_volume_display_name(vol)
                                   for vol in volumes])
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        return volume_options

    def populate_volume_snapshot_id_choices(self, request, context):
        volume_options = [("", _("Select Volume Snapshot"))]
        try:
            snapshots = cinder.volume_snapshot_list(self.request)
            snapshots = [s for s in snapshots
                         if s.status == api.cinder.VOLUME_STATE_AVAILABLE]
            volume_options.extend([self._get_volume_display_name(snap)
                                   for snap in snapshots])
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volume '
                                'snapshots.'))
        return volume_options
예제 #21
0
파일: forms.py 프로젝트: wintamute/horizon
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)
            self.fields['type'].widget = forms.widgets.HiddenInput()
            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 = []
        try:
            volume_types = cinder.volume_type_list(request)
        except Exception:
            redirect_url = reverse("horizon:project:volumes:index")
            error_message = _('Unable to retrieve the volume type list.')
            exceptions.handle(request, error_message, redirect=redirect_url)
        self.fields['type'].choices = [("", _("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['totalGigabytesUsed']
            availableVol = usages['maxTotalVolumes'] - \
                usages['totalVolumesUsed']

            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
            volume_type = data.get('type')

            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
                volume_type = ""
            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 = {}

            volume = cinder.volume_create(request,
                                          data['size'],
                                          data['name'],
                                          data['description'],
                                          volume_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)
예제 #22
0
class UpdateVip(forms.SelfHandlingForm):
    name = forms.CharField(max_length=80, label=_("Name"))
    vip_id = forms.CharField(label=_("ID"),
                             widget=forms.TextInput(
                                 attrs={'readonly': 'readonly'}))
    description = forms.CharField(required=False,
                                  max_length=80, label=_("Description"))
    pool_id = forms.ChoiceField(label=_("Pool"))
    session_persistence = forms.ChoiceField(
        required=False, initial={}, label=_("Session Persistence"))

    cookie_name = forms.CharField(
        initial="", required=False,
        max_length=80, label=_("Cookie Name"),
        help_text=_("Required for APP_COOKIE persistence;"
                    " Ignored otherwise."))

    connection_limit = forms.IntegerField(
        min_value=-1, label=_("Connection Limit"),
        help_text=_("Maximum number of connections allowed "
                    "for the VIP or '-1' if the limit is not set"))
    # TODO(amotoki): make UP/DOWN translatable
    admin_state_up = forms.ChoiceField(choices=[(True, 'UP'), (False, 'DOWN')],
                                       label=_("Admin State"))

    failure_url = 'horizon:project:loadbalancers:index'

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

        pool_id_choices = []
        try:
            tenant_id = request.user.tenant_id
            pools = api.lbaas.pool_list(request, tenant_id=tenant_id)
        except Exception:
            pools = []
            exceptions.handle(request,
                              _('Unable to retrieve pools list.'))
        pools = sorted(pools,
                       key=lambda pool: pool.name)
        for p in pools:
            if (p.vip_id is None) or (p.id == kwargs['initial']['pool_id']):
                pool_id_choices.append((p.id, p.name))
        self.fields['pool_id'].choices = pool_id_choices

        session_persistence_choices = []
        for mode in ('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'):
            session_persistence_choices.append((mode, mode))
        session_persistence_choices.append(('', _('No session persistence')))
        self.fields[
            'session_persistence'].choices = session_persistence_choices

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

        persistence = cleaned_data.get('session_persistence')
        if (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

    def handle(self, request, context):
        context['admin_state_up'] = (context['admin_state_up'] == 'True')
        if context['session_persistence']:
            stype = context['session_persistence']
            if stype == 'APP_COOKIE':
                cookie = context['cookie_name']
                context['session_persistence'] = {'type': stype,
                                                  'cookie_name': cookie}
            else:
                context['session_persistence'] = {'type': stype}
        else:
            context['session_persistence'] = {}

        try:
            data = {'vip': {'name': context['name'],
                            'description': context['description'],
                            'pool_id': context['pool_id'],
                            'session_persistence':
                                context['session_persistence'],
                            'connection_limit': context['connection_limit'],
                            'admin_state_up': context['admin_state_up'],
                            }}
            vip = api.lbaas.vip_update(request, context['vip_id'], **data)
            msg = _('VIP %s was successfully updated.') % context['name']
            LOG.debug(msg)
            messages.success(request, msg)
            return vip
        except Exception:
            msg = _('Failed to update VIP %s') % context['name']
            LOG.info(msg)
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
예제 #23
0
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Volume Name"))
    description = forms.CharField(
        widget=forms.Textarea(attrs={'class': 'modal-body-fixed-width'}),
        label=_("Description"),
        required=False)
    type = forms.ChoiceField(label=_("Type"), required=False)
    size = forms.IntegerField(min_value=1, label=_("Size (GB)"))
    volume_source_type = forms.ChoiceField(
        label=_("Volume Source"),
        required=False,
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'source'
        }))
    snapshot_source = forms.ChoiceField(
        label=_("Use snapshot as a source"),
        widget=forms.SelectWidget(attrs={'class': 'snapshot-selector'},
                                  data_attrs=('size', 'name'),
                                  transform=lambda x: "%s (%sGB)" %
                                  (x.name, x.size)),
        required=False)
    image_source = forms.ChoiceField(
        label=_("Use image as a source"),
        widget=forms.SelectWidget(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.SelectWidget(
            attrs={'class': 'image-selector'},
            data_attrs=('size', 'name'),
            transform=lambda x: "%s (%s)" %
            (x.name, filesizeformat(x.size * 1024 * 1024 * 1024))),
        required=False)
    availability_zone = forms.ChoiceField(
        label=_("Availability Zone"),
        required=False,
        widget=forms.Select(
            attrs={
                'class': 'switched',
                'data-switch-on': 'source',
                'data-source-no_source_type': _('Availability Zone'),
                'data-source-image_source': _('Availability Zone')
            }))

    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 volume type"))] + \
                                      [(type.name, type.name)
                                       for type in volume_types]

        if "snapshot_id" in request.GET:
            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 (%sGB)') \
                            % 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.'))
        elif 'image_id' in request.GET:
            self.fields['availability_zone'].choices = \
                self.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)
                min_disk_size = getattr(image, '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 (%sGB)') \
                                     % 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'])
        elif 'volume_id' in request.GET:
            self.fields['availability_zone'].choices = \
                self.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 (%s)') \
                                 % filesizeformat(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']
        else:
            source_type_choices = []
            self.fields['availability_zone'].choices = \
                self.availability_zones(request)

            try:
                snapshot_list = cinder.volume_snapshot_list(request)
                snapshots = [
                    s for s in snapshot_list if s.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 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

    # Determine whether the extension for Cinder AZs is enabled
    def cinder_az_supported(self, request):
        try:
            return cinder.extension_supported(request, 'AvailabilityZones')
        except Exception:
            exceptions.handle(
                request,
                _('Unable to determine if '
                  'availability zones extension '
                  'is supported.'))
            return False

    def availability_zones(self, request):
        zone_list = []
        if self.cinder_az_supported(request):
            try:
                zones = api.cinder.availability_zone_list(request)
                zone_list = [(zone.zoneName, zone.zoneName) for zone in zones
                             if zone.zoneState['available']]
                zone_list.sort()
            except Exception:
                exceptions.handle(
                    request, _('Unable to retrieve availability '
                               'zones.'))
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 0:
            zone_list.insert(0, ("", _("Any Availability Zone")))

        return zone_list

    def get_volumes(self, request):
        volumes = []
        try:
            volume_list = cinder.volume_list(self.request)
            if volume_list is not None:
                volumes = [
                    v for v in volume_list
                    if v.status == api.cinder.VOLUME_STATE_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 (%sGB)') % 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)
                min_disk_size = getattr(image, 'min_disk', 0)
                if (min_disk_size > 0 and data['size'] < image.min_disk):
                    error_message = _(
                        'The volume size cannot be less than '
                        'the image minimum disk size (%sGB)') % 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 (%sGB)') % 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)iGB cannot be created as '
                                  'you only have %(avail)iGB 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 = {}

            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:
            exceptions.handle(request, ignore=True)
            self.api_error(_("Unable to create volume."))
            return False

    @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)
예제 #24
0
class AddRuleAction(workflows.Action):
    name = forms.CharField(max_length=80, label=_("Name"), required=False)
    description = forms.CharField(max_length=80,
                                  label=_("Description"),
                                  required=False)
    protocol = forms.ChoiceField(
        label=_("Protocol"),
        choices=[('tcp', _('TCP')), ('udp', _('UDP')), ('icmp', _('ICMP')),
                 ('any', _('ANY'))],
    )
    action = forms.ChoiceField(
        label=_("Action"),
        choices=[('allow', _('ALLOW')), ('deny', _('DENY')),
                 ('reject', _('REJECT'))],
    )
    source_ip_address = forms.IPField(label=_("Source IP Address/Subnet"),
                                      version=forms.IPv4 | forms.IPv6,
                                      required=False,
                                      mask=True)
    destination_ip_address = forms.IPField(
        label=_("Destination IP Address/Subnet"),
        version=forms.IPv4 | forms.IPv6,
        required=False,
        mask=True)
    source_port = forms.CharField(max_length=80,
                                  label=_("Source Port/Port Range"),
                                  required=False,
                                  validators=[port_validator])
    destination_port = forms.CharField(max_length=80,
                                       label=_("Destination Port/Port Range"),
                                       required=False,
                                       validators=[port_validator])
    ip_version = forms.ChoiceField(label=_("IP Version"),
                                   required=False,
                                   choices=[('4', '4'), ('6', '6')])
    shared = forms.BooleanField(label=_("Shared"),
                                initial=False,
                                required=False)
    enabled = forms.BooleanField(label=_("Enabled"),
                                 initial=True,
                                 required=False)

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

    def _check_ip_addr_and_ip_version(self, cleaned_data):
        ip_version = int(str(cleaned_data.get('ip_version')))
        src_ip = cleaned_data.get('source_ip_address')
        dst_ip = cleaned_data.get('destination_ip_address')
        msg = _('Source/Destination Network Address and IP version '
                'are inconsistent. Please make them consistent.')
        if (src_ip and netaddr.IPNetwork(src_ip).version != ip_version):
            self._errors['ip_version'] = self.error_class([msg])

        elif (dst_ip and netaddr.IPNetwork(dst_ip).version != ip_version):
            self._errors['ip_version'] = self.error_class([msg])

    def clean(self):
        cleaned_data = super(AddRuleAction, self).clean()
        self._check_ip_addr_and_ip_version(cleaned_data)

    class Meta(object):
        name = _("Rule")
        permissions = ('openstack.services.network', )
        help_text = _("Create a firewall rule.\n\n"
                      "A Firewall rule is an association of the following "
                      "attributes:\n\n"
                      "<li>IP Addresses: The addresses from/to which the "
                      "traffic filtration needs to be applied.</li>"
                      "<li>IP Version: The type of IP packets (IP V4/V6) "
                      "that needs to be filtered.</li>"
                      "<li>Protocol: Type of packets (UDP, ICMP, TCP, Any) "
                      "that needs to be checked.</li>"
                      "<li>Action: Action is the type of filtration "
                      "required, it can be Reject/Deny/Allow data "
                      "packets.</li>\n"
                      "The protocol and action fields are required, all "
                      "others are optional.")
예제 #25
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=_("IP address"),
                            version=forms.IPv4 | forms.IPv6,
                            mask=False,
                            required=False)
    protocol_port = forms.IntegerField(
        label=_("Protocol Port"),
        min_value=1,
        help_text=_("Enter an integer value "
                    "between 1 and 65535."),
        validators=[validators.validate_port_range])
    protocol = forms.ChoiceField(label=_("Protocol"))
    session_persistence = forms.ChoiceField(
        required=False,
        initial={},
        label=_("Session Persistence"),
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'persistence'
        }))
    cookie_name = forms.CharField(
        initial="",
        required=False,
        max_length=80,
        label=_("Cookie Name"),
        help_text=_("Required for APP_COOKIE persistence;"
                    " Ignored otherwise."),
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'persistence',
                'data-persistence-app_cookie': 'APP_COOKIE',
            }))
    connection_limit = forms.IntegerField(
        required=False,
        min_value=-1,
        label=_("Connection Limit"),
        help_text=_("Maximum number of connections allowed "
                    "for the VIP or '-1' if the limit is not set"))
    admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
                                                (False, _('DOWN'))],
                                       label=_("Admin State"))

    def __init__(self, request, *args, **kwargs):
        super(AddVipAction, self).__init__(request, *args, **kwargs)
        tenant_id = request.user.tenant_id
        subnet_id_choices = [('', _("Select a Subnet"))]
        try:
            networks = api.neutron.network_list_for_tenant(request, tenant_id)
        except Exception:
            exceptions.handle(request, _('Unable to retrieve networks list.'))
            networks = []
        for n in networks:
            for s in n['subnets']:
                name = "%s (%s)" % (s.name, s.cidr)
                subnet_id_choices.append((s.id, name))
        self.fields['subnet_id'].choices = subnet_id_choices
        protocol_choices = [('', _("Select a Protocol"))]
        [protocol_choices.append((p, p)) for p in AVAILABLE_PROTOCOLS]
        self.fields['protocol'].choices = protocol_choices

        session_persistence_choices = [('', _("No Session Persistence"))]
        for mode in ('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'):
            session_persistence_choices.append((mode.lower(), mode))
        self.fields[
            'session_persistence'].choices = session_persistence_choices

    def clean(self):
        cleaned_data = super(AddVipAction, self).clean()
        persistence = cleaned_data.get('session_persistence')
        if persistence:
            cleaned_data['session_persistence'] = persistence.upper()
        if (cleaned_data.get('session_persistence') == 'APP_COOKIE'
                and not cleaned_data.get('cookie_name')):
            msg = _('Cookie name is required for APP_COOKIE persistence.')
            self._errors['cookie_name'] = self.error_class([msg])
        return cleaned_data

    class Meta(object):
        name = _("Specify VIP")
        permissions = ('openstack.services.network', )
        help_text_template = 'project/loadbalancers/_create_vip_help.html'
예제 #26
0
class CreateSLA(forms.SelfHandlingForm):
    project_choices = []
    project_id = forms.ChoiceField(choices=project_choices,
                                   label=_("Project"),
                                   help_text=_("The project where the rule will be applied."),
                                   required=True)

    policy_choices = []
    policy_id = forms.ChoiceField(choices=policy_choices,
                                  label=_("Storage Policy"),
                                  help_text=_("The storage policy that you want to assign to the specific project."),
                                  required=True)

    get_bandwidth = forms.CharField(max_length=255,
                                    label=_("GET Bandwidth"),
                                    help_text=_("The GET bandwidth that you want to assign to the specific project."),
                                    widget=forms.TextInput(
                                        attrs={"ng-model": "get_bandwidth", "not-blank": ""}
                                    ))
    put_bandwidth = forms.CharField(max_length=255,
                                    label=_("PUT Bandwidth"),
                                    help_text=_("The PUT bandwidth that you want to assign to the specific project."),
                                    widget=forms.TextInput(
                                        attrs={"ng-model": "put_bandwidth", "not-blank": ""}
                                    ))

    def __init__(self, request, *args, **kwargs):
        # Obtain list of projects
        self.project_choices = [('', 'Select one'), common.get_project_list_choices(request)]
        # Obtain list of storage policies
        self.storage_policy_choices = common.get_storage_policy_list_choices(request, common.ListOptions.by_id())

        # Initialization
        super(CreateSLA, self).__init__(request, *args, **kwargs)

        # Overwrite target_id input form
        self.fields['project_id'] = forms.ChoiceField(choices=self.project_choices,
                                                      label=_("Project"),
                                                      help_text=_("The target project for this SLO."),
                                                      required=True)

        self.fields['policy_id'] = forms.ChoiceField(choices=self.storage_policy_choices,
                                                     label=_("Storage Policy (Ring)"),
                                                     help_text=_("The target storage policy for this SLO."),
                                                     required=True)

    @staticmethod
    def handle(request, data):

        try:
            target = data['project_id'] + '#' + data['policy_id']
            data_get = {'dsl_filter': 'bandwidth', 'slo_name': 'get_bw', 'target': target, 'value': data['get_bandwidth']}
            data_put = {'dsl_filter': 'bandwidth', 'slo_name': 'put_bw', 'target': target, 'value': data['put_bandwidth']}
            response_get = api.fil_add_slo(request, data_get)
            response_put = api.fil_add_slo(request, data_put)

            if (200 <= response_get.status_code < 300) and (200 <= response_put.status_code < 300):
                messages.success(request, _("SLO successfully created."))
                return data
            else:
                raise sdsexception.SdsException("Get SLO: "+response_get.text + "Put SLO: "+response_get.text)
        except Exception as ex:
            redirect = reverse("horizon:crystal:policies:index")
            error_message = "Unable to create SLO.\t %s" % ex.message
            exceptions.handle(request, _(error_message), redirect=redirect)
예제 #27
0
class AddMonitorAction(workflows.Action):
    type = forms.ChoiceField(label=_("Type"),
                             choices=[('ping', _('PING')), ('tcp', _('TCP')),
                                      ('http', _('HTTP')),
                                      ('https', _('HTTPS'))],
                             widget=forms.Select(attrs={
                                 'class': 'switchable',
                                 'data-slug': 'type'
                             }))
    delay = forms.IntegerField(
        min_value=1,
        label=_("Delay"),
        help_text=_("The minimum time in seconds between regular checks "
                    "of a member. It must be greater than or equal to "
                    "timeout"))
    timeout = forms.IntegerField(
        min_value=1,
        label=_("Timeout"),
        help_text=_("The maximum time in seconds for a monitor to wait "
                    "for a reply. It must be less than or equal to delay"))
    max_retries = forms.IntegerField(
        max_value=10,
        min_value=1,
        label=_("Max Retries (1~10)"),
        help_text=_("Number of permissible failures before changing "
                    "the status of member to inactive"))
    http_method = forms.ChoiceField(
        initial="GET",
        required=False,
        choices=[('GET', _('GET'))],
        label=_("HTTP Method"),
        help_text=_("HTTP method used to check health status of a member"),
        widget=forms.Select(
            attrs={
                'class': 'switched',
                'data-switch-on': 'type',
                'data-type-http': _('HTTP Method'),
                'data-type-https': _('HTTP Method')
            }))
    url_path = forms.CharField(initial="/",
                               required=False,
                               max_length=80,
                               label=_("URL"),
                               widget=forms.TextInput(
                                   attrs={
                                       'class': 'switched',
                                       'data-switch-on': 'type',
                                       'data-type-http': _('URL'),
                                       'data-type-https': _('URL')
                                   }))
    expected_codes = forms.RegexField(
        initial="200",
        required=False,
        max_length=80,
        regex=r'^(\d{3}(\s*,\s*\d{3})*)$|^(\d{3}-\d{3})$',
        label=_("Expected HTTP Status Codes"),
        help_text=_("Expected code may be a single value (e.g. 200), "
                    "a list of values (e.g. 200, 202), "
                    "or range of values (e.g. 200-204)"),
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'type',
                'data-type-http': _('Expected HTTP Status Codes'),
                'data-type-https': _('Expected HTTP Status Codes')
            }))
    admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
                                                (False, _('DOWN'))],
                                       label=_("Admin State"))

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

    def clean(self):
        cleaned_data = super(AddMonitorAction, self).clean()
        type_opt = cleaned_data.get('type')
        delay = cleaned_data.get('delay')
        timeout = cleaned_data.get('timeout')

        if (delay is None) or (delay < timeout):
            msg = _('Delay must be greater than or equal to Timeout')
            self._errors['delay'] = self.error_class([msg])

        if type_opt in ['http', 'https']:
            http_method_opt = cleaned_data.get('http_method')
            url_path = cleaned_data.get('url_path')
            expected_codes = cleaned_data.get('expected_codes')

            if not http_method_opt:
                msg = _('Please choose a HTTP method')
                self._errors['http_method'] = self.error_class([msg])
            if not url_path:
                msg = _('Please specify an URL')
                self._errors['url_path'] = self.error_class([msg])
            if not expected_codes:
                msg = _('Please enter a single value (e.g. 200), '
                        'a list of values (e.g. 200, 202), '
                        'or range of values (e.g. 200-204)')
                self._errors['expected_codes'] = self.error_class([msg])
        return cleaned_data

    class Meta(object):
        name = _("Add New Monitor")
        permissions = ('openstack.services.network', )
        help_text = _("Create a monitor template.\n\n"
                      "Select type of monitoring. "
                      "Specify delay, timeout, and retry limits "
                      "required by the monitor. "
                      "Specify method, URL path, and expected "
                      "HTTP codes upon success.")
예제 #28
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=True,
                                   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=True,
                                   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, or "
                                     "2001:db8::/128)"),
                         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 _clean_rule_custom(self, cleaned_data, rule_menu):
        # custom IP protocol rule so we need to fill unused fields so
        # the validation works
        self._update_and_pop_error(cleaned_data, 'icmp_code', None)
        self._update_and_pop_error(cleaned_data, 'icmp_type', 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 == 'tcp' or rule_menu == '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 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):
        redirect = reverse("horizon:project:access_and_security:"
                           "security_groups:detail", args=[data['id']])
        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 exceptions.Conflict as error:
            exceptions.handle(request, error, redirect=redirect)
        except Exception:
            exceptions.handle(request,
                              _('Unable to add rule to security group.'),
                              redirect=redirect)
예제 #29
0
class TemplateForm(forms.SelfHandlingForm):

    class Meta:
        name = _('Select Template')
        help_text = _('Select a template to launch a stack.')

    # TODO(jomara) - update URL choice for template & environment files
    # w/ client side download when applicable
    base_choices = [('file', _('File')),
               ('raw', _('Direct Input'))]
    url_choice = [('url', _('URL'))]
    attributes = {'class': 'switchable', 'data-slug': 'templatesource'}
    template_source = forms.ChoiceField(label=_('Template Source'),
                                        choices=base_choices + url_choice,
                                        widget=forms.Select(attrs=attributes))

    attributes = create_upload_form_attributes(
        'template',
        'file',
        _('Template File'))
    template_upload = forms.FileField(
        label=_('Template File'),
        help_text=_('A local template to upload.'),
        widget=forms.FileInput(attrs=attributes),
        required=False)

    attributes = create_upload_form_attributes(
        'template',
        'url',
        _('Template URL'))
    template_url = forms.URLField(
        label=_('Template URL'),
        help_text=_('An external (HTTP) URL to load the template from.'),
        widget=forms.TextInput(attrs=attributes),
        required=False)

    attributes = create_upload_form_attributes(
        'template',
        'raw',
        _('Template Data'))
    template_data = forms.CharField(
        label=_('Template Data'),
        help_text=_('The raw contents of the template.'),
        widget=forms.widgets.Textarea(attrs=attributes),
        required=False)

    attributes = {'data-slug': 'envsource', 'class': 'switchable'}
    environment_source = forms.ChoiceField(
        label=_('Environment Source'),
        choices=base_choices,
        widget=forms.Select(attrs=attributes),
        required=False)

    attributes = create_upload_form_attributes(
        'env',
        'file',
        _('Environment File'))
    environment_upload = forms.FileField(
        label=_('Environment File'),
        help_text=_('A local environment to upload.'),
        widget=forms.FileInput(attrs=attributes),
        required=False)

    attributes = create_upload_form_attributes(
        'env',
        'raw',
        _('Environment Data'))
    environment_data = forms.CharField(
        label=_('Environment Data'),
        help_text=_('The raw contents of the environment file.'),
        widget=forms.widgets.Textarea(attrs=attributes),
        required=False)

    def __init__(self, *args, **kwargs):
        self.next_view = kwargs.pop('next_view')
        super(TemplateForm, self).__init__(*args, **kwargs)

    def clean(self):
        cleaned = super(TemplateForm, self).clean()

        files = self.request.FILES
        self.clean_uploaded_files('template', _('template'), cleaned, files)
        self.clean_uploaded_files('environment',
            _('environment'),
            cleaned,
            files)

        # Validate the template and get back the params.
        kwargs = {}
        if cleaned['template_data']:
            kwargs['template'] = cleaned['template_data']
        else:
            kwargs['template_url'] = cleaned['template_url']

        if cleaned['environment_data']:
            kwargs['environment'] = cleaned['environment_data']

        try:
            validated = api.heat.template_validate(self.request, **kwargs)
            cleaned['template_validate'] = validated
        except Exception as e:
            raise forms.ValidationError(unicode(e))

        return cleaned

    def clean_uploaded_files(self, prefix, field_label, cleaned, files):
        """Cleans Template & Environment data from form upload.

        Does some of the crunchy bits for processing uploads vs raw
        data depending on what the user specified. Identical process
        for environment data & template data.

        :type prefix: str
        :param prefix: prefix (environment, template) of field
        :type field_label: str
        :param field_label: translated prefix str for messages
        :type input_type: dict
        :param prefix: existing cleaned fields from form
        :rtype: dict
        :return: cleaned dict including environment & template data
        """

        upload_str = prefix + "_upload"
        data_str = prefix + "_data"
        url = cleaned.get(prefix + '_url')
        data = cleaned.get(prefix + '_data')

        has_upload = upload_str in files
        # Uploaded file handler
        if has_upload and not url:
            log_template_name = files[upload_str].name
            LOG.info('got upload %s' % log_template_name)

            tpl = files[upload_str].read()
            if tpl.startswith('{'):
                try:
                    json.loads(tpl)
                except Exception as e:
                    msg = _('There was a problem parsing the'
                            ' %(prefix)s: %(error)s')
                    msg = msg % {'prefix': prefix, 'error': e}
                    raise forms.ValidationError(msg)
            cleaned[data_str] = tpl

        # URL handler
        elif url and (has_upload or data):
            msg = _('Please specify a %s using only one source method.')
            msg = msg % field_label
            raise forms.ValidationError(msg)

        elif prefix == 'template':
            # Check for raw template input - blank environment allowed
            if not url and not data:
                msg = _('You must specify a template via one of the '
                        'available sources.')
                raise forms.ValidationError(msg)

    def create_kwargs(self, data):
        kwargs = {'parameters': data['template_validate'],
                  'environment_data': data['environment_data'],
                  'template_data': data['template_data'],
                  'template_url': data['template_url']}
        if data.get('stack_id'):
            kwargs['stack_id'] = data['stack_id']
        return kwargs

    def handle(self, request, data):
        kwargs = self.create_kwargs(data)
        # NOTE (gabriel): This is a bit of a hack, essentially rewriting this
        # request so that we can chain it as an input to the next view...
        # but hey, it totally works.
        request.method = 'GET'

        return self.next_view.as_view()(request, **kwargs)
예제 #30
0
class SetResumeDetailAction(workflows.Action):
    image_id = forms.ChoiceField(label=_("Image"), required=True)
    name = forms.CharField(max_length=80, label=_("Instance Name"),
                           initial="resumed_vm")
    security_group_ids = forms.MultipleChoiceField(label=_("Security Groups"),
                                       required=True,
                                       initial=["default"],
                                       widget=forms.CheckboxSelectMultiple(),
                                       help_text=_("Launch instance in these "
                                                   "security groups."))

    flavor = forms.ChoiceField(label=_("Flavor"), required=True,
                              help_text=_("Size of image to launch."))
    #keypair_id = forms.DynamicChoiceField(label=_("Keypair"),
    #                                   required=False,
    #                                   help_text=_("Which keypair to use for "
    #                                               "authentication."),
    #                                   add_item_link=KEYPAIR_IMPORT_URL)

    class Meta:
        name = _("Base VM Info")
        help_text_template = ("project/cloudlet/instance/"
                              "_resume_details_help.html")

    def clean(self):
        cleaned_data = super(SetResumeDetailAction, self).clean()
        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:
                image_detail = api.glance.image_list_detailed(
                    request, filters=public
                )
                if len(image_detail) == 2:  # icehouse
                    public_images, _more = image_detail
                elif len(image_detail) == 3: # kilo
                    public_images, _more , has_prev_data = image_detail
            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:
                image_detail = api.glance.image_list_detailed(
                    request, filters=owner
                )
                if len(image_detail) == 2:  # icehouse
                    owned_images, _more = image_detail
                elif len(image_detail) == 3: # kilo
                    owned_images, _more , has_prev_data = image_detail
            except:
                owned_images = []
                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
        base_vms = list()
        for image in images:
            if hasattr(image, 'properties') == True:
                properties = getattr(image, 'properties')
                cloudlet_type = properties.get('cloudlet_type', None)
                if cloudlet_type == CLOUDLET_TYPE.IMAGE_TYPE_BASE_DISK:
                    base_vms.append(image)

        # Remove duplicate images
        image_ids = []
        final_images = []
        for image in base_vms:
            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 Base VM")))
        else:
            choices.insert(0, ("", _("No Base VM is available.")))
        return choices

    def get_help_text(self):
        extra = {}
        try:
            extra['usages'] = quotas.tenant_quota_usages(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.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(SetResumeDetailAction, self).get_help_text(extra)

    def populate_keypair_id_choices(self, request, context):
        try:
            keypairs = api.nova.keypair_list(request)
            keypair_list = [(kp.name, kp.name) for kp in keypairs]
        except:
            keypair_list = []
            exceptions.handle(request,
                              _('Unable to retrieve keypairs.'))
        if keypair_list:
            if len(keypair_list) == 1:
                self.fields['keypair_id'].initial = keypair_list[0][0]
            #keypair_list.insert(0, ("", _("Select a keypair")))
        else:
            keypair_list = (("", _("No keypairs available.")),)
        return keypair_list

    def populate_security_group_ids_choices(self, request, context):
        try:
            groups = api.network.security_group_list(request)
            #groups = api.nova.SecurityGroupManager.list(request)
            security_group_list = [(sg.name, sg.name) for sg in groups]
        except:
            exceptions.handle(request,
                              _('Unable to retrieve list of security groups'))
            security_group_list = []
        return security_group_list

    def populate_flavor_choices(self, request, context):
        # return all flavors of Base VM image 
        try:
            matching_flavors = set()
            flavors = api.nova.flavor_list(request)
            basevm_images = self._get_available_images(request, context)
            for basevm_image in basevm_images:
                if basevm_image.properties is None or\
                        len(basevm_image.properties) == 0:
                    continue
                libvirt_xml_str = basevm_image.properties.get(
                    'base_resource_xml_str', None)
                if libvirt_xml_str is None:
                    continue
                cpu_count, memory_mb = get_resource_size(libvirt_xml_str)
                disk_gb = basevm_image.min_disk
                ret_flavors = find_matching_flavor(flavors,
                                                   cpu_count,
                                                   memory_mb,
                                                   disk_gb)
                matching_flavors.update(ret_flavors)
            if len(matching_flavors) > 0:
                self.fields['flavor'].initial = list(matching_flavors)[0]
            else:
                self.fields['flavor'].initial = (0, "No valid flavor")
        except Exception as e:
            matching_flavors= set()
            exceptions.handle(request,
                              _('Unable to retrieve instance flavors.'))
        return sorted(list(matching_flavors))