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())
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'
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)
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)
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
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
class TestForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255) def handle(self, request, data): return True
class EditTagsForm(ImageForm): image_id = forms.CharField(widget=forms.HiddenInput())
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
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)
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
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.'))
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"
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
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)
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)
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)
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
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
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
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)
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)
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)
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.")
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'
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)
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.")
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 "CIDR". ' 'To allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'remote'})) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24, 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)
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)
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))