def __init__(self, request, *args, **kwargs): super(GeneralConfigAction, self).__init__(request, *args, **kwargs) hlps = helpers.Helpers(request) plugin, hadoop_version = ( workflow_helpers.get_plugin_and_hadoop_version(request)) if not saharaclient.SAHARA_AUTO_IP_ALLOCATION_ENABLED: pools = neutron.floating_ip_pools_list(request) pool_choices = [(pool.id, pool.name) for pool in pools] pool_choices.insert(0, (None, "Do not assign floating IPs")) self.fields['floating_ip_pool'] = forms.ChoiceField( label=_("Floating IP Pool"), choices=pool_choices, required=False) self.fields["use_autoconfig"] = forms.BooleanField( label=_("Auto-configure"), help_text=_("If selected, instances of a node group will be " "automatically configured during cluster " "creation. Otherwise you should manually specify " "configuration values."), required=False, widget=forms.CheckboxInput(), initial=True, ) self.fields["proxygateway"] = forms.BooleanField( label=_("Proxy Gateway"), widget=forms.CheckboxInput(), help_text=_("Sahara will use instances of this node group to " "access other cluster instances."), required=False) self.fields['is_public'] = acl_utils.get_is_public_form( _("node group template")) self.fields['is_protected'] = acl_utils.get_is_protected_form( _("node group template")) self.fields["plugin_name"] = forms.CharField( widget=forms.HiddenInput(), initial=plugin) self.fields["hadoop_version"] = forms.CharField( widget=forms.HiddenInput(), initial=hadoop_version) self.fields["storage"].choices = storage_choices(request) node_parameters = hlps.get_general_node_group_configs( plugin, hadoop_version) for param in node_parameters: self.fields[param.name] = workflow_helpers.build_control(param) # when we copy or edit a node group template then # request contains valuable info in both GET and POST methods req = request.GET.copy() req.update(request.POST) if req.get("guide_template_type"): self.fields["guide_template_type"] = forms.CharField( required=False, widget=forms.HiddenInput(), initial=req.get("guide_template_type")) if is_cinder_enabled(request): volume_types = cinder.volume_type_list(request) else: volume_types = [] self.fields['volume_type'].choices = [(None, _("No volume type"))] + \ [(type.name, type.name) for type in volume_types]
class CreateImageForm(CreateParent): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, widget=forms.Textarea(attrs={'rows': 4}), label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'source' })) image_url_attrs = { 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location'), 'ng-model': 'ctrl.copyFrom', 'ng-change': 'ctrl.selectImageFormat(ctrl.copyFrom)', 'placeholder': 'http://example.com/image.img' } image_url = ImageURLField(label=_("Image Location"), help_text=_("An external (HTTP/HTTPS) URL to " "load the image from."), widget=forms.TextInput(attrs=image_url_attrs), required=False) image_attrs = { 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Image File'), 'ng-model': 'ctrl.imageFile', 'ng-change': 'ctrl.selectImageFormat(ctrl.imageFile.name)', 'image-file-on-change': None } image_file = FileField(label=_("Image File"), help_text=_("A local image to upload."), widget=forms.FileInput(attrs=image_attrs), required=False) kernel = forms.ChoiceField( label=_('Kernel'), required=False, widget=forms.ThemableSelectWidget(transform=lambda x: "%s (%s)" % ( x.name, defaultfilters.filesizeformat(x.size)))) ramdisk = forms.ChoiceField( label=_('Ramdisk'), required=False, widget=forms.ThemableSelectWidget(transform=lambda x: "%s (%s)" % ( x.name, defaultfilters.filesizeformat(x.size)))) disk_format = forms.ChoiceField( label=_('Format'), choices=[], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'ng-model': 'ctrl.diskFormat' })) architecture = forms.CharField( max_length=255, label=_("Architecture"), help_text=_('CPU architecture of the image.'), required=False) min_disk = forms.IntegerField( label=_("Minimum Disk (GB)"), min_value=0, help_text=_('The minimum disk size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) min_ram = forms.IntegerField( label=_("Minimum RAM (MB)"), min_value=0, help_text=_('The minimum memory size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) is_copying = forms.BooleanField( label=_("Copy Data"), initial=True, required=False, help_text=_('Specify this option to copy image data to the image ' 'service. If unspecified, image data will be used in its ' 'current location.'), widget=forms.CheckboxInput( attrs={ 'class': 'switched', 'data-source-url': _('Image Location'), 'data-switch-on': 'source' })) is_public = forms.BooleanField( label=_("Public"), help_text=_('Make the image visible across projects.'), required=False) protected = forms.BooleanField( label=_("Protected"), help_text=_('Prevent the deletion of the image.'), required=False) def __init__(self, request, *args, **kwargs): super(CreateImageForm, self).__init__(request, *args, **kwargs) if (api.glance.get_image_upload_mode() == 'off' or not policy.check( (("image", "upload_image"), ), request)): self._hide_file_source_type() if not policy.check((("image", "set_image_location"), ), request): self._hide_url_source_type() # GlanceV2 feature removals if api.glance.VERSIONS.active >= 2: # NOTE: GlanceV2 doesn't support copy-from feature, sorry! self._hide_is_copying() if not settings.IMAGES_ALLOW_LOCATION: self._hide_url_source_type() if (api.glance.get_image_upload_mode() == 'off' or not policy.check( (("image", "upload_image"), ), request)): # Neither setting a location nor uploading image data is # allowed, so throw an error. msg = _('The current Horizon settings indicate no valid ' 'image creation methods are available. Providing ' 'an image location and/or uploading from the ' 'local file system must be allowed to support ' 'image creation.') messages.error(request, msg) raise ValidationError(msg) if not policy.check((("image", "publicize_image"), ), request): self._hide_is_public() self.fields['disk_format'].choices = \ api.glance.get_image_formats(request) try: kernel_images = api.glance.image_list_detailed( request, filters={'disk_format': 'aki'})[0] except Exception: kernel_images = [] msg = _('Unable to retrieve image list.') messages.error(request, msg) if kernel_images: choices = [('', _("Choose an image"))] for image in kernel_images: choices.append((image.id, image)) self.fields['kernel'].choices = choices else: del self.fields['kernel'] try: ramdisk_images = api.glance.image_list_detailed( request, filters={'disk_format': 'ari'})[0] except Exception: ramdisk_images = [] msg = _('Unable to retrieve image list.') messages.error(request, msg) if ramdisk_images: choices = [('', _("Choose an image"))] for image in ramdisk_images: choices.append((image.id, image)) self.fields['ramdisk'].choices = choices else: del self.fields['ramdisk'] def _hide_file_source_type(self): self.fields['image_file'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'file' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_url_source_type(self): self.fields['image_url'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'url' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_is_public(self): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False def _hide_is_copying(self): self.fields['is_copying'].widget = HiddenInput() self.fields['is_copying'].initial = False def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... source_type = data.get('source_type', None) image_file = data.get('image_file', None) image_url = data.get('image_url', None) if not image_url and not image_file: msg = _("An image file or an external location must be specified.") if source_type == 'file': raise ValidationError({'image_file': [ msg, ]}) else: raise ValidationError({'image_url': [ msg, ]}) else: return data def handle(self, request, data): meta = api.glance.create_image_metadata(data) # Add image source file or URL to metadata if (api.glance.get_image_upload_mode() != 'off' and policy.check( (("image", "upload_image"), ), request) and data.get('image_file', None)): meta['data'] = data['image_file'] elif data.get('is_copying'): meta['copy_from'] = data['image_url'] else: meta['location'] = data['image_url'] try: image = api.glance.image_create(request, **meta) messages.info( request, _('Your image %s has been queued for creation.') % meta['name']) return image except Exception as e: msg = _('Unable to create new image') # TODO(nikunj2512): Fix this once it is fixed in glance client if hasattr(e, 'code') and e.code == 400: if "Invalid disk format" in e.details: msg = _('Unable to create new image: Invalid disk format ' '%s for image.') % meta['disk_format'] elif "Image name too long" in e.details: msg = _('Unable to create new image: Image name too long.') elif "not supported" in e.details: msg = _('Unable to create new image: URL scheme not ' 'supported.') exceptions.handle(request, msg) return False
class SetInstanceDetailsAction(workflows.Action): availability_zone = forms.ChoiceField(label=_("Availability Zone"), required=False) name = forms.CharField(label=_("Instance Name"), max_length=255) 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"), 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=forms.SelectWidget( data_attrs=('volume_size',), transform=lambda x: ("%s (%s)" % (x.name, filesizeformat(x.bytes))))) volume_size = forms.IntegerField(label=_("Device size (GB)"), initial=1, min_value=0, required=False, help_text=_("Volume size in gigabytes " "(integer value).")) device_name = forms.CharField(label=_("Device Name"), required=False, initial="vda", help_text=_("Volume mount point (e.g. 'vda' " "mounts at '/dev/vda'). Leave " "this field blank to let the " "system choose a device name " "for you.")) 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): self._init_images_cache() self.request = request self.context = context super(SetInstanceDetailsAction, self).__init__( request, context, *args, **kwargs) # Hide the device field if the hypervisor doesn't support it. if not nova.can_set_mount_point(): self.fields['device_name'].widget = forms.widgets.HiddenInput() source_type_choices = [ ('', _("Select source")), ("image_id", _("Boot from image")), ("instance_snapshot_id", _("Boot from snapshot")), ] if base.is_service_enabled(request, 'volume'): source_type_choices.append(("volume_id", _("Boot from volume"))) try: if api.nova.extension_supported("BlockDeviceMappingV2Boot", request): source_type_choices.append( ("volume_image_id", _("Boot from image (creates a new volume)"))) except Exception: exceptions.handle(request, _('Unable to retrieve extensions ' 'information.')) source_type_choices.append( ("volume_snapshot_id", _("Boot from volume snapshot (creates a new volume)"))) self.fields['source_type'].choices = source_type_choices 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) try: flavor_id = cleaned_data.get('flavor') # We want to retrieve details for a given flavor, # however flavor_list uses a memoized decorator # so it is used instead of flavor_get to reduce the number # of API calls. flavors = instance_utils.flavor_list(self.request) flavor = [x for x in flavors if x.id == flavor_id][0] except IndexError: flavor = None count_error = [] # Validate cores and ram. available_cores = usages['cores']['available'] if flavor and available_cores < count * flavor.vcpus: count_error.append(_("Cores(Available: %(avail)s, " "Requested: %(req)s)") % {'avail': available_cores, 'req': count * flavor.vcpus}) available_ram = usages['ram']['available'] if flavor and available_ram < count * flavor.ram: count_error.append(_("RAM(Available: %(avail)s, " "Requested: %(req)s)") % {'avail': available_ram, 'req': count * flavor.ram}) if count_error: value_str = ", ".join(count_error) msg = (_('The requested instance cannot be launched. ' 'The following requested resource(s) exceed ' 'quota(s): %s.') % value_str) if count == 1: self._errors['flavor'] = self.error_class([msg]) else: self._errors['count'] = self.error_class([msg]) # Validate our instance source. source_type = self.data.get('source_type', None) if source_type in ('image_id', 'volume_image_id'): if source_type == 'volume_image_id': volume_size = self.data.get('volume_size', None) if not volume_size: msg = _("You must set volume size") self._errors['volume_size'] = self.error_class([msg]) if float(volume_size) <= 0: msg = _("Volume size must be greater than 0") self._errors['volume_size'] = self.error_class([msg]) if not cleaned_data.get('image_id'): msg = _("You must select an image.") self._errors['image_id'] = self.error_class([msg]) else: # Prevents trying to launch an image needing more resources. try: image_id = cleaned_data.get('image_id') # We want to retrieve details for a given image, # however get_available_images uses a cache of image list, # so it is used instead of image_get to reduce the number # of API calls. images = image_utils.get_available_images( self.request, self.context.get('project_id'), self._images_cache) image = [x for x in images if x.id == image_id][0] except IndexError: image = None if image and flavor: props_mapping = (("min_ram", "ram"), ("min_disk", "disk")) for iprop, fprop in props_mapping: if getattr(image, iprop) > 0 and \ getattr(image, iprop) > getattr(flavor, fprop): msg = (_("The flavor '%(flavor)s' is too small " "for requested image.\n" "Minimum requirements: " "%(min_ram)s MB of RAM and " "%(min_disk)s GB of Root Disk.") % {'flavor': flavor.name, 'min_ram': image.min_ram, 'min_disk': image.min_disk}) self._errors['image_id'] = self.error_class([msg]) break # Not necessary to continue the tests. volume_size = cleaned_data.get('volume_size') if volume_size and source_type == 'volume_image_id': volume_size = int(volume_size) img_gigs = functions.bytes_to_gigabytes(image.size) smallest_size = max(img_gigs, image.min_disk) if volume_size < smallest_size: msg = (_("The Volume size is too small for the" " '%(image_name)s' image and has to be" " greater than or equal to " "'%(smallest_size)d' GB.") % {'image_name': image.name, 'smallest_size': smallest_size}) self._errors['volume_size'] = self.error_class( [msg]) elif source_type == 'instance_snapshot_id': if not cleaned_data['instance_snapshot_id']: msg = _("You must select a snapshot.") self._errors['instance_snapshot_id'] = self.error_class([msg]) elif source_type == 'volume_id': if not cleaned_data.get('volume_id'): msg = _("You must select a volume.") self._errors['volume_id'] = self.error_class([msg]) # Prevent launching multiple instances with the same volume. # TODO(gabriel): is it safe to launch multiple instances with # a snapshot since it should be cloned to new volumes? if count > 1: msg = _('Launching multiple instances is only supported for ' 'images and instance snapshots.') raise forms.ValidationError(msg) elif source_type == 'volume_snapshot_id': if not cleaned_data.get('volume_snapshot_id'): msg = _("You must select a snapshot.") self._errors['volume_snapshot_id'] = self.error_class([msg]) return cleaned_data def populate_flavor_choices(self, request, context): flavors = instance_utils.flavor_list(request) if flavors: return instance_utils.sort_flavor_list(request, flavors) return [] def populate_availability_zone_choices(self, request, context): try: zones = api.nova.availability_zone_list(request) except Exception: zones = [] exceptions.handle(request, _('Unable to retrieve availability zones.')) zone_list = [(zone.zoneName, zone.zoneName) for zone in zones if zone.zoneState['available']] zone_list.sort() if not zone_list: zone_list.insert(0, ("", _("No availability zones found"))) elif len(zone_list) > 1: zone_list.insert(0, ("", _("Any Availability Zone"))) return zone_list def get_help_text(self, extra_context=None): extra = extra_context or {} 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 instance_utils.flavor_list(self.request)]) extra['flavors'] = flavors images = image_utils.get_available_images( self.request, self.initial['project_id'], self._images_cache) if images is not None: attrs = [{'id': i.id, 'min_disk': getattr(i, 'min_disk', 0), 'min_ram': getattr(i, 'min_ram', 0)} for i in images] extra['images'] = json.dumps(attrs) except Exception: exceptions.handle(self.request, _("Unable to retrieve quota information.")) return super(SetInstanceDetailsAction, self).get_help_text(extra) def _init_images_cache(self): if not hasattr(self, '_images_cache'): self._images_cache = {} def _get_volume_display_name(self, volume): if hasattr(volume, "volume_id"): vol_type = "snap" visible_label = _("Snapshot") else: vol_type = "vol" visible_label = _("Volume") return (("%s:%s" % (volume.id, vol_type)), (_("%(name)s - %(size)s GB (%(label)s)") % {'name': volume.name, 'size': volume.size, 'label': visible_label})) def populate_image_id_choices(self, request, context): choices = [] images = image_utils.get_available_images(request, context.get('project_id'), self._images_cache) for image in images: image.bytes = image.size image.volume_size = max( image.min_disk, functions.bytes_to_gigabytes(image.bytes)) choices.append((image.id, image)) if context.get('image_id') == image.id and \ 'volume_size' not in context: context['volume_size'] = image.volume_size if choices: choices.sort(key=lambda c: c[1].name) choices.insert(0, ("", _("Select Image"))) else: choices.insert(0, ("", _("No images available"))) return choices def populate_instance_snapshot_id_choices(self, request, context): images = image_utils.get_available_images(request, context.get('project_id'), self._images_cache) choices = [(image.id, image.name) for image in images if image.properties.get("image_type", '') == "snapshot"] if choices: choices.sort(key=operator.itemgetter(1)) choices.insert(0, ("", _("Select Instance Snapshot"))) else: choices.insert(0, ("", _("No snapshots available"))) return choices def populate_volume_id_choices(self, request, context): try: volumes = [self._get_volume_display_name(v) for v in cinder.volume_list(self.request) if (v.status == api.cinder.VOLUME_STATE_AVAILABLE and v.bootable == 'true')] except Exception: volumes = [] exceptions.handle(self.request, _('Unable to retrieve list of volumes.')) if volumes: volumes.insert(0, ("", _("Select Volume"))) else: volumes.insert(0, ("", _("No volumes available"))) return volumes def populate_volume_snapshot_id_choices(self, request, context): try: snapshots = cinder.volume_snapshot_list(self.request) snapshots = [self._get_volume_display_name(s) for s in snapshots if s.status == api.cinder.VOLUME_STATE_AVAILABLE] except Exception: snapshots = [] exceptions.handle(self.request, _('Unable to retrieve list of volume ' 'snapshots.')) if snapshots: snapshots.insert(0, ("", _("Select Volume Snapshot"))) else: snapshots.insert(0, ("", _("No volume snapshots available"))) return snapshots
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" 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) def __init__(self, request, *args, **kwargs): super(JobConfigAction, self).__init__(request, *args, **kwargs) job_ex_id = request.REQUEST.get("job_execution_id") if job_ex_id is not None: job_ex_id = request.REQUEST.get("job_execution_id") job_ex = saharaclient.job_execution_get(request, job_ex_id) job_configs = 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]) 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): job_id = request.REQUEST.get("job_id") or request.REQUEST.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 configs.iteritems(): 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 ]: del configs[rmkey] return (configs, edp_configs) class Meta(object): name = _("Configure") help_text_template = ( "project/data_processing.jobs/_launch_job_configure_help.html")
class UpdateRule(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) description = forms.CharField(required=False, max_length=80, label=_("Description")) protocol = forms.ChoiceField(label=_("Protocol"), required=False, help_text=_('Protocol for the firewall rule')) action = forms.ChoiceField(label=_("Action"), required=False, help_text=_('Action for the firewall rule')) source_ip_address = forms.IPField( label=_("Source IP Address/Subnet"), version=forms.IPv4 | forms.IPv6, required=False, mask=True, help_text=_('Source IP address or subnet')) destination_ip_address = forms.IPField( label=_('Destination IP Address/Subnet'), version=forms.IPv4 | forms.IPv6, required=False, mask=True, help_text=_('Destination IP address or subnet')) source_port = forms.CharField( max_length=80, label=_("Source Port/Port Range"), required=False, validators=[port_validator], help_text=_('Source port (integer in [1, 65535] or range in a:b)')) destination_port = forms.CharField( max_length=80, label=_("Destination Port/Port Range"), required=False, validators=[port_validator], help_text=_('Destination port (integer in [1, 65535] or range' ' in a:b)')) shared = forms.BooleanField(label=_("Shared"), required=False) enabled = forms.BooleanField(label=_("Enabled"), required=False) failure_url = 'horizon:project:firewalls:index' def __init__(self, request, *args, **kwargs): super(UpdateRule, self).__init__(request, *args, **kwargs) protocol = kwargs['initial']['protocol'].upper() action = kwargs['initial']['action'].upper() protocol_choices = [(protocol, protocol)] for tup in [('TCP', _('TCP')), ('UDP', _('UDP')), ('ICMP', _('ICMP'))]: if tup[0] != protocol: protocol_choices.append(tup) self.fields['protocol'].choices = protocol_choices action_choices = [(action, action)] for tup in [('ALLOW', _('ALLOW')), ('DENY', _('DENY'))]: if tup[0] != action: action_choices.append(tup) self.fields['action'].choices = action_choices def handle(self, request, context): rule_id = self.initial['rule_id'] name_or_id = context.get('name') or rule_id for f in [ 'source_ip_address', 'destination_ip_address', 'source_port', 'destination_port' ]: if not context[f]: context[f] = None try: rule = api.fwaas.rule_update(request, rule_id, **context) msg = _('Rule %s was successfully updated.') % name_or_id LOG.debug(msg) messages.success(request, msg) return rule except Exception as e: msg = _('Failed to update rule %(name)s: %(reason)s' % { 'name': name_or_id, 'reason': e }) LOG.error(msg) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class ProjectRequestForm(forms.SelfHandlingForm): prjaction = forms.ChoiceField( label=_('Project action'), choices = [ ('newprj', _('Create new project')), ], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'actsource' }) ) newprj = forms.CharField( label=_('Personal project'), max_length=OS_SNAME_LEN, required=False, widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Project name') }) ) prjdescr = forms.CharField( label=_("Project description"), required=False, widget=forms.widgets.Textarea(attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Project description') }) ) prjpriv = forms.BooleanField( label=_("Private project"), required=False, initial=False, widget=forms.widgets.CheckboxInput(attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Private project') }) ) selprj = forms.MultipleChoiceField( label=_('Available projects'), required=False, widget=forms.SelectMultiple(attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-selprj': _('Select existing project') }), ) notes = forms.CharField( label=_('Notes'), required=False, widget=forms.widgets.Textarea() ) def __init__(self, *args, **kwargs): super(ProjectRequestForm, self).__init__(*args, **kwargs) auth_prjs = [ pitem.name for pitem in self.request.user.authorized_tenants ] pendPReq = PrjRequest.objects.filter(registration__userid=self.request.user.id) self.pendingProjects = [ prjreq.project.projectname for prjreq in pendPReq ] excl_prjs = auth_prjs + self.pendingProjects prj_list = Project.objects.exclude(projectname__in=excl_prjs) prj_list = prj_list.filter(status__in=[PRJ_PUBLIC, PRJ_COURSE], projectid__isnull=False) prjEntries = [ (prj_entry.projectname, prj_entry.projectname) for prj_entry in prj_list ] if len(prjEntries): self.fields['selprj'].choices = prjEntries self.fields['prjaction'].choices = [ ('newprj', _('Create new project')), ('selprj', _('Select existing projects')) ] def clean(self): data = super(ProjectRequestForm, self).clean() if data['prjaction'] == 'newprj': if not data['newprj']: raise ValidationError(_('Project name is required.')) tmpm = PRJ_REGEX.search(data['newprj']) if tmpm: raise ValidationError(_('Bad character "%s" for project name.') % tmpm.group(0)) elif data['prjaction'] == 'selprj': if not data['selprj']: raise ValidationError(_('Missing selected project.')) return data @sensitive_variables('data') def handle(self, request, data): with transaction.atomic(): registration = Registration.objects.filter(userid=request.user.id)[0] prj_action = data['prjaction'] prjlist = list() if prj_action == 'selprj': for project in data['selprj']: prjlist.append((project, "", PRJ_PUBLIC, False)) elif prj_action == 'newprj': prjlist.append(( data['newprj'], data['prjdescr'], PRJ_PRIVATE if data['prjpriv'] else PRJ_PUBLIC, True )) newprjlist = list() exstprjlist = list() for prjitem in prjlist: if prjitem[3]: try: prjArgs = { 'projectname' : prjitem[0], 'description' : prjitem[1], 'status' : prjitem[2] } project = Project.objects.create(**prjArgs) newprjlist.append(project.projectname) except IntegrityError: messages.error(request, _("Project %s already exists") % prjitem[0]) LOG.error("Cannot create project %s" % prjitem[0]) return False elif prjitem[0] in self.pendingProjects: continue else: project = Project.objects.get(projectname=prjitem[0]) exstprjlist.append(project) reqArgs = { 'registration' : registration, 'project' : project, 'flowstatus' : PSTATUS_PENDING, 'notes' : data['notes'] } reqPrj = PrjRequest(**reqArgs) reqPrj.save() # # Send notification to cloud admins for project creation # for prj_name in newprjlist: noti_params = { 'username' : request.user.username, 'project' : prj_name } notifyAdmin(request=self.request, action=NEWPRJ_REQ_TYPE, context=noti_params) # # Send notifications to project managers # for prj_item in exstprjlist: admin_emails = list() for prj_role in PrjRole.objects.filter(project=prj_item): for email_obj in EMail.objects.filter(registration=prj_role.registration): admin_emails.append(email_obj.email) noti_params = { 'username' : request.user.username, 'project' : prj_item.projectname } notifyProject(request=request, rcpt=admin_emails, action=MEMBER_REQUEST, context=noti_params) return True
class CreateSubnetInfoAction(workflows.Action): with_subnet = forms.BooleanField( label=_("Create Subnet"), widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-slug': 'with_subnet', 'data-hide-tab': 'create_network__' 'createsubnetdetail' 'action', 'data-hide-on-checked': 'false' }), initial=True, required=False) subnet_name = forms.CharField( max_length=255, widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', }), label=_("Subnet Name"), required=False) cidr = forms.IPField(label=_("Network Address"), required=False, initial="", widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet', 'data-is-required': 'true' }), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.Select( attrs={ 'class': 'switchable switched', 'data-slug': 'ipversion', 'data-switch-on': 'with_subnet' }), label=_("IP Version")) gateway_ip = forms.IPField( label=_("Gateway IP"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'with_subnet gateway_ip' }), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address " "(e.g. 192.168.0.1 for 192.168.0.0/24, " "2001:DB8::1 for 2001:DB8::/48). " "If you use the default, leave blank. " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), widget=forms.CheckboxInput( attrs={ 'class': 'switched switchable', 'data-slug': 'gateway_ip', 'data-switch-on': 'with_subnet', 'data-hide-on-checked': 'true' }), initial=False, required=False) msg = _('Specify "Network Address" or ' 'clear "Create Subnet" checkbox.') class Meta(object): name = _("Subnet") help_text = _('Create a subnet associated with the new network, ' 'in which case "Network Address" must be specified. ' 'If you wish to create a network without a subnet, ' 'uncheck the "Create Subnet" checkbox.') def __init__(self, request, context, *args, **kwargs): super(CreateSubnetInfoAction, self).__init__(request, context, *args, **kwargs) if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get( 'enable_ipv6', True): self.fields['ip_version'].widget = forms.HiddenInput() self.fields['ip_version'].initial = 4 def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') ip_version = int(cleaned_data.get('ip_version')) gateway_ip = cleaned_data.get('gateway_ip') no_gateway = cleaned_data.get('no_gateway') if not cidr: raise forms.ValidationError(self.msg) if cidr: subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if (ip_version == 4 and subnet.prefixlen == 32) or \ (ip_version == 6 and subnet.prefixlen == 128): msg = _("The subnet in the Network Address is " "too small (/%s).") % subnet.prefixlen raise forms.ValidationError(msg) if not no_gateway and gateway_ip: if netaddr.IPAddress(gateway_ip).version is not ip_version: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) if not is_create and not no_gateway and not gateway_ip: msg = _('Specify IP address of gateway or ' 'check "Disable Gateway".') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateSubnetInfoAction, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data
class CreateSubnetDetailAction(workflows.Action): enable_dhcp = forms.BooleanField(label=_("Enable DHCP"), initial=True, required=False) allocation_pools = forms.CharField( widget=forms.Textarea(), label=_("Allocation Pools"), help_text=_("IP address allocation pools. Each entry is " "<start_ip_address>,<end_ip_address> " "(e.g., 192.168.1.100,192.168.1.120) " "and one entry per line."), required=False) dns_nameservers = forms.CharField( widget=forms.widgets.Textarea(), label=_("DNS Name Servers"), help_text=_("IP address list of DNS name servers for this subnet. " "One entry per line."), required=False) host_routes = forms.CharField( widget=forms.widgets.Textarea(), label=_("Host Routes"), help_text=_("Additional routes announced to the hosts. " "Each entry is <destination_cidr>,<nexthop> " "(e.g., 192.168.200.0/24,10.56.1.254) " "and one entry per line."), required=False) class Meta: name = _("Subnet Detail") help_text = _('You can specify additional attributes for the subnet.') def _convert_ip_address(self, ip, field_name): try: return netaddr.IPAddress(ip) except (netaddr.AddrFormatError, ValueError): msg = _('%(field_name)s: Invalid IP address ' '(value=%(ip)s)' % dict(field_name=field_name, ip=ip)) raise forms.ValidationError(msg) def _convert_ip_network(self, network, field_name): try: return netaddr.IPNetwork(network) except (netaddr.AddrFormatError, ValueError): msg = _('%(field_name)s: Invalid IP address ' '(value=%(network)s)' % dict(field_name=field_name, network=network)) raise forms.ValidationError(msg) def _check_allocation_pools(self, allocation_pools): for p in allocation_pools.split('\n'): p = p.strip() if not p: continue pool = p.split(',') if len(pool) != 2: msg = _('Start and end addresses must be specified ' '(value=%s)') % p raise forms.ValidationError(msg) start, end = [ self._convert_ip_address(ip, "allocation_pools") for ip in pool ] if start > end: msg = _('Start address is larger than end address ' '(value=%s)') % p raise forms.ValidationError(msg) def _check_dns_nameservers(self, dns_nameservers): for ns in dns_nameservers.split('\n'): ns = ns.strip() if not ns: continue self._convert_ip_address(ns, "dns_nameservers") def _check_host_routes(self, host_routes): for r in host_routes.split('\n'): r = r.strip() if not r: continue route = r.split(',') if len(route) != 2: msg = _('Host Routes format error: ' 'Destination CIDR and nexthop must be specified ' '(value=%s)') % r raise forms.ValidationError(msg) self._convert_ip_network(route[0], "host_routes") self._convert_ip_address(route[1], "host_routes") def clean(self): cleaned_data = super(CreateSubnetDetailAction, self).clean() self._check_allocation_pools(cleaned_data.get('allocation_pools')) self._check_host_routes(cleaned_data.get('host_routes')) self._check_dns_nameservers(cleaned_data.get('dns_nameservers')) return cleaned_data
class CreateSubnetInfoAction(workflows.Action): with_subnet = forms.BooleanField(label=_("Create Subnet"), initial=True, required=False) subnet_name = forms.CharField(max_length=255, label=_("Subnet Name"), help_text=_("Subnet Name. This field is " "optional."), required=False) cidr = fields.IPField(label=_("Network Address"), required=False, initial="", help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24)"), version=fields.IPv4 | fields.IPv6, mask=True) ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')], label=_("IP Version")) gateway_ip = fields.IPField( label=_("Gateway IP (optional)"), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address (e.g. 192.168.0.1 for " "192.168.0.0/24). " "If you use the default, leave blank. " "If you want to use no gateway, " "check 'Disable Gateway' below."), version=fields.IPv4 | fields.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), initial=False, required=False) class Meta: name = _("Subnet") help_text = _('You can create a subnet associated with the new ' 'network, in which case "Network Address" must be ' 'specified. If you wish to create a network WITHOUT a ' 'subnet, uncheck the "Create Subnet" checkbox.') def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') ip_version = int(cleaned_data.get('ip_version')) gateway_ip = cleaned_data.get('gateway_ip') no_gateway = cleaned_data.get('no_gateway') if not cidr: msg = _('Specify "Network Address" or ' 'clear "Create Subnet" checkbox.') raise forms.ValidationError(msg) if cidr: subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if (ip_version == 4 and subnet.prefixlen == 32) or \ (ip_version == 6 and subnet.prefixlen == 128): msg = _( "The subnet in the Network Address is too small (/%s)." % subnet.prefixlen) raise forms.ValidationError(msg) if not no_gateway and gateway_ip: if netaddr.IPAddress(gateway_ip).version is not ip_version: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) if not is_create and not no_gateway and not gateway_ip: msg = _('Specify IP address of gateway or ' 'check "Disable Gateway".') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateSubnetInfoAction, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data
class TenantsTable(tables.DataTable): name = tables.Column('name', verbose_name=_('Name'), link=("horizon:identity:projects:detail"), form_field=forms.CharField(max_length=64), update_action=UpdateCell) description = tables.Column(lambda obj: getattr(obj, 'description', None), verbose_name=_('Description'), form_field=forms.CharField( widget=forms.Textarea(attrs={'rows': 4}), required=False), update_action=UpdateCell) id = tables.Column('id', verbose_name=_('Project ID')) enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, filters=(filters.yesno, filters.capfirst), form_field=forms.BooleanField(label=_('Enabled'), required=False), update_action=UpdateCell) if api.keystone.VERSIONS.active >= 3: domain_name = tables.Column('domain_name', verbose_name=_('Domain Name')) enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, filters=(filters.yesno, filters.capfirst), form_field=forms.BooleanField( label=_('Enabled'), required=False), update_action=UpdateCell) def get_project_detail_link(self, project): # this method is an ugly monkey patch, needed because # the column link method does not provide access to the request if policy.check((("identity", "identity:get_project"), ), self.request, target={"project": project}): return reverse("horizon:identity:projects:detail", args=(project.id, )) return None def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs): super(TenantsTable, self).__init__(request, data=data, needs_form_wrapper=needs_form_wrapper, **kwargs) # see the comment above about ugly monkey patches self.columns['name'].get_link_url = self.get_project_detail_link class Meta(object): name = "tenants" verbose_name = _("Projects") row_class = UpdateRow row_actions = (UpdateMembersLink, UpdateGroupsLink, UpdateProject, UsageLink, ModifyQuotas, DeleteTenantsAction, RescopeTokenToProject) table_actions = (TenantFilterAction, CreateProject, DeleteTenantsAction) pagination_param = "tenant_marker"
def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) # NOTE(vkmc): choose only those share protocols that are enabled # FIXME(vkmc): this should be better implemented by having a # capabilities endpoint on the control plane. manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {}) self.enabled_share_protocols = manila_features.get( 'enabled_share_protocols', ['NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS', 'MapRFS']) self.enable_public_shares = manila_features.get( 'enable_public_shares', True) share_networks = manila.share_network_list(request) share_types = manila.share_type_list(request) self.fields['share_type'].choices = ( [("", "")] + [(st.name, st.name) for st in share_types]) availability_zones = manila.availability_zone_list(request) self.fields['availability_zone'].choices = ( [("", "")] + [(az.name, az.name) for az in availability_zones]) if features.is_share_groups_enabled(): share_groups = manila.share_group_list(request) self.fields['sg'] = forms.ChoiceField( label=_("Share Group"), choices=[("", "")] + [(sg.id, sg.name or sg.id) for sg in share_groups], required=False) self.sn_field_name_prefix = 'share-network-choices-' for st in share_types: extra_specs = st.get_keys() dhss = extra_specs.get('driver_handles_share_servers') # NOTE(vponomaryov): Set and tie share-network field only for # share types with enabled handling of share servers. if (isinstance(dhss, str) and dhss.lower() in ['true', '1']): sn_choices = ( [('', '')] + [(sn.id, sn.name or sn.id) for sn in share_networks]) sn_field_name = self.sn_field_name_prefix + st.name sn_field = forms.ChoiceField( label=_("Share Network"), required=True, choices=sn_choices, widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'sharetype', 'data-sharetype-%s' % st.name: _("Share Network"), })) self.fields[sn_field_name] = sn_field self.fields['share_source_type'] = forms.ChoiceField( label=_("Share Source"), required=False, widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'source'})) self.fields['snapshot'] = forms.ChoiceField( label=_("Use snapshot as a source"), widget=forms.fields.SelectWidget( attrs={'class': 'switched', 'data-switch-on': 'source', 'data-source-snapshot': _('Snapshot')}, data_attrs=('size', 'name'), transform=lambda x: "%s (%sGiB)" % (x.name, x.size)), required=False) self.fields['metadata'] = forms.CharField( label=_("Metadata"), required=False, widget=forms.Textarea(attrs={'rows': 4})) if self.enable_public_shares: self.fields['is_public'] = forms.BooleanField( label=_("Make visible to users from all projects"), required=False) self.fields['share_proto'].choices = [(sp, sp) for sp in self.enabled_share_protocols] if ("snapshot_id" in request.GET or kwargs.get("data", {}).get("snapshot")): try: snapshot = self.get_snapshot( request, request.GET.get("snapshot_id", kwargs.get("data", {}).get("snapshot"))) self.fields['name'].initial = snapshot.name self.fields['size'].initial = snapshot.size self.fields['snapshot'].choices = ((snapshot.id, snapshot),) try: # Set the share type from the original share orig_share = manila.share_get(request, snapshot.share_id) # NOTE(vponomaryov): we should use share type name, not ID, # because we use names in our choices above. self.fields['share_type'].initial = ( orig_share.share_type_name) except Exception: pass self.fields['size'].help_text = _( 'Share size must be equal to or greater than the snapshot ' 'size (%sGiB)') % snapshot.size del self.fields['share_source_type'] except Exception: exceptions.handle(request, _('Unable to load the specified snapshot.')) else: source_type_choices = [] try: snapshot_list = manila.share_snapshot_list(request) snapshots = [s for s in snapshot_list if s.status == 'available'] if snapshots: source_type_choices.append(("snapshot", _("Snapshot"))) choices = [('', _("Choose a snapshot"))] + \ [(s.id, s) for s in snapshots] self.fields['snapshot'].choices = choices else: del self.fields['snapshot'] except Exception: exceptions.handle(request, _("Unable to retrieve " "share snapshots.")) if source_type_choices: choices = ([('no_source_type', _("No source, empty share"))] + source_type_choices) self.fields['share_source_type'].choices = choices else: del self.fields['share_source_type']
class CreateNetwork(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) tenant_id = forms.ChoiceField(label=_("Project")) if api.neutron.is_port_profiles_supported(): widget = None else: widget = forms.HiddenInput() net_profile_id = forms.ChoiceField(label=_("Network Profile"), required=False, widget=widget) admin_state = forms.BooleanField(label=_("Admin State"), initial=True, required=False) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) external = forms.BooleanField(label=_("External Network"), initial=False, required=False) @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices if api.neutron.is_port_profiles_supported(): self.fields['net_profile_id'].choices = ( self.get_network_profile_choices(request)) def get_network_profile_choices(self, request): profile_choices = [('', _("Select a profile"))] for profile in self._get_profiles(request, 'network'): profile_choices.append((profile.id, profile.name)) return profile_choices def _get_profiles(self, request, type_p): profiles = [] try: profiles = api.neutron.profile_list(request, type_p) except Exception: msg = _('Network Profiles could not be retrieved.') exceptions.handle(request, msg) return profiles def handle(self, request, data): try: params = {'name': data['name'], 'tenant_id': data['tenant_id'], 'admin_state_up': data['admin_state'], 'shared': data['shared'], 'router:external': data['external']} if api.neutron.is_port_profiles_supported(): params['net_profile_id'] = data['net_profile_id'] network = api.neutron.network_create(request, **params) msg = _('Network %s was successfully created.') % data['name'] LOG.debug(msg) messages.success(request, msg) return network except Exception: redirect = reverse('horizon:admin:networks:index') msg = _('Failed to create network %s') % data['name'] exceptions.handle(request, msg, redirect=redirect)
class CreateNetwork(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) tenant_id = forms.ThemableChoiceField(label=_("Project")) network_type = forms.ChoiceField( label=_("Provider Network Type"), help_text=_("The physical mechanism by which the virtual " "network is implemented."), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'network_type' })) physical_network = forms.CharField( max_length=255, label=_("Physical Network"), help_text=_("The name of the physical network over which the " "virtual network is implemented. Specify one of the " "physical networks defined in your neutron deployment."), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) segmentation_id = forms.IntegerField( label=_("Segmentation ID"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) admin_state = forms.BooleanField(label=_("Enable Admin State"), initial=True, required=False) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) external = forms.BooleanField(label=_("External Network"), initial=False, required=False) with_subnet = forms.BooleanField(label=_("Create Subnet"), widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-slug': 'with_subnet', 'data-hide-tab': 'create_network__' 'createsubnetinfo' 'action,' 'create_network__' 'createsubnetdetail' 'action', 'data-hide-on-checked': 'false' }), initial=True, required=False) az_hints = forms.MultipleChoiceField( label=_("Availability Zone Hints"), required=False, help_text=_("Availability zones where the DHCP agents may be " "scheduled. Leaving this unset is equivalent to " "selecting all availability zones")) @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices try: is_extension_supported = \ api.neutron.is_extension_supported(request, 'provider') except Exception: msg = _("Unable to verify Neutron service providers") exceptions.handle(self.request, msg) self._hide_provider_network_type() is_extension_supported = False if is_extension_supported: neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) self.seg_id_range = SEGMENTATION_ID_RANGE.copy() seg_id_range = neutron_settings.get('segmentation_id_range') if seg_id_range: self.seg_id_range.update(seg_id_range) self.provider_types = PROVIDER_TYPES.copy() extra_provider_types = neutron_settings.get('extra_provider_types') if extra_provider_types: self.provider_types.update(extra_provider_types) self.nettypes_with_seg_id = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_segmentation_id'] ] self.nettypes_with_physnet = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_physical_network'] ] supported_provider_types = neutron_settings.get( 'supported_provider_types', DEFAULT_PROVIDER_TYPES) if supported_provider_types == ['*']: supported_provider_types = DEFAULT_PROVIDER_TYPES undefined_provider_types = [ net_type for net_type in supported_provider_types if net_type not in self.provider_types ] if undefined_provider_types: LOG.error('Undefined provider network types are found: %s', undefined_provider_types) seg_id_help = [ _("For %(type)s networks, valid IDs are %(min)s to %(max)s.") % { 'type': net_type, 'min': self.seg_id_range[net_type][0], 'max': self.seg_id_range[net_type][1] } for net_type in self.nettypes_with_seg_id ] self.fields['segmentation_id'].help_text = ' '.join(seg_id_help) # Register network types which require segmentation ID attrs = dict( ('data-network_type-%s' % network_type, _('Segmentation ID')) for network_type in self.nettypes_with_seg_id) self.fields['segmentation_id'].widget.attrs.update(attrs) physical_networks = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get('physical_networks', []) if physical_networks: self.fields['physical_network'] = forms.ThemableChoiceField( label=_("Physical Network"), choices=[(net, net) for net in physical_networks], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'network_type', }), help_text=_("The name of the physical network over " "which the virtual network is implemented."), ) # Register network types which require physical network attrs = dict( ('data-network_type-%s' % network_type, _('Physical Network')) for network_type in self.nettypes_with_physnet) self.fields['physical_network'].widget.attrs.update(attrs) network_type_choices = [ (net_type, self.provider_types[net_type]['display_name']) for net_type in supported_provider_types ] if len(network_type_choices) == 0: self._hide_provider_network_type() else: self.fields['network_type'].choices = network_type_choices try: if api.neutron.is_extension_supported(request, 'network_availability_zone'): zones = api.neutron.list_availability_zones( self.request, 'network', 'available') self.fields['az_hints'].choices = [(zone['name'], zone['name']) for zone in zones] else: del self.fields['az_hints'] except Exception: msg = _('Failed to get availability zone list.') messages.warning(request, msg) del self.fields['az_hints'] def _hide_provider_network_type(self): self.fields['network_type'].widget = forms.HiddenInput() self.fields['physical_network'].widget = forms.HiddenInput() self.fields['segmentation_id'].widget = forms.HiddenInput() self.fields['network_type'].required = False self.fields['physical_network'].required = False self.fields['segmentation_id'].required = False def handle(self, request, data): try: params = { 'name': data['name'], 'tenant_id': data['tenant_id'], 'admin_state_up': data['admin_state'], 'shared': data['shared'], 'router:external': data['external'] } if api.neutron.is_extension_supported(request, 'provider'): network_type = data['network_type'] params['provider:network_type'] = network_type if network_type in self.nettypes_with_physnet: params['provider:physical_network'] = ( data['physical_network']) if network_type in self.nettypes_with_seg_id: params['provider:segmentation_id'] = ( data['segmentation_id']) if 'az_hints' in data and data['az_hints']: params['availability_zone_hints'] = data['az_hints'] network = api.neutron.network_create(request, **params) LOG.debug('Network %s was successfully created.', data['name']) return network except Exception: redirect = reverse('horizon:admin:networks:index') msg = _('Failed to create network %s') % data['name'] exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateNetwork, self).clean() if api.neutron.is_extension_supported(self.request, 'provider'): self._clean_physical_network(cleaned_data) self._clean_segmentation_id(cleaned_data) return cleaned_data def _clean_physical_network(self, data): network_type = data.get('network_type') if ('physical_network' in self._errors and network_type not in self.nettypes_with_physnet): # In this case the physical network is not required, so we can # ignore any errors. del self._errors['physical_network'] def _clean_segmentation_id(self, data): network_type = data.get('network_type') if 'segmentation_id' in self._errors: if (network_type not in self.nettypes_with_seg_id and not self.data.get("segmentation_id")): # In this case the segmentation ID is not required, so we can # ignore the field is required error. del self._errors['segmentation_id'] elif network_type in self.nettypes_with_seg_id: seg_id = data.get('segmentation_id') seg_id_range = { 'min': self.seg_id_range[network_type][0], 'max': self.seg_id_range[network_type][1] } if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']: msg = (_('For a %(network_type)s network, valid segmentation ' 'IDs are %(min)s through %(max)s.') % { 'network_type': network_type, 'min': seg_id_range['min'], 'max': seg_id_range['max'] }) self._errors['segmentation_id'] = self.error_class([msg])
class GeneralConfigAction(workflows.Action): nodegroup_name = forms.CharField(label=_("Template Name")) description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 4})) flavor = forms.ChoiceField(label=_("OpenStack Flavor")) availability_zone = forms.ChoiceField( label=_("Availability Zone"), help_text=_("Launch instances in this availability zone."), required=False, widget=forms.Select(attrs={"class": "availability_zone_field"})) storage = forms.ChoiceField( label=_("Storage location"), help_text=_("Choose a storage location"), choices=[], widget=forms.Select(attrs={ "class": "storage_field switchable", 'data-slug': 'storage_loc' })) volumes_per_node = forms.IntegerField( label=_("Volumes per node"), required=False, initial=1, widget=forms.TextInput( attrs={ "class": "volume_per_node_field switched", "data-switch-on": "storage_loc", "data-storage_loc-cinder_volume": _('Volumes per node') })) volumes_size = forms.IntegerField( label=_("Volumes size (GB)"), required=False, initial=10, widget=forms.TextInput( attrs={ "class": "volume_size_field switched", "data-switch-on": "storage_loc", "data-storage_loc-cinder_volume": _('Volumes size (GB)') })) volume_type = forms.ChoiceField( label=_("Volumes type"), required=False, widget=forms.Select( attrs={ "class": "volume_type_field switched", "data-switch-on": "storage_loc", "data-storage_loc-cinder_volume": _('Volumes type') })) volume_local_to_instance = forms.BooleanField( label=_("Volume local to instance"), required=False, help_text=_("Instance and attached volumes will be created on the " "same physical host"), widget=forms.CheckboxInput( attrs={ "class": "volume_local_to_instance_field switched", "data-switch-on": "storage_loc", "data-storage_loc-cinder_volume": _('Volume local to instance') })) volumes_availability_zone = forms.ChoiceField( label=_("Volumes Availability Zone"), help_text=_("Create volumes in this availability zone."), required=False, widget=forms.Select( attrs={ "class": "volumes_availability_zone_field switched", "data-switch-on": "storage_loc", "data-storage_loc-cinder_volume": _( 'Volumes Availability Zone') })) image = forms.DynamicChoiceField(label=_("Base Image"), required=False, add_item_link=BASE_IMAGE_URL) hidden_configure_field = forms.CharField( required=False, widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"})) def __init__(self, request, *args, **kwargs): super(GeneralConfigAction, self).__init__(request, *args, **kwargs) hlps = helpers.Helpers(request) plugin, hadoop_version = ( workflow_helpers.get_plugin_and_hadoop_version(request)) if not saharaclient.SAHARA_AUTO_IP_ALLOCATION_ENABLED: pools = neutron.floating_ip_pools_list(request) pool_choices = [(pool.id, pool.name) for pool in pools] pool_choices.insert(0, (None, "Do not assign floating IPs")) self.fields['floating_ip_pool'] = forms.ChoiceField( label=_("Floating IP Pool"), choices=pool_choices, required=False) self.fields["use_autoconfig"] = forms.BooleanField( label=_("Auto-configure"), help_text=_("If selected, instances of a node group will be " "automatically configured during cluster " "creation. Otherwise you should manually specify " "configuration values."), required=False, widget=forms.CheckboxInput(), initial=True, ) self.fields["proxygateway"] = forms.BooleanField( label=_("Proxy Gateway"), widget=forms.CheckboxInput(), help_text=_("Sahara will use instances of this node group to " "access other cluster instances."), required=False) self.fields['is_public'] = acl_utils.get_is_public_form( _("node group template")) self.fields['is_protected'] = acl_utils.get_is_protected_form( _("node group template")) self.fields["plugin_name"] = forms.CharField( widget=forms.HiddenInput(), initial=plugin) self.fields["hadoop_version"] = forms.CharField( widget=forms.HiddenInput(), initial=hadoop_version) self.fields["storage"].choices = storage_choices(request) node_parameters = hlps.get_general_node_group_configs( plugin, hadoop_version) for param in node_parameters: self.fields[param.name] = workflow_helpers.build_control(param) # when we copy or edit a node group template then # request contains valuable info in both GET and POST methods req = request.GET.copy() req.update(request.POST) if req.get("guide_template_type"): self.fields["guide_template_type"] = forms.CharField( required=False, widget=forms.HiddenInput(), initial=req.get("guide_template_type")) if is_cinder_enabled(request): volume_types = cinder.volume_type_list(request) else: volume_types = [] self.fields['volume_type'].choices = [(None, _("No volume type"))] + \ [(type.name, type.name) for type in volume_types] def populate_flavor_choices(self, request, context): flavors = nova_utils.flavor_list(request) if flavors: return nova_utils.sort_flavor_list(request, flavors) return [] def populate_availability_zone_choices(self, request, context): # The default is None, i.e. not specifying any availability zone az_list = [(None, _('No availability zone specified'))] az_list.extend([(az.zoneName, az.zoneName) for az in nova_utils.availability_zone_list(request) if az.zoneState['available']]) return az_list def populate_volumes_availability_zone_choices(self, request, context): az_list = [(None, _('No availability zone specified'))] if is_cinder_enabled(request): az_list.extend([ (az.zoneName, az.zoneName) for az in cinder_utils.availability_zone_list(request) if az.zoneState['available'] ]) return az_list def populate_image_choices(self, request, context): return workflow_helpers.populate_image_choices(self, request, context, empty_choice=True) def get_help_text(self): extra = dict() plugin_name, hadoop_version = ( workflow_helpers.get_plugin_and_hadoop_version(self.request)) extra["plugin_name"] = plugin_name extra["hadoop_version"] = hadoop_version plugin = saharaclient.plugin_get_version_details( self.request, plugin_name, hadoop_version) extra["deprecated"] = workflow_helpers.is_version_of_plugin_deprecated( plugin, hadoop_version) return super(GeneralConfigAction, self).get_help_text(extra) class Meta(object): name = _("Configure Node Group Template") help_text_template = "nodegroup_templates/_configure_general_help.html"
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")) admin_state_up = forms.BooleanField(label=_("Admin State"), required=False) 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.pools_get(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): 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 UpdateImageForm(forms.SelfHandlingForm): image_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, label=_("Description"), required=False) kernel = forms.CharField( max_length=36, label=_("Kernel ID"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) ramdisk = forms.CharField( max_length=36, label=_("Ramdisk ID"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) architecture = forms.CharField( label=_("Architecture"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) disk_format = forms.ChoiceField( label=_("Format"), ) minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"), min_value=0, 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)"), min_value=0, help_text=_('The minimum memory size' ' required to boot the' ' image. If unspecified,' ' this value defaults to' ' 0 (no minimum).'), required=False) public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, request, *args, **kwargs): super(UpdateImageForm, self).__init__(request, *args, **kwargs) self.fields['disk_format'].choices = [(value, name) for value, name in IMAGE_FORMAT_CHOICES if value] if not policy.check((("image", "publicize_image"),), request): self.fields['public'].widget = forms.CheckboxInput( attrs={'readonly': 'readonly'}) def handle(self, request, data): image_id = data['image_id'] error_updating = _('Unable to update image "%s".') if data['disk_format'] in ['aki', 'ari', 'ami']: container_format = data['disk_format'] else: container_format = 'bare' meta = {'is_public': data['public'], 'protected': data['protected'], 'disk_format': data['disk_format'], 'container_format': container_format, 'name': data['name'], 'min_ram': (data['minimum_ram'] or 0), 'min_disk': (data['minimum_disk'] or 0), 'properties': {'description': data['description']}} if data['kernel']: meta['properties']['kernel_id'] = data['kernel'] if data['ramdisk']: meta['properties']['ramdisk_id'] = data['ramdisk'] if data['architecture']: meta['properties']['architecture'] = data['architecture'] # Ensure we do not delete properties that have already been # set on an image. meta['purge_props'] = False try: image = api.glance.image_update(request, image_id, **meta) messages.success(request, _('Image was successfully updated.')) return image except Exception: exceptions.handle(request, error_updating % image_id)
class CreateNetwork(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) tenant_id = forms.ThemableChoiceField(label=_("Project")) if api.neutron.is_port_profiles_supported(): widget = None else: widget = forms.HiddenInput() net_profile_id = forms.ChoiceField(label=_("Network Profile"), required=False, widget=widget) network_type = forms.ChoiceField( label=_("Provider Network Type"), help_text=_("The physical mechanism by which the virtual " "network is implemented."), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'network_type' })) physical_network = forms.CharField( max_length=255, label=_("Physical Network"), help_text=_("The name of the physical network over which the " "virtual network is implemented."), initial='default', widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) segmentation_id = forms.IntegerField( label=_("Segmentation ID"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) admin_state = forms.ThemableChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State")) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) external = forms.BooleanField(label=_("External Network"), initial=False, required=False) @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices if api.neutron.is_port_profiles_supported(): self.fields['net_profile_id'].choices = ( self.get_network_profile_choices(request)) try: is_extension_supported = \ api.neutron.is_extension_supported(request, 'provider') except Exception: msg = _("Unable to verify Neutron service providers") exceptions.handle(self.request, msg) self._hide_provider_network_type() is_extension_supported = False if is_extension_supported: neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) self.seg_id_range = SEGMENTATION_ID_RANGE.copy() seg_id_range = neutron_settings.get('segmentation_id_range') if seg_id_range: self.seg_id_range.update(seg_id_range) self.provider_types = PROVIDER_TYPES.copy() extra_provider_types = neutron_settings.get('extra_provider_types') if extra_provider_types: self.provider_types.update(extra_provider_types) self.nettypes_with_seg_id = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_segmentation_id'] ] self.nettypes_with_physnet = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_physical_network'] ] supported_provider_types = neutron_settings.get( 'supported_provider_types', DEFAULT_PROVIDER_TYPES) if supported_provider_types == ['*']: supported_provider_types = DEFAULT_PROVIDER_TYPES undefined_provider_types = [ net_type for net_type in supported_provider_types if net_type not in self.provider_types ] if undefined_provider_types: LOG.error('Undefined provider network types are found: %s', undefined_provider_types) seg_id_help = [ _("For %(type)s networks, valid IDs are %(min)s to %(max)s.") % { 'type': net_type, 'min': self.seg_id_range[net_type][0], 'max': self.seg_id_range[net_type][1] } for net_type in self.nettypes_with_seg_id ] self.fields['segmentation_id'].help_text = ' '.join(seg_id_help) # Register network types which require segmentation ID attrs = dict( ('data-network_type-%s' % network_type, _('Segmentation ID')) for network_type in self.nettypes_with_seg_id) self.fields['segmentation_id'].widget.attrs.update(attrs) # Register network types which require physical network attrs = dict( ('data-network_type-%s' % network_type, _('Physical Network')) for network_type in self.nettypes_with_physnet) self.fields['physical_network'].widget.attrs.update(attrs) network_type_choices = [ (net_type, self.provider_types[net_type]['display_name']) for net_type in supported_provider_types ] if len(network_type_choices) == 0: self._hide_provider_network_type() else: self.fields['network_type'].choices = network_type_choices def get_network_profile_choices(self, request): profile_choices = [('', _("Select a profile"))] for profile in self._get_profiles(request, 'network'): profile_choices.append((profile.id, profile.name)) return profile_choices def _get_profiles(self, request, type_p): profiles = [] try: profiles = api.neutron.profile_list(request, type_p) except Exception: msg = _('Network Profiles could not be retrieved.') exceptions.handle(request, msg) return profiles def _hide_provider_network_type(self): self.fields['network_type'].widget = forms.HiddenInput() self.fields['physical_network'].widget = forms.HiddenInput() self.fields['segmentation_id'].widget = forms.HiddenInput() self.fields['network_type'].required = False self.fields['physical_network'].required = False self.fields['segmentation_id'].required = False def handle(self, request, data): try: params = { 'name': data['name'], 'tenant_id': data['tenant_id'], 'admin_state_up': (data['admin_state'] == 'True'), 'shared': data['shared'], 'router:external': data['external'] } if api.neutron.is_port_profiles_supported(): params['net_profile_id'] = data['net_profile_id'] if api.neutron.is_extension_supported(request, 'provider'): network_type = data['network_type'] params['provider:network_type'] = network_type if network_type in self.nettypes_with_physnet: params['provider:physical_network'] = ( data['physical_network']) if network_type in self.nettypes_with_seg_id: params['provider:segmentation_id'] = ( data['segmentation_id']) network = api.neutron.network_create(request, **params) msg = _('Network %s was successfully created.') % data['name'] LOG.debug(msg) messages.success(request, msg) return network except Exception: redirect = reverse('horizon:admin:networks:index') msg = _('Failed to create network %s') % data['name'] exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateNetwork, self).clean() if api.neutron.is_extension_supported(self.request, 'provider'): self._clean_physical_network(cleaned_data) self._clean_segmentation_id(cleaned_data) return cleaned_data def _clean_physical_network(self, data): network_type = data.get('network_type') if ('physical_network' in self._errors and network_type not in self.nettypes_with_physnet): # In this case the physical network is not required, so we can # ignore any errors. del self._errors['physical_network'] def _clean_segmentation_id(self, data): network_type = data.get('network_type') if 'segmentation_id' in self._errors: if network_type not in self.nettypes_with_seg_id: # In this case the segmentation ID is not required, so we can # ignore any errors. del self._errors['segmentation_id'] elif network_type in self.nettypes_with_seg_id: seg_id = data.get('segmentation_id') seg_id_range = { 'min': self.seg_id_range[network_type][0], 'max': self.seg_id_range[network_type][1] } if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']: msg = (_('For a %(network_type)s network, valid segmentation ' 'IDs are %(min)s through %(max)s.') % { 'network_type': network_type, 'min': seg_id_range['min'], 'max': seg_id_range['max'] }) self._errors['segmentation_id'] = self.error_class([msg])
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source'})) copy_from = forms.CharField(max_length=255, label=_("Image Location"), help_text=_("An external (HTTP) URL to load " "the image from."), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location'), 'ng-model': 'copyFrom', 'ng-change': 'selectImageFormat(copyFrom)'}), required=False) image_file = forms.FileField(label=_("Image File"), help_text=_("A local image to upload."), widget=forms.FileInput(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Image File'), 'ng-model': 'imageFile', 'ng-change': 'selectImageFormat(imageFile.name)', 'image-file-on-change': None}), required=False) disk_format = forms.ChoiceField(label=_('Format'), choices=[], widget=forms.Select(attrs={ 'class': 'switchable', 'ng-model': 'diskFormat'})) architecture = forms.CharField(max_length=255, label=_("Architecture"), required=False) minimum_disk = forms.IntegerField( label=_("Minimum Disk (GB)"), min_value=0, 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)"), min_value=0, help_text=_('The minimum memory size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) is_public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, request, *args, **kwargs): super(CreateImageForm, self).__init__(request, *args, **kwargs) if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or not policy.check((("image", "upload_image"),), request)): self._hide_file_source_type() if not policy.check((("image", "set_image_location"),), request): self._hide_url_source_type() if not policy.check((("image", "publicize_image"),), request): self._hide_is_public() self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES def _hide_file_source_type(self): self.fields['image_file'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [choice for choice in source_type.choices if choice[0] != 'file'] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_url_source_type(self): self.fields['copy_from'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [choice for choice in source_type.choices if choice[0] != 'url'] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_is_public(self): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... image_file = data.get('image_file', None) image_url = data.get('copy_from', None) if not image_url and not image_file: raise ValidationError( _("A image or external image location must be specified.")) elif image_url and image_file: raise ValidationError( _("Can not specify both image and external image location.")) else: return data def handle(self, request, data): # Glance does not really do anything with container_format at the # moment. It requires it is set to the same disk_format for the three # Amazon image types, otherwise it just treats them as 'bare.' As such # we will just set that to be that here instead of bothering the user # with asking them for information we can already determine. if data['disk_format'] in ('ami', 'aki', 'ari',): container_format = data['disk_format'] else: container_format = 'bare' meta = {'is_public': data['is_public'], 'protected': data['protected'], 'disk_format': data['disk_format'], 'container_format': container_format, 'min_disk': (data['minimum_disk'] or 0), 'min_ram': (data['minimum_ram'] or 0), 'name': data['name'], 'properties': {}} if data['description']: meta['properties']['description'] = data['description'] if data['architecture']: meta['properties']['architecture'] = data['architecture'] if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and policy.check((("image", "upload_image"),), request) and data.get('image_file', None)): meta['data'] = self.files['image_file'] else: meta['copy_from'] = data['copy_from'] try: image = api.glance.image_create(request, **meta) messages.success(request, _('Your image %s has been queued for creation.') % data['name']) return image except Exception: exceptions.handle(request, _('Unable to create new image.'))
class CreateSubnetDetailAction(workflows.Action): enable_dhcp = forms.BooleanField(label=_("Enable DHCP"), initial=True, required=False) ipv6_modes = forms.ChoiceField( label=_("IPv6 Address Configuration Mode"), widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'ipversion', 'data-ipversion-6': _("IPv6 Address Configuration Mode"), }), initial=utils.IPV6_DEFAULT_MODE, required=False, help_text=_("Specifies how IPv6 addresses and additional information " "are configured. We can specify SLAAC/DHCPv6 stateful/" "DHCPv6 stateless provided by OpenStack, " "or specify no option. " "'No options specified' means addresses are configured " "manually or configured by a non-OpenStack system.")) allocation_pools = forms.CharField( widget=forms.Textarea(attrs={'rows': 4}), label=_("Allocation Pools"), help_text=_("IP address allocation pools. Each entry is: " "start_ip_address,end_ip_address " "(e.g., 192.168.1.100,192.168.1.120) " "and one entry per line."), required=False) dns_nameservers = forms.CharField( widget=forms.widgets.Textarea(attrs={'rows': 4}), label=_("DNS Name Servers"), help_text=_("IP address list of DNS name servers for this subnet. " "One entry per line."), required=False) host_routes = forms.CharField( widget=forms.widgets.Textarea(attrs={'rows': 4}), label=_("Host Routes"), help_text=_("Additional routes announced to the hosts. " "Each entry is: destination_cidr,nexthop " "(e.g., 192.168.200.0/24,10.56.1.254) " "and one entry per line."), required=False) class Meta(object): name = _("Subnet Details") help_text = _('Specify additional attributes for the subnet.') def __init__(self, request, context, *args, **kwargs): super(CreateSubnetDetailAction, self).__init__(request, context, *args, **kwargs) if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get( 'enable_ipv6', True): self.fields['ipv6_modes'].widget = forms.HiddenInput() def populate_ipv6_modes_choices(self, request, context): return [(value, _("%s (Default)") % label) if value == utils.IPV6_DEFAULT_MODE else (value, label) for value, label in utils.IPV6_MODE_CHOICES] def _convert_ip_address(self, ip, field_name): try: return netaddr.IPAddress(ip) except (netaddr.AddrFormatError, ValueError): msg = (_('%(field_name)s: Invalid IP address (value=%(ip)s)') % { 'field_name': field_name, 'ip': ip }) raise forms.ValidationError(msg) def _convert_ip_network(self, network, field_name): try: return netaddr.IPNetwork(network) except (netaddr.AddrFormatError, ValueError): msg = ( _('%(field_name)s: Invalid IP address (value=%(network)s)') % { 'field_name': field_name, 'network': network }) raise forms.ValidationError(msg) def _check_allocation_pools(self, allocation_pools): for p in allocation_pools.split('\n'): p = p.strip() if not p: continue pool = p.split(',') if len(pool) != 2: msg = _('Start and end addresses must be specified ' '(value=%s)') % p raise forms.ValidationError(msg) start, end = [ self._convert_ip_address(ip, "allocation_pools") for ip in pool ] if start > end: msg = _('Start address is larger than end address ' '(value=%s)') % p raise forms.ValidationError(msg) def _check_dns_nameservers(self, dns_nameservers): for ns in dns_nameservers.split('\n'): ns = ns.strip() if not ns: continue self._convert_ip_address(ns, "dns_nameservers") def _check_host_routes(self, host_routes): for r in host_routes.split('\n'): r = r.strip() if not r: continue route = r.split(',') if len(route) != 2: msg = _('Host Routes format error: ' 'Destination CIDR and nexthop must be specified ' '(value=%s)') % r raise forms.ValidationError(msg) self._convert_ip_network(route[0], "host_routes") self._convert_ip_address(route[1], "host_routes") def clean(self): cleaned_data = super(CreateSubnetDetailAction, self).clean() self._check_allocation_pools(cleaned_data.get('allocation_pools')) self._check_host_routes(cleaned_data.get('host_routes')) self._check_dns_nameservers(cleaned_data.get('dns_nameservers')) return cleaned_data
class CreateSubnetInfoAction(workflows.Action): subnet_name = forms.CharField(max_length=255, widget=forms.TextInput(attrs={ }), label=_("Subnet Name"), required=False) ipam = forms.DynamicTypedChoiceField(label=_("IPAM"), required=False, empty_value=None, add_item_link=IPAM_CREATE_URL, help_text=_("Choose IPAM that will be " "associated with the IP Block")) address_source = forms.ChoiceField( required=False, label=_('Network Address Source'), choices=[('manual', _('Enter Network Address manually')), ('subnetpool', _('Allocate Network Address from a pool'))], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'source', })) subnetpool = forms.ChoiceField( label=_("Address pool"), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switched switchable', 'data-slug': 'subnetpool', 'data-switch-on': 'source', 'data-source-subnetpool': _('Address pool')}, data_attrs=('name', 'prefixes', 'ip_version', 'min_prefixlen', 'max_prefixlen', 'default_prefixlen'), transform=lambda x: "%s (%s)" % (x.name, ", ".join(x.prefixes)) if 'prefixes' in x else "%s" % (x.name)), required=False) prefixlen = forms.ChoiceField(widget=forms.ThemableSelectWidget(attrs={ 'class': 'switched', 'data-switch-on': 'subnetpool', }), label=_('Network Mask'), required=False) cidr = forms.IPField(label=_("Network Address"), required=False, initial="", widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-manual': _("Network Address"), }), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'ipversion', }), label=_("IP Version"), required=False) gateway_ip = forms.IPField( label=_("Gateway IP"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'gateway_ip', 'data-source-manual': _("Gateway IP") }), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address " "(e.g. 192.168.0.1 for 192.168.0.0/24, " "2001:DB8::1 for 2001:DB8::/48). " "If you use the default, leave blank. " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), widget=forms.CheckboxInput(attrs={ 'class': 'switchable', 'data-slug': 'gateway_ip', 'data-hide-on-checked': 'true' }), initial=False, required=False) check_subnet_range = True class Meta(object): name = _("Subnet") help_text = _('Creates a subnet associated with the network.' ' You need to enter a valid "Network Address"' ' and "Gateway IP". If you did not enter the' ' "Gateway IP", the first value of a network' ' will be assigned by default. If you do not want' ' gateway please check the "Disable Gateway" checkbox.' ' Advanced configuration is available by clicking on' ' the "Subnet Details" tab.') def __init__(self, request, context, *args, **kwargs): super(CreateSubnetInfoAction, self).__init__(request, context, *args, **kwargs) if 'with_subnet' in context: self.fields['with_subnet'] = forms.BooleanField( initial=context['with_subnet'], required=False, widget=forms.HiddenInput() ) if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get('enable_ipv6', True): self.fields['ip_version'].widget = forms.HiddenInput() self.fields['ip_version'].initial = 4 try: if api.neutron.is_extension_supported(request, 'subnet_allocation'): self.fields['subnetpool'].choices = \ self.get_subnetpool_choices(request) else: self.hide_subnetpool_choices() except Exception: self.hide_subnetpool_choices() msg = _('Unable to initialize subnetpools') exceptions.handle(request, msg) if len(self.fields['subnetpool'].choices) > 1: # Pre-populate prefixlen choices to satisfy Django # ChoiceField Validation. This is overridden w/data from # subnetpool on select. self.fields['prefixlen'].choices = \ zip(list(range(0, 128 + 1)), list(range(0, 128 + 1))) # Populate data-fields for switching the prefixlen field # when user selects a subnetpool other than # "Provider default pool" for (id, name) in self.fields['subnetpool'].choices: if not len(id): continue key = 'data-subnetpool-' + id self.fields['prefixlen'].widget.attrs[key] = \ _('Network Mask') else: self.hide_subnetpool_choices() # Create IPAM choices tenant_id = self.request.user.tenant_id try: ipams = ipam_summary(self.request) if ipams: ipam_choices = [(ipam.id, "{0} ({1})".format(ipam.fq_name[2], ipam.fq_name[1])) for ipam in ipams] ipam_choices.append(('None', 'None')) else: ipam_choices = [('None', 'Create a new IPAM')] except: ipam_choices = [('None', 'None')] exceptions.handle(self.request, _('Unable to retrieve ipam list')) self.fields['ipam'].choices = ipam_choices def get_subnetpool_choices(self, request): subnetpool_choices = [('', _('Select a pool'))] for subnetpool in api.neutron.subnetpool_list(request): subnetpool_choices.append((subnetpool.id, subnetpool)) return subnetpool_choices def hide_subnetpool_choices(self): self.fields['address_source'].widget = forms.HiddenInput() self.fields['subnetpool'].choices = [] self.fields['subnetpool'].widget = forms.HiddenInput() self.fields['prefixlen'].widget = forms.HiddenInput() def _check_subnet_range(self, subnet, allow_cidr): allowed_net = netaddr.IPNetwork(allow_cidr) return subnet in allowed_net def _check_cidr_allowed(self, ip_version, subnet): if not self.check_subnet_range: return allowed_cidr = getattr(settings, "ALLOWED_PRIVATE_SUBNET_CIDR", {}) version_str = 'ipv%s' % ip_version allowed_ranges = allowed_cidr.get(version_str, []) if allowed_ranges: under_range = any(self._check_subnet_range(subnet, allowed_range) for allowed_range in allowed_ranges) if not under_range: range_str = ', '.join(allowed_ranges) msg = (_("CIDRs allowed for user private %(ip_ver)s " "networks are %(allowed)s.") % {'ip_ver': '%s' % version_str, 'allowed': range_str}) raise forms.ValidationError(msg) def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') ipam = cleaned_data.get('ipam') ip_version = int(cleaned_data.get('ip_version')) gateway_ip = cleaned_data.get('gateway_ip') no_gateway = cleaned_data.get('no_gateway') address_source = cleaned_data.get('address_source') subnetpool = cleaned_data.get('subnetpool') if not subnetpool and address_source == 'subnetpool': msg = _('Specify "Address pool" or select ' '"Enter Network Address manually" and specify ' '"Network Address".') raise forms.ValidationError(msg) if not cidr and address_source != 'subnetpool': msg = _('Specify "Network Address" or ' 'clear "Create Subnet" checkbox in previous step.') raise forms.ValidationError(msg) if cidr: subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if (ip_version == 4 and subnet.prefixlen == 32) or \ (ip_version == 6 and subnet.prefixlen == 128): msg = _("The subnet in the Network Address is " "too small (/%s).") % subnet.prefixlen self._errors['cidr'] = self.error_class([msg]) self._check_cidr_allowed(ip_version, subnet) if not no_gateway and gateway_ip: if netaddr.IPAddress(gateway_ip).version is not ip_version: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) if not is_create and not no_gateway and not gateway_ip: msg = _('Specify IP address of gateway or ' 'check "Disable Gateway" checkbox.') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateSubnetInfoAction, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Router Name"), required=False) admin_state_up = forms.BooleanField(label=_("Enable Admin State"), initial=True, required=False) external_network = forms.ThemableChoiceField(label=_("External Network"), required=False) enable_snat = forms.BooleanField(label=_("Enable SNAT"), initial=True, required=False) mode = forms.ThemableChoiceField(label=_("Router Type")) ha = forms.ThemableChoiceField(label=_("High Availability Mode")) az_hints = forms.MultipleChoiceField( label=_("Availability Zone Hints"), required=False, help_text=_("Availability Zones where the router may be scheduled. " "Leaving this unset is equivalent to selecting all " "Availability Zones")) failure_url = 'horizon:project:routers:index' def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) self.dvr_allowed = api.neutron.get_feature_permission( self.request, "dvr", "create") if self.dvr_allowed: mode_choices = [('server_default', _('Use Server Default')), ('centralized', _('Centralized')), ('distributed', _('Distributed'))] self.fields['mode'].choices = mode_choices else: del self.fields['mode'] self.ha_allowed = api.neutron.get_feature_permission( self.request, "l3-ha", "create") if self.ha_allowed: ha_choices = [('server_default', _('Use Server Default')), ('enabled', _('Enable HA mode')), ('disabled', _('Disable HA mode'))] self.fields['ha'].choices = ha_choices else: del self.fields['ha'] networks = self._get_network_list(request) if networks: self.fields['external_network'].choices = networks else: del self.fields['external_network'] self.enable_snat_allowed = self.initial['enable_snat_allowed'] if (not networks or not self.enable_snat_allowed): del self.fields['enable_snat'] try: az_supported = api.neutron.is_extension_supported( self.request, 'router_availability_zone') if az_supported: zones = api.neutron.list_availability_zones( self.request, 'router', 'available') self.fields['az_hints'].choices = [(zone['name'], zone['name']) for zone in zones] else: del self.fields['az_hints'] except Exception: msg = _("Failed to get availability zone list.") exceptions.handle(self.request, msg) del self.fields['az_hints'] def _get_network_list(self, request): search_opts = {'router:external': True} try: networks = api.neutron.network_list(request, **search_opts) except Exception as e: LOG.info('Failed to get network list: %s', e) msg = _('Failed to get network list.') messages.warning(request, msg) networks = [] choices = [(network.id, network.name or network.id) for network in networks] if choices: choices.insert(0, ("", _("Select network"))) return choices def handle(self, request, data): try: params = { 'name': data['name'], 'admin_state_up': data['admin_state_up'] } if 'external_network' in data and data['external_network']: params['external_gateway_info'] = { 'network_id': data['external_network'] } if self.enable_snat_allowed: params['external_gateway_info']['enable_snat'] = \ data['enable_snat'] if 'az_hints' in data and data['az_hints']: params['availability_zone_hints'] = data['az_hints'] if (self.dvr_allowed and data['mode'] != 'server_default'): params['distributed'] = (data['mode'] == 'distributed') if (self.ha_allowed and data['ha'] != 'server_default'): params['ha'] = (data['ha'] == 'enabled') router = api.neutron.router_create(request, **params) message = _('Router %s was successfully created.') % data['name'] messages.success(request, message) return router except Exception as exc: LOG.info('Failed to create router: %s', exc) if exc.status_code == 409: msg = _('Quota exceeded for resource router.') else: msg = _('Failed to create router "%s".') % data['name'] redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect) return False
class CreateNamespaceForm(forms.SelfHandlingForm): source_type = forms.ChoiceField( label=_('Namespace Definition Source'), choices=[('file', _('Metadata Definition File')), ('raw', _('Direct Input'))], widget=forms.ThemableSelectWidget(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-required-when-shown': 'true', '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-required-when-shown': 'true', 'data-source-raw': _('Namespace JSON') }), required=False) public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def clean(self): data = super().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 ImportImageForm(forms.SelfHandlingForm): name = forms.CharField( max_length="255", label=_("Name"), widget=forms.TextInput(attrs={'placeholder': 'ubuntu-base'}), required=True) image_file = forms.FileField(label=_("Image File"), help_text=("A local image to upload."), required=False) is_public = forms.BooleanField(label=_("Public"), required=False, initial=True) def __init__(self, *args, **kwargs): super(ImportImageForm, self).__init__(*args, **kwargs) if not settings.HORIZON_IMAGES_ALLOW_UPLOAD: self.fields['image_file'].widget = HiddenInput() def clean(self): data = super(ImportImageForm, self).clean() # check validity of zip file zipbase = None try: zipbase = zipfile.ZipFile(data['image_file']) if BaseVMPackage.MANIFEST_FILENAME not in zipbase.namelist(): msg = _('File is not valid (No manifest file)') raise ValidationError(msg) xml = zipbase.read(BaseVMPackage.MANIFEST_FILENAME) tree = etree.fromstring( xml, etree.XMLParser(schema=BaseVMPackage.schema)) except Exception as e: msg = 'File is not valid (Not a zipped base VM)' raise ValidationError(_(msg)) # Create attributes base_hashvalue = tree.get('hash_value') matching_base = find_basevm_by_sha256(self.request, base_hashvalue) if matching_base is not None: msg = "Base VM exists : UUID(%s)" % matching_base.id raise ValidationError(_(msg)) disk_name = tree.find(BaseVMPackage.NSP + 'disk').get('path') memory_name = tree.find(BaseVMPackage.NSP + 'memory').get('path') diskhash_name = tree.find(BaseVMPackage.NSP + 'disk_hash').get('path') memoryhash_name = tree.find(BaseVMPackage.NSP + 'memory_hash').get('path') temp_dir = mkdtemp(prefix="cloudlet-base-") LOG.info("Decompressing zipfile to temp dir(%s)\n" % (temp_dir)) zipbase.extractall(temp_dir) disk_path = os.path.join(temp_dir, disk_name) memory_path = os.path.join(temp_dir, memory_name) diskhash_path = os.path.join(temp_dir, diskhash_name) memoryhash_path = os.path.join(temp_dir, memoryhash_name) data['base_hashvalue'] = base_hashvalue data['base_disk_path'] = disk_path data['base_memory_path'] = memory_path data['base_diskhash_path'] = diskhash_path data['base_memoryhash_path'] = memoryhash_path return data def handle(self, request, data): basevm_name = data['name'] base_hashvalue = data['base_hashvalue'] disk_path = data['base_disk_path'] memory_path = data['base_memory_path'] diskhash_path = data['base_diskhash_path'] memoryhash_path = data['base_memoryhash_path'] # upload base disk def _create_param(filepath, image_name, image_type, disk_size, mem_size): properties = { "image_type": "snapshot", "image_location": "snapshot", CLOUDLET_TYPE.PROPERTY_KEY_CLOUDLET: "True", CLOUDLET_TYPE.PROPERTY_KEY_CLOUDLET_TYPE: image_type, CLOUDLET_TYPE.PROPERTY_KEY_BASE_UUID: base_hashvalue, } param = { "name": "%s" % image_name, "data": open(filepath, "rb"), "size": os.path.getsize(filepath), "is_public": True, "disk_format": "raw", "container_format": "bare", "min_disk": disk_size, "min_ram": mem_size, "properties": properties, } return param try: # create new flavor if nothing matches memory_header = elijah_memory_util._QemuMemoryHeader( open(memory_path)) libvirt_xml_str = memory_header.xml cpu_count, memory_size_mb = get_resource_size(libvirt_xml_str) disk_gb = int( math.ceil( os.path.getsize(disk_path) / 1024.0 / 1024.0 / 1024.0)) flavors = api.nova.flavor_list(request) ref_flavors = find_matching_flavor(flavors, cpu_count, memory_size_mb, disk_gb) if len(ref_flavors) == 0: flavor_name = "cloudlet-flavor-%s" % basevm_name api.nova.flavor_create(self.request, flavor_name, memory_size_mb, cpu_count, disk_gb, is_public=True) msg = "Create new flavor %s with (cpu:%d, memory:%d, disk:%d)" %\ (flavor_name, cpu_count, memory_size_mb, disk_gb) LOG.info(msg) # upload Base VM disk_param = _create_param(disk_path, basevm_name + "-disk", CLOUDLET_TYPE.IMAGE_TYPE_BASE_DISK, disk_gb, memory_size_mb) memory_param = _create_param(memory_path, basevm_name + "-memory", CLOUDLET_TYPE.IMAGE_TYPE_BASE_MEM, disk_gb, memory_size_mb) diskhash_param = _create_param( diskhash_path, basevm_name + "-diskhash", CLOUDLET_TYPE.IMAGE_TYPE_BASE_DISK_HASH, disk_gb, memory_size_mb) memoryhash_param = _create_param( memoryhash_path, basevm_name + "-memhash", CLOUDLET_TYPE.IMAGE_TYPE_BASE_MEM_HASH, disk_gb, memory_size_mb) LOG.info("upload base memory to glance") glance_memory = api.glance.image_create(request, **memory_param) LOG.info("upload base disk hash to glance") glance_diskhash = api.glance.image_create(request, **diskhash_param) LOG.info("upload base memory hash to glance") glance_memoryhash = api.glance.image_create( request, **memoryhash_param) glance_ref = { CLOUDLET_TYPE.IMAGE_TYPE_BASE_MEM: glance_memory.id, CLOUDLET_TYPE.IMAGE_TYPE_BASE_DISK_HASH: glance_diskhash.id, CLOUDLET_TYPE.IMAGE_TYPE_BASE_MEM_HASH: glance_memoryhash.id, CLOUDLET_TYPE.PROPERTY_KEY_BASE_RESOURCE: libvirt_xml_str.replace("\n", "") # API cannot send '\n' } disk_param['properties'].update(glance_ref) LOG.info("upload base disk to glance") glance_memory = api.glance.image_create(request, **disk_param) LOG.info("SUCCESS") msg = "Your image %s has been queued for creation." % basevm_name messages.success(request, _(msg)) except: exceptions.handle(request, _('Unable to import image.')) dirpath = os.path.dirname(disk_path) if os.path.exists(dirpath): shutil.rmtree(dirpath) return True
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) parameters = forms.CharField(widget=forms.widgets.HiddenInput, required=True) 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.') }, required=True) timeout_mins = forms.IntegerField( initial=60, label=_('Creation Timeout (minutes)'), help_text=_('Stack creation timeout in minutes.'), required=True) 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'), required=True, 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_key, 'help_text': param.get('Description', ''), 'required': param.get('Default', None) is None } param_type = param.get('Type', None) 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']) 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 data.iteritems() 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') try: api.heat.stack_create(self.request, **fields) messages.success(request, _("Stack creation started.")) return True except Exception as e: msg = exception_to_validation_msg(e) exceptions.handle(request, msg or _('Stack creation failed.'))
class UpdateImageForm(forms.SelfHandlingForm): image_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, widget=forms.Textarea(attrs={'rows': 4}), label=_("Description"), required=False) kernel = forms.CharField( max_length=36, label=_("Kernel ID"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) ramdisk = forms.CharField( max_length=36, label=_("Ramdisk ID"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) architecture = forms.CharField( label=_("Architecture"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}), ) disk_format = forms.ThemableChoiceField(label=_("Format"), ) min_disk = forms.IntegerField( label=_("Minimum Disk (GB)"), min_value=0, help_text=_('The minimum disk size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) min_ram = forms.IntegerField( label=_("Minimum RAM (MB)"), min_value=0, help_text=_('The minimum memory size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) is_public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, request, *args, **kwargs): super(UpdateImageForm, self).__init__(request, *args, **kwargs) self.fields['disk_format'].choices = [ (value, name) for value, name in IMAGE_FORMAT_CHOICES if value ] if not policy.check((("image", "publicize_image"), ), request): self.fields['is_public'].widget = forms.CheckboxInput( attrs={ 'readonly': 'readonly', 'disabled': 'disabled' }) self.fields['is_public'].help_text = _( 'Non admin users are not allowed to make images public.') def handle(self, request, data): image_id = data['image_id'] error_updating = _('Unable to update image "%s".') meta = api.glance.create_image_metadata(data) try: image = api.glance.image_update(request, image_id, **meta) messages.success(request, _('Image was successfully updated.')) return image except Exception: exceptions.handle(request, error_updating % image_id)
class CreateStackForm(forms.SelfHandlingForm): param_prefix = '__param_' class Meta(object): 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) if self._stack_password_enabled(): self.fields['password'] = forms.CharField( label=_('Password for user "%s"') % self.request.user.username, help_text=_('This is required for operations to be performed ' 'throughout the lifecycle of the stack'), widget=forms.PasswordInput()) self._build_parameter_fields(parameters) def _stack_password_enabled(self): stack_settings = getattr(settings, 'OPENSTACK_HEAT_STACK', {}) return stack_settings.get('enable_user_pass', True) def _build_parameter_fields(self, template_validate): self.help_text = template_validate['Description'] params = template_validate.get('Parameters', {}) if template_validate.get('ParameterGroups'): params_in_order = [] for group in template_validate['ParameterGroups']: for param in group.get('parameters', []): if param in params: params_in_order.append((param, params[param])) else: # no parameter groups, simply sorted to make the order fixed params_in_order = sorted(params.items()) for param_key, param in params_in_order: field = None field_key = self.param_prefix + param_key field_args = { 'initial': param.get('Default', None), 'label': param.get('Label', param_key), 'help_text': html.escape(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 'CustomConstraint' in param: choices = self._populate_custom_choices( param['CustomConstraint']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif 'AllowedValues' in param: choices = map(lambda x: (x, x), param['AllowedValues']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif param_type == 'Json' and 'Default' in param: field_args['initial'] = json.dumps(param['Default']) field = forms.CharField(**field_args) elif param_type in ('CommaDelimitedList', 'String', 'Json'): if 'MinLength' in param: field_args['min_length'] = int(param['MinLength']) field_args['required'] = field_args['min_length'] > 0 if 'MaxLength' in param: field_args['max_length'] = int(param['MaxLength']) if hidden: field_args['widget'] = forms.PasswordInput() 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) # heat-api currently returns the boolean type in lowercase # (see https://bugs.launchpad.net/heat/+bug/1361448) # so for better compatibility both are checked here elif param_type in ('Boolean', 'boolean'): field = forms.BooleanField(**field_args) if field: self.fields[field_key] = field @sensitive_variables('password') def handle(self, request, data): prefix_length = len(self.param_prefix) params_list = [(k[prefix_length:], v) for (k, v) in 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), } if data.get('password'): fields['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) def _populate_custom_choices(self, custom_type): if custom_type == 'neutron.network': return instance_utils.network_field_data(self.request, True) if custom_type == 'nova.keypair': return instance_utils.keypair_field_data(self.request, True) if custom_type == 'glance.image': return image_utils.image_field_data(self.request, True) if custom_type == 'nova.flavor': return instance_utils.flavor_field_data(self.request, True) return []
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" 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) def __init__(self, request, *args, **kwargs): super(JobConfigAction, self).__init__(request, *args, **kwargs) job_ex_id = request.REQUEST.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]) 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): job_id = request.REQUEST.get("job_id") or request.REQUEST.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 ]: del configs[rmkey] return (configs, edp_configs) class Meta(object): name = _("Configure") help_text_template = ( "project/data_processing.jobs/_launch_job_configure_help.html")
def _build_parameter_fields(self, template_validate): self.help_text = template_validate['Description'] params = template_validate.get('Parameters', {}) if template_validate.get('ParameterGroups'): params_in_order = [] for group in template_validate['ParameterGroups']: for param in group.get('parameters', []): if param in params: params_in_order.append((param, params[param])) else: # no parameter groups, simply sorted to make the order fixed params_in_order = sorted(params.items()) for param_key, param in params_in_order: field = None field_key = self.param_prefix + param_key field_args = { 'initial': param.get('Default', None), 'label': param.get('Label', param_key), 'help_text': html.escape(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 'CustomConstraint' in param: choices = self._populate_custom_choices( param['CustomConstraint']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif 'AllowedValues' in param: choices = map(lambda x: (x, x), param['AllowedValues']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif param_type == 'Json' and 'Default' in param: field_args['initial'] = json.dumps(param['Default']) field = forms.CharField(**field_args) elif param_type in ('CommaDelimitedList', 'String', 'Json'): if 'MinLength' in param: field_args['min_length'] = int(param['MinLength']) field_args['required'] = field_args['min_length'] > 0 if 'MaxLength' in param: field_args['max_length'] = int(param['MaxLength']) if hidden: field_args['widget'] = forms.PasswordInput() 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) # heat-api currently returns the boolean type in lowercase # (see https://bugs.launchpad.net/heat/+bug/1361448) # so for better compatibility both are checked here elif param_type in ('Boolean', 'boolean'): field = forms.BooleanField(**field_args) if field: self.fields[field_key] = field
class CreateUserForm(PasswordMixin, BaseUserForm, AddExtraColumnMixIn): # Hide the domain_id and domain_name by default domain_id = forms.CharField(label=_("Domain ID"), required=False, widget=forms.HiddenInput()) domain_name = forms.CharField(label=_("Domain Name"), required=False, widget=forms.HiddenInput()) name = forms.CharField(max_length=255, label=_("User Name")) description = forms.CharField( widget=forms.widgets.Textarea(attrs={'rows': 4}), label=_("Description"), required=False) email = forms.EmailField(label=_("Email"), required=False) project = forms.ThemableDynamicChoiceField(label=_("Primary Project"), required=PROJECT_REQUIRED, add_item_link=ADD_PROJECT_URL) role_id = forms.ThemableChoiceField(label=_("Role"), required=PROJECT_REQUIRED) enabled = forms.BooleanField(label=_("Enabled"), required=False, initial=True) def __init__(self, *args, **kwargs): roles = kwargs.pop('roles') super(CreateUserForm, self).__init__(*args, **kwargs) # Reorder form fields from multiple inheritance ordering = [ "domain_id", "domain_name", "name", "description", "email", "password", "confirm_password", "project", "role_id", "enabled" ] self.add_extra_fields(ordering) self.fields = collections.OrderedDict( (key, self.fields[key]) for key in ordering) role_choices = [(role.id, role.name) for role in roles] self.fields['role_id'].choices = role_choices # For keystone V3, display the two fields in read-only if api.keystone.VERSIONS.active >= 3: readonlyInput = forms.TextInput(attrs={'readonly': 'readonly'}) self.fields["domain_id"].widget = readonlyInput self.fields["domain_name"].widget = readonlyInput # For keystone V2.0, hide description field else: self.fields["description"].widget = forms.HiddenInput() # We have to protect the entire "data" dict because it contains the # password and confirm_password strings. @sensitive_variables('data') def handle(self, request, data): domain = api.keystone.get_default_domain(self.request, False) try: LOG.info('Creating user with name "%s"', data['name']) desc = data["description"] if "email" in data: data['email'] = data['email'] or None # add extra information if api.keystone.VERSIONS.active >= 3: EXTRA_INFO = getattr(settings, 'USER_TABLE_EXTRA_INFO', {}) kwargs = dict((key, data.get(key)) for key in EXTRA_INFO) else: kwargs = {} new_user = \ api.keystone.user_create(request, name=data['name'], email=data['email'], description=desc or None, password=data['password'], project=data['project'] or None, enabled=data['enabled'], domain=domain.id, **kwargs) messages.success( request, _('User "%s" was successfully created.') % data['name']) if data['project'] and data['role_id']: roles = api.keystone.roles_for_user(request, new_user.id, data['project']) or [] assigned = [ role for role in roles if role.id == str(data['role_id']) ] if not assigned: try: api.keystone.add_tenant_user_role( request, data['project'], new_user.id, data['role_id']) except Exception: exceptions.handle( request, _('Unable to add user ' 'to primary project.')) return new_user except exceptions.Conflict: msg = _('User name "%s" is already used.') % data['name'] messages.error(request, msg) except Exception: exceptions.handle(request, _('Unable to create user.'))
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Name"), required=True) description = forms.CharField(widget=forms.widgets.Textarea(), label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) copy_from = forms.CharField(max_length="255", label=_("Image Location"), help_text=_("An external (HTTP) URL to load " "the image from."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location') }), required=False) image_file = forms.FileField(label=_("Image File"), help_text=_("A local image to upload."), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Image File') }), required=False) disk_format = forms.ChoiceField( label=_('Format'), required=True, choices=[('', ''), ('aki', _('AKI - Amazon Kernel ' 'Image')), ('ami', _('AMI - Amazon Machine ' 'Image')), ('ari', _('ARI - Amazon Ramdisk ' 'Image')), ('iso', _('ISO - Optical Disk Image')), ('qcow2', _('QCOW2 - QEMU Emulator')), ('raw', 'Raw'), ('vdi', 'VDI'), ('vhd', 'VHD'), ('vmdk', 'VMDK')], widget=forms.Select(attrs={'class': 'switchable'})) minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"), help_text=_( 'The minimum disk size' ' required to boot the' ' image. If unspecified, this' ' value defaults to 0' ' (no minimum).'), required=False) minimum_ram = forms.IntegerField(label=_("Minimum Ram (MB)"), help_text=_('The minimum disk size' ' required to boot the' ' image. If unspecified, this' ' value defaults to 0 (no' ' minimum).'), required=False) is_public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, *args, **kwargs): super(CreateImageForm, self).__init__(*args, **kwargs) if not settings.HORIZON_IMAGES_ALLOW_UPLOAD: self.fields['image_file'].widget = HiddenInput() def clean(self): data = super(CreateImageForm, self).clean() if not data['copy_from'] and not data['image_file']: raise ValidationError( _("A image or external image location must be specified.")) elif data['copy_from'] and data['image_file']: raise ValidationError( _("Can not specify both image and external image location.")) else: return data def handle(self, request, data): # Glance does not really do anything with container_format at the # moment. It requires it is set to the same disk_format for the three # Amazon image types, otherwise it just treats them as 'bare.' As such # we will just set that to be that here instead of bothering the user # with asking them for information we can already determine. if data['disk_format'] in ( 'ami', 'aki', 'ari', ): container_format = data['disk_format'] else: container_format = 'bare' meta = { 'is_public': data['is_public'], 'protected': data['protected'], 'disk_format': data['disk_format'], 'container_format': container_format, 'min_disk': (data['minimum_disk'] or 0), 'min_ram': (data['minimum_ram'] or 0), 'name': data['name'], 'properties': {} } if data['description']: meta['properties']['description'] = data['description'] if settings.HORIZON_IMAGES_ALLOW_UPLOAD and data['image_file']: meta['data'] = self.files['image_file'] else: meta['copy_from'] = data['copy_from'] try: image = api.glance.image_create(request, **meta) messages.success( request, _('Your image %s has been queued for creation.') % data['name']) return image except Exception: exceptions.handle(request, _('Unable to create new image.'))