class GeneralConfigAction(workflows.Action): job_name = forms.CharField(label=_("Name")) job_type = forms.ChoiceField(label=_("Job Type"), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'jobtype' })) main_binary = forms.DynamicChoiceField( label=_("Choose a main binary"), required=False, help_text=_("Choose the binary which " "should be used in this Job."), add_item_link=JOB_BINARY_CREATE_URL, widget=fields.DynamicSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'jobtype', 'data-jobtype-pig': _("Choose a main binary"), 'data-jobtype-hive': _("Choose a main binary"), 'data-jobtype-shell': _("Choose a shell script"), 'data-jobtype-spark': _("Choose a main binary"), 'data-jobtype-storm': _("Choose a main binary"), 'data-jobtype-mapreduce.streaming': _("Choose a main binary") })) job_description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 4})) def __init__(self, request, context, *args, **kwargs): super(GeneralConfigAction, self).__init__(request, context, *args, **kwargs) if request.REQUEST.get("guide_job_type"): self.fields["job_type"].initial = ( request.REQUEST.get("guide_job_type").lower()) def populate_job_type_choices(self, request, context): choices = [] choices_list = saharaclient.job_types_list(request) for choice in choices_list: job_type = choice.name.lower() if job_type in helpers.JOB_TYPE_MAP: choices.append((job_type, helpers.JOB_TYPE_MAP[job_type][0])) return choices def populate_main_binary_choices(self, request, context): job_binaries = saharaclient.job_binary_list(request) choices = [(job_binary.id, job_binary.name) for job_binary in job_binaries] choices.insert(0, ('', _("-- not selected --"))) return choices def clean(self): cleaned_data = super(workflows.Action, self).clean() job_type = cleaned_data.get("job_type", "") if job_type in ["Java", "MapReduce"]: cleaned_data['main_binary'] = None return cleaned_data class Meta(object): name = _("Create Job Template") help_text_template = ( "project/data_processing.jobs/_create_job_help.html")
class CreatePartition(forms.SelfHandlingForm): host_id = forms.CharField(label=_("host_id"), initial='host_id', widget=forms.widgets.HiddenInput) ihost_uuid = forms.CharField(label=_("ihost_uuid"), initial='ihost_uuid', widget=forms.widgets.HiddenInput) idisk_uuid = forms.CharField(label=_("idisk_uuid"), initial='idisk_uuid', widget=forms.widgets.HiddenInput) hostname = forms.CharField( label=_("Hostname"), initial='hostname', widget=forms.TextInput(attrs={'readonly': 'readonly'})) disks = forms.ChoiceField(label=_("Disks"), required=True, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'disk' }), help_text=_("Disk to create partition on.")) size_mib = forms.IntegerField( label=_("Partition Size MiB"), required=True, initial=1024, widget=forms.TextInput(attrs={'data-slug': 'size_mib'}), help_text=_("Size in MiB for the new " "partition.")) type_guid = forms.CharField( label=_("Partition Type"), initial='LVM Physical Volume', widget=forms.TextInput(attrs={'readonly': 'readonly'})) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(CreatePartition, self).__init__(*args, **kwargs) # Populate disk choices. host_uuid = kwargs['initial']['ihost_uuid'] avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid) disk_tuple_list = [] for d in avail_disk_list: disk_model = d.get_model_num() if disk_model is not None and "floppy" in disk_model.lower(): continue if d.available_mib == 0: continue disk_tuple_list.append( (d.uuid, "%s (path: %s size:%s available_mib:%s type: %s)" % (d.device_node, d.device_path, str( d.size_mib), str(d.available_mib), d.device_type))) self.fields['disks'].choices = disk_tuple_list def clean(self): cleaned_data = super(CreatePartition, self).clean() return cleaned_data def handle(self, request, data): host_id = data['host_id'] disks = data['disks'][:] data['idisk_uuid'] = disks try: del data['host_id'] del data['disks'] del data['hostname'] data['type_guid'] = sysinv.USER_PARTITION_PHYS_VOL # The REST API takes care of creating the partition. partition = api.sysinv.host_disk_partition_create(request, **data) msg = _('Partition was successfully created.') LOG.debug(msg) messages.success(request, msg) return partition except exc.ClientException as ce: msg = _('Failed to create partition.') LOG.info(msg) LOG.error(ce) # Allow REST API error message to appear on UI messages.error(request, ce) # Redirect to host details pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to create partition.') LOG.info(msg) LOG.error(e) # If not a REST API error, throw default. redirect = reverse(self.failure_url, args=[host_id]) return exceptions.handle(request, message=e, redirect=redirect)
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.Select(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': e} raise forms.ValidationError(msg) return script else: return None
class AddStorageVolume(forms.SelfHandlingForm): # Only allowed to choose 'osd' FUNCTION_CHOICES = ( ('osd', _("osd")), ('journal', _("journal")), # ('monitor', _("monitor")), ) host_id = forms.CharField(label=_("host_id"), initial='host_id', widget=forms.widgets.HiddenInput) ihost_uuid = forms.CharField(label=_("ihost_uuid"), initial='ihost_uuid', widget=forms.widgets.HiddenInput) idisk_uuid = forms.CharField(label=_("idisk_uuid"), initial='idisk_uuid', widget=forms.widgets.HiddenInput) tier_uuid = forms.CharField(label=_("tier_uuid"), initial='tier_uuid', widget=forms.widgets.HiddenInput) hostname = forms.CharField( label=_("Hostname"), initial='hostname', widget=forms.TextInput(attrs={'readonly': 'readonly'})) function = forms.ChoiceField( label=_("Function"), required=False, choices=FUNCTION_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'function' })) disks = forms.ChoiceField(label=_("Disks"), required=True, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'disk' }), help_text=_("Assign disk to a storage volume.")) journal_locations = forms.ChoiceField( label=_("Journal"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'function', 'data-function-osd': _("Journal") }), help_text=_("Assign disk to a " "journal storage " "volume.")) journal_size_mib = forms.CharField( label=_("Journal Size MiB"), required=False, initial=sysinv.JOURNAL_DEFAULT_SIZE, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'function', 'data-function-osd': _("Journal Size MiB") }), help_text=_("Journal's size for the" "current OSD.")) tiers = forms.ChoiceField(label=_("Storage Tier"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'function', 'data-function-osd': _("Storage Tier") }), help_text=_("Assign OSD to a storage tier.")) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(AddStorageVolume, self).__init__(*args, **kwargs) # Populate available disk choices this_stor_uuid = 0 host_uuid = kwargs['initial']['ihost_uuid'] ihost = api.sysinv.host_get(self.request, host_uuid) ceph_caching = ((ihost.capabilities.get('pers_subtype') == sysinv.PERSONALITY_SUBTYPE_CEPH_CACHING)) avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid) disk_tuple_list = [] for d in avail_disk_list: if d.istor_uuid and d.istor_uuid != this_stor_uuid: continue is_rootfs_device = \ (('stor_function' in d.capabilities) and (d.capabilities['stor_function'] == 'rootfs')) if is_rootfs_device: continue disk_model = d.get_model_num() if disk_model is not None and "floppy" in disk_model.lower(): continue if (ceph_caching and d.device_type != 'SSD' and d.device_type != 'NVME'): continue disk_tuple_list.append( (d.uuid, "%s (path: %s size:%s model:%s type: %s)" % (d.device_node, d.device_path, str( d.size_mib), disk_model, d.device_type))) # Get the cluster cluster_list = api.sysinv.cluster_list(self.request) cluster_uuid = cluster_list[0].uuid # Populate the available tiers for OSD assignment avail_tier_list = api.sysinv.storage_tier_list(self.request, cluster_uuid) tier_tuple_list = [(t.uuid, t.name) for t in avail_tier_list] # Populate available journal choices. If no journal is available, # then the journal is collocated. if ceph_caching: avail_journal_list = [] else: avail_journal_list = api.sysinv.host_stor_get_by_function( self.request, host_uuid, 'journal') journal_tuple_list = [] if avail_journal_list: for j in avail_journal_list: journal_tuple_list.append((j.uuid, "%s " % j.uuid)) else: journal_tuple_list.append((None, "Collocated with OSD")) self.fields['journal_size_mib'].widget.attrs['disabled'] = \ 'disabled' if ceph_caching: self.fields['function'].choices = ( AddStorageVolume.FUNCTION_CHOICES[:1]) self.fields['disks'].choices = disk_tuple_list self.fields['journal_locations'].choices = journal_tuple_list self.fields['tiers'].choices = tier_tuple_list def clean(self): cleaned_data = super(AddStorageVolume, self).clean() # host_id = cleaned_data.get('host_id') # ihost_uuid = cleaned_data.get('ihost_uuid') # disks = cleaned_data.get('disks') return cleaned_data def handle(self, request, data): host_id = data['host_id'] # host_uuid = data['ihost_uuid'] disks = data['disks'][:] # copy tiers = data['tiers'][:] # copy # GUI only allows one disk to be picked data['idisk_uuid'] = disks # GUI only allows one tier to be picked data['tier_uuid'] = tiers # Obtain journal information. journal = data['journal_locations'][:] if journal: data['journal_location'] = journal else: data['journal_location'] = None data['journal_size_mib'] = sysinv.JOURNAL_DEFAULT_SIZE try: del data['host_id'] del data['disks'] del data['tiers'] del data['hostname'] del data['journal_locations'] # The REST API takes care of creating the stor # and updating disk.foristorid stor = api.sysinv.host_stor_create(request, **data) msg = _('Storage volume was successfully created.') LOG.debug(msg) messages.success(request, msg) return stor except exc.ClientException as ce: msg = _('Failed to create storage volume.') LOG.info(msg) LOG.error(ce) # Allow REST API error message to appear on UI messages.error(request, ce) # Redirect to host details pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to create storage volume.') LOG.info(msg) LOG.error(e) # if not a rest API error, throw default redirect = reverse(self.failure_url, args=[host_id]) return exceptions.handle(request, message=e, redirect=redirect)
class AddPhysicalVolume(forms.SelfHandlingForm): PV_TYPE_CHOICES = ( ('disk', _("Disk")), ('partition', _("Partition")), ) host_id = forms.CharField(label=_("host_id"), initial='host_id', widget=forms.widgets.HiddenInput) ihost_uuid = forms.CharField(label=_("ihost_uuid"), initial='ihost_uuid', widget=forms.widgets.HiddenInput) disk_or_part_uuid = forms.CharField(label=_("disk_or_part_uuid"), initial='disk_or_part_uuid', widget=forms.widgets.HiddenInput) hostname = forms.CharField( label=_("Hostname"), initial='hostname', widget=forms.TextInput(attrs={'readonly': 'readonly'})) lvg = forms.ChoiceField(label=_("Local Volume Group"), required=True, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'lvg' }), help_text=_("Associate this physical volume to a " "volume group ")) pv_type = forms.ChoiceField( label=_("PV Type"), required=False, choices=PV_TYPE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'pv_type', 'initial': 'Disk' })) disks = forms.ChoiceField(label=_("Disks"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'pv_type', 'data-pv_type-disk': _("Disks") }), help_text=_("Assign disk to physical volume.")) partitions = forms.ChoiceField( label=_("Partitions"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'pv_type', 'data-pv_type-partition': _("Partitions") }), help_text=_("Assign partition to physical " "volume.")) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(AddPhysicalVolume, self).__init__(*args, **kwargs) # Populate available partition, disk, and volume group choices host_uuid = kwargs['initial']['ihost_uuid'] host_id = kwargs['initial']['host_id'] host = api.sysinv.host_get(self.request, host_id) subfunctions = host.subfunctions compatible_lvgs = [] if host.personality.lower().startswith( api.sysinv.PERSONALITY_CONTROLLER): compatible_lvgs += [ api.sysinv.LVG_CGTS_VG, api.sysinv.LVG_CINDER_VOLUMES ] if api.sysinv.SUBFUNCTIONS_COMPUTE in subfunctions: compatible_lvgs += [api.sysinv.LVG_NOVA_LOCAL] avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid) ilvg_list = api.sysinv.host_lvg_list(self.request, host_uuid) partitions = api.sysinv.host_disk_partition_list( self.request, host_uuid) ipv_list = api.sysinv.host_pv_list(self.request, host_uuid) disk_tuple_list = [] partitions_tuple_list = [] ilvg_tuple_list = [] pv_cinder_volumes = next( (pv for pv in ipv_list if pv.lvm_vg_name == api.sysinv.LVG_CINDER_VOLUMES), None) for lvg in ilvg_list: if (lvg.lvm_vg_name in compatible_lvgs and lvg.vg_state in [api.sysinv.LVG_ADD, api.sysinv.LVG_PROV]): if (lvg.lvm_vg_name == api.sysinv.LVG_CINDER_VOLUMES and pv_cinder_volumes): continue ilvg_tuple_list.append((lvg.uuid, lvg.lvm_vg_name)) for disk in avail_disk_list: capabilities = disk.capabilities if capabilities.get('stor_function') == 'rootfs': continue # TODO(rchurch): re-factor elif capabilities.get('device_function') == 'cinder_device': continue else: break for d in avail_disk_list: disk_cap = d.capabilities # TODO(rchurch): re-factor is_cinder_device = \ (('device_function' in disk_cap) and (disk_cap['device_function'] == 'cinder_device')) is_rootfs_device = \ (('stor_function' in disk_cap) and (disk_cap['stor_function'] == 'rootfs')) disk_model = d.get_model_num() # TODO(rchurch): re-factor if not d.ipv_uuid and is_cinder_device: continue if is_rootfs_device or d.ipv_uuid: continue if disk_model is not None and "floppy" in disk_model.lower(): continue disk_tuple_list.append( (d.uuid, "%s (path:%s size:%s model:%s)" % (d.device_node, d.device_path, str(d.size_mib), disk_model))) for p in partitions: if p.type_guid != api.sysinv.USER_PARTITION_PHYS_VOL: continue if p.ipv_uuid: continue if p.status == api.sysinv.PARTITION_IN_USE_STATUS: # If partition is in use, but the PV it is attached to # is in a "removing" state, we should allow the partition # to be listed as a possible option. for pv in ipv_list: if (pv.disk_or_part_device_path == p.device_path and pv.pv_state == api.sysinv.PV_DEL): break else: continue partitions_tuple_list.append( (p.uuid, "%s (size:%s)" % (p.device_path, str(p.size_mib)))) self.fields['disks'].choices = disk_tuple_list self.fields['lvg'].choices = ilvg_tuple_list self.fields['partitions'].choices = partitions_tuple_list def clean(self): cleaned_data = super(AddPhysicalVolume, self).clean() return cleaned_data def handle(self, request, data): host_id = data['host_id'] lvgs = data['lvg'][:] # GUI only allows one disk to be picked if data['pv_type'] == 'disk': data['disk_or_part_uuid'] = data['disks'][:] else: data['disk_or_part_uuid'] = data['partitions'][:] data['ilvg_uuid'] = lvgs try: del data['host_id'] del data['disks'] del data['hostname'] del data['lvg'] del data['partitions'] stor = api.sysinv.host_pv_create(request, **data) msg = _('Physical volume was successfully created.') messages.success(request, msg) return stor except exc.ClientException as ce: msg = _('Failed to create physical volume.') # Allow REST API error message to appear on UI w_msg = str(ce) if ('Warning:' in w_msg): LOG.info(ce) messages.warning(request, w_msg.split(':', 1)[-1]) else: LOG.error(ce) messages.error(request, w_msg) # Redirect to host details pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to create physical volume.') LOG.error(e) # if not a rest API error, throw default redirect = reverse(self.failure_url, args=[host_id]) return exceptions.handle(request, message=e, redirect=redirect)
class UpdateMemory(forms.SelfHandlingForm): VSWITCH_HP_SIZE_CHOICES = (('2', _("2 MB")), ('1024', _("1 GB"))) APP_HP_SETTING_CHOICES = (('percent', _('Percent Value')), ('integer', _('Integer Value'))) memtotal_mib = forms.CharField(label=_("memtotal_mib"), required=False, widget=forms.widgets.HiddenInput) memtotal_mib_two = forms.CharField(label=_("memtotal_mib_two"), required=False, widget=forms.widgets.HiddenInput) memtotal_mib_three = forms.CharField(label=_("memtotal_mib_three"), required=False, widget=forms.widgets.HiddenInput) memtotal_mib_four = forms.CharField(label=_("memtotal_mib_four"), required=False, widget=forms.widgets.HiddenInput) size_mib_2M = forms.CharField(label=_("size_mib_2M"), required=False, widget=forms.widgets.HiddenInput, initial=constants.MIB_2M) size_mib_1G = forms.CharField(label=_("size_mib_1G"), required=False, widget=forms.widgets.HiddenInput, initial=constants.MIB_1G) host = forms.CharField(label=_("host"), required=False, widget=forms.widgets.HiddenInput) host_id = forms.CharField(label=_("host_id"), required=False, widget=forms.widgets.HiddenInput) platform_memory = forms.CharField( label=_("#(MiB) of Platform Memory for Node 0"), required=False) vm_hugepages_nr_percentage = forms.ChoiceField( label=_("Application Huge Pages 1G/2M Setting Type"), required=False, choices=APP_HP_SETTING_CHOICES, help_text="Take Application Hugepages as a Percentage or Integer.", widget=forms.ThemableSelectWidget( attrs={ 'class': 'switchable', 'data-slug': 'vm_hugepages_nr_percentage' })) vm_hugepages_nr_2M = forms.CharField( label=_("% of Application 2M Hugepages Node 0"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 2M Hugepages Node 0', 'data-vm_hugepages_nr_percentage-integer': '# of Application 2M Hugepages Node 0' })) vm_hugepages_nr_1G = forms.CharField( label=_("# of Application 1G Hugepages Node 0"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 1G Hugepages Node 0', 'data-vm_hugepages_nr_percentage-integer': '# of Application 1G Hugepages Node 0' })) vswitch_hugepages_reqd = forms.CharField( label=_("# of vSwitch 1G Hugepages Node 0"), required=False) vswitch_hugepages_size_mib = forms.ChoiceField( label=_("vSwitch Hugepage Size Node 0"), required=False, choices=VSWITCH_HP_SIZE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vswitch_hugepages_size_mib' })) platform_memory_two = forms.CharField( label=_("#(MiB) of Platform Memory for Node 1"), required=False) vm_hugepages_nr_2M_two = forms.CharField( label=_("# of Application 2M Hugepages Node 1"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 2M Hugepages Node 1', 'data-vm_hugepages_nr_percentage-integer': '# of Application 2M Hugepages Node 1' })) vm_hugepages_nr_1G_two = forms.CharField( label=_("# of Application 1G Hugepages Node 1"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 1G Hugepages Node 1', 'data-vm_hugepages_nr_percentage-integer': '# of Application 1G Hugepages Node 1' })) vswitch_hugepages_reqd_two = forms.CharField( label=_("# of vSwitch 1G Hugepages Node 1"), required=False) vswitch_hugepages_size_mib_two = forms.ChoiceField( label=_("vSwitch Hugepage Size Node 1"), required=False, choices=VSWITCH_HP_SIZE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vswitch_hugepages_size_mib' })) platform_memory_three = forms.CharField( label=_("#(MiB) of Platform Memory for Node 2"), required=False) vm_hugepages_nr_2M_three = forms.CharField( label=_("# of Application 2M Hugepages Node 2"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 2M Hugepages Node 2', 'data-vm_hugepages_nr_percentage-integer': '# of Application 2M Hugepages Node 2' })) vm_hugepages_nr_1G_three = forms.CharField( label=_("# of Application 1G Hugepages Node 2"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 1G Hugepages Node 2', 'data-vm_hugepages_nr_percentage-integer': '# of Application 1G Hugepages Node 2' })) vswitch_hugepages_reqd_three = forms.CharField( label=_("# of vSwitch 1G Hugepages Node 2"), required=False) vswitch_hugepages_size_mib_three = forms.ChoiceField( label=_("vSwitch Hugepage Size Node 2"), required=False, choices=VSWITCH_HP_SIZE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vswitch_hugepages_size_mib' })) platform_memory_four = forms.CharField( label=_("#(MiB) of Platform Memory for Node 3"), required=False) vm_hugepages_nr_2M_four = forms.CharField( label=_("# of Application 2M Hugepages Node 3"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 2M Hugepages Node 3', 'data-vm_hugepages_nr_percentage-integer': '# of Application 2M Hugepages Node 3' })) vm_hugepages_nr_1G_four = forms.CharField( label=_("# of Application 1G Hugepages Node 3"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'vm_hugepages_nr_percentage', 'data-vm_hugepages_nr_percentage-percent': '% of Application 1G Hugepages Node 3', 'data-vm_hugepages_nr_percentage-integer': '# of Application 1G Hugepages Node 3' })) vswitch_hugepages_reqd_four = forms.CharField( label=_("# of vSwitch 1G Hugepages Node 3"), required=False) vswitch_hugepages_size_mib_four = forms.ChoiceField( label=_("vSwitch Hugepage Size Node 3"), required=False, choices=VSWITCH_HP_SIZE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vswitch_hugepages_size_mib' })) failure_url = 'horizon:admin:inventory:detail' def __init__(self, request, *args, **kwargs): super(UpdateMemory, self).__init__(request, *args, **kwargs) self.host = kwargs['initial']['host'] self.vswitch_type = stx_api.sysinv.get_vswitch_type(request) LOG.debug("vswitch_type is %s", self.vswitch_type) memory_fieldsets = [{ 'platform_memory': self.fields['platform_memory'], 'vm_hugepages_nr_2M': self.fields['vm_hugepages_nr_2M'], 'vm_hugepages_nr_1G': self.fields['vm_hugepages_nr_1G'], 'vswitch_hugepages_size_mib': self.fields['vswitch_hugepages_size_mib'], 'vswitch_hugepages_reqd': self.fields['vswitch_hugepages_reqd'], 'memtotal_mib': self.fields['memtotal_mib'] }, { 'platform_memory': self.fields['platform_memory_two'], 'vm_hugepages_nr_2M': self.fields['vm_hugepages_nr_2M_two'], 'vm_hugepages_nr_1G': self.fields['vm_hugepages_nr_1G_two'], 'vswitch_hugepages_size_mib': self.fields['vswitch_hugepages_size_mib_two'], 'vswitch_hugepages_reqd': self.fields['vswitch_hugepages_reqd_two'], 'memtotal_mib': self.fields['memtotal_mib_two'] }, { 'platform_memory': self.fields['platform_memory_three'], 'vm_hugepages_nr_2M': self.fields['vm_hugepages_nr_2M_three'], 'vm_hugepages_nr_1G': self.fields['vm_hugepages_nr_1G_three'], 'vswitch_hugepages_size_mib': self.fields['vswitch_hugepages_size_mib_three'], 'vswitch_hugepages_reqd': self.fields['vswitch_hugepages_reqd_three'], 'memtotal_mib': self.fields['memtotal_mib_three'] }, { 'platform_memory': self.fields['platform_memory_four'], 'vm_hugepages_nr_2M': self.fields['vm_hugepages_nr_2M_four'], 'vm_hugepages_nr_1G': self.fields['vm_hugepages_nr_1G_four'], 'vswitch_hugepages_size_mib': self.fields['vswitch_hugepages_size_mib_four'], 'vswitch_hugepages_reqd': self.fields['vswitch_hugepages_reqd_four'], 'memtotal_mib': self.fields['memtotal_mib_four'] }] count = 0 for m in self.host.memorys: count = count + 1 for n in self.host.nodes: if m.inode_uuid == n.uuid: field_set = memory_fieldsets[int(n.numa_node)] platform_field = field_set['platform_memory'] platform_field.help_text = \ 'Minimum platform memory(MiB): ' + \ str(m.minimum_platform_reserved_mib) platform_field.initial = str(m.platform_reserved_mib) field_set['memtotal_mib'].initial = m.memtotal_mib vm_hugepages_nr_percentage_field = \ self.fields['vm_hugepages_nr_percentage'] if (m.vm_pending_as_percentage == "True"): vm_hugepages_nr_percentage_field.initial = "percent" else: vm_hugepages_nr_percentage_field.initial = "integer" vm_2M_field = field_set['vm_hugepages_nr_2M'] vm_2M_field.help_text = \ 'Maximum 2M pages: ' + \ str(m.vm_hugepages_possible_2M) if m.vm_hugepages_nr_2M_pending or \ m.vm_hugepages_nr_2M_pending == 0: vm_2M_field.initial = str(m.vm_hugepages_nr_2M_pending) elif m.vm_hugepages_nr_2M: if (m.vm_pending_as_percentage == "True"): vm_2M_field.initial = str( round(m.vm_hugepages_nr_2M * 100 / m.vm_hugepages_possible_2M)) else: vm_2M_field.initial = str(m.vm_hugepages_nr_2M) else: vm_2M_field.initial = '0' vm_1G_field = field_set['vm_hugepages_nr_1G'] vm_1g_supported = m.vm_hugepages_use_1G != 'False' if vm_1g_supported: help_msg = 'Maximum 1G pages: ' + \ str(m.vm_hugepages_possible_1G) else: help_msg = 'This node does not support 1G hugepages' vm_1G_field.help_text = help_msg if m.vm_hugepages_nr_1G_pending or \ m.vm_hugepages_nr_1G_pending == 0: vm_1G_field.initial = str(m.vm_hugepages_nr_1G_pending) elif m.vm_hugepages_nr_1G: if (m.vm_pending_as_percentage == "True"): vm_1G_field.initial = \ str(int(m.vm_hugepages_nr_1G * 100 / m.vm_hugepages_possible_1G)) else: vm_1G_field.initial = str(m.vm_hugepages_nr_1G) elif vm_1g_supported: vm_1G_field.initial = '0' else: vm_1G_field.initial = '' if not vm_1g_supported: vm_1G_field.widget.attrs['disabled'] = 'disabled' vswitch_hp_reqd_field = field_set['vswitch_hugepages_reqd'] vswitch_hp_reqd_field.help_text = \ 'Maximum vSwitch pages' vswitch_hp_size_mib_field = \ field_set['vswitch_hugepages_size_mib'] vswitch_hp_size_mib_field.help_text = \ 'vSwitch hugepage size' if m.vswitch_hugepages_reqd: vswitch_hp_reqd_field.initial = \ str(m.vswitch_hugepages_reqd) elif m.vswitch_hugepages_nr: vswitch_hp_reqd_field.initial = \ str(m.vswitch_hugepages_nr) if m.vswitch_hugepages_size_mib: vswitch_hp_size_mib_field.initial = \ str(m.vswitch_hugepages_size_mib) if self.vswitch_type is None: LOG.debug("vswitch_hp field is hidden") vswitch_hp_size_mib_field.widget = \ forms.widgets.HiddenInput() vswitch_hp_reqd_field.widget = \ forms.widgets.HiddenInput() break while count < 4: field_set = memory_fieldsets[count] field_set['platform_memory'].widget = \ forms.widgets.HiddenInput() field_set['vm_hugepages_nr_2M'].widget = \ forms.widgets.HiddenInput() field_set['vm_hugepages_nr_1G'].widget = \ forms.widgets.HiddenInput() field_set['vswitch_hugepages_reqd'].widget = \ forms.widgets.HiddenInput() field_set['vswitch_hugepages_size_mib'].widget = \ forms.widgets.HiddenInput() count += 1 def clean(self): cleaned_data = super(UpdateMemory, self).clean() # host_id = cleaned_data.get('host_id') return cleaned_data def handle(self, request, data): host_id = data['host_id'] del data['host_id'] del data['host'] node = [] node.append('node0') if data['platform_memory_two'] or \ data['vm_hugepages_nr_2M_two'] or \ data['vm_hugepages_nr_1G_two'] or \ data['vswitch_hugepages_size_mib_two'] or \ data['vswitch_hugepages_reqd_two']: node.append('node1') if data['platform_memory_three'] or \ data['vm_hugepages_nr_2M_three'] or \ data['vm_hugepages_nr_1G_three'] or \ data['vswitch_hugepages_size_mib_three'] or \ data['vswitch_hugepages_reqd_three']: node.append('node2') if data['platform_memory_four'] or \ data['vm_hugepages_nr_2M_four'] or \ data['vm_hugepages_nr_1G_four'] or \ data['vswitch_hugepages_size_mib_four'] or \ data['vswitch_hugepages_reqd_four']: node.append('node3') # host = api.sysinv.host_get(request, host_id) pages_1G = {} pages_2M = {} plat_mem = {} pages_vs_size = {} pages_vs_reqd = {} # Node 0 arguments if not data['platform_memory']: del data['platform_memory'] else: plat_mem['node0'] = data['platform_memory'] if not data['vm_hugepages_nr_2M']: del data['vm_hugepages_nr_2M'] else: pages_2M['node0'] = data['vm_hugepages_nr_2M'] if not data['vm_hugepages_nr_1G']: del data['vm_hugepages_nr_1G'] else: pages_1G['node0'] = data['vm_hugepages_nr_1G'] if self.vswitch_type: if not data['vswitch_hugepages_size_mib']: del data['vswitch_hugepages_size_mib'] else: pages_vs_size['node0'] = data['vswitch_hugepages_size_mib'] if not data['vswitch_hugepages_reqd']: del data['vswitch_hugepages_reqd'] else: pages_vs_reqd['node0'] = data['vswitch_hugepages_reqd'] # Node 1 arguments if not data['platform_memory_two']: del data['platform_memory_two'] else: plat_mem['node1'] = data['platform_memory_two'] if not data['vm_hugepages_nr_2M_two']: del data['vm_hugepages_nr_2M_two'] else: pages_2M['node1'] = data['vm_hugepages_nr_2M_two'] if not data['vm_hugepages_nr_1G_two']: del data['vm_hugepages_nr_1G_two'] else: pages_1G['node1'] = data['vm_hugepages_nr_1G_two'] if self.vswitch_type: if not data['vswitch_hugepages_size_mib_two']: del data['vswitch_hugepages_size_mib_two'] else: pages_vs_size['node1'] = data['vswitch_hugepages_size_mib_two'] if not data['vswitch_hugepages_reqd_two']: del data['vswitch_hugepages_reqd_two'] else: pages_vs_reqd['node1'] = data['vswitch_hugepages_reqd_two'] # Node 2 arguments if not data['platform_memory_three']: del data['platform_memory_three'] else: plat_mem['node2'] = data['platform_memory_three'] if not data['vm_hugepages_nr_2M_three']: del data['vm_hugepages_nr_2M_three'] else: pages_2M['node2'] = data['vm_hugepages_nr_2M_three'] if not data['vm_hugepages_nr_1G_three']: del data['vm_hugepages_nr_1G_three'] else: pages_1G['node2'] = data['vm_hugepages_nr_1G_three'] if self.vswitch_type: if not data['vswitch_hugepages_size_mib_three']: del data['vswitch_hugepages_size_mib_three'] else: pages_vs_size['node2'] = \ data['vswitch_hugepages_size_mib_three'] if not data['vswitch_hugepages_reqd']: del data['vswitch_hugepages_reqd'] else: pages_vs_reqd['node2'] = \ data['vswitch_hugepages_reqd_three'] # Node 3 arguments if not data['platform_memory_four']: del data['platform_memory_four'] else: plat_mem['node3'] = data['platform_memory_four'] if not data['vm_hugepages_nr_2M_four']: del data['vm_hugepages_nr_2M_four'] else: pages_2M['node3'] = data['vm_hugepages_nr_2M_four'] if not data['vm_hugepages_nr_1G_four']: del data['vm_hugepages_nr_1G_four'] else: pages_1G['node3'] = data['vm_hugepages_nr_1G_four'] if self.vswitch_type: if not data['vswitch_hugepages_size_mib_four']: del data['vswitch_hugepages_size_mib_four'] else: pages_vs_size['node3'] = \ data['vswitch_hugepages_size_mib_four'] if not data['vswitch_hugepages_reqd_four']: del data['vswitch_hugepages_reqd_four'] else: pages_vs_reqd['node3'] = data['vswitch_hugepages_reqd_four'] try: for nd in node: node_found = False memory = None for m in self.host.memorys: for n in self.host.nodes: if m.inode_uuid == n.uuid: if int(n.numa_node) == int(node.index(nd)): memory = m node_found = True break if node_found: break if node_found: new_data = {} new_data["vm_pending_as_percentage"] = ("True" if str( data["vm_hugepages_nr_percentage"]) == "percent" else "False") if nd in plat_mem: new_data['platform_reserved_mib'] = plat_mem[nd] if nd in pages_2M: new_data['vm_hugepages_nr_2M_pending'] = pages_2M[nd] if nd in pages_1G: new_data['vm_hugepages_nr_1G_pending'] = pages_1G[nd] if self.vswitch_type: if nd in pages_vs_size: new_data['vswitch_hugepages_size_mib'] = \ pages_vs_size[nd] if nd in pages_vs_reqd: new_data['vswitch_hugepages_reqd'] = \ pages_vs_reqd[nd] if new_data: stx_api.sysinv.host_memory_update( request, memory.uuid, **new_data) else: msg = _('Failed to find %s') % nd messages.error(request, msg) LOG.error(msg) # Redirect to failure pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) msg = _('Memory allocation has been successfully ' 'updated.') LOG.debug(msg) messages.success(request, msg) return self.host.memorys except exc.ClientException as ce: # Allow REST API error message to appear on UI messages.error(request, ce) LOG.error(ce) # Redirect to failure pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception: msg = _('Failed to update memory allocation') LOG.info(msg) redirect = reverse(self.failure_url, args=[host_id]) exceptions.handle(request, msg, redirect=redirect)
class AddResourceForm(forms.SelfHandlingForm): label = forms.CharField(label=_("Label"), max_length=255, required=True) rackid = forms.CharField(label=_("Rack ID"), widget=forms.HiddenInput()) rack_label = forms.CharField(label=_("Rack"), max_length=255, required=True) eiaLocation = forms.CharField(label=_("EIA Location"), max_length=3, required=False) ip_address = forms.CharField( label=_("Host Name or IP Address"), max_length=255, required=True, help_text=_("Specify the fully qualified host name or IP V4 address " "used by Operational Management to access the " " resource.")) auth_method = forms.ChoiceField( label=_('Authentication Method'), choices=[(AUTH_METHOD_USER_PWD, _('User ID and Password')), (AUTH_METHOD_USER_KEY, _('User ID and SSH Key'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'auth' }), help_text=_("Indicates the type of credential information " "used by Operational Management to access the resource.")) userID = forms.CharField(label=_("User ID"), max_length=255, required=True) password = forms.CharField(label=_("password"), required=True, widget=forms.PasswordInput(attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-0': _("Password") }, render_value=False)) sshKey = forms.CharField(widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-1': _("SSH Key"), 'rows': 4 }), required=True, help_text=_( "Paste the private SSH key for the specified" " resource.")) passphrase = forms.CharField(label=_("passphrase"), required=False, widget=forms.PasswordInput( attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-1': _("Passphrase") }, render_value=False)) def __init__(self, request, *args, **kwargs): super(AddResourceForm, self).__init__(request, *args, **kwargs) # User should only be allowed to add to the rack being displayed self.fields["rack_label"].widget = forms.TextInput( attrs={'readonly': 'readonly'}) def clean(self): cleaned_data = super(AddResourceForm, self).clean() self._clean_auth_ids(cleaned_data) return cleaned_data def _clean_auth_ids(self, data): auth_method = data.get('auth_method') # if User ID + SSH Key was chosen, we can ignore any 'password' errors if (auth_method == AUTH_METHOD_USER_KEY) and ('password' in self._errors): del self._errors['password'] # if User ID + Password was chosen, we can ignore any 'sshKey' errors if (auth_method == AUTH_METHOD_USER_PWD) and ('sshKey' in self._errors): del self._errors['sshKey'] def handle(self, request, data): __method__ = 'forms.AddResourceForm.handle' try: # Need to ensure we pass along the correct password (either # password or phassphrase), and that we don't accidently pass # the SSH key password_value = None if data['auth_method'] != AUTH_METHOD_USER_KEY: data['sshKey'] = None if 'password' in data: password_value = data['password'] else: if 'passphrase' in data: password_value = data['passphrase'] logging.debug( "%s: Attempting to add a resource to rack: %s, using" " label: %s, address: %s, user id: %s, eia location" " %s, and authentication method: %s", __method__, self.initial['rackid'], data['label'], data['ip_address'], data['userID'], data['eiaLocation'], data['auth_method']) # pass in "None" for resource type in add_resource -- so that the # API will determine type for us (rc, result_dict) = resource_mgr.add_resource( data['label'], None, data['ip_address'], data['userID'].strip(), password_value, self.initial['rackid'], data['eiaLocation'], data['sshKey']) if rc is not 0: # Log details of the unsuccessful attempt. logging.error( "%s: Attempt to add a resource to rack: %s, " "using label: %s, address: %s, user id: %s, " "eia location %s, and authentication method:" " %s failed.", __method__, self.initial['rackid'], data['label'], data['ip_address'], data['userID'], data['eiaLocation'], data['auth_method']) logging.error( "%s: Unable to add resource %s to rack %s. A Non-0 " " return code returned from resource_mgr.add_resource." " The return code is: %s. Details of the attempt: " " %s", __method__, data['label'], self.initial['rackid'], rc, result_dict) msg = str('Attempt to add resource ' + data['label'] + ' was ' + 'not successful. Details of the attempt: ' + result_dict) messages.error(request, msg) # Return false in this case so that the dialog is not # dismissed. This gives the end-user a chance to # update the dialog w/o having to re-enter all the # information a second time. return False else: msg = str('Resource ' + data['label'] + ' successfully' + ' added.') messages.success(request, msg) return True except Exception as e: logging.error( "%s: Exception received trying to add a resource. " "Exception is: %s", __method__, e) exceptions.handle(request, _('Unable to add resource.')) # In this case, return True so that the dialog closes. # In theory, there are no values the end-user could change # to the dialog inputs that would allow us to proceed. return True
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Volume Name"), required=False) description = forms.CharField(max_length=255, widget=forms.Textarea(attrs={ 'class': 'modal-body-fixed-width', 'rows': 4 }), label=_("Description"), required=False) volume_source_type = forms.ChoiceField( label=_("Volume Source"), required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) snapshot_source = forms.ChoiceField( label=_("Use snapshot as a source"), widget=forms.SelectWidget(attrs={'class': 'snapshot-selector'}, data_attrs=('size', 'name'), transform=lambda x: "%s (%sGB)" % (x.name, x.size)), required=False) image_source = forms.ChoiceField( label=_("Use image as a source"), widget=forms.SelectWidget(attrs={'class': 'image-selector'}, data_attrs=('size', 'name', 'min_disk'), transform=lambda x: "%s (%s)" % (x.name, filesizeformat(x.bytes))), required=False) volume_source = forms.ChoiceField( label=_("Use a volume as source"), widget=forms.SelectWidget( attrs={'class': 'image-selector'}, data_attrs=('size', 'name'), transform=lambda x: "%s (%s)" % (x.name, filesizeformat(x.size * 1024 * 1024 * 1024))), required=False) type = forms.ChoiceField(label=_("Type"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-no_source_type': _('Type'), 'data-source-image_source': _('Type') })) size = forms.IntegerField(min_value=1, initial=1, label=_("Size (GB)")) availability_zone = forms.ChoiceField( label=_("Availability Zone"), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-no_source_type': _('Availability Zone'), 'data-source-image_source': _('Availability Zone') })) def prepare_source_fields_if_snapshot_specified(self, request): try: snapshot = self.get_snapshot(request, request.GET["snapshot_id"]) self.fields['name'].initial = snapshot.name self.fields['size'].initial = snapshot.size self.fields['snapshot_source'].choices = ((snapshot.id, snapshot), ) try: # Set the volume type from the original volume orig_volume = cinder.volume_get(request, snapshot.volume_id) self.fields['type'].initial = orig_volume.volume_type except Exception: pass self.fields['size'].help_text = ( _('Volume size must be equal to or greater than the ' 'snapshot size (%sGB)') % snapshot.size) del self.fields['image_source'] del self.fields['volume_source'] del self.fields['volume_source_type'] del self.fields['availability_zone'] except Exception: exceptions.handle(request, _('Unable to load the specified snapshot.')) def prepare_source_fields_if_image_specified(self, request): self.fields['availability_zone'].choices = \ availability_zones(request) try: image = self.get_image(request, request.GET["image_id"]) image.bytes = image.size self.fields['name'].initial = image.name min_vol_size = functions.bytes_to_gigabytes(image.size) size_help_text = (_('Volume size must be equal to or greater ' 'than the image size (%s)') % filesizeformat(image.size)) properties = getattr(image, 'properties', {}) min_disk_size = (getattr(image, 'min_disk', 0) or properties.get('min_disk', 0)) if (min_disk_size > min_vol_size): min_vol_size = min_disk_size size_help_text = (_('Volume size must be equal to or ' 'greater than the image minimum ' 'disk size (%sGB)') % min_disk_size) self.fields['size'].initial = min_vol_size self.fields['size'].help_text = size_help_text self.fields['image_source'].choices = ((image.id, image), ) del self.fields['snapshot_source'] del self.fields['volume_source'] del self.fields['volume_source_type'] except Exception: msg = _('Unable to load the specified image. %s') exceptions.handle(request, msg % request.GET['image_id']) def prepare_source_fields_if_volume_specified(self, request): self.fields['availability_zone'].choices = \ availability_zones(request) volume = None try: volume = self.get_volume(request, request.GET["volume_id"]) except Exception: msg = _('Unable to load the specified volume. %s') exceptions.handle(request, msg % request.GET['volume_id']) if volume is not None: self.fields['name'].initial = volume.name self.fields['description'].initial = volume.description min_vol_size = volume.size size_help_text = (_('Volume size must be equal to or greater ' 'than the origin volume size (%s)') % filesizeformat(volume.size)) self.fields['size'].initial = min_vol_size self.fields['size'].help_text = size_help_text self.fields['volume_source'].choices = ((volume.id, volume), ) self.fields['type'].initial = volume.type del self.fields['snapshot_source'] del self.fields['image_source'] del self.fields['volume_source_type'] def prepare_source_fields_default(self, request): source_type_choices = [] self.fields['availability_zone'].choices = \ availability_zones(request) try: available = api.cinder.VOLUME_STATE_AVAILABLE snapshots = cinder.volume_snapshot_list( request, search_opts=dict(status=available)) if snapshots: source_type_choices.append(("snapshot_source", _("Snapshot"))) choices = [('', _("Choose a snapshot"))] + \ [(s.id, s) for s in snapshots] self.fields['snapshot_source'].choices = choices else: del self.fields['snapshot_source'] except Exception: exceptions.handle(request, _("Unable to retrieve volume snapshots.")) images = utils.get_available_images(request, request.user.tenant_id) if images: source_type_choices.append(("image_source", _("Image"))) choices = [('', _("Choose an image"))] for image in images: image.bytes = image.size image.size = functions.bytes_to_gigabytes(image.bytes) choices.append((image.id, image)) self.fields['image_source'].choices = choices else: del self.fields['image_source'] volumes = self.get_volumes(request) if volumes: source_type_choices.append(("volume_source", _("Volume"))) choices = [('', _("Choose a volume"))] for volume in volumes: choices.append((volume.id, volume)) self.fields['volume_source'].choices = choices else: del self.fields['volume_source'] if source_type_choices: choices = ([('no_source_type', _("No source, empty volume"))] + source_type_choices) self.fields['volume_source_type'].choices = choices else: del self.fields['volume_source_type'] def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) volume_types = cinder.volume_type_list(request) self.fields['type'].choices = [("", _("No volume type"))] + \ [(type.name, type.name) for type in volume_types] if "snapshot_id" in request.GET: self.prepare_source_fields_if_snapshot_specified(request) elif 'image_id' in request.GET: self.prepare_source_fields_if_image_specified(request) elif 'volume_id' in request.GET: self.prepare_source_fields_if_volume_specified(request) else: self.prepare_source_fields_default(request) def clean(self): cleaned_data = super(CreateForm, self).clean() source_type = self.cleaned_data.get('volume_source_type') if (source_type == 'image_source' and not cleaned_data.get('image_source')): msg = _('Image source must be specified') self._errors['image_source'] = self.error_class([msg]) elif (source_type == 'snapshot_source' and not cleaned_data.get('snapshot_source')): msg = _('Snapshot source must be specified') self._errors['snapshot_source'] = self.error_class([msg]) elif (source_type == 'volume_source' and not cleaned_data.get('volume_source')): msg = _('Volume source must be specified') self._errors['volume_source'] = self.error_class([msg]) return cleaned_data def get_volumes(self, request): volumes = [] try: available = api.cinder.VOLUME_STATE_AVAILABLE volumes = cinder.volume_list(self.request, search_opts=dict(status=available)) except Exception: exceptions.handle(request, _('Unable to retrieve list of volumes.')) return volumes def handle(self, request, data): try: usages = quotas.tenant_limit_usages(self.request) availableGB = usages['maxTotalVolumeGigabytes'] - \ usages['gigabytesUsed'] availableVol = usages['maxTotalVolumes'] - usages['volumesUsed'] snapshot_id = None image_id = None volume_id = None source_type = data.get('volume_source_type', None) az = data.get('availability_zone', None) or None if (data.get("snapshot_source", None) and source_type in [None, 'snapshot_source']): # Create from Snapshot snapshot = self.get_snapshot(request, data["snapshot_source"]) snapshot_id = snapshot.id if (data['size'] < snapshot.size): error_message = (_('The volume size cannot be less than ' 'the snapshot size (%sGB)') % snapshot.size) raise ValidationError(error_message) az = None elif (data.get("image_source", None) and source_type in [None, 'image_source']): # Create from Snapshot image = self.get_image(request, data["image_source"]) image_id = image.id image_size = functions.bytes_to_gigabytes(image.size) if (data['size'] < image_size): error_message = (_('The volume size cannot be less than ' 'the image size (%s)') % filesizeformat(image.size)) raise ValidationError(error_message) properties = getattr(image, 'properties', {}) min_disk_size = (getattr(image, 'min_disk', 0) or properties.get('min_disk', 0)) if (min_disk_size > 0 and data['size'] < min_disk_size): error_message = (_('The volume size cannot be less than ' 'the image minimum disk size (%sGB)') % min_disk_size) raise ValidationError(error_message) elif (data.get("volume_source", None) and source_type in [None, 'volume_source']): # Create from volume volume = self.get_volume(request, data["volume_source"]) volume_id = volume.id if data['size'] < volume.size: error_message = (_('The volume size cannot be less than ' 'the source volume size (%sGB)') % volume.size) raise ValidationError(error_message) else: if type(data['size']) is str: data['size'] = int(data['size']) if availableGB < data['size']: error_message = _('A volume of %(req)iGB cannot be created as ' 'you only have %(avail)iGB of your quota ' 'available.') params = {'req': data['size'], 'avail': availableGB} raise ValidationError(error_message % params) elif availableVol <= 0: error_message = _('You are already using all of your available' ' volumes.') raise ValidationError(error_message) metadata = {} volume = cinder.volume_create(request, data['size'], data['name'], data['description'], data['type'], snapshot_id=snapshot_id, image_id=image_id, metadata=metadata, availability_zone=az, source_volid=volume_id) message = _('Creating volume "%s"') % data['name'] messages.info(request, message) return volume except ValidationError as e: self.api_error(e.messages[0]) return False except Exception: exceptions.handle(request, ignore=True) self.api_error(_("Unable to create volume.")) return False @memoized def get_snapshot(self, request, id): return cinder.volume_snapshot_get(request, id) @memoized def get_image(self, request, id): return glance.image_get(request, id) @memoized def get_volume(self, request, id): return cinder.volume_get(request, id)
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Name"), required=True) description = forms.CharField(widget=forms.widgets.Textarea(), label=_("Description"), required=False) 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'], 'properties': {} } if data['description']: meta['properties']['description'] = data['description'] if settings.HORIZON_IMAGES_ALLOW_UPLOAD and data['image_file']: meta['data'] = self.files['image_file'] else: meta['copy_from'] = data['copy_from'] try: image = api.glance.image_create(request, **meta) messages.success( request, _('Your image %s has been queued for creation.' % data['name'])) return image except: exceptions.handle(request, _('Unable to create new image.'))
class DefinitionForm(forms.SelfHandlingForm): definition_source = forms.ChoiceField( label=_('Definition Source'), choices=[('file', _('File')), ('raw', _('Direct Input'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'definitionsource' })) definition_upload = forms.FileField( label=_('Definition File'), help_text=_('A local definition to upload.'), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'definitionsource', 'data-required-when-shown': 'true', 'data-definitionsource-file': _('Definition File') }), required=False) definition_data = forms.CharField( label=_('Definition Data'), help_text=_('The raw contents of the definition.'), widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'definitionsource', 'data-required-when-shown': 'true', 'data-definitionsource-raw': _('Definition Data'), 'rows': 4 }), required=False) def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(DefinitionForm, self).__init__(*args, **kwargs) def clean(self): cleaned_data = super(DefinitionForm, self).clean() if cleaned_data.get('definition_upload'): files = self.request.FILES cleaned_data['definition'] = files['definition_upload'].read() elif cleaned_data.get('definition_data'): cleaned_data['definition'] = cleaned_data['definition_data'] else: raise forms.ValidationError( _('You must specify the definition source.')) try: validated = api.workbook_validate(self.request, cleaned_data['definition']) except Exception as e: raise forms.ValidationError(six.text_type(e)) if not validated.get('valid'): raise forms.ValidationError( validated.get('error', _('Validated failed'))) return cleaned_data def handle(self, request, data): kwargs = {'definition': data['definition']} request.method = 'GET' return self.next_view.as_view()(request, **kwargs)
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) image_url = forms.CharField( max_length=255, label=_("Image Location"), help_text=_("An external (HTTP) URL to load " "the image from."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location'), 'ng-model': 'copyFrom', 'ng-change': 'selectImageFormat(copyFrom)' }), required=False) image_file = forms.FileField( label=_("Image File"), help_text=_("A local image to upload."), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Image File'), 'ng-model': 'imageFile', 'ng-change': 'selectImageFormat(imageFile.name)', 'image-file-on-change': None }), required=False) disk_format = forms.ChoiceField( label=_('Format'), choices=[], widget=forms.Select(attrs={ 'class': 'switchable', 'ng-model': 'diskFormat' })) architecture = forms.CharField(max_length=255, label=_("Architecture"), required=False) minimum_disk = forms.IntegerField( label=_("Minimum Disk (GB)"), min_value=0, help_text=_('The minimum disk size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) minimum_ram = forms.IntegerField( label=_("Minimum RAM (MB)"), min_value=0, help_text=_('The minimum memory size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) is_copying = forms.BooleanField( label=_("Copy Data"), initial=True, required=False, help_text=_('Specify this option to copy image data to the image ' 'service. If unspecified, image data will be used in its ' 'current location.'), widget=forms.CheckboxInput( attrs={ 'class': 'switched', 'data-source-url': _('Image Location'), 'data-switch-on': 'source' })) is_public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, request, *args, **kwargs): super(CreateImageForm, self).__init__(request, *args, **kwargs) if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or not policy.check( (("image", "upload_image"), ), request)): self._hide_file_source_type() if not policy.check((("image", "set_image_location"), ), request): self._hide_url_source_type() if not policy.check((("image", "publicize_image"), ), request): self._hide_is_public() self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES def _hide_file_source_type(self): self.fields['image_file'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'file' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_url_source_type(self): self.fields['image_url'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'url' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_is_public(self): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... image_file = data.get('image_file', None) image_url = data.get('image_url', None) if not image_url and not image_file: raise ValidationError( _("A image or external image location must be specified.")) elif image_url and image_file: raise ValidationError( _("Can not specify both image and external image location.")) else: return data def handle(self, request, data): # Glance does not really do anything with container_format at the # moment. It requires it is set to the same disk_format for the three # Amazon image types, otherwise it just treats them as 'bare.' As such # we will just set that to be that here instead of bothering the user # with asking them for information we can already determine. if data['disk_format'] in ( 'ami', 'aki', 'ari', ): container_format = data['disk_format'] else: container_format = 'bare' meta = { 'is_public': data['is_public'], 'protected': data['protected'], 'disk_format': data['disk_format'], 'container_format': container_format, 'min_disk': (data['minimum_disk'] or 0), 'min_ram': (data['minimum_ram'] or 0), 'name': data['name'], 'properties': {} } if data['description']: meta['properties']['description'] = data['description'] if data['architecture']: meta['properties']['architecture'] = data['architecture'] if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and policy.check( (("image", "upload_image"), ), request) and data.get('image_file', None)): meta['data'] = self.files['image_file'] elif data['is_copying']: meta['copy_from'] = data['image_url'] else: meta['location'] = data['image_url'] if data['geoTag']: geoTag = json.loads(data['geoTag']) if geoTag.has_key('trust'): meta['properties']['trust'] = geoTag['trust'] if geoTag.has_key('tags'): meta['properties']['tags'] = simplejson.dumps(geoTag['tags']) try: image = api.glance.image_create(request, **meta) messages.success( request, _('Your image %s has been queued for creation.') % data['name']) return image except Exception as e: msg = _('Unable to create new image') # TODO(nikunj2512): Fix this once it is fixed in glance client if hasattr(e, 'code') and e.code == 400: if "Invalid disk format" in e.details: msg = _('Unable to create new image: Invalid disk format ' '%s for image.') % data['disk_format'] elif "Image name too long" in e.details: msg = _('Unable to create new image: Image name too long.') exceptions.handle(request, msg) return False
def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) # NOTE(vkmc): choose only those share protocols that are enabled # FIXME(vkmc): this should be better implemented by having a # capabilities endpoint on the control plane. manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {}) self.enabled_share_protocols = manila_features.get( 'enabled_share_protocols', ['NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS', 'MapRFS']) self.enable_public_shares = manila_features.get( 'enable_public_shares', True) share_networks = manila.share_network_list(request) share_types = manila.share_type_list(request) self.fields['share_type'].choices = ([("", "")] + [(st.name, st.name) for st in share_types]) availability_zones = manila.availability_zone_list(request) self.fields['availability_zone'].choices = ( [("", "")] + [(az.name, az.name) for az in availability_zones]) self.sn_field_name_prefix = 'share-network-choices-' for st in share_types: extra_specs = st.get_keys() dhss = extra_specs.get('driver_handles_share_servers') # NOTE(vponomaryov): Set and tie share-network field only for # share types with enabled handling of share servers. if (isinstance(dhss, six.string_types) and dhss.lower() in ['true', '1']): sn_choices = ([('', '')] + [(sn.id, sn.name or sn.id) for sn in share_networks]) sn_field_name = self.sn_field_name_prefix + st.name sn_field = forms.ChoiceField( label=_("Share Network"), required=True, choices=sn_choices, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'sharetype', 'data-sharetype-%s' % st.name: _("Share Network"), })) self.fields[sn_field_name] = sn_field self.fields['share_source_type'] = forms.ChoiceField( label=_("Share Source"), required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) self.fields['snapshot'] = forms.ChoiceField( label=_("Use snapshot as a source"), widget=forms.fields.SelectWidget(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-snapshot': _('Snapshot') }, data_attrs=('size', 'name'), transform=lambda x: "%s (%sGiB)" % (x.name, x.size)), required=False) self.fields['metadata'] = forms.CharField( label=_("Metadata"), required=False, widget=forms.Textarea(attrs={'rows': 4})) if self.enable_public_shares: self.fields['is_public'] = forms.BooleanField( label=_("Make visible for all"), required=False, help_text=( "If set then all tenants will be able to see this share.")) self.fields['share_proto'].choices = [ (sp, sp) for sp in self.enabled_share_protocols ] if ("snapshot_id" in request.GET or kwargs.get("data", {}).get("snapshot")): try: snapshot = self.get_snapshot( request, request.GET.get("snapshot_id", kwargs.get("data", {}).get("snapshot"))) self.fields['name'].initial = snapshot.name self.fields['size'].initial = snapshot.size self.fields['snapshot'].choices = ((snapshot.id, snapshot), ) try: # Set the share type from the original share orig_share = manila.share_get(request, snapshot.share_id) # NOTE(vponomaryov): we should use share type name, not ID, # because we use names in our choices above. self.fields['share_type'].initial = ( orig_share.share_type_name) except Exception: pass self.fields['size'].help_text = _( 'Share size must be equal to or greater than the snapshot ' 'size (%sGiB)') % snapshot.size del self.fields['share_source_type'] except Exception: exceptions.handle(request, _('Unable to load the specified snapshot.')) else: source_type_choices = [] try: snapshot_list = manila.share_snapshot_list(request) snapshots = [ s for s in snapshot_list if s.status == 'available' ] if snapshots: source_type_choices.append(("snapshot", _("Snapshot"))) choices = [('', _("Choose a snapshot"))] + \ [(s.id, s) for s in snapshots] self.fields['snapshot'].choices = choices else: del self.fields['snapshot'] except Exception: exceptions.handle(request, _("Unable to retrieve " "share snapshots.")) if source_type_choices: choices = ([('no_source_type', _("No source, empty share"))] + source_type_choices) self.fields['share_source_type'].choices = choices else: del self.fields['share_source_type']
class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length="255", label=_("Share Name")) description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 3})) share_proto = forms.ChoiceField(label=_("Share Protocol"), required=True) size = forms.IntegerField(min_value=1, label=_("Size (GiB)")) share_type = forms.ChoiceField( label=_("Share Type"), required=True, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'sharetype' })) availability_zone = forms.ChoiceField(label=_("Availability Zone"), required=False) def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) # NOTE(vkmc): choose only those share protocols that are enabled # FIXME(vkmc): this should be better implemented by having a # capabilities endpoint on the control plane. manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {}) self.enabled_share_protocols = manila_features.get( 'enabled_share_protocols', ['NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS', 'MapRFS']) self.enable_public_shares = manila_features.get( 'enable_public_shares', True) share_networks = manila.share_network_list(request) share_types = manila.share_type_list(request) self.fields['share_type'].choices = ([("", "")] + [(st.name, st.name) for st in share_types]) availability_zones = manila.availability_zone_list(request) self.fields['availability_zone'].choices = ( [("", "")] + [(az.name, az.name) for az in availability_zones]) self.sn_field_name_prefix = 'share-network-choices-' for st in share_types: extra_specs = st.get_keys() dhss = extra_specs.get('driver_handles_share_servers') # NOTE(vponomaryov): Set and tie share-network field only for # share types with enabled handling of share servers. if (isinstance(dhss, six.string_types) and dhss.lower() in ['true', '1']): sn_choices = ([('', '')] + [(sn.id, sn.name or sn.id) for sn in share_networks]) sn_field_name = self.sn_field_name_prefix + st.name sn_field = forms.ChoiceField( label=_("Share Network"), required=True, choices=sn_choices, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'sharetype', 'data-sharetype-%s' % st.name: _("Share Network"), })) self.fields[sn_field_name] = sn_field self.fields['share_source_type'] = forms.ChoiceField( label=_("Share Source"), required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) self.fields['snapshot'] = forms.ChoiceField( label=_("Use snapshot as a source"), widget=forms.fields.SelectWidget(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-snapshot': _('Snapshot') }, data_attrs=('size', 'name'), transform=lambda x: "%s (%sGiB)" % (x.name, x.size)), required=False) self.fields['metadata'] = forms.CharField( label=_("Metadata"), required=False, widget=forms.Textarea(attrs={'rows': 4})) if self.enable_public_shares: self.fields['is_public'] = forms.BooleanField( label=_("Make visible for all"), required=False, help_text=( "If set then all tenants will be able to see this share.")) self.fields['share_proto'].choices = [ (sp, sp) for sp in self.enabled_share_protocols ] if ("snapshot_id" in request.GET or kwargs.get("data", {}).get("snapshot")): try: snapshot = self.get_snapshot( request, request.GET.get("snapshot_id", kwargs.get("data", {}).get("snapshot"))) self.fields['name'].initial = snapshot.name self.fields['size'].initial = snapshot.size self.fields['snapshot'].choices = ((snapshot.id, snapshot), ) try: # Set the share type from the original share orig_share = manila.share_get(request, snapshot.share_id) # NOTE(vponomaryov): we should use share type name, not ID, # because we use names in our choices above. self.fields['share_type'].initial = ( orig_share.share_type_name) except Exception: pass self.fields['size'].help_text = _( 'Share size must be equal to or greater than the snapshot ' 'size (%sGiB)') % snapshot.size del self.fields['share_source_type'] except Exception: exceptions.handle(request, _('Unable to load the specified snapshot.')) else: source_type_choices = [] try: snapshot_list = manila.share_snapshot_list(request) snapshots = [ s for s in snapshot_list if s.status == 'available' ] if snapshots: source_type_choices.append(("snapshot", _("Snapshot"))) choices = [('', _("Choose a snapshot"))] + \ [(s.id, s) for s in snapshots] self.fields['snapshot'].choices = choices else: del self.fields['snapshot'] except Exception: exceptions.handle(request, _("Unable to retrieve " "share snapshots.")) if source_type_choices: choices = ([('no_source_type', _("No source, empty share"))] + source_type_choices) self.fields['share_source_type'].choices = choices else: del self.fields['share_source_type'] def clean(self): cleaned_data = super(CreateForm, self).clean() errors = [k for k in self.errors.viewkeys()] # NOTE(vponomaryov): skip errors for share-network fields that are not # related to chosen share type. for k in errors: st_name = k.split(self.sn_field_name_prefix)[-1] chosen_st = cleaned_data.get('share_type') if (k.startswith(self.sn_field_name_prefix) and st_name != chosen_st): cleaned_data[k] = 'Not set' self.errors.pop(k, None) share_type = cleaned_data.get('share_type') if share_type: cleaned_data['share_network'] = cleaned_data.get( self.sn_field_name_prefix + share_type) return cleaned_data def handle(self, request, data): try: snapshot_id = None source_type = data.get('share_source_type', None) share_network = data.get('share_network', None) if (data.get("snapshot", None) and source_type in [None, 'snapshot']): # Create from Snapshot snapshot = self.get_snapshot(request, data["snapshot"]) snapshot_id = snapshot.id if (data['size'] < snapshot.size): error_message = _('The share size cannot be less than the ' 'snapshot size (%sGiB)') % snapshot.size raise ValidationError(error_message) else: data['size'] = int(data['size']) metadata = {} try: set_dict, unset_list = utils.parse_str_meta(data['metadata']) if unset_list: msg = _("Expected only pairs of key=value.") raise ValidationError(message=msg) metadata = set_dict except ValidationError as e: self.api_error(e.messages[0]) return False is_public = self.enable_public_shares and data['is_public'] share = manila.share_create( request, size=data['size'], name=data['name'], description=data['description'], proto=data['share_proto'], share_network=share_network, snapshot_id=snapshot_id, share_type=data['share_type'], is_public=is_public, metadata=metadata, availability_zone=data['availability_zone']) message = _('Creating share "%s"') % data['name'] messages.success(request, message) return share 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 share.")) return False @memoized def get_snapshot(self, request, id): return manila.share_snapshot_get(request, id)
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, widget=forms.Textarea(attrs={'rows': 4}), label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) image_url_attrs = { 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location'), 'ng-model': 'ctrl.copyFrom', 'ng-change': 'ctrl.selectImageFormat(ctrl.copyFrom)' } image_url = ImageURLField(label=_("Image Location"), help_text=_("An external (HTTP/HTTPS) URL to " "load the image from."), widget=forms.TextInput(attrs=image_url_attrs), required=False) image_attrs = { 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Image File'), 'ng-model': 'ctrl.imageFile', 'ng-change': 'ctrl.selectImageFormat(ctrl.imageFile.name)', 'image-file-on-change': None } image_file = forms.FileField(label=_("Image File"), help_text=_("A local image to upload."), widget=forms.FileInput(attrs=image_attrs), required=False) kernel = forms.ChoiceField( label=_('Kernel'), required=False, widget=forms.SelectWidget(transform=lambda x: "%s (%s)" % ( x.name, defaultfilters.filesizeformat(x.size)))) ramdisk = forms.ChoiceField( label=_('Ramdisk'), required=False, widget=forms.SelectWidget(transform=lambda x: "%s (%s)" % ( x.name, defaultfilters.filesizeformat(x.size)))) disk_format = forms.ChoiceField( label=_('Format'), choices=[], widget=forms.Select(attrs={ 'class': 'switchable', 'ng-model': 'ctrl.diskFormat' })) architecture = forms.CharField( max_length=255, label=_("Architecture"), help_text=_('CPU architecture of the image.'), required=False) minimum_disk = forms.IntegerField( label=_("Minimum Disk (GB)"), min_value=0, help_text=_('The minimum disk size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) minimum_ram = forms.IntegerField( label=_("Minimum RAM (MB)"), min_value=0, help_text=_('The minimum memory size required to boot the image. ' 'If unspecified, this value defaults to 0 (no minimum).'), required=False) is_copying = forms.BooleanField( label=_("Copy Data"), initial=True, required=False, help_text=_('Specify this option to copy image data to the image ' 'service. If unspecified, image data will be used in its ' 'current location.'), widget=forms.CheckboxInput( attrs={ 'class': 'switched', 'data-source-url': _('Image Location'), 'data-switch-on': 'source' })) is_public = forms.BooleanField( label=_("Public"), help_text=_('Make the image visible across projects.'), required=False) protected = forms.BooleanField( label=_("Protected"), help_text=_('Prevent the deletion of the image.'), required=False) def __init__(self, request, *args, **kwargs): super(CreateImageForm, self).__init__(request, *args, **kwargs) if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or not policy.check( (("image", "upload_image"), ), request)): self._hide_file_source_type() if not policy.check((("image", "set_image_location"), ), request): self._hide_url_source_type() if not policy.check((("image", "publicize_image"), ), request): self._hide_is_public() self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES try: kernel_images = api.glance.image_list_detailed( request, filters={'disk_format': 'aki'})[0] except Exception: kernel_images = [] msg = _('Unable to retrieve image list.') messages.error(request, msg) if kernel_images: choices = [('', _("Choose an image"))] for image in kernel_images: choices.append((image.id, image)) self.fields['kernel'].choices = choices else: del self.fields['kernel'] try: ramdisk_images = api.glance.image_list_detailed( request, filters={'disk_format': 'ari'})[0] except Exception: ramdisk_images = [] msg = _('Unable to retrieve image list.') messages.error(request, msg) if ramdisk_images: choices = [('', _("Choose an image"))] for image in ramdisk_images: choices.append((image.id, image)) self.fields['ramdisk'].choices = choices else: del self.fields['ramdisk'] def _hide_file_source_type(self): self.fields['image_file'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'file' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_url_source_type(self): self.fields['image_url'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'url' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_is_public(self): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... image_file = data.get('image_file', None) image_url = data.get('image_url', None) if not image_url and not image_file: raise ValidationError( _("A image or external image location must be specified.")) elif image_url and image_file: raise ValidationError( _("Can not specify both image and external image location.")) else: return data def handle(self, request, data): meta = create_image_metadata(data) # Add image source file or URL to metadata if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and policy.check( (("image", "upload_image"), ), request) and data.get('image_file', None)): meta['data'] = self.files['image_file'] elif data['is_copying']: meta['copy_from'] = data['image_url'] else: meta['location'] = data['image_url'] try: image = api.glance.image_create(request, **meta) messages.info( request, _('Your image %s has been queued for creation.') % meta['name']) return image except Exception as e: msg = _('Unable to create new image') # TODO(nikunj2512): Fix this once it is fixed in glance client if hasattr(e, 'code') and e.code == 400: if "Invalid disk format" in e.details: msg = _('Unable to create new image: Invalid disk format ' '%s for image.') % meta['disk_format'] elif "Image name too long" in e.details: msg = _('Unable to create new image: Image name too long.') elif "not supported" in e.details: msg = _('Unable to create new image: URL scheme not ' 'supported.') exceptions.handle(request, msg) return False
class 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_COMPUTE: _("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_COMPUTE: _("Host Name") })) location = forms.CharField(label=_("Location"), initial='location', required=False, help_text=_("Physical location of Host.")) 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 cinder_backend = stx_api.sysinv.get_cinder_backend(request) if stx_api.sysinv.CINDER_BACKEND_CEPH not in cinder_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can only be controller. 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_CHOICE_CONTROLLER self.fields['personality'].widget.attrs['disabled'] = 'disabled' # Remove compute personality if in DC mode and region if getattr(self.request.user, 'services_region', None) == 'RegionOne' \ and getattr(settings, 'DC_MODE', False): self.fields['personality'].choices = \ [choice for choice in self.fields['personality'].choices if choice[0] != stx_api.sysinv.PERSONALITY_COMPUTE] # 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['disabled'] = 'disabled' 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 if personality and self.system_mode != constants.SYSTEM_MODE_SIMPLEX: self.fields['personality'].widget.attrs['disabled'] = 'disabled' 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 'compute' in host.subfunctions: mem_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 Cpu Profile Choices 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 # 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['cpuProfile'].widget = forms.widgets.HiddenInput() self.fields[ 'interfaceProfile'].widget = forms.widgets.HiddenInput() if ((personality == 'storage' or 'compute' 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() 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_COMPUTE) else: self._subfunctions = stx_api.sysinv.PERSONALITY_CONTROLLER cleaned_data['subfunctions'] = self._subfunctions return cleaned_data
def __init__(self, request, *args, **kwargs): super(RegistrForm, self).__init__(request, *args, **kwargs) self.registr_err = None initial = kwargs['initial'] if 'initial' in kwargs else dict() self.fields['username'] = forms.CharField( label=_('User name'), max_length=OS_LNAME_LEN, widget=forms.HiddenInput if 'username' in initial else forms.TextInput) self.fields['federated'] = forms.CharField(max_length=OS_LNAME_LEN, widget=forms.HiddenInput) self.fields['givenname'] = forms.CharField( label=_('First name'), max_length=OS_LNAME_LEN, widget=forms.HiddenInput if 'givenname' in initial else forms.TextInput) self.fields['sn'] = forms.CharField( label=_('Last name'), max_length=OS_LNAME_LEN, widget=forms.HiddenInput if 'sn' in initial else forms.TextInput) if initial['needpwd']: self.fields['pwd'] = forms.RegexField( label=_("Password"), max_length=PWD_LEN, widget=forms.PasswordInput(render_value=False), regex=validators.password_validator(), error_messages={ 'invalid': validators.password_validator_msg() }) self.fields['repwd'] = forms.CharField( label=_("Confirm Password"), max_length=PWD_LEN, widget=forms.PasswordInput(render_value=False)) self.fields['email'] = forms.EmailField( label=_('Email Address'), max_length=EMAIL_LEN, widget=forms.HiddenInput if 'email' in initial else forms.TextInput) self.fields['prjaction'] = forms.ChoiceField( label=_('Project action'), #choices = <see later> widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'actsource' })) self.fields['newprj'] = forms.CharField( label=_('Personal project'), max_length=OS_SNAME_LEN, required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Project name') })) self.fields['prjdescr'] = forms.CharField( label=_("Project description"), required=False, widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Project description') })) self.fields['prjpriv'] = forms.BooleanField( label=_("Private project"), required=False, initial=False, widget=forms.widgets.CheckboxInput( attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-newprj': _('Private project') })) self.fields['selprj'] = forms.MultipleChoiceField( label=_('Available projects'), required=False, widget=forms.SelectMultiple( attrs={ 'class': 'switched', 'data-switch-on': 'actsource', 'data-actsource-selprj': _('Select existing project') }), ) self.fields['organization'] = forms.CharField( label=_('Organization'), required=True, widget=forms.HiddenInput if 'organization' in initial else forms.TextInput) phone_regex = settings.HORIZON_CONFIG.get( 'phone_regex', '^\s*\+*[0-9]+[0-9\s.]+\s*$') self.fields['phone'] = forms.RegexField( label=_('Phone number'), required=True, regex=phone_regex, error_messages={'invalid': _("Wrong phone format")}) self.fields['contactper'] = forms.CharField(label=_('Contact person'), required=False) self.fields['notes'] = forms.CharField(label=_('Notes'), required=False, widget=forms.widgets.Textarea()) self.fields['aupok'] = forms.CharField(widget=forms.HiddenInput, initial='reject') import_guest_project() missing_guest = True avail_prjs = list() for prj_entry in Project.objects.exclude(status=PRJ_PRIVATE): if prj_entry.status == PRJ_GUEST: missing_guest = False elif prj_entry.projectid: avail_prjs.append( (prj_entry.projectname, prj_entry.projectname)) self.fields['selprj'].choices = avail_prjs if missing_guest: p_choices = [('selprj', _('Select existing projects')), ('newprj', _('Create new project'))] else: p_choices = [('selprj', _('Select existing projects')), ('newprj', _('Create new project')), ('guestprj', _('Use guest project'))] self.fields['prjaction'].choices = p_choices
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-' + stx_api.sysinv.BM_TYPE_GENERIC: 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-' + stx_api.sysinv.BM_TYPE_GENERIC: 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-' + stx_api.sysinv.BM_TYPE_GENERIC: 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-' + stx_api.sysinv.BM_TYPE_GENERIC: FIELD_LABEL_BM_CONFIRM_PASSWORD }), required=False) def clean(self): cleaned_data = super(BoardManagementAction, self).clean() if cleaned_data.get('bm_type'): if 'bm_ip' not in cleaned_data or not cleaned_data['bm_ip']: raise forms.ValidationError( _('Board management IP address is required.')) raise forms.ValidationError( _('Board management MAC 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 CreateForm(forms.SelfHandlingForm): tenantP = forms.ChoiceField(label=_("Project"), required=True) 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")] # Populate available project_id/name choices all_projects = [] try: # Get list of available projects. all_projects, has_more = api.keystone.tenant_list(request) projects_list = [(project.id, project.name) for project in all_projects] except Exception: projects_list = [] exceptions.handle(self.request, _('Unable to retrieve list of tenants.')) self.fields['tenantP'].choices = projects_list def handle(self, request, data): try: 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) project_id = None if data['tenantP']: project_id = data['tenantP'] server_group = stx_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 as e: exceptions.handle(request, ignore=True) self.api_error(_("Unable to create server group.")) return False
class EditResourceForm(forms.SelfHandlingForm): label = forms.CharField(label=_("Label"), max_length=255, required=True) rackid = forms.ChoiceField(label=_("Rack"), required=True) eiaLocation = forms.CharField(label=_("EIA Location"), max_length=3, required=False) ip_address = forms.CharField( label=_("Host Name or IP Address"), max_length=255, required=True, help_text=_("Specify the fully qualified host name or IP V4" " address used by Operational Management to access" " the resource.")) auth_method = forms.ChoiceField( label=_('Authentication Method'), choices=[(AUTH_METHOD_USER_PWD, _('User ID and Password')), (AUTH_METHOD_USER_KEY, _('User ID and SSH Key'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'auth' }), help_text=_("Indicates the type of credential information used " "by Operational Management to access the resource.")) userID = forms.CharField(label=_("User ID"), max_length=255, required=True) password = forms.CharField( label=_("password"), widget=forms.PasswordInput(attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-0': _("Password") }, render_value=False), help_text=_( "This is the password that Operational Management should" " use when accessing the resource. Specifying a value here does" " not change the password of the user on the resource.")) sshKey = forms.CharField( widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-1': _("SSH Key"), 'rows': 4 }), help_text=_("Paste the private SSH key for the specified resource.")) passphrase = forms.CharField(label=_("passphrase"), widget=forms.PasswordInput( attrs={ 'class': 'switched', 'data-switch-on': 'auth', 'data-auth-1': _("Passphrase") }, render_value=False), required=False) def __init__(self, request, *args, **kwargs): super(EditResourceForm, self).__init__(request, *args, **kwargs) self.fields['rackid'].choices = create_rack_choices(request) def clean(self): cleaned_data = super(EditResourceForm, self).clean() self._clean_auth_ids(cleaned_data) return cleaned_data def _clean_auth_ids(self, data): auth_method = data.get('auth_method') auth_method_changed = int(auth_method) != self.initial['auth_method'] # if we have an sshKey value (but it's empty), or we don't # have an sshKey value at all if (('sshKey' in data and data['sshKey'] == u'') or ('sshKey' not in data)): # and if the auth method has changed to user/key if auth_method_changed and (auth_method == AUTH_METHOD_USER_KEY): # we really do have an error self._errors['sshKey'] = [ "Because you are changing the authentication" " method for this resource to 'User ID and" " SSH Key', you must supply an SSH Key value." ] # else if the auth method is user/key, and the # user id value has changed from its initial value elif ((auth_method == AUTH_METHOD_USER_KEY) and (self.initial['userID'] != data['userID'])): # we really do have an error self._errors['sshKey'] = [ "Because you are using the authentication" " method 'User ID and SSH Key' for this " " resource and are changing the User ID," " you must supply an SSH Key value." ] # else we don't have an sshKey (and) we don't need an ssh key # (no error needed) else: del self._errors['sshKey'] else: # we have an ssh key -- no error needed if 'sshKey' in self._errors: del self._errors['sshKey'] # if we have a password value (but it's empty), or we don't # have a password value at all if (('password' in data and data['password'] == u'') or ('password' not in data)): # and if the auth method has changed to user/password if auth_method_changed and (auth_method == AUTH_METHOD_USER_PWD): # we really do have an error self._errors['password'] = [ "Because you are changing the authentication" " method for this resource to 'User ID and" " Password', you must supply a password value." ] # else if the auth method is user/password, and the user id # value has changed from its initial value elif ((auth_method == AUTH_METHOD_USER_PWD) and (self.initial['userID'] != data['userID'])): # we really do have an error self._errors['password'] = [ "Because you are using the authentication" " method 'User ID and Password' for this" " resource and are changing the User ID," " you must supply password value." ] # else we don't have a password (and) we don't need a password # (no error needed) else: del self._errors['password'] else: # we have have a password -- no error needed if 'password' in self._errors: del self._errors['password'] def handle(self, request, data): __method__ = 'forms.EditResourceForm.handle' try: # We only want to pass the changed fields to the API. # Determine what fields have changed (we will process # authentication-related fields after this block) new_label = None if ('label' in data) and (self.initial['label'] != data['label']): new_label = data['label'] new_user_id = None if ('userID' in data) and (self.initial['userID'] != data['userID']): new_user_id = data['userID'] new_ip_address = None if ('ip_address' in data) and (self.initial['ip_address'] != data['ip_address']): new_ip_address = data['ip_address'] new_rackid = None if ('rackid' in data) and (self.initial['rackid'] != int( data['rackid'])): new_rackid = data['rackid'] new_eia_location = None if ('eiaLocation' in data) and (self.initial['eiaLocation'] != data['eiaLocation']): new_eia_location = data['eiaLocation'] # Need to ensure we pass correct authentication-related fields new_password = None new_ssh_key = None if data['auth_method'] != AUTH_METHOD_USER_KEY: # user id and password authentication if 'password' in data and data['password'] is not None: new_password = data['password'] else: # user id and SSH key (and passphrase) authentication if 'sshKey' in data and data['sshKey'] is not None: new_ssh_key = data['sshKey'] # only specify a passphrase if we are setting the sshKey if 'passphrase' in data and data['passphrase'] is not None: new_password = data['passphrase'] # pass in "None" for original resource label; also, we will # pass in the resource ID so the API knows which resource is # being edited logging.debug( "%s: Attempting to edit resource %s on rack: %s, " "using label: %s, address: %s, user id: %s, eia " "location %s, new rack %s, and authentication " " method: %s", __method__, self.initial['label'], self.initial['rackid'], new_label, new_ip_address, new_user_id, new_eia_location, new_rackid, data['auth_method']) (rc, result_dict) = resource_mgr.change_resource_properties( None, self.initial['resourceId'], new_label, new_user_id, new_password, new_ip_address, new_rackid, new_eia_location, new_ssh_key) if rc is not 0: # Log details of the unsuccessful attempt. logging.error( "%s: Attempt to edit resource %s on rack: %s," " using new label: %s, address: %s, user id:" " %s, eia location %s, new rack %s, and" " authentication method: %s failed.", __method__, self.initial['label'], self.initial['rackid'], new_label, new_ip_address, new_user_id, new_eia_location, new_rackid, data['auth_method']) logging.error( "%s: Unable to edit resource %s to rack %s. A Non-0" " return code returned from resource_mgr.edit_resource." " The return code is: %s. Details of the attempt: " " %s", __method__, self.initial['label'], self.initial['rackid'], rc, result_dict) # failure case -- so use the initial label for logging/message msg = str('Attempt to edit resource ' + self.initial['label'] + " was not successful." + ' Details of the attempt: ' + result_dict) messages.error(request, msg) # Return false in this case so that the dialog is not # dismissed. This gives the end-user a chance to # update the dialog w/o having to re-enter all the # information a second time. return False else: # must have a 0 rc -- display completion msg -- use current # label for message msg = str('Resource ' + data['label'] + ' successfully edited.') messages.success(request, msg) return True except Exception as e: logging.error( "%s: Exception received trying to edit resource." " Exception is: %s", __method__, e) exceptions.handle(request, _('Unable to edit selected resource.')) # In this case, return True so that the dialog closes. # In theory, there are no values the end-user could change # to the dialog inputs that would allow us to proceed. return True
class AddSensorGroup(forms.SelfHandlingForm): DATA_TYPE_CHOICES = ( (None, _("<Select Sensor Data type>")), ('analog', _("Analog Sensor")), ('discrete', _("Discrete Sensor")), ) host_uuid = forms.CharField(label=_("host_uuid"), initial='host_uuid', widget=forms.widgets.HiddenInput) hostname = forms.CharField( label=_("Hostname"), initial='hostname', widget=forms.TextInput(attrs={'readonly': 'readonly'})) sensorgroup_name = forms.CharField( label=_("Sensor Group Name"), required=True, widget=forms.TextInput(attrs={'readonly': 'readonly'})) sensorgroup_datatype = forms.ChoiceField( label=_("Sensor Group Data Type"), required=True, choices=DATA_TYPE_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'datatype' })) sensortype = forms.CharField( label=_("Sensor Type"), required=True, widget=forms.TextInput(attrs={'readonly': 'readonly'})) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(AddSensorGroup, self).__init__(*args, **kwargs) def clean(self): cleaned_data = super(AddSensorGroup, self).clean() return cleaned_data def handle(self, request, data): host_id = data['host_id'] try: del data['host_id'] del data['hostname'] # The REST API takes care of creating the sensorgroup and assoc sensorgroup = stx_api.sysinv.host_sensorgroup_create( request, **data) msg = _('Sensor group was successfully created.') LOG.debug(msg) messages.success(request, msg) return sensorgroup except exc.ClientException as ce: msg = _('Failed to create sensor group.') LOG.info(msg) LOG.error(ce) # Allow REST API error message to appear on UI messages.error(request, ce) # Redirect to host details pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to create sensor group.') LOG.info(msg) LOG.error(e) # if not a rest API error, throw default redirect = reverse(self.failure_url, args=[host_id]) return exceptions.handle(request, message=e, redirect=redirect)
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) kwargs = { 'name': data['name'], 'policies': policies, 'metadata': metadata, 'project_id': project_id } server_group = stx_nova.server_group_create(request, **kwargs) 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 CreateNetworkProfile(forms.SelfHandlingForm): """Create Network Profile form.""" name = forms.CharField(max_length=255, label=_("Name")) segment_type = forms.ChoiceField( label=_('Segment Type'), choices=[('vlan', _('VLAN')), ('overlay', _('Overlay')), ('trunk', _('Trunk'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'segtype' })) # Sub type options available for Overlay segment type sub_type = forms.ChoiceField(label=_('Sub Type'), choices=[('native_vxlan', _('Native VXLAN')), ('enhanced', _('Enhanced VXLAN')), ('other', _('Other'))], required=False, widget=forms.Select( attrs={ 'class': 'switchable switched', 'data-slug': 'subtype', 'data-switch-on': 'segtype', 'data-segtype-overlay': _("Sub Type") })) # Sub type options available for Trunk segment type sub_type_trunk = forms.ChoiceField( label=_('Sub Type'), choices=[('vlan', _('VLAN'))], required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'segtype', 'data-segtype-trunk': _("Sub Type") })) segment_range = forms.CharField( max_length=255, label=_("Segment Range"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'segtype', 'data-segtype-vlan': _("Segment Range"), 'data-segtype-overlay': _("Segment Range") }), help_text=_("1-4093 for VLAN; " "5000-10000 for Overlay")) multicast_ip_range = forms.CharField( max_length=30, label=_("Multicast IP Range"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'subtype', 'data-subtype-native_vxlan': _("Multicast IP Range") }), help_text=_("Multicast IPv4 range" "(e.g. 224.0.0.0-" "224.0.0.100)")) other_subtype = forms.CharField( max_length=255, label=_("Sub Type Value (Manual Input)"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'subtype', 'data-subtype-other': _("Sub Type Value " "(Manual Input)") }), help_text=_("Enter parameter (e.g. GRE)")) physical_network = forms.CharField( max_length=255, label=_("Physical Network"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'segtype', 'data-segtype-vlan': _("Physical Network") })) project = forms.ChoiceField(label=_("Project"), required=False) def __init__(self, request, *args, **kwargs): super(CreateNetworkProfile, self).__init__(request, *args, **kwargs) self.fields['project'].choices = get_tenant_choices(request) def clean(self): # If sub_type is 'other' or 'trunk' then # assign this new value for sub_type cleaned_data = super(CreateNetworkProfile, self).clean() segment_type = cleaned_data.get('segment_type') if segment_type == 'overlay': sub_type = cleaned_data.get('sub_type') if sub_type == 'other': other_subtype = cleaned_data.get('other_subtype') cleaned_data['sub_type'] = other_subtype LOG.debug('subtype is now %(params)s', {'params': other_subtype}) elif segment_type == 'trunk': sub_type_trunk = cleaned_data.get('sub_type_trunk') cleaned_data['sub_type'] = sub_type_trunk LOG.debug('subtype is now %(params)s', {'params': sub_type_trunk}) return cleaned_data def handle(self, request, data): try: LOG.debug('request = %(req)s, params = %(params)s', { 'req': request, 'params': data }) params = { 'name': data['name'], 'segment_type': data['segment_type'], 'sub_type': data['sub_type'], 'segment_range': data['segment_range'], 'physical_network': data['physical_network'], 'multicast_ip_range': data['multicast_ip_range'], 'tenant_id': data['project'] } profile = api.neutron.profile_create(request, **params) msg = _('Network Profile %s ' 'was successfully created.') % data['name'] LOG.debug(msg) messages.success(request, msg) return profile except Exception: redirect = reverse('horizon:router:nexus1000v:index') msg = _('Failed to create network profile %s') % data['name'] LOG.error(msg) exceptions.handle(request, msg, redirect=redirect)
class AddLocalVolumeGroup(forms.SelfHandlingForm): host_id = forms.CharField(label=_("host_id"), initial='host_id', widget=forms.widgets.HiddenInput) ihost_uuid = forms.CharField(label=_("ihost_uuid"), initial='ihost_uuid', widget=forms.widgets.HiddenInput) hostname = forms.CharField( label=_("Hostname"), initial='hostname', widget=forms.TextInput(attrs={'readonly': 'readonly'})) lvm_vg_name = forms.ChoiceField( label=_("Local Volume Group Name"), required=True, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'lvm_vg_name' })) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(AddLocalVolumeGroup, self).__init__(*args, **kwargs) # Populate available volume group choices host_uuid = kwargs['initial']['ihost_uuid'] host_id = kwargs['initial']['host_id'] host = api.sysinv.host_get(self.request, host_id) subfunctions = host.subfunctions # LVGs that are considered as "present" in the system are those # in an adding or provisioned state. ilvg_list = api.sysinv.host_lvg_list(self.request, host_uuid) current_lvg_states = [api.sysinv.LVG_ADD, api.sysinv.LVG_PROV] current_lvgs = [ lvg.lvm_vg_name for lvg in ilvg_list if lvg.vg_state in current_lvg_states ] compatible_lvgs = [] if host.personality.lower().startswith( api.sysinv.PERSONALITY_CONTROLLER): compatible_lvgs += [api.sysinv.LVG_CINDER_VOLUMES] if api.sysinv.SUBFUNCTIONS_COMPUTE in subfunctions: compatible_lvgs += [api.sysinv.LVG_NOVA_LOCAL] allowed_lvgs = set(compatible_lvgs) - set(current_lvgs) ilvg_tuple_list = [] for lvg in allowed_lvgs: ilvg_tuple_list.append((lvg, lvg)) self.fields['lvm_vg_name'].choices = ilvg_tuple_list def clean(self): cleaned_data = super(AddLocalVolumeGroup, self).clean() return cleaned_data def handle(self, request, data): host_id = data['host_id'] try: del data['host_id'] del data['hostname'] # The REST API takes care of creating the stor # and updating disk.foristorid lvg = api.sysinv.host_lvg_create(request, **data) msg = _('Local volume group was successfully created.') LOG.debug(msg) messages.success(request, msg) return lvg except exc.ClientException as ce: msg = _('Failed to create local volume group.') LOG.info(msg) LOG.error(ce) # Allow REST API error message to appear on UI w_msg = str(ce) if ('Warning' in w_msg): LOG.info(ce) messages.warning(request, w_msg.split(':', 1)[-1]) else: LOG.error(ce) messages.error(request, w_msg) # Redirect to host details pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to create local volume group.') LOG.info(msg) LOG.error(e) # if not a rest API error, throw default redirect = reverse(self.failure_url, args=[host_id]) return exceptions.handle(request, message=e, redirect=redirect)
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) ip_protocol = forms.ChoiceField(label=_('Rule'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'protocol'})) port_or_range = forms.ChoiceField(label=_('Open'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.Select(attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'protocol', 'data-protocol-tcp': _('Open'), 'data-protocol-udp': _('Open')})) 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=[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=[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=[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': 'protocol', 'data-protocol-icmp': _('Type')}), 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': 'protocol', 'data-protocol-icmp': _('Code')}), validators=[validate_port_range]) source = forms.ChoiceField(label=_('Source'), 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': 'source'})) cidr = fields.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24)"), version=fields.IPv4 | fields.IPv6, mask=True, widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'source', 'data-source-cidr': _('CIDR')})) security_group = forms.ChoiceField(label=_('Security Group'), required=False, widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-sg': _('Security ' 'Group')})) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # source group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', {}) common_rules = [(k, _(rules_dict[k]['name'])) for k in rules_dict] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule'))] self.fields['ip_protocol'].choices = custom_rules + common_rules self.rules = rules_dict def clean(self): cleaned_data = super(AddRule, self).clean() ip_proto = cleaned_data.get('ip_protocol') port_or_range = cleaned_data.get("port_or_range") source = cleaned_data.get("source") icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) if ip_proto == 'icmp': 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 xrange(-1, 256): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if icmp_code not in xrange(-1, 256): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) cleaned_data['from_port'] = icmp_type cleaned_data['to_port'] = icmp_code elif ip_proto == 'tcp' or ip_proto == 'udp': if port_or_range == "port": cleaned_data["from_port"] = port cleaned_data["to_port"] = port if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) else: cleaned_data['ip_protocol'] = self.rules[ip_proto]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[ip_proto]['from_port']) cleaned_data['to_port'] = int(self.rules[ip_proto]['to_port']) if source == "cidr": cleaned_data['security_group'] = None else: cleaned_data['cidr'] = None return cleaned_data def handle(self, request, data): try: rule = api.nova.security_group_rule_create( request, get_int_or_uuid(data['id']), data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group']) messages.success(request, _('Successfully added rule: %s') % unicode(rule)) return rule except: 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 EditStorageVolume(forms.SelfHandlingForm): id = forms.CharField(widget=forms.widgets.HiddenInput) journal_locations = forms.ChoiceField( label=_("Journal"), required=False, widget=forms.Select(attrs={'data-slug': 'journal_locations'}), help_text=_("Assign disk to journal " "storage volume.")) journal_size_mib = forms.CharField( label=_("Journal Size MiB"), required=False, initial=sysinv.JOURNAL_DEFAULT_SIZE, widget=forms.TextInput(attrs={'data-slug': 'journal_size_mib'}), help_text=_("Journal's size for the " "current OSD.")) failure_url = 'horizon:admin:inventory:detail' def __init__(self, request, *args, **kwargs): super(EditStorageVolume, self).__init__(request, *args, **kwargs) stor = api.sysinv.host_stor_get(self.request, kwargs['initial']['uuid']) initial_journal_location = kwargs['initial']['journal_location'] host_uuid = kwargs['initial']['host_uuid'] # Populate available journal choices. If no journal is available, # then the journal is collocated. avail_journal_list = api.sysinv.host_stor_get_by_function( self.request, host_uuid, 'journal') journal_tuple_list = [] if stor.uuid == initial_journal_location: journal_tuple_list.append((stor.uuid, "Collocated with OSD")) else: journal_tuple_list.append( (initial_journal_location, "%s " % initial_journal_location)) if avail_journal_list: for j in avail_journal_list: if j.uuid != initial_journal_location: journal_tuple_list.append((j.uuid, "%s " % j.uuid)) if stor.uuid != initial_journal_location: journal_tuple_list.append((stor.uuid, "Collocated with OSD")) self.fields['journal_locations'].choices = journal_tuple_list def handle(self, request, data): stor_id = data['id'] try: # Obtain journal information. journal = data['journal_locations'][:] if journal: data['journal_location'] = journal else: data['journal_location'] = None data['journal_size_mib'] = sysinv.JOURNAL_DEFAULT_SIZE del data['journal_locations'] del data['id'] # The REST API takes care of updating the stor journal information. stor = api.sysinv.host_stor_update(request, stor_id, **data) msg = _('Storage volume was successfully updated.') LOG.debug(msg) messages.success(request, msg) return stor except exc.ClientException as ce: msg = _('Failed to update storage volume.') LOG.info(msg) LOG.error(ce) # Allow REST API error message to appear on UI. messages.error(request, ce) # Redirect to host details pg. redirect = reverse(self.failure_url, args=[stor_id]) return shortcuts.redirect(redirect) except Exception as e: msg = _('Failed to update storage volume.') LOG.info(msg) LOG.error(e) # if not a rest API error, throw default redirect = reverse(self.failure_url, args=[stor_id]) return exceptions.handle(request, message=e, redirect=redirect)
class GeneralConfigAction(workflows.Action): data_source_name = forms.CharField(label=_("Name")) data_source_type = forms.ChoiceField( label=_("Data Source Type"), widget=forms.Select(attrs={ "class": "switchable", "data-slug": "ds_type" })) data_source_manila_share = forms.ChoiceField( label=_("Manila share"), required=False, widget=forms.Select( attrs={ "class": "switched", "data-switch-on": "ds_type", "data-ds_type-manila": _("Manila share") })) data_source_url = forms.CharField( label=_("URL"), widget=forms.TextInput( attrs={ "class": "switched", "data-switch-on": "ds_type", "data-ds_type-manila": _("Path on share"), "data-ds_type-swift": _("URL"), "data-ds_type-hdfs": _("URL"), "data-ds_type-maprfs": _("URL") })) data_source_credential_user = forms.CharField( label=_("Source username"), required=False, widget=forms.TextInput( attrs={ "class": "switched", "data-switch-on": "ds_type", "data-ds_type-swift": _("Source username") })) data_source_credential_pass = forms.CharField(widget=forms.PasswordInput( attrs={ 'class': 'switched', 'data-switch-on': 'ds_type', 'data-ds_type-swift': _("Source password"), 'autocomplete': 'off' }), label=_("Source password"), required=False) data_source_description = forms.CharField( label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 4})) is_public = acl_utils.get_is_public_form(_("data source")) is_protected = acl_utils.get_is_protected_form(_("data source")) def __init__(self, request, *args, **kwargs): super(GeneralConfigAction, self).__init__(request, *args, **kwargs) self.fields["data_source_type"].choices = [("swift", "Swift"), ("hdfs", "HDFS"), ("maprfs", "MapR FS")] # If Manila is running, enable it as a choice for a data source if saharaclient.base.is_service_enabled(request, 'share'): self.fields["data_source_type"].choices.append( ("manila", "Manila")) self.fields["data_source_manila_share"].choices = ( self.populate_manila_share_choices(request)) def populate_manila_share_choices(self, request): try: shares = manilaclient.share_list(request) choices = [(s.id, s.name) for s in shares] except Exception: exceptions.handle(request, _("Failed to get list of shares")) choices = [] return choices class Meta(object): name = _("Create Data Source") help_text_template = "data_sources/_create_data_source_help.html"
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 = fields.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24)"), version=fields.IPv4 | fields.IPv6, mask=True, 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 clean(self): cleaned_data = super(AddRule, self).clean() rule_menu = cleaned_data.get('rule_menu') port_or_range = cleaned_data.get("port_or_range") remote = cleaned_data.get("remote") icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) if rule_menu == 'icmp': 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 xrange(-1, 256): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if icmp_code not in xrange(-1, 256): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) cleaned_data['from_port'] = icmp_type cleaned_data['to_port'] = icmp_code elif rule_menu == 'tcp' or rule_menu == 'udp': cleaned_data['ip_protocol'] = rule_menu if port_or_range == "port": cleaned_data["from_port"] = port cleaned_data["to_port"] = port if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) elif rule_menu == 'custom': pass else: 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']) cleaned_data['direction'] = self.rules[rule_menu].get('direction') # 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 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' if remote == "cidr": cleaned_data['security_group'] = None else: cleaned_data['cidr'] = None # If cleaned_data does not contain cidr, cidr is already marked # as invalid, 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') % unicode(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 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_COMPUTE: _("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_COMPUTE: 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_COMPUTE: 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 cinder_backend = stx_api.sysinv.get_cinder_backend(request) if stx_api.sysinv.CINDER_BACKEND_CEPH not in cinder_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can only be controller. 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_CHOICE_CONTROLLER self.fields['personality'].widget.attrs['disabled'] = 'disabled' # Remove compute personality if in DC mode and region if getattr(self.request.user, 'services_region', None) == 'RegionOne' \ and getattr(settings, 'DC_MODE', False): self.fields['personality'].choices = \ [choice for choice in self.fields['personality'].choices if choice[0] != stx_api.sysinv.PERSONALITY_COMPUTE] def clean(self): cleaned_data = super(AddHostInfoAction, self).clean() return cleaned_data
class TemplateForm(forms.SelfHandlingForm): class Meta(object): name = _('Select Template') help_text = _('Select a template to launch a stack.') # TODO(jomara) - update URL choice for template & environment files # w/ client side download when applicable base_choices = [('file', _('File')), ('raw', _('Direct Input'))] url_choice = [('url', _('URL'))] attributes = {'class': 'switchable', 'data-slug': 'templatesource'} template_source = forms.ChoiceField(label=_('Template Source'), choices=base_choices + url_choice, widget=forms.Select(attrs=attributes)) attributes = create_upload_form_attributes('template', 'file', _('Template File')) template_upload = forms.FileField( label=_('Template File'), help_text=_('A local template to upload.'), widget=forms.FileInput(attrs=attributes), required=False) attributes = create_upload_form_attributes('template', 'url', _('Template URL')) template_url = forms.URLField( label=_('Template URL'), help_text=_('An external (HTTP) URL to load the template from.'), widget=forms.TextInput(attrs=attributes), required=False) attributes = create_upload_form_attributes('template', 'raw', _('Template Data')) template_data = forms.CharField( label=_('Template Data'), help_text=_('The raw contents of the template.'), widget=forms.widgets.Textarea(attrs=attributes), required=False) attributes = {'data-slug': 'envsource', 'class': 'switchable'} environment_source = forms.ChoiceField( label=_('Environment Source'), choices=base_choices, widget=forms.Select(attrs=attributes), required=False) attributes = create_upload_form_attributes('env', 'file', _('Environment File')) environment_upload = forms.FileField( label=_('Environment File'), help_text=_('A local environment to upload.'), widget=forms.FileInput(attrs=attributes), required=False) attributes = create_upload_form_attributes('env', 'raw', _('Environment Data')) environment_data = forms.CharField( label=_('Environment Data'), help_text=_('The raw contents of the environment file.'), widget=forms.widgets.Textarea(attrs=attributes), required=False) def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(TemplateForm, self).__init__(*args, **kwargs) def clean(self): cleaned = super(TemplateForm, self).clean() files = self.request.FILES self.clean_uploaded_files('template', _('template'), cleaned, files) self.clean_uploaded_files('environment', _('environment'), cleaned, files) # Validate the template and get back the params. kwargs = {} if cleaned['environment_data']: kwargs['environment'] = cleaned['environment_data'] try: files, tpl =\ api.heat.get_template_files(cleaned.get('template_data'), cleaned.get('template_url')) kwargs['files'] = files kwargs['template'] = tpl validated = api.heat.template_validate(self.request, **kwargs) cleaned['template_validate'] = validated cleaned['template_validate']['files'] = files cleaned['template_validate']['template'] = tpl except Exception as e: raise forms.ValidationError(six.text_type(e)) return cleaned def clean_uploaded_files(self, prefix, field_label, cleaned, files): """Cleans Template & Environment data from form upload. Does some of the crunchy bits for processing uploads vs raw data depending on what the user specified. Identical process for environment data & template data. :type prefix: str :param prefix: prefix (environment, template) of field :type field_label: str :param field_label: translated prefix str for messages :type input_type: dict :param prefix: existing cleaned fields from form :rtype: dict :return: cleaned dict including environment & template data """ upload_str = prefix + "_upload" data_str = prefix + "_data" url = cleaned.get(prefix + '_url') data = cleaned.get(prefix + '_data') has_upload = upload_str in files # Uploaded file handler if has_upload and not url: log_template_name = files[upload_str].name LOG.info('got upload %s' % log_template_name) tpl = files[upload_str].read() if tpl.startswith('{'): try: json.loads(tpl) except Exception as e: msg = _('There was a problem parsing the' ' %(prefix)s: %(error)s') msg = msg % {'prefix': prefix, 'error': e} raise forms.ValidationError(msg) cleaned[data_str] = tpl # URL handler elif url and (has_upload or data): msg = _('Please specify a %s using only one source method.') msg = msg % field_label raise forms.ValidationError(msg) elif prefix == 'template': # Check for raw template input - blank environment allowed if not url and not data: msg = _('You must specify a template via one of the ' 'available sources.') raise forms.ValidationError(msg) def create_kwargs(self, data): kwargs = { 'parameters': data['template_validate'], 'environment_data': data['environment_data'] } if data.get('stack_id'): kwargs['stack_id'] = data['stack_id'] return kwargs def handle(self, request, data): kwargs = self.create_kwargs(data) # NOTE (gabriel): This is a bit of a hack, essentially rewriting this # request so that we can chain it as an input to the next view... # but hey, it totally works. request.method = 'GET' return self.next_view.as_view()(request, **kwargs)
class CreateImageForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(widget=forms.widgets.Textarea(attrs={ 'class': 'modal-body-fixed-width', 'rows': 2 }), label=_("Description"), required=False) source_type = forms.ChoiceField( label=_('Image Source'), required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) copy_from = forms.CharField( max_length=255, label=_("Image Location"), help_text=_("An external (HTTP) URL to load " "the image from."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Image Location'), 'ng-model': 'copyFrom', 'ng-change': 'selectImageFormat(copyFrom)' }), required=False) image_file = forms.FileField( label=_("Load File"), help_text=_("A local image to upload."), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Load File'), 'ng-model': 'imageFile', 'ng-change': 'selectImageFormat(imageFile.name)', 'image-file-on-change': None }), required=False) disk_format = forms.ChoiceField( label=_('Format'), choices=[], widget=forms.Select(attrs={ 'class': 'switchable', 'ng-model': 'diskFormat' })) architecture = forms.CharField( max_length=255, label=_("Architecture"), widget=forms.TextInput(attrs={'readonly': 'readonly'}), required=False) minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"), min_value=0, help_text=_( 'The minimum disk size' ' required to boot the' ' image. If unspecified, this' ' value defaults to 0' ' (no minimum).'), required=False) minimum_ram = forms.IntegerField(label=_("Minimum RAM (MB)"), min_value=0, help_text=_('The minimum memory size' ' required to boot the' ' image. If unspecified, this' ' value defaults to 0 (no' ' minimum).'), required=False) # is_public = forms.BooleanField(label=_("Public"), required=False) # protected = forms.BooleanField(label=_("Protected"), required=False) def __init__(self, request, *args, **kwargs): super(CreateImageForm, self).__init__(request, *args, **kwargs) #if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or if (api.glance.get_image_upload_mode() == 'off' or not policy.check( (("image", "upload_image"), ), request)): self._hide_file_source_type() if not policy.check((("image", "set_image_location"), ), request): self._hide_url_source_type() if not policy.check((("image", "publicize_image"), ), request): self._hide_is_public() self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES def _hide_file_source_type(self): self.fields['image_file'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'file' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_url_source_type(self): self.fields['copy_from'].widget = HiddenInput() source_type = self.fields['source_type'] source_type.choices = [ choice for choice in source_type.choices if choice[0] != 'url' ] if len(source_type.choices) == 1: source_type.widget = HiddenInput() def _hide_is_public(self): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... image_url = data.get('copy_from', None) image_file = data.get('image_file', None) if not image_url and not image_file: raise ValidationError( _("A image or external image location must be specified.")) elif image_url and image_file: raise ValidationError( _("Can not specify both image and external image location.")) else: return data def handle(self, request, data): # Glance does not really do anything with container_format at the # moment. It requires it is set to the same disk_format for the three # Amazon image types, otherwise it just treats them as 'bare.' As such # we will just set that to be that here instead of bothering the user # with asking them for information we can already determine. if data['disk_format'] in ( 'ami', 'aki', 'ari', ): container_format = data['disk_format'] else: container_format = 'bare' data['is_public'] = 'public' properties = {} if data.get('description'): properties['description'] = data['description'] if data.get('kernel'): properties['kernel_id'] = data['kernel'] if data.get('ramdisk'): properties['ramdisk_id'] = data['ramdisk'] if data.get('architecture'): properties['architecture'] = data['architecture'] meta = { 'visibility': data['is_public'], 'protected': False, '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'] } meta.update(properties) if (api.glance.get_image_upload_mode() != 'off' and policy.check( (("image", "upload_image"), ), request) and data.get('image_file', None)): meta['data'] = self.files['image_file'] else: meta['copy_from'] = data['copy_from'] try: image = api.glance.image_create(request, **meta) messages.success( request, _('Your image %s has been queued for creation.') % data['name']) if request.session.has_key('last_activity'): request.session['last_activity'] = int(time.time()) return image except Exception: exceptions.handle(request, _('Unable to create new image.'))