class UpdateIpam(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(label=_("Name"), widget=forms.TextInput( attrs={'readonly': 'readonly'}), validators=[validators.validate_slug]) dnsmethod = forms.ChoiceField(label=_('DNS Method'), choices=[('default', _('Default')), ('vdns', _('Virtual DNS')), ('tenantdns', _('Tenant DNS')), ('none', _('None'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'dnsmethod'})) vdns = forms.CharField(label=_("Virtual DNS"), required=False, help_text=_("FQ Name of Virtual DNS i.e. default-domain:vdns"), widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-vdns': _('Virtual DNS')})) tenantdns = fields.IPField(label=_("Tenant DNS Server IP"), required=False, help_text=_("Tenant managed DNS Server's IP Address"), version=fields.IPv4, mask=False, widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-tenantdns': _('Tenant DNS Server IP')})) ntpip = fields.IPField(label=_("NTP Server IP"), required=False, help_text=_("IP Address of the NTP Server"), version=fields.IPv4, mask=False, widget=forms.TextInput()) domainname = forms.CharField(label=_("Domain Name"), required=False, help_text=_("Domain Name i.e. openstack.org"), widget=forms.TextInput()) def __init__(self, *args, **kwargs): ipam_obj = kwargs.pop('ipam_obj', {}) mgmt = ipam_obj.__dict__['_apidict']['mgmt'] super(UpdateIpam, self).__init__(*args, **kwargs) if mgmt['ipam_dns_method'] == 'default-dns-server': self.fields['dnsmethod'].initial = 'default' if mgmt['ipam_dns_method'] == 'tenant-dns-server': self.fields['dnsmethod'].initial = 'tenantdns' if 'ipam_dns_server' in mgmt and \ 'tenant_dns_server_address' in mgmt['ipam_dns_server'] and \ 'ip_address' in mgmt['ipam_dns_server']['tenant_dns_server_address'] : for ip in mgmt['ipam_dns_server']['tenant_dns_server_address']['ip_address']: self.fields['tenantdns'].initial = ip if mgmt['ipam_dns_method'] == 'virtual-dns-server': self.fields['dnsmethod'].initial = 'vdns' if 'ipam_dns_server' in mgmt and \ 'virtual_dns_server_name' in mgmt['ipam_dns_server'] and \ mgmt['ipam_dns_server']['virtual_dns_server_name'] != None : self.fields['vdns'].initial = mgmt['ipam_dns_server']['virtual_dns_server_name'] if mgmt['ipam_dns_method'] == 'none': self.fields['dnsmethod'].initial = 'none' if 'dhcp_option_list' in mgmt: dhcp_list = mgmt['dhcp_option_list'] if dhcp_list and 'dhcp_option' in dhcp_list: for entry in mgmt['dhcp_option_list']['dhcp_option']: if entry['dhcp_option_name'] == '4': self.fields['ntpip'].initial = entry['dhcp_option_value'] if entry['dhcp_option_name'] == '15': self.fields['domainname'].initial = entry['dhcp_option_value'] def clean(self): cleaned_data = super(UpdateIpam, self).clean() name = cleaned_data.get("name") dnsmethod = cleaned_data.get("dnsmethod") vdns = cleaned_data.get("vdns") tenantdns = cleaned_data.get("tenantdns") ntpip = cleaned_data.get("ntpip") domainname = cleaned_data.get("domainname") if dnsmethod == 'vdns' and not len(vdns): msg = _('Virtual DNS : Enter a valid Virtual DNS in FQN format') raise ValidationError(msg) if dnsmethod == 'tenantdns': if not tenantdns: msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) elif not len(tenantdns): msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) return cleaned_data def handle(self, request, data): params = {'mgmt': {'ipam_method': None, 'dhcp_option_list': {'dhcp_option':[]}}} if data['domainname']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '15', 'dhcp_option_value': data['domainname']}) if data['ntpip']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '4', 'dhcp_option_value': data['ntpip']}) params['mgmt']['ipam_dns_server'] = {} params['mgmt']['ipam_dns_server']['tenant_dns_server_address'] = {} params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = None if data['dnsmethod'] == 'default': params['mgmt']['ipam_dns_method'] = 'default-dns-server' if data['dnsmethod'] == 'none': params['mgmt']['ipam_dns_method'] = 'none' if data['dnsmethod'] == 'tenantdns': params['mgmt']['ipam_dns_method'] = 'tenant-dns-server' if data['tenantdns']: params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'] = [] params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'].append(data['tenantdns']) if data['dnsmethod'] == 'vdns': params['mgmt']['ipam_dns_method'] = 'virtual-dns-server' params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = data['vdns'] try: ipam = ipam_modify(request, ipam_id=data['id'], **params) messages.success(request, _('Successfully updated network ipam: %s') % data['name']) return ipam except Exception: redirect = reverse("horizon:project:networking:index") exceptions.handle(request, _('Unable to update network ipam.'), redirect=redirect)
class AddHostInfoAction(workflows.Action): FIELD_LABEL_PERSONALITY = _("Personality") FIELD_LABEL_HOSTNAME = _("Host Name") FIELD_LABEL_MGMT_MAC = _("Management MAC Address") FIELD_LABEL_MGMT_IP = _("Management IP Address") personality = forms.ChoiceField( label=FIELD_LABEL_PERSONALITY, help_text=_("Host Personality"), choices=PERSONALITY_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'personality' })) subfunctions = forms.ChoiceField( label=FIELD_LABEL_PERFORMANCE_PROFILE, choices=PERFORMANCE_CHOICES, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Personality Sub-Type") })) hostname = forms.RegexField( label=FIELD_LABEL_HOSTNAME, max_length=255, required=False, regex=r'^[\w\.\-]+$', error_messages={ 'invalid': _('Name may only contain letters,' ' numbers, underscores, ' 'periods and hyphens.') }, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: FIELD_LABEL_HOSTNAME, })) mgmt_mac = forms.MACAddressField( label=FIELD_LABEL_MGMT_MAC, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: FIELD_LABEL_MGMT_MAC, 'data-personality-' + stx_api.sysinv.PERSONALITY_CONTROLLER: FIELD_LABEL_MGMT_MAC, 'data-personality-' + stx_api.sysinv.PERSONALITY_STORAGE: FIELD_LABEL_MGMT_MAC, })) class Meta(object): name = _("Host Info") help_text = _( "From here you can add the configuration for a new host.") def __init__(self, request, *arg, **kwargs): super(AddHostInfoAction, self).__init__(request, *arg, **kwargs) # pesonality cannot be storage if ceph is not configured storage_backend = stx_api.sysinv.get_storage_backend(request) if stx_api.sysinv.STORAGE_BACKEND_CEPH not in storage_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can be controller or worker. systems = stx_api.sysinv.system_list(request) system_type = systems[0].to_dict().get('system_type') if system_type == constants.TS_AIO: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE def clean(self): cleaned_data = super(AddHostInfoAction, self).clean() return cleaned_data
class BoardManagementAction(workflows.Action): FIELD_LABEL_BM_IP = _("Board Management Controller IP Address") FIELD_LABEL_BM_USERNAME = _("Board Management Controller User Name") FIELD_LABEL_BM_PASSWORD = _("Board Management Controller Password") FIELD_LABEL_BM_CONFIRM_PASSWORD = _("Confirm Password") bm_type = forms.ChoiceField(label=_("Board Management Controller Type "), choices=BM_TYPES_CHOICES, required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'bm_type' })) bm_ip = forms.IPField( label=FIELD_LABEL_BM_IP, required=False, help_text=_("IP address of the Board Management Controller" " (e.g. 172.25.0.0)"), version=forms.IPv4 | forms.IPv6, mask=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_IP, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_IP, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_IP })) bm_username = forms.CharField( label=FIELD_LABEL_BM_USERNAME, required=False, widget=forms.TextInput( attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_USERNAME, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_USERNAME, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_USERNAME })) bm_password = forms.RegexField( label=FIELD_LABEL_BM_PASSWORD, widget=forms.PasswordInput( render_value=False, attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_PASSWORD }), regex=validators.password_validator(), required=False, error_messages={'invalid': validators.password_validator_msg()}) bm_confirm_password = forms.CharField( label=FIELD_LABEL_BM_CONFIRM_PASSWORD, widget=forms.PasswordInput( render_value=False, attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_CONFIRM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_CONFIRM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_CONFIRM_PASSWORD }), required=False) def clean(self): cleaned_data = super(BoardManagementAction, self).clean() if cleaned_data.get('bm_type') != \ sysinv_const.HOST_BM_TYPE_DEPROVISIONED: if 'bm_ip' not in cleaned_data or not cleaned_data['bm_ip']: raise forms.ValidationError( _('Board management IP address is required.')) if 'bm_username' not in cleaned_data or not \ cleaned_data['bm_username']: raise forms.ValidationError( _('Board management user name is required.')) if 'bm_password' in cleaned_data: if cleaned_data['bm_password'] != cleaned_data.get( 'bm_confirm_password', None): raise forms.ValidationError( _('Board management passwords do not match.')) else: cleaned_data.pop('bm_ip') cleaned_data.pop('bm_username') return cleaned_data # 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): # Throw away the password confirmation, we're done with it. data.pop('bm_confirm_password', None)
class JobBinaryCreateForm(forms.SelfHandlingForm): NEW_SCRIPT = "%%%NEWSCRIPT%%%" UPLOAD_BIN = "%%%UPLOADFILE%%%" job_binary_name = forms.CharField(label=_("Name"), required=True) job_binary_type = forms.ChoiceField(label=_("Storage type"), required=True) job_binary_url = forms.CharField(label=_("URL"), required=False, widget=LabeledInput()) job_binary_internal = forms.ChoiceField(label=_("Internal binary"), required=False) job_binary_file = forms.FileField(label=_("Upload File"), required=False) job_binary_script_name = forms.CharField(label=_("Script name"), required=False) job_binary_script = forms.CharField(label=_("Script text"), required=False, widget=forms.Textarea()) job_binary_username = forms.CharField(label=_("Username"), required=False) job_binary_password = forms.CharField( label=_("Password"), required=False, widget=forms.PasswordInput(attrs={'autocomplete': 'off'})) job_binary_description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea()) def __init__(self, request, *args, **kwargs): super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs) self.help_text_template = "job_binaries/_create_job_binary_help.html" self.fields["job_binary_type"].choices =\ [("internal-db", "Internal database"), ("swift", "Swift")] self.fields["job_binary_internal"].choices =\ self.populate_job_binary_internal_choices(request) def populate_job_binary_internal_choices(self, request): sahara = saharaclient.client(request) job_binaries = sahara.job_binary_internals.list() choices = [(job_binary.id, job_binary.name) for job_binary in job_binaries] choices.insert(0, (self.NEW_SCRIPT, '*Create a script')) choices.insert(0, (self.UPLOAD_BIN, '*Upload a new file')) return choices def handle(self, request, context): try: sahara = saharaclient.client(request) extra = {} bin_url = "%s://%s" % (context["job_binary_type"], context["job_binary_url"]) if (context["job_binary_type"] == "internal-db"): bin_url = self.handle_internal(request, context) elif (context["job_binary_type"] == "swift"): extra = self.handle_swift(request, context) sahara.job_binaries.create(context["job_binary_name"], bin_url, context["job_binary_description"], extra) messages.success(request, "Successfully created job binary") return True except api_base.APIException as e: messages.error(request, str(e)) return False except Exception as e: messages.error(request, str(e)) return False def get_help_text(self, extra_context=None): text = "" extra_context = extra_context or {} if self.help_text_template: tmpl = template.loader.get_template(self.help_text_template) context = template.RequestContext(self.request, extra_context) text += tmpl.render(context) else: text += linebreaks(force_unicode(self.help_text)) return safe(text) class Meta: name = _("Create Job Binary") help_text_template = \ ("job_binaries/_create_job_binary_help.html") def handle_internal(self, request, context): result = "" sahara = saharaclient.client(request) bin_id = context["job_binary_internal"] if (bin_id == self.UPLOAD_BIN): result = sahara.job_binary_internals.create( self.get_unique_binary_name( request, request.FILES["job_binary_file"].name), request.FILES["job_binary_file"].read()) elif (bin_id == self.NEW_SCRIPT): result = sahara.job_binary_internals.create( self.get_unique_binary_name(request, context["job_binary_script_name"]), context["job_binary_script"]) bin_id = result.id return "internal-db://%s" % bin_id def handle_swift(self, request, context): username = context["job_binary_username"] password = context["job_binary_password"] extra = {"user": username, "password": password} return extra def get_unique_binary_name(self, request, base_name): sahara = saharaclient.client(request) internals = sahara.job_binary_internals.list() names = [internal.name for internal in internals] if base_name in names: return "%s_%s" % (base_name, uuid.uuid1()) return base_name
class AttachForm(forms.SelfHandlingForm): instance = forms.ChoiceField(label=_("Attach to Server Group"), help_text=_("Select an server group to " "attach to.")) device = forms.CharField(label=_("Device Name")) def __init__(self, *args, **kwargs): super(AttachForm, self).__init__(*args, **kwargs) # Hide the device field if the hypervisor doesn't support it. hypervisor_features = getattr(settings, "OPENSTACK_HYPERVISOR_FEATURES", {}) can_set_mount_point = hypervisor_features.get("can_set_mount_point", True) if not can_set_mount_point: self.fields['device'].widget = forms.widgets.HiddenInput() self.fields['device'].required = False # populate volume_id volume = kwargs.get('initial', {}).get("volume", None) if volume: volume_id = volume.id else: volume_id = None self.fields['volume_id'] = forms.CharField(widget=forms.HiddenInput(), initial=volume_id) # Populate instance choices instance_list = kwargs.get('initial', {}).get('instances', []) instances = [] for instance in instance_list: if instance.status in tables.ACTIVE_STATES and \ not any(instance.id == att["server_id"] for att in volume.attachments): instances.append( (instance.id, '%s (%s)' % (instance.name, instance.id))) if instances: instances.insert(0, ("", _("Select an instance"))) else: instances = (("", _("No instances available")), ) self.fields['instance'].choices = instances def handle(self, request, data): instance_choices = dict(self.fields['instance'].choices) instance_name = instance_choices.get(data['instance'], _("Unknown instance (None)")) # The name of the instance in the choices list has the ID appended to # it, so let's slice that off... instance_name = instance_name.rsplit(" (")[0] try: attach = api.nova.instance_volume_attach(request, data['volume_id'], data['instance'], data.get('device', '')) volume = cinder.volume_get(request, data['volume_id']) if not volume.display_name: volume_name = volume.id else: volume_name = volume.display_name message = _('Attaching volume %(vol)s to instance ' '%(inst)s on %(dev)s.') % { "vol": volume_name, "inst": instance_name, "dev": attach.device } messages.info(request, message) return True except Exception: redirect = reverse("horizon:project:volumes:index") exceptions.handle(request, _('Unable to attach volume.'), redirect=redirect)
class SetInstanceDetailsAction(workflows.Action): availability_zone = forms.ThemableChoiceField(label=_("Availability Zone"), required=False) name = forms.CharField(label=_("Instance Name"), max_length=255) flavor = forms.ThemableChoiceField(label=_("Flavor"), help_text=_("Size of image to launch.")) count = forms.IntegerField(label=_("Number of Instances"), min_value=1, initial=1) source_type = forms.ThemableChoiceField( label=_("Instance Boot Source"), help_text=_("Choose Your Boot Source " "Type.")) instance_snapshot_id = forms.ThemableChoiceField( label=_("Instance Snapshot"), required=False) volume_id = forms.ThemableChoiceField(label=_("Volume"), required=False) volume_snapshot_id = forms.ThemableChoiceField(label=_("Volume Snapshot"), required=False) image_id = forms.ChoiceField( label=_("Image Name"), required=False, widget=forms.ThemableSelectWidget( data_attrs=('volume_size',), transform=lambda x: ("%s (%s)" % (x.name, filesizeformat(x.bytes))))) volume_size = forms.IntegerField(label=_("Device size (GB)"), initial=1, min_value=0, required=False, help_text=_("Volume size in gigabytes " "(integer value).")) device_name = forms.CharField(label=_("Device Name"), required=False, initial="vda", help_text=_("Volume mount point (e.g. 'vda' " "mounts at '/dev/vda'). Leave " "this field blank to let the " "system choose a device name " "for you.")) vol_delete_on_instance_delete = forms.BooleanField( label=_("Delete Volume on Instance Delete"), initial=False, required=False, help_text=_("Delete volume when the instance is deleted")) class Meta(object): name = _("Details") help_text_template = ("project/instances/" "_launch_details_help.html") def __init__(self, request, context, *args, **kwargs): self._init_images_cache() self.request = request self.context = context super(SetInstanceDetailsAction, self).__init__( request, context, *args, **kwargs) # Hide the device field if the hypervisor doesn't support it. if not nova.can_set_mount_point(): self.fields['device_name'].widget = forms.widgets.HiddenInput() source_type_choices = [ ('', _("Select source")), ("image_id", _("Boot from image")), ("instance_snapshot_id", _("Boot from snapshot")), ] if cinder.is_volume_service_enabled(request): source_type_choices.append(("volume_id", _("Boot from volume"))) try: if api.nova.extension_supported("BlockDeviceMappingV2Boot", request): source_type_choices.append( ("volume_image_id", _("Boot from image (creates a new volume)"))) except Exception: exceptions.handle(request, _('Unable to retrieve extensions ' 'information.')) source_type_choices.append( ("volume_snapshot_id", _("Boot from volume snapshot (creates a new volume)"))) self.fields['source_type'].choices = source_type_choices @memoized.memoized_method def _get_flavor(self, flavor_id): try: # We want to retrieve details for a given flavor, # however flavor_list uses a memoized decorator # so it is used instead of flavor_get to reduce the number # of API calls. flavors = instance_utils.flavor_list(self.request) flavor = [x for x in flavors if x.id == flavor_id][0] except IndexError: flavor = None return flavor @memoized.memoized_method def _get_image(self, image_id): try: # We want to retrieve details for a given image, # however get_available_images uses a cache of image list, # so it is used instead of image_get to reduce the number # of API calls. images = image_utils.get_available_images( self.request, self.context.get('project_id'), self._images_cache) image = [x for x in images if x.id == image_id][0] except IndexError: image = None return image def _check_quotas(self, cleaned_data): count = cleaned_data.get('count', 1) # Prevent launching more instances than the quota allows usages = quotas.tenant_quota_usages( self.request, targets=('instances', 'cores', 'ram', 'volumes', )) available_count = usages['instances']['available'] if available_count < count: msg = (_('The requested instance(s) cannot be launched ' 'as your quota will be exceeded: Available: ' '%(avail)s, Requested: %(req)s.') % {'avail': available_count, 'req': count}) raise forms.ValidationError(msg) source_type = cleaned_data.get('source_type') if source_type in ('volume_image_id', 'volume_snapshot_id'): available_volume = usages['volumes']['available'] if available_volume < count: msg = (_('The requested instance cannot be launched. ' 'Requested volume exceeds quota: Available: ' '%(avail)s, Requested: %(req)s.') % {'avail': available_volume, 'req': count}) raise forms.ValidationError(msg) flavor_id = cleaned_data.get('flavor') flavor = self._get_flavor(flavor_id) count_error = [] # Validate cores and ram. available_cores = usages['cores']['available'] if flavor and available_cores < count * flavor.vcpus: count_error.append(_("Cores(Available: %(avail)s, " "Requested: %(req)s)") % {'avail': available_cores, 'req': count * flavor.vcpus}) available_ram = usages['ram']['available'] if flavor and available_ram < count * flavor.ram: count_error.append(_("RAM(Available: %(avail)s, " "Requested: %(req)s)") % {'avail': available_ram, 'req': count * flavor.ram}) if count_error: value_str = ", ".join(count_error) msg = (_('The requested instance cannot be launched. ' 'The following requested resource(s) exceed ' 'quota(s): %s.') % value_str) if count == 1: self._errors['flavor'] = self.error_class([msg]) else: self._errors['count'] = self.error_class([msg]) def _check_flavor_for_image(self, cleaned_data): # Prevents trying to launch an image needing more resources. image_id = cleaned_data.get('image_id') image = self._get_image(image_id) flavor_id = cleaned_data.get('flavor') flavor = self._get_flavor(flavor_id) if not image or not flavor: return props_mapping = (("min_ram", "ram"), ("min_disk", "disk")) for iprop, fprop in props_mapping: if (getattr(image, iprop) > 0 and getattr(flavor, fprop) > 0 and getattr(image, iprop) > getattr(flavor, fprop)): msg = (_("The flavor '%(flavor)s' is too small " "for requested image.\n" "Minimum requirements: " "%(min_ram)s MB of RAM and " "%(min_disk)s GB of Root Disk.") % {'flavor': flavor.name, 'min_ram': image.min_ram, 'min_disk': image.min_disk}) self._errors['image_id'] = self.error_class([msg]) break # Not necessary to continue the tests. def _check_volume_for_image(self, cleaned_data): image_id = cleaned_data.get('image_id') image = self._get_image(image_id) volume_size = cleaned_data.get('volume_size') if not image or not volume_size: return volume_size = int(volume_size) img_gigs = functions.bytes_to_gigabytes(image.size) smallest_size = max(img_gigs, image.min_disk) if volume_size < smallest_size: msg = (_("The Volume size is too small for the" " '%(image_name)s' image and has to be" " greater than or equal to " "'%(smallest_size)d' GB.") % {'image_name': image.name, 'smallest_size': smallest_size}) self._errors['volume_size'] = self.error_class([msg]) def _check_source_image(self, cleaned_data): if not cleaned_data.get('image_id'): msg = _("You must select an image.") self._errors['image_id'] = self.error_class([msg]) else: self._check_flavor_for_image(cleaned_data) def _check_source_volume_image(self, cleaned_data): volume_size = self.data.get('volume_size', None) if not volume_size: msg = _("You must set volume size") self._errors['volume_size'] = self.error_class([msg]) if float(volume_size) <= 0: msg = _("Volume size must be greater than 0") self._errors['volume_size'] = self.error_class([msg]) if not cleaned_data.get('image_id'): msg = _("You must select an image.") self._errors['image_id'] = self.error_class([msg]) return else: self._check_flavor_for_image(cleaned_data) self._check_volume_for_image(cleaned_data) def _check_source_instance_snapshot(self, cleaned_data): # using the array form of get blows up with KeyError # if instance_snapshot_id is nil if not cleaned_data.get('instance_snapshot_id'): msg = _("You must select a snapshot.") self._errors['instance_snapshot_id'] = self.error_class([msg]) def _check_source_volume(self, cleaned_data): if not cleaned_data.get('volume_id'): msg = _("You must select a volume.") self._errors['volume_id'] = self.error_class([msg]) # Prevent launching multiple instances with the same volume. # TODO(gabriel): is it safe to launch multiple instances with # a snapshot since it should be cloned to new volumes? count = cleaned_data.get('count', 1) if count > 1: msg = _('Launching multiple instances is only supported for ' 'images and instance snapshots.') raise forms.ValidationError(msg) def _check_source_volume_snapshot(self, cleaned_data): if not cleaned_data.get('volume_snapshot_id'): msg = _("You must select a snapshot.") self._errors['volume_snapshot_id'] = self.error_class([msg]) def _check_source(self, cleaned_data): # Validate our instance source. source_type = self.data.get('source_type', None) source_check_methods = { 'image_id': self._check_source_image, 'volume_image_id': self._check_source_volume_image, 'instance_snapshot_id': self._check_source_instance_snapshot, 'volume_id': self._check_source_volume, 'volume_snapshot_id': self._check_source_volume_snapshot } check_method = source_check_methods.get(source_type) if check_method: check_method(cleaned_data) def clean(self): cleaned_data = super(SetInstanceDetailsAction, self).clean() self._check_quotas(cleaned_data) self._check_source(cleaned_data) return cleaned_data def populate_flavor_choices(self, request, context): return instance_utils.flavor_field_data(request, False) def populate_availability_zone_choices(self, request, context): try: zones = api.nova.availability_zone_list(request) except Exception: zones = [] exceptions.handle(request, _('Unable to retrieve availability zones.')) zone_list = [(zone.zoneName, zone.zoneName) for zone in zones if zone.zoneState['available']] zone_list.sort() if not zone_list: zone_list.insert(0, ("", _("No availability zones found"))) elif len(zone_list) > 1: zone_list.insert(0, ("", _("Any Availability Zone"))) return zone_list def get_help_text(self, extra_context=None): extra = {} if extra_context is None else dict(extra_context) try: extra['usages'] = quotas.tenant_limit_usages(self.request) extra['usages_json'] = json.dumps(extra['usages']) extra['cinder_enabled'] = \ base.is_service_enabled(self.request, 'volume') flavors = json.dumps([f._info for f in instance_utils.flavor_list(self.request)]) extra['flavors'] = flavors images = image_utils.get_available_images( self.request, self.initial['project_id'], self._images_cache) if images is not None: attrs = [{'id': i.id, 'min_disk': getattr(i, 'min_disk', 0), 'min_ram': getattr(i, 'min_ram', 0), 'size': functions.bytes_to_gigabytes(i.size)} for i in images] extra['images'] = json.dumps(attrs) except Exception: exceptions.handle(self.request, _("Unable to retrieve quota information.")) return super(SetInstanceDetailsAction, self).get_help_text(extra) def _init_images_cache(self): if not hasattr(self, '_images_cache'): self._images_cache = {} def _get_volume_display_name(self, volume): if hasattr(volume, "volume_id"): vol_type = "snap" visible_label = _("Snapshot") else: vol_type = "vol" visible_label = _("Volume") return (("%s:%s" % (volume.id, vol_type)), (_("%(name)s - %(size)s GB (%(label)s)") % {'name': volume.name, 'size': volume.size, 'label': visible_label})) def populate_image_id_choices(self, request, context): choices = [] images = image_utils.get_available_images(request, context.get('project_id'), self._images_cache) for image in images: if image.properties.get("image_type", '') != "snapshot": image.bytes = getattr( image, 'virtual_size', None) or image.size image.volume_size = max( image.min_disk, functions.bytes_to_gigabytes(image.bytes)) choices.append((image.id, image)) if context.get('image_id') == image.id and \ 'volume_size' not in context: context['volume_size'] = image.volume_size if choices: choices.sort(key=lambda c: c[1].name or '') choices.insert(0, ("", _("Select Image"))) else: choices.insert(0, ("", _("No images available"))) return choices def populate_instance_snapshot_id_choices(self, request, context): images = image_utils.get_available_images(request, context.get('project_id'), self._images_cache) choices = [(image.id, image.name) for image in images if image.properties.get("image_type", '') == "snapshot"] if choices: choices.sort(key=operator.itemgetter(1)) choices.insert(0, ("", _("Select Instance Snapshot"))) else: choices.insert(0, ("", _("No snapshots available"))) return choices def populate_volume_id_choices(self, request, context): volumes = [] try: if cinder.is_volume_service_enabled(request): available = api.cinder.VOLUME_STATE_AVAILABLE volumes = [self._get_volume_display_name(v) for v in cinder.volume_list(self.request, search_opts=dict(status=available, bootable=True))] except Exception: exceptions.handle(self.request, _('Unable to retrieve list of volumes.')) if volumes: volumes.insert(0, ("", _("Select Volume"))) else: volumes.insert(0, ("", _("No volumes available"))) return volumes def populate_volume_snapshot_id_choices(self, request, context): snapshots = [] try: if cinder.is_volume_service_enabled(request): available = api.cinder.VOLUME_STATE_AVAILABLE snapshots = [self._get_volume_display_name(s) for s in cinder.volume_snapshot_list( self.request, search_opts=dict(status=available))] except Exception: exceptions.handle(self.request, _('Unable to retrieve list of volume ' 'snapshots.')) if snapshots: snapshots.insert(0, ("", _("Select Volume Snapshot"))) else: snapshots.insert(0, ("", _("No volume snapshots available"))) return snapshots
class UpdateIPSecSiteConnection(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ipsecsiteconnection_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(required=False, max_length=80, label=_("Description")) peer_address = forms.IPField( label=_("Peer gateway public IPv4/IPv6 Address or FQDN"), help_text=_("Peer gateway public IPv4/IPv6 address or FQDN for " "the VPN Connection"), version=forms.IPv4 | forms.IPv6, mask=False) peer_id = forms.IPField( label=_("Peer router identity for authentication (Peer ID)"), help_text=_("Peer router identity for authentication. " "Can be IPv4/IPv6 address, e-mail, key ID, or FQDN"), version=forms.IPv4 | forms.IPv6, mask=False) peer_cidrs = forms.MultiIPField(label=_("Remote peer subnet(s)"), help_text=_( "Remote peer subnet(s) address(es) " "with mask(s) in CIDR format " "separated with commas if needed " "(e.g. 20.1.0.0/24, 21.1.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) psk = forms.CharField(widget=forms.PasswordInput(render_value=True), max_length=80, label=_("Pre-Shared Key (PSK) string")) mtu = forms.IntegerField( min_value=68, required=False, label=_("Maximum Transmission Unit size for the connection"), help_text=_("Equal to or greater than 68 if the local subnet is IPv4. " "Equal to or greater than 1280 if the local subnet " "is IPv6.")) dpd_action = forms.ChoiceField(label=_("Dead peer detection actions"), required=False, choices=[('hold', _('hold')), ('clear', _('clear')), ('disabled', _('disabled')), ('restart', _('restart')), ('restart-by-peer', _('restart-by-peer'))]) dpd_interval = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection interval"), help_text=_("Valid integer lesser than the DPD timeout")) dpd_timeout = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection timeout"), help_text=_("Valid integer greater than the DPD interval")) initiator = forms.ChoiceField(label=_("Initiator state"), required=False, choices=[ ('bi-directional', _('bi-directional')), ('response-only', _('response-only')) ]) admin_state_up = forms.BooleanField(label=_("Enable Admin State"), required=False) failure_url = 'horizon:project:vpn:index' def clean(self): cleaned_data = super(UpdateIPSecSiteConnection, self).clean() interval = cleaned_data.get('dpd_interval') timeout = cleaned_data.get('dpd_timeout') if not interval < timeout: msg = _("DPD Timeout must be greater than DPD Interval") self._errors['dpd_timeout'] = self.error_class([msg]) return cleaned_data def handle(self, request, context): try: data = { 'ipsec_site_connection': { 'name': context['name'], 'description': context['description'], 'peer_address': context['peer_address'], 'peer_id': context['peer_id'], 'peer_cidrs': context['peer_cidrs'].replace(" ", "").split(","), 'psk': context['psk'], 'mtu': context['mtu'], 'dpd': { 'action': context['dpd_action'], 'interval': context['dpd_interval'], 'timeout': context['dpd_timeout'] }, 'initiator': context['initiator'], 'admin_state_up': context['admin_state_up'], } } ipsecsiteconnection = api.vpn.ipsecsiteconnection_update( request, context['ipsecsiteconnection_id'], **data) msg = (_('IPSec Site Connection %s was successfully updated.') % context['name']) messages.success(request, msg) return ipsecsiteconnection except Exception as e: LOG.info('Failed to update IPSec Site Connection %(id)s: %(exc)s', { 'id': context['ipsecsiteconnection_id'], 'exc': e }) msg = (_('Failed to update IPSec Site Connection %s') % context['name']) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Name"), required=True) copy_from = forms.CharField(max_length="255", label=_("Image Location"), help_text=_("An external (HTTP) URL to load " "the image from."), required=False) image_file = forms.FileField(label=_("Image File"), help_text=("A local image to upload."), 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'] } 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: exceptions.handle(request, _('Unable to create new image.'))
class AssociateIPAction(workflows.Action): ip_id = forms.DynamicTypedChoiceField(label=_("IP Address"), coerce=filters.get_int_or_uuid, empty_value=None, add_item_link=ALLOCATE_URL) instance_id = forms.ChoiceField(label=_("Instance")) class Meta: name = _("IP Address") help_text = _("Select the IP address you wish to associate with " "the selected instance or port.") def __init__(self, *args, **kwargs): super(AssociateIPAction, self).__init__(*args, **kwargs) if api.base.is_service_enabled(self.request, 'network'): label = _("Port to be associated") else: label = _("Instance to be associated") self.fields['instance_id'].label = label # If AssociateIP is invoked from instance menu, instance_id parameter # is passed in URL. In Neutron based Floating IP implementation # an association target is not an instance but a port, so we need # to get an association target based on a received instance_id # and set the initial value of instance_id ChoiceField. q_instance_id = self.request.GET.get('instance_id') if q_instance_id: targets = self._get_target_list() target_id = api.network.floating_ip_target_get_by_instance( self.request, q_instance_id, targets) self.initial['instance_id'] = target_id def populate_ip_id_choices(self, request, context): ips = [] redirect = reverse('horizon:project:access_and_security:index') try: ips = api.network.tenant_floating_ip_list(self.request) except neutron_exc.ConnectionFailed: exceptions.handle(self.request, redirect=redirect) except Exception: exceptions.handle(self.request, _('Unable to retrieve floating IP addresses.'), redirect=redirect) options = sorted([(ip.id, ip.ip) for ip in ips if not ip.port_id]) if options: options.insert(0, ("", _("Select an IP address"))) else: options = [("", _("No floating IP addresses allocated"))] return options @memoized.memoized_method def _get_target_list(self): targets = [] try: targets = api.network.floating_ip_target_list(self.request) except Exception: redirect = reverse('horizon:project:access_and_security:index') exceptions.handle(self.request, _('Unable to retrieve instance list.'), redirect=redirect) return targets def populate_instance_id_choices(self, request, context): targets = self._get_target_list() instances = [] for target in targets: instances.append((target.id, target.name)) # Sort instances for easy browsing instances = sorted(instances, key=lambda x: x[1]) neutron_enabled = api.base.is_service_enabled(request, 'network') if instances: if neutron_enabled: label = _("Select a port") else: label = _("Select an instance") instances.insert(0, ("", label)) else: if neutron_enabled: label = _("No ports available") else: label = _("No instances available") instances = (("", label), ) return instances
class ImportOpaqueData(forms.SelfHandlingForm): name = forms.RegexField(required=False, max_length=255, label=_("Data Name"), regex=shared_forms.NAME_REGEX, error_messages=shared_forms.ERROR_MESSAGES) source_type = forms.ChoiceField( label=_('Source'), required=False, choices=[('file', _('File')), ('raw', _('Direct Input'))], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'source' })) object_file = forms.FileField(label=_("Choose file"), help_text=_("A local file to upload."), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('File') }), required=False) direct_input = forms.CharField( label=_('Object Bytes'), help_text=_('The bytes of the object, represented in hex.'), widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-raw': _('Bytes') }), required=False) def __init__(self, request, *args, **kwargs): super(ImportOpaqueData, self).__init__(request, *args, **kwargs) def clean(self): data = super(ImportOpaqueData, self).clean() # The data can be missing based on particular upload # conditions. Code defensively for it here... data_file = data.get('object_file', None) data_raw = data.get('direct_input', None) if data_raw and data_file: raise forms.ValidationError( _("Cannot specify both file and direct input.")) if not data_raw and not data_file: raise forms.ValidationError( _("No input was provided for the object value.")) try: if data_file: data_bytes = self.files['object_file'].read() else: data_str = data['direct_input'] data_bytes = binascii.unhexlify(data_str) data['object_bytes'] = base64.b64encode(data_bytes) except Exception as e: msg = _('There was a problem loading the object: %s. ' 'Is the object valid and in the correct format?') % e raise forms.ValidationError(msg) return data def handle(self, request, data): try: data_bytes = data.get('object_bytes') data_uuid = client.import_object( request, data=data_bytes, name=data['name'], object_type=opaque_data.OpaqueData) if data['name']: data_identifier = data['name'] else: data_identifier = data_uuid messages.success( request, _('Successfully imported object: %s') % data_identifier) return data_uuid except Exception as e: msg = _('Unable to import object: %s') messages.error(msg % e) exceptions.handle(request, ignore=True) self.api_error(_('Unable to import object.')) return False
class UpdateUserForm(BaseUserForm): id = forms.CharField(label=_("ID"), widget=forms.HiddenInput) name = forms.CharField(label=_("User Name")) email = forms.EmailField(label=_("Email")) password = forms.RegexField(label=_("Password"), widget=forms.PasswordInput(render_value=False), regex=validators.password_validator(), required=False, error_messages={'invalid': validators.password_validator_msg()}) confirm_password = forms.CharField( label=_("Confirm Password"), widget=forms.PasswordInput(render_value=False), required=False) tenant_id = forms.ChoiceField(label=_("Primary Project")) def __init__(self, request, *args, **kwargs): super(UpdateUserForm, self).__init__(request, *args, **kwargs) if api.keystone.keystone_can_edit_user() is False: for field in ('name', 'email', 'password', 'confirm_password'): self.fields.pop(field) # We have to protect the entire "data" dict because it contains the # password and confirm_password strings. @sensitive_variables('data', 'password') def handle(self, request, data): failed, succeeded = [], [] user_is_editable = api.keystone.keystone_can_edit_user() user = data.pop('id') tenant = data.pop('tenant_id') if user_is_editable: password = data.pop('password') data.pop('confirm_password', None) if user_is_editable: # Update user details msg_bits = (_('name'), _('email')) try: api.keystone.user_update(request, user, **data) succeeded.extend(msg_bits) except: failed.extend(msg_bits) exceptions.handle(request, ignore=True) # Update default tenant msg_bits = (_('primary project'),) try: api.keystone.user_update_tenant(request, user, tenant) succeeded.extend(msg_bits) except: failed.append(msg_bits) exceptions.handle(request, ignore=True) # Check for existing roles # Show a warning if no role exists for the tenant user_roles = api.keystone.roles_for_user(request, user, tenant) if not user_roles: messages.warning(request, _('The user %s has no role defined for' + ' that project.') % data.get('name', None)) if user_is_editable: # If present, update password # FIXME(gabriel): password change should be its own form and view if password: msg_bits = (_('password'),) try: api.keystone.user_update_password(request, user, password) succeeded.extend(msg_bits) except: failed.extend(msg_bits) exceptions.handle(request, ignore=True) if succeeded: messages.success(request, _('User has been updated successfully.')) if failed: failed = map(force_unicode, failed) messages.error(request, _('Unable to update %(attributes)s for the user.') % {"attributes": ", ".join(failed)}) return True
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Volume Name")) description = forms.CharField(widget=forms.Textarea, label=_("Description"), required=False) type = forms.ChoiceField(label=_("Type"), required=False) size = forms.IntegerField(min_value=1, label=_("Size (GB)")) encryption = forms.ChoiceField(label=_("Encryption"), required=False) volume_source_type = forms.ChoiceField(label=_("Volume Source"), required=False) snapshot_source = forms.ChoiceField( label=_("Use snapshot as a source"), widget=SelectWidget(attrs={'class': 'snapshot-selector'}, data_attrs=('size', 'display_name'), transform=lambda x: ("%s (%sGB)" % (x.display_name, x.size))), required=False) image_source = forms.ChoiceField( label=_("Use image as a source"), widget=SelectWidget(attrs={'class': 'image-selector'}, data_attrs=('size', 'name'), transform=lambda x: ("%s (%s)" % (x.name, filesizeformat(x.bytes)))), required=False) def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) volume_types = cinder.volume_type_list(request) self.fields['type'].choices = [("", "")] + \ [(type.name, type.name) for type in volume_types] # Hide the volume encryption field if the hypervisor doesn't support it # NOTE: as of Grizzly this is not yet supported in Nova so enabling # this setting will not do anything useful hypervisor_features = getattr(settings, "OPENSTACK_HYPERVISOR_FEATURES", {}) can_encrypt_volumes = hypervisor_features.get("can_encrypt_volumes", False) if can_encrypt_volumes: # TODO(laura-glendenning) get from api call in future encryption_options = {"LUKS": "dmcrypt LUKS"} self.fields['encryption'].choices = [("", "")] + \ [(enc, display) for enc, display in encryption_options.items()] else: self.fields['encryption'].widget = forms.widgets.HiddenInput() self.fields['encryption'].required = False if ("snapshot_id" in request.GET): try: snapshot = self.get_snapshot(request, request.GET["snapshot_id"]) self.fields['name'].initial = snapshot.display_name self.fields['size'].initial = snapshot.size self.fields['snapshot_source'].choices = ((snapshot.id, snapshot), ) try: # Set the volume type from the original volume orig_volume = cinder.volume_get(request, snapshot.volume_id) self.fields['type'].initial = orig_volume.volume_type except: pass self.fields['size'].help_text = _( 'Volume size must be equal ' 'to or greater than the snapshot size (%sGB)' % snapshot.size) del self.fields['image_source'] del self.fields['volume_source_type'] except: exceptions.handle(request, _('Unable to load the specified snapshot.')) elif ('image_id' in request.GET): try: image = self.get_image(request, request.GET["image_id"]) image.bytes = image.size self.fields['name'].initial = image.name self.fields['size'].initial = bytes_to_gigabytes(image.size) self.fields['image_source'].choices = ((image.id, image), ) self.fields['size'].help_text = _( 'Volume size must be equal ' 'to or greater than the image size (%s)' % filesizeformat(image.size)) del self.fields['snapshot_source'] del self.fields['volume_source_type'] except: msg = _('Unable to load the specified image. %s') exceptions.handle(request, msg % request.GET['image_id']) else: source_type_choices = [] try: snapshots = cinder.volume_snapshot_list(request) if snapshots: source_type_choices.append( ("snapshot_source", _("Snapshot"))) choices = [('', _("Choose a snapshot"))] + \ [(s.id, s) for s in snapshots] self.fields['snapshot_source'].choices = choices else: del self.fields['snapshot_source'] except: exceptions.handle(request, _("Unable to retrieve " "volume snapshots.")) images = get_available_images(request, request.user.tenant_id) if images: source_type_choices.append(("image_source", _("Image"))) choices = [('', _("Choose an image"))] for image in images: image.bytes = image.size image.size = bytes_to_gigabytes(image.bytes) choices.append((image.id, image)) self.fields['image_source'].choices = choices else: del self.fields['image_source'] if source_type_choices: choices = ( [('no_source_type', _("No source, empty volume."))] + source_type_choices) self.fields['volume_source_type'].choices = choices else: del self.fields['volume_source_type'] def handle(self, request, data): try: # FIXME(johnp): cinderclient currently returns a useless # error message when the quota is exceeded when trying to create # a volume, so we need to check for that scenario here before we # send it off to try and create. usages = cinder.tenant_absolute_limits(self.request) volumes = cinder.volume_list(self.request) total_size = sum( [getattr(volume, 'size', 0) for volume in volumes]) usages['gigabytesUsed'] = total_size usages['volumesUsed'] = len(volumes) availableGB = usages['maxTotalVolumeGigabytes'] -\ usages['gigabytesUsed'] availableVol = usages['maxTotalVolumes'] - usages['volumesUsed'] snapshot_id = None image_id = None source_type = data.get('volume_source_type', None) if (data.get("snapshot_source", None) and source_type in [None, 'snapshot_source']): # Create from Snapshot snapshot = self.get_snapshot(request, data["snapshot_source"]) snapshot_id = snapshot.id if (data['size'] < snapshot.size): error_message = _('The volume size cannot be less than ' 'the snapshot size (%sGB)' % snapshot.size) raise ValidationError(error_message) elif (data.get("image_source", None) and source_type in [None, 'image_source']): # Create from Snapshot image = self.get_image(request, data["image_source"]) image_id = image.id image_size = bytes_to_gigabytes(image.size) if (data['size'] < image_size): error_message = _('The volume size cannot be less than ' 'the image size (%s)' % filesizeformat(image.size)) raise ValidationError(error_message) else: if type(data['size']) is str: data['size'] = int(data['size']) if availableGB < data['size']: error_message = _('A volume of %(req)iGB cannot be created as ' 'you only have %(avail)iGB of your quota ' 'available.') params = {'req': data['size'], 'avail': availableGB} raise ValidationError(error_message % params) elif availableVol <= 0: error_message = _('You are already using all of your available' ' volumes.') raise ValidationError(error_message) metadata = {} if data['encryption']: metadata['encryption'] = data['encryption'] volume = cinder.volume_create(request, data['size'], data['name'], data['description'], data['type'], snapshot_id=snapshot_id, image_id=image_id, metadata=metadata) message = _('Creating volume "%s"') % data['name'] messages.info(request, message) return volume except ValidationError as e: self.api_error(e.messages[0]) return False except: exceptions.handle(request, ignore=True) self.api_error(_("Unable to create volume.")) return False @memoized def get_snapshot(self, request, id): return cinder.volume_snapshot_get(request, id) @memoized def get_image(self, request, id): return glance.image_get(request, id)
class JobConfigAction(workflows.Action): MAIN_CLASS = "edp.java.main_class" STORM_PYLEUS_TOPOLOGY_NAME = "topology_name" JAVA_OPTS = "edp.java.java_opts" EDP_MAPPER = "edp.streaming.mapper" EDP_REDUCER = "edp.streaming.reducer" EDP_PREFIX = "edp." EDP_HBASE_COMMON_LIB = "edp.hbase_common_lib" EDP_ADAPT_FOR_OOZIE = "edp.java.adapt_for_oozie" EDP_ADAPT_SPARK_SWIFT = "edp.spark.adapt_for_swift" EDP_SUBST_DATASOURCE_NAME = "edp.substitute_data_source_for_name" EDP_SUBST_DATASOURCE_UUID = "edp.substitute_data_source_for_uuid" property_name = forms.ChoiceField( required=False, ) job_configs = forms.CharField( required=False, widget=forms.HiddenInput()) job_params = forms.CharField( required=False, widget=forms.HiddenInput()) job_args_array = forms.CharField( required=False, widget=forms.HiddenInput()) job_type = forms.CharField( required=False, widget=forms.HiddenInput()) main_class = forms.CharField(label=_("Main Class"), required=False) topology_name = forms.CharField( label=_("Topology Name"), help_text=_("Use the same topology name as defined in your " ".yaml file"), required=False) java_opts = forms.CharField(label=_("Java Opts"), required=False) streaming_mapper = forms.CharField(label=_("Mapper"), required=False) streaming_reducer = forms.CharField(label=_("Reducer"), required=False) hbase_common_lib = forms.BooleanField( label=_("Use HBase Common library"), help_text=_("Run HBase EDP Jobs with common HBase library on HDFS"), required=False, initial=True) adapt_oozie = forms.BooleanField( label=_("Adapt For Oozie"), help_text=_("Automatically modify the Hadoop configuration" " so that job config values are set and so that" " Oozie will handle exit codes correctly."), required=False, initial=True) adapt_spark_swift = forms.BooleanField( label=_("Enable Swift Paths"), help_text=_("Modify the configuration so that swift URLs can " "be dereferenced through HDFS at runtime."), required=False, initial=True) datasource_substitute = forms.BooleanField( label=_("Use Data Source Substitution for Names and UUIDs"), help_text=_("Substitute data source objects for URLs of " "the form datasource://name or uuid."), required=False, initial=True) def __init__(self, request, *args, **kwargs): super(JobConfigAction, self).__init__(request, *args, **kwargs) req = request.GET or request.POST job_ex_id = req.get("job_execution_id") if job_ex_id is not None: job_ex = saharaclient.job_execution_get(request, job_ex_id) job = saharaclient.job_get(request, job_ex.job_id) job_configs, interface_args = _merge_interface_with_configs( job.interface, job_ex.job_configs) edp_configs = {} if 'configs' in job_configs: configs, edp_configs = ( self.clean_edp_configs(job_configs['configs'])) self.fields['job_configs'].initial = ( json.dumps(configs)) if 'params' in job_configs: self.fields['job_params'].initial = ( json.dumps(job_configs['params'])) if 'args' in job_configs: self.fields['job_args_array'].initial = ( json.dumps(job_configs['args'])) if self.MAIN_CLASS in edp_configs: self.fields['main_class'].initial = ( edp_configs[self.MAIN_CLASS]) if self.STORM_PYLEUS_TOPOLOGY_NAME in edp_configs: self.fields['topology_name'].initial = ( edp_configs[self.STORM_PYLEUS_TOPOLOGY_NAME]) if self.JAVA_OPTS in edp_configs: self.fields['java_opts'].initial = ( edp_configs[self.JAVA_OPTS]) if self.EDP_MAPPER in edp_configs: self.fields['streaming_mapper'].initial = ( edp_configs[self.EDP_MAPPER]) if self.EDP_REDUCER in edp_configs: self.fields['streaming_reducer'].initial = ( edp_configs[self.EDP_REDUCER]) if self.EDP_HBASE_COMMON_LIB in edp_configs: self.fields['hbase_common_lib'].initial = ( edp_configs[self.EDP_HBASE_COMMON_LIB]) if self.EDP_ADAPT_FOR_OOZIE in edp_configs: self.fields['adapt_oozie'].initial = ( edp_configs[self.EDP_ADAPT_FOR_OOZIE]) if self.EDP_ADAPT_SPARK_SWIFT in edp_configs: self.fields['adapt_spark_swift'].initial = ( edp_configs[self.EDP_ADAPT_SPARK_SWIFT]) if (self.EDP_SUBST_DATASOURCE_NAME in edp_configs or self.EDP_SUBST_DATASOURCE_UUID in edp_configs): self.fields['datasource_substitute'].initial = ( edp_configs.get(self.EDP_SUBST_DATASOURCE_UUID, True) or edp_configs.get(self.EDP_SUBST_DATASOURCE_NAME, True)) def clean(self): cleaned_data = super(workflows.Action, self).clean() job_type = cleaned_data.get("job_type", None) if job_type != "MapReduce.Streaming": if "streaming_mapper" in self._errors: del self._errors["streaming_mapper"] if "streaming_reducer" in self._errors: del self._errors["streaming_reducer"] return cleaned_data def populate_property_name_choices(self, request, context): req = request.GET or request.POST job_id = req.get("job_id") or req.get("job") job_type = saharaclient.job_get(request, job_id).type job_configs = ( saharaclient.job_get_configs(request, job_type).job_config) choices = [(param['value'], param['name']) for param in job_configs['configs']] return choices def clean_edp_configs(self, configs): edp_configs = {} for key, value in configs.items(): 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.STORM_PYLEUS_TOPOLOGY_NAME, self.JAVA_OPTS, self.EDP_ADAPT_FOR_OOZIE, self.EDP_ADAPT_SPARK_SWIFT, self.EDP_SUBST_DATASOURCE_UUID, self.EDP_SUBST_DATASOURCE_NAME, ]: del configs[rmkey] return (configs, edp_configs) class Meta(object): name = _("Configure") help_text_template = "job_templates/_launch_job_configure_help.html"
class CreateNetworkIpam(forms.SelfHandlingForm): name = forms.CharField(label=_("Name"), error_messages={ 'required': _('This field is required.'), 'invalid': _("The string may only contain" " ASCII characters and numbers.")}, validators=[validators.validate_slug]) dnsmethod = forms.ChoiceField(label=_('DNS Method'), choices=[('default', _('Default')), ('vdns', _('Virtual DNS')), ('tenantdns', _('Tenant DNS')), ('none', _('None'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'dnsmethod'})) vdns = forms.CharField(label=_("Virtual DNS"), required=False, help_text=_("FQ Name of Virtual DNS i.e. default-domain:vdns"), widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-vdns': _('Virtual DNS')})) tenantdns = fields.IPField(label=_("Tenant DNS Server IP"), required=False, help_text=_("Tenant managed DNS Server's IP Address"), version=fields.IPv4, mask=False, widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-tenantdns': _('Tenant DNS Server IP')})) ntpip = fields.IPField(label=_("NTP Server IP"), required=False, help_text=_("IP Address of the NTP Server"), version=fields.IPv4, mask=False, widget=forms.TextInput()) domainname = forms.CharField(label=_("Domain Name"), required=False, help_text=_("Domain Name i.e. openstack.org"), widget=forms.TextInput()) def clean(self): cleaned_data = super(CreateNetworkIpam, self).clean() name = cleaned_data.get("name") dnsmethod = cleaned_data.get("dnsmethod") vdns = cleaned_data.get("vdns") tenantdns = cleaned_data.get("tenantdns") ntpip = cleaned_data.get("ntpip") domainname = cleaned_data.get("domainname") if dnsmethod == 'vdns' and not len(vdns): msg = _('Virtual DNS : Enter a valid Virtual DNS in FQN format') raise ValidationError(msg) if dnsmethod == 'tenantdns': if not tenantdns: msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) elif not len(tenantdns): msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) return cleaned_data def handle(self, request, data): params = {'name': data['name'], 'mgmt': {'ipam_method': None, 'dhcp_option_list': {'dhcp_option':[]}}} if data['domainname']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '15', 'dhcp_option_value': data['domainname']}) if data['ntpip']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '4', 'dhcp_option_value': data['ntpip']}) params['mgmt']['ipam_dns_server'] = {} params['mgmt']['ipam_dns_server']['tenant_dns_server_address'] = {} params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = None if data['dnsmethod'] == 'default': params['mgmt']['ipam_dns_method'] = 'default-dns-server' if data['dnsmethod'] == 'none': params['mgmt']['ipam_dns_method'] = 'none' if data['dnsmethod'] == 'tenantdns': params['mgmt']['ipam_dns_method'] = 'tenant-dns-server' if data['tenantdns']: params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'] = [] params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'].append(data['tenantdns']) if data['dnsmethod'] == 'vdns': params['mgmt']['ipam_dns_method'] = 'virtual-dns-server' params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = data['vdns'] try: ipam = ipam_create(request, **params) messages.success(request, _('Successfully created network ipam: %s') % data['name']) return ipam except Exception: redirect = reverse("horizon:project:networking:index") exceptions.handle(request, _('Unable to create network ipam.'), redirect=redirect)
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField( label=_('Rule'), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'rule_menu' })) description = forms.CharField( label=_('Description'), required=False, max_length=255, widget=forms.Textarea(attrs={'rows': 2}), help_text=_('A brief description of the security group rule ' 'you are adding')) # "direction" field is enabled only when custom mode. # It is because most common rules in local_settings.py is meaningful # when its direction is 'ingress'. direction = forms.ChoiceField( label=_('Direction'), required=False, widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Direction'), 'data-rule_menu-udp': _('Direction'), 'data-rule_menu-icmp': _('Direction'), 'data-rule_menu-custom': _('Direction'), 'data-rule_menu-all_tcp': _('Direction'), 'data-rule_menu-all_udp': _('Direction'), 'data-rule_menu-all_icmp': _('Direction'), })) ip_protocol = forms.IntegerField( label=_('IP Protocol'), required=False, help_text=_("Enter an integer value between 0 and 255."), validators=[utils_validators.validate_ip_protocol], widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-custom': _('IP Protocol') })) port_or_range = forms.ChoiceField( label=_('Open Port'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Open Port'), 'data-rule_menu-udp': _('Open Port') })) port = forms.IntegerField( label=_("Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-port': _('Port') }), validators=[utils_validators.validate_port_range]) from_port = forms.IntegerField( label=_("From Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-range': _('From Port') }), validators=[utils_validators.validate_port_range]) to_port = forms.IntegerField( label=_("To Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-range': _('To Port') }), validators=[utils_validators.validate_port_range]) icmp_type = forms.IntegerField( label=_("Type"), required=False, help_text=_("Enter a value for ICMP type " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Type') }), validators=[utils_validators.validate_icmp_type_range]) icmp_code = forms.IntegerField( label=_("Code"), required=False, help_text=_("Enter a value for ICMP code " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Code') }), validators=[utils_validators.validate_icmp_code_range]) remote = forms.ChoiceField( label=_('Remote'), choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))], help_text=_('To specify an allowed IP ' 'range, select "CIDR". ' 'To allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'remote' })) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24, or " "2001:db8::/128)"), version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'remote', 'data-remote-cidr': _('CIDR') })) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'remote', 'data-remote-sg': _('Security Group') })) # When cidr is used ethertype is determined from IP version of cidr. # When source group, ethertype needs to be specified explicitly. ethertype = forms.ChoiceField(label=_('Ether Type'), required=False, choices=[('IPv4', _('IPv4')), ('IPv6', _('IPv6'))], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-slug': 'ethertype', 'data-switch-on': 'remote', 'data-remote-sg': _('Ether Type') })) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # remote group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices # TODO(amotoki): settings.SECURITY_GROUP_RULES may contains 'backend' # parameter. If 'backend' is used, error message should be emitted. backend = 'neutron' rules_dict = settings.SECURITY_GROUP_RULES common_rules = [(k, rules_dict[k]['name']) for k in rules_dict if rules_dict[k].get('backend', backend) == backend] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule')), ('custom', _('Other Protocol'))] self.fields['rule_menu'].choices = custom_rules + common_rules self.rules = rules_dict self.fields['direction'].choices = [('ingress', _('Ingress')), ('egress', _('Egress'))] self.fields['ip_protocol'].help_text = _( "Enter an integer value between -1 and 255 " "(-1 means wild card).") self.fields['port_or_range'].choices = [ ('port', _('Port')), ('range', _('Port Range')), ('all', _('All ports')), ] if not setting_utils.get_dict_config('OPENSTACK_NEUTRON_NETWORK', 'enable_ipv6'): self.fields['cidr'].version = forms.IPv4 self.fields['ethertype'].widget = forms.TextInput( attrs={'readonly': 'readonly'}) self.fields['ethertype'].initial = 'IPv4' try: is_desc_supported = api.neutron.is_extension_supported( self.request, 'standard-attr-description') except Exception: exceptions.handle( self.request, _('Failed to check if description field is supported.')) is_desc_supported = False if not is_desc_supported: del self.fields['description'] def _update_and_pop_error(self, cleaned_data, key, value): cleaned_data[key] = value self.errors.pop(key, None) def _clean_rule_icmp(self, cleaned_data, rule_menu): icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) if icmp_type == -1 and icmp_code != -1: msg = _('ICMP code is provided but ICMP type is missing.') raise ValidationError(msg) if self.errors.get('icmp_type'): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if self.errors.get('icmp_code'): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) self._update_and_pop_error(cleaned_data, 'from_port', icmp_type) self._update_and_pop_error(cleaned_data, 'to_port', icmp_code) self._update_and_pop_error(cleaned_data, 'port', None) def _clean_rule_tcp_udp(self, cleaned_data, rule_menu): port_or_range = cleaned_data.get("port_or_range") from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if port_or_range == 'all': self._update_and_pop_error(cleaned_data, 'port', None) self._update_and_pop_error(cleaned_data, 'from_port', None) self._update_and_pop_error(cleaned_data, 'to_port', None) elif port_or_range == "port": self._update_and_pop_error(cleaned_data, 'from_port', port) self._update_and_pop_error(cleaned_data, 'to_port', port) if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: self._update_and_pop_error(cleaned_data, 'port', None) if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) def _clean_rule_custom(self, cleaned_data, rule_menu): # custom IP protocol rule so we need to fill unused fields so # the validation works unused_fields = [ 'icmp_code', 'icmp_type', 'from_port', 'to_port', 'port' ] for unused_field in unused_fields: self._update_and_pop_error(cleaned_data, unused_field, None) def _apply_rule_menu(self, cleaned_data, rule_menu): cleaned_data['ip_protocol'] = self.rules[rule_menu]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[rule_menu]['from_port']) cleaned_data['to_port'] = int(self.rules[rule_menu]['to_port']) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']: direction = self.rules[rule_menu].get('direction') cleaned_data['direction'] = direction def _clean_rule_menu(self, cleaned_data): rule_menu = cleaned_data.get('rule_menu') if rule_menu == 'icmp': self._clean_rule_icmp(cleaned_data, rule_menu) elif rule_menu in ('tcp', 'udp'): self._clean_rule_tcp_udp(cleaned_data, rule_menu) elif rule_menu == 'custom': self._clean_rule_custom(cleaned_data, rule_menu) else: self._apply_rule_menu(cleaned_data, rule_menu) def _adjust_ip_protocol_of_icmp(self, data): # Note that this needs to be called after IPv4/IPv6 is determined. try: ip_protocol = int(data['ip_protocol']) except ValueError: # string representation of IP protocol ip_protocol = data['ip_protocol'] is_ipv6 = data['ethertype'] == 'IPv6' if isinstance(ip_protocol, int): # When IP protocol number is specified, we assume a user # knows more detail on IP protocol number, # so a warning message on a mismatch between IP proto number # and IP version is displayed. if is_ipv6 and ip_protocol == 1: msg = _('58 (ipv6-icmp) should be specified for IPv6 ' 'instead of 1.') self._errors['ip_protocol'] = self.error_class([msg]) elif not is_ipv6 and ip_protocol == 58: msg = _('1 (icmp) should be specified for IPv4 ' 'instead of 58.') self._errors['ip_protocol'] = self.error_class([msg]) else: # ICMPv6 uses different an IP protocol name and number. # To allow 'icmp' for both IPv4 and IPv6 in the form, # we need to replace 'icmp' with 'ipv6-icmp' based on IP version. if is_ipv6 and ip_protocol == 'icmp': data['ip_protocol'] = 'ipv6-icmp' def clean(self): cleaned_data = super(AddRule, self).clean() self._clean_rule_menu(cleaned_data) # NOTE(amotoki): There are two cases where cleaned_data['direction'] # is empty: (1) Nova Security Group is used. Since "direction" is # HiddenInput, direction field exists but its value is ''. # (2) Template except all_* is used. In this case, the default value # is None. To make sure 'direction' field has 'ingress' or 'egress', # fill this field here if it is not specified. if not cleaned_data['direction']: cleaned_data['direction'] = 'ingress' remote = cleaned_data.get("remote") if remote == "cidr": self._update_and_pop_error(cleaned_data, 'security_group', None) else: self._update_and_pop_error(cleaned_data, 'cidr', None) # If cleaned_data does not contain a non-empty value, IPField already # has validated it, so skip the further validation for cidr. # In addition cleaned_data['cidr'] is None means source_group is used. if 'cidr' in cleaned_data and cleaned_data['cidr'] is not None: cidr = cleaned_data['cidr'] if not cidr: msg = _('CIDR must be specified.') self._errors['cidr'] = self.error_class([msg]) else: # If cidr is specified, ethertype is determined from IP address # version. It is used only when Neutron is enabled. ip_ver = netaddr.IPNetwork(cidr).version cleaned_data['ethertype'] = 'IPv6' if ip_ver == 6 else 'IPv4' self._adjust_ip_protocol_of_icmp(cleaned_data) return cleaned_data def handle(self, request, data): redirect = reverse("horizon:project:security_groups:detail", args=[data['id']]) params = {} if 'description' in data: params['description'] = data['description'] try: rule = api.neutron.security_group_rule_create( request, filters.get_int_or_uuid(data['id']), data['direction'], data['ethertype'], data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group'], **params) messages.success(request, _('Successfully added rule: %s') % rule) return rule except exceptions.Conflict as error: exceptions.handle(request, error, redirect=redirect) except Exception: exceptions.handle(request, _('Unable to add rule to security group.'), redirect=redirect)
class UpdateUserForm(BaseUserForm): # 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()) id = forms.CharField(label=_("ID"), widget=forms.HiddenInput) name = forms.CharField(label=_("User Name")) email = forms.EmailField( label=_("Email"), required=False) password = forms.RegexField( label=_("Password"), widget=forms.PasswordInput(render_value=False), regex=validators.password_validator(), required=False, error_messages={'invalid': validators.password_validator_msg()}) confirm_password = forms.CharField( label=_("Confirm Password"), widget=forms.PasswordInput(render_value=False), required=False) project = forms.ChoiceField(label=_("Primary Project"), required=PROJECT_REQUIRED) no_autocomplete = True def __init__(self, request, *args, **kwargs): super(UpdateUserForm, self).__init__(request, *args, **kwargs) if api.keystone.keystone_can_edit_user() is False: for field in ('name', 'email', 'password', 'confirm_password'): self.fields.pop(field) # 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 # We have to protect the entire "data" dict because it contains the # password and confirm_password strings. @sensitive_variables('data', 'password') def handle(self, request, data): user = data.pop('id') # Throw away the password confirmation, we're done with it. data.pop('confirm_password', None) data.pop('domain_id') data.pop('domain_name') try: if "email" in data: data['email'] = data['email'] or None response = api.keystone.user_update(request, user, **data) messages.success(request, _('User has been updated successfully.')) except Exception: response = exceptions.handle(request, ignore=True) messages.error(request, _('Unable to update the user.')) if isinstance(response, http.HttpResponse): return response else: return True
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField( label=_('Rule'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'rule_menu' })) # "direction" field is enabled only when custom mode. # It is because most common rules in local_settings.py is meaningful # when its direction is 'ingress'. direction = forms.ChoiceField( label=_('Direction'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Direction'), 'data-rule_menu-udp': _('Direction'), 'data-rule_menu-icmp': _('Direction'), 'data-rule_menu-custom': _('Direction'), 'data-rule_menu-all_tcp': _('Direction'), 'data-rule_menu-all_udp': _('Direction'), 'data-rule_menu-all_icmp': _('Direction'), })) ip_protocol = forms.IntegerField( label=_('IP Protocol'), required=False, help_text=_("Enter an integer value between 0 and 255 " "(or -1 which means wildcard)."), validators=[utils_validators.validate_ip_protocol], widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-custom': _('IP Protocol') })) port_or_range = forms.ChoiceField( label=_('Open Port'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.Select( attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Open Port'), 'data-rule_menu-udp': _('Open Port') })) port = forms.IntegerField( label=_("Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-port': _('Port') }), validators=[utils_validators.validate_port_range]) from_port = forms.IntegerField( label=_("From Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('From Port') }), validators=[utils_validators.validate_port_range]) to_port = forms.IntegerField( label=_("To Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('To Port') }), validators=[utils_validators.validate_port_range]) icmp_type = forms.IntegerField( label=_("Type"), required=False, help_text=_("Enter a value for ICMP type " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Type') }), validators=[utils_validators.validate_port_range]) icmp_code = forms.IntegerField( label=_("Code"), required=False, help_text=_("Enter a value for ICMP code " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Code') }), validators=[utils_validators.validate_port_range]) remote = forms.ChoiceField(label=_('Remote'), choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))], help_text=_('To specify an allowed IP ' 'range, select "CIDR". ' 'To allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'remote' })) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-cidr': _('CIDR') })) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-sg': _('Security ' 'Group') })) # When cidr is used ethertype is determined from IP version of cidr. # When source group, ethertype needs to be specified explicitly. ethertype = forms.ChoiceField(label=_('Ether Type'), required=False, choices=[('IPv4', _('IPv4')), ('IPv6', _('IPv6'))], widget=forms.Select( attrs={ 'class': 'switched', 'data-slug': 'ethertype', 'data-switch-on': 'remote', 'data-remote-sg': _('Ether Type') })) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # remote group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices backend = api.network.security_group_backend(self.request) rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', []) common_rules = [(k, rules_dict[k]['name']) for k in rules_dict if rules_dict[k].get('backend', backend) == backend] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule'))] if backend == 'neutron': custom_rules.append(('custom', _('Other Protocol'))) self.fields['rule_menu'].choices = custom_rules + common_rules self.rules = rules_dict if backend == 'neutron': self.fields['direction'].choices = [('ingress', _('Ingress')), ('egress', _('Egress'))] else: # direction and ethertype are not supported in Nova secgroup. self.fields['direction'].widget = forms.HiddenInput() self.fields['ethertype'].widget = forms.HiddenInput() # ip_protocol field is to specify arbitrary protocol number # and it is available only for neutron security group. self.fields['ip_protocol'].widget = forms.HiddenInput() def _update_and_pop_error(self, cleaned_data, key, value): cleaned_data[key] = value self.errors.pop(key, None) def _clean_rule_icmp(self, cleaned_data, rule_menu): icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) if icmp_type is None: msg = _('The ICMP type is invalid.') raise ValidationError(msg) if icmp_code is None: msg = _('The ICMP code is invalid.') raise ValidationError(msg) if icmp_type not in range(-1, 256): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if icmp_code not in range(-1, 256): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) self._update_and_pop_error(cleaned_data, 'from_port', icmp_type) self._update_and_pop_error(cleaned_data, 'to_port', icmp_code) self._update_and_pop_error(cleaned_data, 'port', None) def _clean_rule_tcp_udp(self, cleaned_data, rule_menu): port_or_range = cleaned_data.get("port_or_range") from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if port_or_range == "port": self._update_and_pop_error(cleaned_data, 'from_port', port) self._update_and_pop_error(cleaned_data, 'to_port', port) if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: self._update_and_pop_error(cleaned_data, 'port', None) if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) def _apply_rule_menu(self, cleaned_data, rule_menu): cleaned_data['ip_protocol'] = self.rules[rule_menu]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[rule_menu]['from_port']) cleaned_data['to_port'] = int(self.rules[rule_menu]['to_port']) if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']: direction = self.rules[rule_menu].get('direction') cleaned_data['direction'] = direction def _clean_rule_menu(self, cleaned_data): rule_menu = cleaned_data.get('rule_menu') if rule_menu == 'icmp': self._clean_rule_icmp(cleaned_data, rule_menu) elif rule_menu == 'tcp' or rule_menu == 'udp': self._clean_rule_tcp_udp(cleaned_data, rule_menu) elif rule_menu == 'custom': pass else: self._apply_rule_menu(cleaned_data, rule_menu) def clean(self): cleaned_data = super(AddRule, self).clean() self._clean_rule_menu(cleaned_data) # NOTE(amotoki): There are two cases where cleaned_data['direction'] # is empty: (1) Nova Security Group is used. Since "direction" is # HiddenInput, direction field exists but its value is ''. # (2) Template except all_* is used. In this case, the default value # is None. To make sure 'direction' field has 'ingress' or 'egress', # fill this field here if it is not specified. if not cleaned_data['direction']: cleaned_data['direction'] = 'ingress' remote = cleaned_data.get("remote") if remote == "cidr": self._update_and_pop_error(cleaned_data, 'security_group', None) else: self._update_and_pop_error(cleaned_data, 'cidr', None) # If cleaned_data does not contain a non-empty value, IPField already # has validated it, so skip the further validation for cidr. # In addition cleaned_data['cidr'] is None means source_group is used. if 'cidr' in cleaned_data and cleaned_data['cidr'] is not None: cidr = cleaned_data['cidr'] if not cidr: msg = _('CIDR must be specified.') self._errors['cidr'] = self.error_class([msg]) else: # If cidr is specified, ethertype is determined from IP address # version. It is used only when Neutron is enabled. ip_ver = netaddr.IPNetwork(cidr).version cleaned_data['ethertype'] = 'IPv6' if ip_ver == 6 else 'IPv4' return cleaned_data def handle(self, request, data): try: rule = api.network.security_group_rule_create( request, filters.get_int_or_uuid(data['id']), data['direction'], data['ethertype'], data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group']) messages.success( request, _('Successfully added rule: %s') % six.text_type(rule)) return rule except Exception: redirect = reverse( "horizon:project:access_and_security:" "security_groups:detail", args=[data['id']]) exceptions.handle(request, _('Unable to add rule to security group.'), redirect=redirect)
class CreateUserForm(BaseUserForm): # 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")) email = forms.EmailField( label=_("Email"), required=False) password = forms.RegexField( label=_("Password"), widget=forms.PasswordInput(render_value=False), regex=validators.password_validator(), error_messages={'invalid': validators.password_validator_msg()}) confirm_password = forms.CharField( label=_("Confirm Password"), widget=forms.PasswordInput(render_value=False)) project = forms.DynamicChoiceField(label=_("Primary Project"), required=PROJECT_REQUIRED, add_item_link=ADD_PROJECT_URL) role_id = forms.ChoiceField(label=_("Role"), required=PROJECT_REQUIRED) no_autocomplete = True def __init__(self, *args, **kwargs): roles = kwargs.pop('roles') super(CreateUserForm, self).__init__(*args, **kwargs) 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 # 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) try: LOG.info('Creating user with name "%s"' % data['name']) if "email" in data: data['email'] = data['email'] or None new_user = api.keystone.user_create(request, name=data['name'], email=data['email'], password=data['password'], project=data['project'], enabled=True, domain=domain.id) 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 Exception: exceptions.handle(request, _('Unable to create user.'))
class TemplateForm(forms.SelfHandlingForm): class Meta: name = _('Select Template') help_text = _('From here you can select a template to launch ' 'a stack.') template_source = forms.ChoiceField( label=_('Template Source'), choices=[('url', _('URL')), ('file', _('File')), ('raw', _('Direct Input'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) template_upload = forms.FileField( label=_('Template File'), help_text=_('A local template to upload.'), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Template File') }), required=False) template_url = forms.URLField( label=_('Template URL'), help_text=_('An external (HTTP) URL to load the template from.'), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Template URL') }), required=False) template_data = forms.CharField( label=_('Template Data'), help_text=_('The raw contents of the template.'), widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-raw': _('Template Data') }), required=False) def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(TemplateForm, self).__init__(*args, **kwargs) def clean(self): cleaned = super(TemplateForm, self).clean() template_url = cleaned.get('template_url') template_data = cleaned.get('template_data') files = self.request.FILES has_upload = 'template_upload' in files # Uploaded file handler if has_upload and not template_url: log_template_name = self.request.FILES['template_upload'].name LOG.info('got upload %s' % log_template_name) tpl = self.request.FILES['template_upload'].read() if tpl.startswith('{'): try: json.loads(tpl) except Exception as e: msg = _('There was a problem parsing the template: %s') % e raise forms.ValidationError(msg) cleaned['template_data'] = tpl # URL handler elif template_url and (has_upload or template_data): msg = _('Please specify a template using only one source method.') raise forms.ValidationError(msg) # Check for raw template input elif not template_url and not template_data: msg = _('You must specify a template via one of the ' 'available sources.') raise forms.ValidationError(msg) # Validate the template and get back the params. kwargs = {} if cleaned['template_data']: kwargs['template'] = cleaned['template_data'] else: kwargs['template_url'] = cleaned['template_url'] try: validated = api.heat.template_validate(self.request, **kwargs) cleaned['template_validate'] = validated except Exception as e: msg = exception_to_validation_msg(e) if not msg: msg = _('An unknown problem occurred validating the template.') LOG.exception(msg) raise forms.ValidationError(msg) return cleaned def handle(self, request, data): kwargs = { 'parameters': data['template_validate'], 'template_data': data['template_data'], 'template_url': data['template_url'] } # NOTE (gabriel): This is a bit of a hack, essentially rewriting this # request so that we can chain it as an input to the next view... # but hey, it totally works. request.method = 'GET' return self.next_view.as_view()(request, **kwargs)
class AddInterface(forms.SelfHandlingForm): subnet_id = forms.ChoiceField(label=_("Subnet")) ip_address = forms.IPField(label=_("IP Address (optional)"), required=False, initial="", help_text=_( "Specify an IP address for the interface " "created (e.g. 192.168.0.254)."), version=forms.IPv4 | forms.IPv6, mask=False) router_name = forms.CharField( label=_("Router Name"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) router_id = forms.CharField( label=_("Router ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) failure_url = 'horizon:project:routers:detail' def __init__(self, request, *args, **kwargs): super(AddInterface, self).__init__(request, *args, **kwargs) c = self.populate_subnet_id_choices(request) self.fields['subnet_id'].choices = c def populate_subnet_id_choices(self, request): tenant_id = self.request.user.tenant_id networks = [] router_subnet_ids = [] router_id = request.REQUEST.get('router_id', self.initial.get('router_id')) try: networks = api.neutron.network_list_for_tenant(request, tenant_id) if router_id: ports = api.neutron.port_list(request, device_id=router_id) router_subnet_ids = [ fixed_ip["subnet_id"] for port in ports for fixed_ip in port.fixed_ips ] except Exception as e: msg = _('Failed to get network list %s') % e LOG.info(msg) messages.error(request, msg) if router_id: redirect = reverse(self.failure_url, args=[router_id]) else: redirect = reverse('horizon:project:routers:index') exceptions.handle(request, msg, redirect=redirect) return choices = [] for n in networks: net_name = n.name + ': ' if n.name else '' choices += [(subnet.id, '%s%s (%s)' % (net_name, subnet.cidr, subnet.name or subnet.id)) for subnet in n['subnets'] if subnet.id not in router_subnet_ids] if choices: choices.insert(0, ("", _("Select Subnet"))) else: choices.insert(0, ("", _("No subnets available"))) return choices def handle(self, request, data): if data['ip_address']: port = self._add_interface_by_port(request, data) else: port = self._add_interface_by_subnet(request, data) msg = _('Interface added') if port: msg += ' ' + port.fixed_ips[0]['ip_address'] LOG.debug(msg) messages.success(request, msg) return True def _add_interface_by_subnet(self, request, data): router_id = data['router_id'] try: router_inf = api.neutron.router_add_interface( request, router_id, subnet_id=data['subnet_id']) except Exception as e: self._handle_error(request, router_id, e) try: port = api.neutron.port_get(request, router_inf['port_id']) except Exception: # Ignore an error when port_get() since it is just # to get an IP address for the interface. port = None return port def _add_interface_by_port(self, request, data): router_id = data['router_id'] subnet_id = data['subnet_id'] try: subnet = api.neutron.subnet_get(request, subnet_id) except Exception: msg = _('Unable to get subnet "%s"') % subnet_id self._handle_error(request, router_id, msg) try: ip_address = data['ip_address'] body = { 'network_id': subnet.network_id, 'fixed_ips': [{ 'subnet_id': subnet.id, 'ip_address': ip_address }] } port = api.neutron.port_create(request, **body) except Exception as e: self._handle_error(request, router_id, e) try: api.neutron.router_add_interface(request, router_id, port_id=port.id) except Exception as e: self._delete_port(request, port) self._handle_error(request, router_id, e) return port def _handle_error(self, request, router_id, reason): msg = _('Failed to add_interface: %s') % reason LOG.info(msg) redirect = reverse(self.failure_url, args=[router_id]) exceptions.handle(request, msg, redirect=redirect) def _delete_port(self, request, port): try: api.neutron.port_delete(request, port.id) except Exception: msg = _('Failed to delete port %s') % port.id LOG.info(msg) exceptions.handle(request, msg)
class UpdateIKEPolicy(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ikepolicy_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(required=False, max_length=80, label=_("Description")) # Currently this field has only one choice, so mark it as readonly. auth_algorithm = forms.ChoiceField( label=_("Authorization algorithm"), choices=[('sha1', _('sha1'))], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) encryption_algorithm = forms.ChoiceField(label=_("Encryption algorithm"), choices=[ ('3des', _('3des')), ('aes-128', _('aes-128')), ('aes-192', _('aes-192')), ('aes-256', _('aes-256')) ], required=False) ike_version = forms.ChoiceField(label=_("IKE version"), choices=[('v1', _('v1')), ('v2', _('v2'))], required=False) # Currently this field has only one choice, so mark it as readonly. lifetime_units = forms.ChoiceField( label=_("Lifetime units for IKE keys"), choices=[('seconds', _('seconds'))], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) lifetime_value = forms.IntegerField( min_value=60, label=_("Lifetime value for IKE keys"), help_text=_("Equal to or greater than 60"), required=False) pfs = forms.ChoiceField(label=_("Perfect Forward Secrecy"), choices=[('group2', _('group2')), ('group5', _('group5')), ('group14', _('group14'))], required=False) # Currently this field has only one choice, so mark it as readonly. phase1_negotiation_mode = forms.ChoiceField( label=_("IKE Phase1 negotiation mode"), choices=[('main', 'main')], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) failure_url = 'horizon:project:vpn:index' def handle(self, request, context): try: data = { 'ikepolicy': { 'name': context['name'], 'description': context['description'], 'auth_algorithm': context['auth_algorithm'], 'encryption_algorithm': context['encryption_algorithm'], 'ike_version': context['ike_version'], 'lifetime': { 'units': context['lifetime_units'], 'value': context['lifetime_value'] }, 'pfs': context['pfs'], 'phase1_negotiation_mode': context['phase1_negotiation_mode'], } } ikepolicy = api.vpn.ikepolicy_update(request, context['ikepolicy_id'], **data) msg = (_('IKE Policy %s was successfully updated.') % context['name']) messages.success(request, msg) return ikepolicy except Exception as e: LOG.info('Failed to update IKE Policy %(id)s: %(exc)s', { 'id': context['ikepolicy_id'], 'exc': e }) msg = _('Failed to update IKE Policy %s') % context['name'] redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Router Name")) admin_state_up = forms.ChoiceField(label=_("Admin State"), choices=[(True, _('UP')), (False, _('DOWN'))], required=False) external_network = forms.ChoiceField(label=_("External Network"), required=True) mode = forms.ChoiceField(label=_("Router Type")) ha = forms.ChoiceField(label=_("High Availability Mode")) 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'] def _get_network_list(self, request): search_opts = {'router:external': True} try: networks = api.neutron.network_list(request, **search_opts) except Exception: msg = _('Failed to get network list.') LOG.info(msg) 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']} if 'admin_state_up' in data and data['admin_state_up']: params['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.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: if exc.status_code == 409: msg = _('Quota exceeded for resource router.') else: msg = _('Failed to create router "%s".') % data['name'] LOG.info(msg) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect) return False
class OnBoardMEA(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField( widget=forms.widgets.Textarea(attrs={'rows': 4}), label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('TOSCA Template Source'), required=False, choices=[('file', _('TOSCA Template File')), ('raw', _('Direct Input'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) toscal_file = forms.FileField( label=_("TOSCA Template File"), help_text=_("A local TOSCA template file to upload."), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('TOSCA Template File') }), required=False) direct_input = forms.CharField( label=_('TOSCA YAML'), help_text=_('The YAML formatted contents of a TOSCA template.'), widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-raw': _('TOSCA YAML') }), required=False) def __init__(self, request, *args, **kwargs): super(OnBoardMEA, self).__init__(request, *args, **kwargs) def clean(self): data = super(OnBoardMEA, self).clean() # The key can be missing based on particular upload # conditions. Code defensively for it here... toscal_file = data.get('toscal_file', None) toscal_raw = data.get('direct_input', None) source_type = data.get("source_type") if source_type == "file" and not toscal_file: raise ValidationError(_("No TOSCA template file selected.")) if source_type == "raw" and not toscal_raw: raise ValidationError(_("No direct input specified.")) if toscal_file and not toscal_file.name.endswith(('.yaml', '.csar')): raise ValidationError( _("Only .yaml or .csar file uploads \ are supported")) try: if toscal_file: toscal_str = self.files['toscal_file'].read() else: toscal_str = data['direct_input'] # toscal = yaml.loads(toscal_str) data['tosca'] = toscal_str 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: toscal = data['tosca'] mead_name = data['name'] mead_description = data['description'] tosca_arg = { 'mead': { 'name': mead_name, 'description': mead_description, 'attributes': { 'mead': toscal } } } mead_instance = api.apmec.create_mead(request, tosca_arg) messages.success( request, _('MEA Catalog entry %s has been created.') % mead_instance['mead']['name']) return toscal except Exception as e: msg = _('Unable to create TOSCA. %s') msg %= e.message.split('Failed validating', 1)[0] exceptions.handle(request, message=msg) return False
class AddTaskDetailsAction(workflows.Action): scheduleId = forms.IntegerField(label=_("ScheduleId)"), required=True, min_value=1, max_value=9999999, help_text=_("ScheduleId")) name = forms.CharField(label=_("Name"), required=True, max_length=80, help_text=_("Name")) description = forms.CharField(label=_("Description"), required=True, max_length=120, help_text=_("Description")) hour = forms.IntegerField(label=_("Hour)"), required=True, min_value=1, max_value=12, help_text=_("1-12")) min = forms.IntegerField(label=_("Minute"), required=True, min_value=0, max_value=59, help_text=_("0 - 59")) period = forms.ChoiceField(label=_("Period"), choices=[('AM', 'AM'), ('PM', 'PM')], required=True, help_text=_("AM or PM")) start = forms.DateField( label=_("Start date"), required=False, input_formats=("%Y-%m-%d", ), help_text=_("YYYY-MM-DD"), widget=forms.DateInput(attrs={'data-date-format': 'yyyy-mm-dd'})) end = forms.DateField( label=_("End date"), required=True, input_formats=("%Y-%m-%d", ), help_text=_("YYYY-MM-DD"), widget=forms.DateInput(attrs={'data-date-format': 'yyyy-mm-dd'})) enabled = forms.BooleanField(label=_("Enabled"), required=False, help_text=_("Enabled")) class Meta: name = _("Details") def __init__(self, request, context, *args, **kwargs): self.request = request self.context = context super(AddTaskDetailsAction, self).__init__(request, context, *args, **kwargs)
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Server Group Name")) policy = forms.ChoiceField(label=_("Policy"), required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'policy_ht' })) is_best_effort = forms.BooleanField(label=_("Best Effort"), required=False) group_size = forms.IntegerField( min_value=1, label=_("Max Group Size (Instances)"), required=False, widget=forms.TextInput( attrs={ 'class': 'switchable switched', 'data-switch-on': 'policy_ht', 'data-policy_ht-anti-affinity': 'Max Group Size (Instances)', 'data-policy_ht-affinity': 'Max Group Size (Instances)' })) group_size_ht = forms.IntegerField( label=_("Max Group Size (Instances)"), required=False, widget=forms.TextInput( attrs={ 'readonly': 'readonly', 'class': 'switchable switched', 'data-switch-on': 'policy_ht', 'data-policy_ht-affinity-hyperthread': 'Max Group Size (Instances)' })) def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) self.fields['policy'].choices = [("anti-affinity", "anti-affinity"), ("affinity", "affinity")] def handle(self, request, data): try: project_id = self.request.user.tenant_id policy = data['policy'] policies = [] if policy: policies.append(policy) metadata = {} if data['is_best_effort']: metadata['wrs-sg:best_effort'] = "true" group_size = data['group_size'] group_size_ht = data['group_size_ht'] if group_size: metadata['wrs-sg:group_size'] = str(group_size) elif group_size_ht: metadata['wrs-sg:group_size'] = str(group_size_ht) server_group = nova.server_group_create(request, data['name'], project_id, metadata, policies) return server_group except ValidationError as e: self.api_error(e.messages[0]) return False except Exception: exceptions.handle(request, ignore=True) self.api_error(_("Unable to create server group.")) return False
class UpdateProviderNetworkRange(forms.SelfHandlingForm): failure_url = 'horizon:admin:datanets:datanets:detail' providernet_id = forms.CharField(widget=forms.HiddenInput()) providernet_range_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField( max_length=255, label=_("Name"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(max_length=255, label=_("Description"), required=False) minimum = forms.IntegerField(label=_("Minimum"), min_value=1) maximum = forms.IntegerField(label=_("Maximum"), min_value=1) shared = forms.BooleanField(widget=forms.HiddenInput(), required=False) tenant_id = forms.CharField(widget=forms.HiddenInput(), required=False) # VXLAN specific fields mode_widget = forms.TextInput(attrs={'readonly': 'readonly'}) mode = forms.CharField(label=_("mode"), required=False, widget=mode_widget) group_widget = forms.TextInput(attrs={'readonly': 'readonly'}) group = forms.CharField(max_length=255, label=_("Multicast Group Address"), required=False, widget=group_widget) port_widget = forms.RadioSelect(attrs={'disabled': 'disabled'}) port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')), ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')), ('8472', _('Legacy VXLAN UDP port (8472)'))] port = forms.ChoiceField(label=_("UDP Port"), required=False, widget=port_widget, choices=port_choices) ttl_widget = forms.TextInput(attrs={'readonly': 'readonly'}) ttl = forms.IntegerField(label=_("TTL"), required=False, widget=ttl_widget) def __init__(self, request, *args, **kwargs): super(UpdateProviderNetworkRange, self).__init__(request, *args, **kwargs) initial = kwargs['initial'] if 'mode' not in initial: del self.fields["mode"] if 'group' not in initial or initial.get('mode') == 'static': del self.fields["group"] if 'port' not in initial: del self.fields["port"] if 'ttl' not in initial: del self.fields["ttl"] def handle(self, request, data): try: params = { 'description': data['description'], 'minimum': data['minimum'], 'maximum': data['maximum'] } providernet_range = stx_api.neutron.provider_network_range_modify( request, data['providernet_range_id'], **params) msg = (_('Provider network range %s was successfully updated.') % data['providernet_range_id']) LOG.debug(msg) messages.success(request, msg) return providernet_range except neutron_exceptions.NeutronClientException as e: LOG.info(str(e)) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, str(e), redirect=redirect) except Exception: msg = (_('Failed to update provider network range %s') % data['providernet_range_id']) LOG.info(msg) redirect = reverse(self.failure_url, args=[data['providernet_id']]) exceptions.handle(request, msg, redirect=redirect)
class UpdateHostInfoAction(workflows.Action): host_id = forms.CharField(widget=forms.widgets.HiddenInput) personality = forms.ChoiceField( label=_("Personality"), choices=PERSONALITY_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'personality' })) subfunctions = forms.ChoiceField( label=FIELD_LABEL_PERFORMANCE_PROFILE, choices=PERFORMANCE_CHOICES, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Performance Profile") })) hostname = forms.RegexField( label=_("Host Name"), max_length=255, required=False, regex=r'^[\w\.\-]+$', error_messages={ 'invalid': _('Name may only contain letters,' ' numbers, underscores, ' 'periods and hyphens.') }, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Host Name") })) location = forms.CharField(label=_("Location"), initial='location', required=False, help_text=_("Physical location of Host.")) clock_synchronization = forms.ChoiceField( label=_("Clock Synchronization"), choices=stx_api.sysinv.CLOCK_SYNCHRONIZATION_CHOICES) cpuProfile = forms.ChoiceField(label=_("CPU Profile"), required=False) interfaceProfile = forms.ChoiceField(label=_("Interface Profile"), required=False) diskProfile = forms.ChoiceField(label=_("Storage Profile"), required=False) memoryProfile = forms.ChoiceField(label=_("Memory Profile"), required=False) ttys_dcd = forms.BooleanField( label=_("Serial Console Data Carrier Detect"), required=False, help_text=_("Enable serial line data carrier detection. " "When selected, dropping carrier detect on the serial " "port revoke any active session and a new login " "process is initiated when a new connection is detected.")) class Meta(object): name = _("Host Info") help_text = _( "From here you can update the configuration of the current host.\n" "Note: this will not affect the resources allocated to any" " existing" " instances using this host until the host is rebooted.") def __init__(self, request, *args, **kwargs): super(UpdateHostInfoAction, self).__init__(request, *args, **kwargs) # pesonality cannot be storage if ceph is not configured storage_backend = stx_api.sysinv.get_storage_backend(request) if stx_api.sysinv.STORAGE_BACKEND_CEPH not in storage_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can only be controller or worker. systems = stx_api.sysinv.system_list(request) self.system_mode = systems[0].to_dict().get('system_mode') self.system_type = systems[0].to_dict().get('system_type') if self.system_type == constants.TS_AIO: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # hostname cannot be modified once it is set if self.initial['hostname']: self.fields['hostname'].widget.attrs['readonly'] = 'readonly' self.fields['hostname'].required = False # subfunctions cannot be modified once it is set if self.initial['subfunctions']: self.fields['subfunctions'].widget.attrs['readonly'] = 'readonly' self.fields['subfunctions'].required = False # personality cannot be modified once it is set host_id = self.initial['host_id'] personality = self.initial['personality'] mem_profile_configurable = False cpu_profile_configurable = False if personality and self.system_mode != constants.SYSTEM_MODE_SIMPLEX: self.fields['personality'].widget.attrs['readonly'] = 'readonly' self.fields['personality'].required = False self._personality = personality host = stx_api.sysinv.host_get(self.request, host_id) host.nodes = stx_api.sysinv.host_node_list(self.request, host.uuid) host.cpus = stx_api.sysinv.host_cpu_list(self.request, host.uuid) host.ports = stx_api.sysinv.host_port_list(self.request, host.uuid) host.disks = stx_api.sysinv.host_disk_list(self.request, host.uuid) if 'worker' in host.subfunctions: mem_profile_configurable = True cpu_profile_configurable = True host.memory = stx_api.sysinv.host_memory_list( self.request, host.uuid) else: del self.fields['memoryProfile'] if host.nodes and host.cpus and host.ports: # Populate Available Interface Profile Choices try: avail_interface_profile_list = \ stx_api.sysinv.host_interfaceprofile_list(self.request) interface_profile_tuple_list = [ ('', _("Copy from an available interface profile.")) ] for ip in avail_interface_profile_list: if ifprofile_applicable(request, host, ip): interface_profile_tuple_list.append( (ip.profilename, ip.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of interface profiles.')) interface_profile_tuple_list = [] self.fields[ 'interfaceProfile'].choices = interface_profile_tuple_list else: self.fields[ 'interfaceProfile'].widget = forms.widgets.HiddenInput() stor_model = sysinv.get_ceph_storage_model(request) if ((personality == 'storage' or (personality == 'controller' and stor_model == sysinv_const.CEPH_CONTROLLER_MODEL) or 'worker' in host._subfunctions) and host.disks): # Populate Available Disk Profile Choices try: disk_profile_tuple_list = [ ('', _("Copy from an available storage profile.")) ] avail_disk_profile_list = \ stx_api.sysinv.host_diskprofile_list(self.request) for dp in avail_disk_profile_list: if diskprofile_applicable(host, dp): disk_profile_tuple_list.append( (dp.profilename, dp.profilename)) except Exception as e: LOG.exception(e) exceptions.handle( self.request, _('Unable to retrieve list of storage profiles.')) disk_profile_tuple_list = [] self.fields['diskProfile'].choices = disk_profile_tuple_list else: self.fields['diskProfile'].widget = forms.widgets.HiddenInput() # Populate Available Cpu Profile Choices if cpu_profile_configurable and host.nodes and host.memory: try: avail_cpu_profile_list = \ stx_api.sysinv.host_cpuprofile_list(self.request) host_profile = icpu_utils.HostCpuProfile( host.subfunctions, host.cpus, host.nodes) cpu_profile_tuple_list = [ ('', _("Copy from an available cpu profile.")) ] for ip in avail_cpu_profile_list: nodes = stx_api.sysinv.host_node_list( self.request, ip.uuid) cpu_profile = icpu_utils.CpuProfile(ip.cpus, nodes) if host_profile.profile_applicable(cpu_profile): cpu_profile_tuple_list.append( (ip.profilename, ip.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of cpu profiles.')) cpu_profile_tuple_list = [] self.fields['cpuProfile'].choices = cpu_profile_tuple_list else: self.fields['cpuProfile'].widget = forms.widgets.HiddenInput() if mem_profile_configurable and host.nodes and host.memory: # Populate Available Memory Profile Choices try: avail_memory_profile_list = \ stx_api.sysinv.host_memprofile_list(self.request) memory_profile_tuple_list = [ ('', _("Copy from an available memory profile.")) ] for mp in avail_memory_profile_list: if memoryprofile_applicable(host, host._subfunctions, mp): memory_profile_tuple_list.append( (mp.profilename, mp.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of memory profiles.')) memory_profile_tuple_list = [] self.fields[ 'memoryProfile'].choices = memory_profile_tuple_list else: self.fields['cpuProfile'].widget = forms.widgets.HiddenInput() self.fields['interfaceProfile'].widget = forms.widgets.HiddenInput( ) self.fields['diskProfile'].widget = forms.widgets.HiddenInput() self.fields['memoryProfile'].widget = forms.widgets.HiddenInput() def clean_location(self): try: host_id = self.cleaned_data['host_id'] host = stx_api.sysinv.host_get(self.request, host_id) location = host._location location['locn'] = self.cleaned_data.get('location') return location except Exception: msg = _('Unable to get host data') exceptions.check_message(["Connection", "refused"], msg) raise def clean(self): cleaned_data = super(UpdateHostInfoAction, self).clean() disabled = self.fields['personality'].widget.attrs.get('disabled') if disabled == 'disabled': if self.system_type == constants.TS_AIO: self._personality = 'controller' cleaned_data['personality'] = self._personality if cleaned_data['personality'] == stx_api.sysinv.PERSONALITY_STORAGE: self._subfunctions = stx_api.sysinv.PERSONALITY_STORAGE cleaned_data['subfunctions'] = self._subfunctions elif cleaned_data['personality'] == \ stx_api.sysinv.PERSONALITY_CONTROLLER: if self.system_type == constants.TS_AIO: self._subfunctions = (stx_api.sysinv.PERSONALITY_CONTROLLER + ',' + stx_api.sysinv.PERSONALITY_WORKER) else: self._subfunctions = stx_api.sysinv.PERSONALITY_CONTROLLER cleaned_data['subfunctions'] = self._subfunctions return cleaned_data
class CreateProviderNetworkRange(forms.SelfHandlingForm): providernet_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(max_length=255, label=_("Name"), required=False) description = forms.CharField(max_length=255, label=_("Description"), required=False) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False, widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-hide-on-checked': 'true', 'data-slug': 'is_shared' })) tenant_id = forms.ChoiceField( label=_("Project"), required=False, widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'is_shared' })) minimum = forms.IntegerField(label=_("Minimum"), min_value=1) maximum = forms.IntegerField(label=_("Maximum"), min_value=1) # VXLAN specific fields mode_choices = [('dynamic', _('Multicast VXLAN')), ('static', _('Static VXLAN'))] mode = forms.ChoiceField(label=_("Mode"), initial='dynamic', required=False, choices=mode_choices, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vxlan_mode' })) group_help = (_("Specify the IPv4 or IPv6 multicast address for these " "VXLAN instances")) group = forms.CharField( max_length=255, label=_("Multicast Group Address"), initial="239.0.0.1", required=False, help_text=group_help, widget=forms.TextInput( attrs={ 'class': 'switchable switched', 'data-slug': 'vxlan_group', 'data-switch-on': 'vxlan_mode', 'data-vxlan_mode-dynamic': 'Multicast Group Address' })) port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')), ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')), ('8472', _('Legacy VXLAN UDP port (8472)'))] port = forms.ChoiceField(label=_("UDP Port"), required=True, widget=forms.RadioSelect(), choices=port_choices) ttl = forms.IntegerField( label=_("TTL"), required=False, initial=1, min_value=1, max_value=255, help_text=( _("Specify the time-to-live value for these VXLAN instances"))) def __init__(self, request, *args, **kwargs): super(CreateProviderNetworkRange, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) # noqa pylint: disable=unused-variable for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices initial = kwargs['initial'] if 'providernet_type' in initial: providernet_type = initial['providernet_type'] if providernet_type != "vxlan": del self.fields["mode"] del self.fields["group"] del self.fields["port"] del self.fields["ttl"] def handle(self, request, data): try: params = { 'providernet_id': data['providernet_id'], 'name': data['name'], 'description': data['description'], 'minimum': data['minimum'], 'maximum': data['maximum'], 'shared': data['shared'], 'tenant_id': data['tenant_id'] } if not data['tenant_id']: params['shared'] = True if self.initial['providernet_type'] == "vxlan": params['mode'] = data['mode'] if params['mode'] == 'dynamic': params['group'] = data['group'] params['port'] = int(data['port']) params['ttl'] = int(data['ttl']) providernet_range = stx_api.neutron.provider_network_range_create( request, **params) msg = (_('Provider network range %s was successfully created.') % providernet_range['id']) LOG.debug(msg) messages.success(request, msg) return providernet_range except neutron_exceptions.NeutronClientException as e: LOG.info(str(e)) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, str(e), redirect=redirect) except Exception: msg = _('Failed to create a provider' ' network range for network %s') \ % data['providernet_id'] LOG.info(msg) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateProviderNetworkRange, self).clean() if not cleaned_data["shared"] and not cleaned_data["tenant_id"]: msg = "Project must be specified for non-shared Segmentation Range" raise forms.ValidationError(msg) if cleaned_data["shared"]: cleaned_data["tenant_id"] = ""
class CustomizeAction(workflows.Action): class Meta(object): name = _("Post-Creation") help_text_template = ("project/instances/" "_launch_customize_help.html") source_choices = [('', _('Select Script Source')), ('raw', _('Direct Input')), ('file', _('File'))] attributes = {'class': 'switchable', 'data-slug': 'scriptsource'} script_source = forms.ChoiceField( label=_('Customization Script Source'), choices=source_choices, widget=forms.ThemableSelectWidget(attrs=attributes), required=False) script_help = _("A script or set of commands to be executed after the " "instance has been built (max 16kb).") script_upload = forms.FileField( label=_('Script File'), help_text=script_help, widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'scriptsource', 'data-scriptsource-file': _('Script File') }), required=False) script_data = forms.CharField( label=_('Script Data'), help_text=script_help, widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'scriptsource', 'data-scriptsource-raw': _('Script Data') }), required=False) def __init__(self, *args): super(CustomizeAction, self).__init__(*args) def clean(self): cleaned = super(CustomizeAction, self).clean() files = self.request.FILES script = self.clean_uploaded_files('script', files) if script is not None: cleaned['script_data'] = script return cleaned def clean_uploaded_files(self, prefix, files): upload_str = prefix + "_upload" has_upload = upload_str in files if has_upload: upload_file = files[upload_str] log_script_name = upload_file.name LOG.info('got upload %s' % log_script_name) if upload_file._size > 16 * units.Ki: # 16kb msg = _('File exceeds maximum size (16kb)') raise forms.ValidationError(msg) else: script = upload_file.read() if script != "": try: normalize_newlines(script) except Exception as e: msg = _('There was a problem parsing the' ' %(prefix)s: %(error)s') msg = msg % { 'prefix': prefix, 'error': six.text_type(e) } raise forms.ValidationError(msg) return script else: return None
def __init__(self, *args, **kwargs): super(FloatingIpAssociate, self).__init__(*args, **kwargs) instancelist = kwargs.get('initial', {}).get('instances', []) self.fields['instance_id'] = forms.ChoiceField(choices=instancelist, label=_("Instance"))