class JobBinaryCreateForm(forms.SelfHandlingForm): NEW_SCRIPT = "newscript" UPLOAD_BIN = "uploadfile" job_binary_name = forms.CharField(label=_("Name")) job_binary_type = forms.ChoiceField( label=_("Storage type"), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'jb_type' })) job_binary_url = forms.CharField(label=_("URL"), required=False, widget=LabeledInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('URL') })) job_binary_internal = forms.ChoiceField( label=_("Internal binary"), required=False, widget=forms.Select( attrs={ 'class': 'switched switchable', 'data-slug': 'jb_internal', 'data-switch-on': 'jb_type', 'data-jb_type-internal-db': _('Internal Binary') })) job_binary_file = forms.FileField( label=_("Upload File"), required=False, widget=forms.ClearableFileInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-uploadfile': _("Upload File") })) job_binary_script_name = forms.CharField( label=_("Script name"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-newscript': _("Script name") })) job_binary_script = forms.CharField( label=_("Script text"), required=False, widget=forms.Textarea( attrs={ 'rows': 4, 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-newscript': _("Script text") })) job_binary_username = forms.CharField( label=_("Username"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('Username') })) job_binary_password = forms.CharField( label=_("Password"), required=False, widget=forms.PasswordInput( attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('Password') })) job_binary_description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea()) def __init__(self, request, *args, **kwargs): super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs) self.help_text_template = ("project/data_processing.job_binaries/" "_create_job_binary_help.html") self.fields["job_binary_type"].choices =\ [("internal-db", "Internal database"), ("swift", "Swift")] self.fields["job_binary_internal"].choices =\ self.populate_job_binary_internal_choices(request) def populate_job_binary_internal_choices(self, request): try: job_binaries = saharaclient.job_binary_internal_list(request) except Exception: exceptions.handle(request, _("Failed to get list of internal binaries.")) job_binaries = [] choices = [(job_binary.id, job_binary.name) for job_binary in job_binaries] choices.insert(0, (self.NEW_SCRIPT, '*Create a script')) choices.insert(0, (self.UPLOAD_BIN, '*Upload a new file')) return choices def handle(self, request, context): try: extra = {} bin_url = "%s://%s" % (context["job_binary_type"], context["job_binary_url"]) if (context["job_binary_type"] == "internal-db"): bin_url = self.handle_internal(request, context) elif (context["job_binary_type"] == "swift"): extra = self.handle_swift(request, context) bin_object = saharaclient.job_binary_create( request, context["job_binary_name"], bin_url, context["job_binary_description"], extra) messages.success(request, "Successfully created job binary") return bin_object except Exception: exceptions.handle(request, _("Unable to create job binary")) return False def get_help_text(self, extra_context=None): text = "" extra_context = extra_context or {} if self.help_text_template: tmpl = template.loader.get_template(self.help_text_template) context = template.RequestContext(self.request, extra_context) text += tmpl.render(context) else: text += defaultfilters.linebreaks(force_text(self.help_text)) return defaultfilters.safe(text) class Meta(object): name = _("Create Job Binary") help_text_template = ("project/data_processing.job_binaries/" "_create_job_binary_help.html") def handle_internal(self, request, context): result = "" bin_id = context["job_binary_internal"] if (bin_id == self.UPLOAD_BIN): try: result = saharaclient.job_binary_internal_create( request, self.get_unique_binary_name( request, request.FILES["job_binary_file"].name), request.FILES["job_binary_file"].read()) bin_id = result.id except Exception: exceptions.handle(request, _("Unable to upload job binary")) return None elif (bin_id == self.NEW_SCRIPT): try: result = saharaclient.job_binary_internal_create( request, self.get_unique_binary_name( request, context["job_binary_script_name"]), context["job_binary_script"]) bin_id = result.id except Exception: exceptions.handle(request, _("Unable to create job binary")) return None return "internal-db://%s" % bin_id def handle_swift(self, request, context): username = context["job_binary_username"] password = context["job_binary_password"] extra = {"user": username, "password": password} return extra def get_unique_binary_name(self, request, base_name): try: internals = saharaclient.job_binary_internal_list(request) except Exception: internals = [] exceptions.handle(request, _("Failed to fetch internal binary list")) names = [internal.name for internal in internals] if base_name in names: return "%s_%s" % (base_name, uuid.uuid1()) return base_name
class StopPluginForm(forms.SelfHandlingForm): uuid = forms.CharField(label=_("Plugin ID"), widget=forms.HiddenInput) name = forms.CharField( label=_('Plugin Name'), widget=forms.TextInput(attrs={'readonly': 'readonly'}) ) delay = forms.IntegerField( label=_("Delay in secs"), required=False, help_text=_("OPTIONAL: seconds to wait before stopping the plugin") ) board_list = forms.MultipleChoiceField( label=_("Boards List"), widget=forms.SelectMultiple( attrs={'class': 'switchable', 'data-slug': 'slug-stop-boards'}), help_text=_("Select boards in this pool ") ) def __init__(self, *args, **kwargs): super(StopPluginForm, self).__init__(*args, **kwargs) # input=kwargs.get('initial',{}) boardslist_length = len(kwargs["initial"]["board_list"]) self.fields["board_list"].choices = kwargs["initial"]["board_list"] self.fields["board_list"].max_length = boardslist_length def handle(self, request, data): counter = 0 if not data["delay"]: data["delay"] = {} else: data["delay"] = {"delay": data["delay"]} for board in data["board_list"]: for key, value in self.fields["board_list"].choices: if key == board: try: plugin = None plugin = iotronic.plugin_action(request, key, data["uuid"], "PluginStop", data["delay"]) # LOG.debug("API: %s %s", plugin, request) message_text = "Plugin stopped successfully on board "\ + str(value) + "." messages.success(request, _(message_text)) if counter != len(data["board_list"]) - 1: counter += 1 else: return plugin except Exception: message_text = "Unable to stop plugin on board " \ + str(value) + "." exceptions.handle(request, _(message_text)) break
class CreatePort(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) admin_state = forms.BooleanField(label=_("Enable Admin State"), initial=True, required=False) device_id = forms.CharField(max_length=100, label=_("Device ID"), help_text=_("Device ID attached to the port"), required=False) device_owner = forms.CharField( max_length=100, label=_("Device Owner"), help_text=_("Owner of the device attached to the port"), required=False) specify_ip = forms.ThemableChoiceField( label=_("Specify IP address or subnet"), help_text=_("To specify a subnet or a fixed IP, select any options."), initial=False, required=False, choices=[('', _("Unspecified")), ('subnet_id', _("Subnet")), ('fixed_ip', _("Fixed IP Address"))], widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'specify_ip', })) subnet_id = forms.ThemableChoiceField( label=_("Subnet"), required=False, widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'specify_ip', 'data-specify_ip-subnet_id': _('Subnet'), })) fixed_ip = forms.IPField( label=_("Fixed IP Address"), required=False, help_text=_("Specify the subnet IP address for the new port"), version=forms.IPv4 | forms.IPv6, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'specify_ip', 'data-specify_ip-fixed_ip': _('Fixed IP Address'), })) mac_address = forms.MACAddressField( label=_("MAC Address"), required=False, help_text=_("Specify the MAC address for the new port")) failure_url = 'horizon:project:networks:detail' def __init__(self, request, *args, **kwargs): super(CreatePort, self).__init__(request, *args, **kwargs) # prepare subnet choices and input area for each subnet subnet_choices = self._get_subnet_choices(kwargs['initial']) if subnet_choices: subnet_choices.insert(0, ('', _("Select a subnet"))) self.fields['subnet_id'].choices = subnet_choices else: self.fields['specify_ip'].widget = forms.HiddenInput() self.fields['subnet_id'].widget = forms.HiddenInput() self.fields['fixed_ip'].widget = forms.HiddenInput() if api.neutron.is_extension_supported(request, 'mac-learning'): self.fields['mac_state'] = forms.BooleanField( label=_("MAC Learning State"), initial=False, required=False) try: if api.neutron.is_extension_supported(request, 'port-security'): self.fields['port_security_enabled'] = forms.BooleanField( label=_("Port Security"), help_text=_("Enable anti-spoofing rules for the port"), initial=True, required=False) except Exception: msg = _("Unable to retrieve port security state") exceptions.handle(self.request, msg) def _get_subnet_choices(self, kwargs): try: network_id = kwargs['network_id'] network = api.neutron.network_get(self.request, network_id) except Exception: return [] return [(subnet.id, '%s %s' % (subnet.name_or_id, subnet.cidr)) for subnet in network.subnets] def handle(self, request, data): try: params = { 'network_id': self.initial['network_id'], 'admin_state_up': data['admin_state'], 'name': data['name'], 'device_id': data['device_id'], 'device_owner': data['device_owner'] } if data.get('specify_ip') == 'subnet_id': if data.get('subnet_id'): params['fixed_ips'] = [{"subnet_id": data['subnet_id']}] elif data.get('specify_ip') == 'fixed_ip': if data.get('fixed_ip'): params['fixed_ips'] = [{"ip_address": data['fixed_ip']}] if data.get('mac_state'): params['mac_learning_enabled'] = data['mac_state'] if 'port_security_enabled' in data: params['port_security_enabled'] = data['port_security_enabled'] # Send mac_address only when it is specified. if data['mac_address']: params['mac_address'] = data['mac_address'] port = api.neutron.port_create(request, **params) if port['name']: msg = _('Port %s was successfully created.') % port['name'] else: msg = _('Port %s was successfully created.') % port['id'] messages.success(request, msg) return port except Exception as e: LOG.info('Failed to create a port for network %(id)s: %(exc)s', { 'id': self.initial['network_id'], 'exc': e }) msg = (_('Failed to create a port for network %s') % self.initial['network_id']) redirect = reverse(self.failure_url, args=(self.initial['network_id'], )) exceptions.handle(request, msg, redirect=redirect)
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.ThemableSelectWidget(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.ThemableSelectWidget(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) if django.VERSION >= (1, 9): # Note(Itxaka): On django>=1.9 Charfield has an strip option that # we need to set to False as to not hit # https://bugs.launchpad.net/python-heatclient/+bug/1546166 environment_data.strip = False template_data.strip = 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': six.text_type(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 TemplateForm(forms.SelfHandlingForm): class Meta: name = _('Select Template') help_text = _('From here you can select a template to launch ' 'a stack.') template_source = forms.ChoiceField( label=_('Template Source'), choices=[('url', _('URL')), ('file', _('File')), ('raw', _('Direct Input'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' })) template_upload = forms.FileField( label=_('Template File'), help_text=_('A local template to upload.'), widget=forms.FileInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-file': _('Template File') }), required=False) template_url = forms.URLField( label=_('Template URL'), help_text=_('An external (HTTP) URL to load the template from.'), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-url': _('Template URL') }), required=False) template_data = forms.CharField( label=_('Template Data'), help_text=_('The raw contents of the template.'), widget=forms.widgets.Textarea( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-raw': _('Template Data') }), required=False) def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(TemplateForm, self).__init__(*args, **kwargs) def clean(self): cleaned = super(TemplateForm, self).clean() template_url = cleaned.get('template_url') template_data = cleaned.get('template_data') files = self.request.FILES has_upload = 'template_upload' in files # Uploaded file handler if has_upload and not template_url: log_template_name = self.request.FILES['template_upload'].name LOG.info('got upload %s' % log_template_name) tpl = self.request.FILES['template_upload'].read() if tpl.startswith('{'): try: json.loads(tpl) except Exception as e: msg = _('There was a problem parsing the template: %s') % e raise forms.ValidationError(msg) cleaned['template_data'] = tpl # URL handler elif template_url and (has_upload or template_data): msg = _('Please specify a template using only one source method.') raise forms.ValidationError(msg) # Check for raw template input elif not template_url and not template_data: msg = _('You must specify a template via one of the ' 'available sources.') raise forms.ValidationError(msg) # Validate the template and get back the params. kwargs = {} if cleaned['template_data']: kwargs['template'] = cleaned['template_data'] else: kwargs['template_url'] = cleaned['template_url'] try: validated = api.heat.template_validate(self.request, **kwargs) cleaned['template_validate'] = validated except Exception as e: msg = exception_to_validation_msg(e) if not msg: msg = _('An unknown problem occurred validating the template.') LOG.exception(msg) raise forms.ValidationError(msg) return cleaned def handle(self, request, data): kwargs = { 'parameters': data['template_validate'], 'template_data': data['template_data'], 'template_url': data['template_url'] } # NOTE (gabriel): This is a bit of a hack, essentially rewriting this # request so that we can chain it as an input to the next view... # but hey, it totally works. request.method = 'GET' return self.next_view.as_view()(request, **kwargs)
class UpdatePort(forms.SelfHandlingForm): network_id = forms.CharField(widget=forms.HiddenInput()) port_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) name = forms.CharField(max_length=255, label=_("Name"), required=False) admin_state = forms.BooleanField(label=_("Enable Admin State"), required=False) failure_url = 'horizon:project:networks:detail' def __init__(self, request, *args, **kwargs): super(UpdatePort, self).__init__(request, *args, **kwargs) try: if api.neutron.is_extension_supported(request, 'binding'): neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) supported_vnic_types = neutron_settings.get( 'supported_vnic_types', ['*']) if supported_vnic_types: if supported_vnic_types == ['*']: vnic_type_choices = VNIC_TYPES else: vnic_type_choices = [ vnic_type for vnic_type in VNIC_TYPES if vnic_type[0] in supported_vnic_types ] self.fields['binding__vnic_type'] = forms.ChoiceField( choices=vnic_type_choices, label=_("Binding: VNIC Type"), help_text=_( "The VNIC type that is bound to the neutron port"), required=False) except Exception: msg = _("Unable to verify the VNIC types extension in Neutron") exceptions.handle(self.request, msg) try: if api.neutron.is_extension_supported(request, 'mac-learning'): self.fields['mac_state'] = forms.BooleanField( label=_("MAC Learning State"), initial=False, required=False) except Exception: msg = _("Unable to retrieve MAC learning state") exceptions.handle(self.request, msg) try: if api.neutron.is_extension_supported(request, 'port-security'): self.fields['port_security_enabled'] = forms.BooleanField( label=_("Port Security"), help_text=_("Enable anti-spoofing rules for the port"), required=False) except Exception: msg = _("Unable to retrieve port security state") exceptions.handle(self.request, msg) def handle(self, request, data): try: LOG.debug('params = %s', data) extension_kwargs = {} if 'binding__vnic_type' in data: extension_kwargs['binding__vnic_type'] = \ data['binding__vnic_type'] if 'mac_state' in data: extension_kwargs['mac_learning_enabled'] = data['mac_state'] if 'port_security_enabled' in data: extension_kwargs['port_security_enabled'] = \ data['port_security_enabled'] port = api.neutron.port_update(request, data['port_id'], name=data['name'], admin_state_up=data['admin_state'], **extension_kwargs) msg = _('Port %s was successfully updated.') % data['port_id'] messages.success(request, msg) return port except Exception as e: LOG.info('Failed to update port %(id)s: %(exc)s', { 'id': data['port_id'], 'exc': e }) msg = _('Failed to update port %s') % data['port_id'] redirect = reverse(self.failure_url, args=[data['network_id']]) exceptions.handle(request, msg, redirect=redirect)
def __init__(self, request, *args, **kwargs): super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs) self.help_text_template = ("project/data_processing.job_binaries/" "_create_job_binary_help.html") self.fields["job_binary_name"] = forms.CharField(label=_("Name")) self.fields["job_binary_type"] = forms.ChoiceField( label=_("Storage type"), widget=forms.Select( attrs={ 'class': 'switchable', 'data-slug': 'jb_type' })) self.fields["job_binary_url"] = forms.CharField( label=_("URL"), required=False, widget=LabeledInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('URL') })) self.fields["job_binary_internal"] = forms.ChoiceField( label=_("Internal binary"), required=False, widget=forms.Select( attrs={ 'class': 'switched switchable', 'data-slug': 'jb_internal', 'data-switch-on': 'jb_type', 'data-jb_type-internal-db': _('Internal Binary') })) self.fields["job_binary_file"] = forms.FileField( label=_("Upload File"), required=False, widget=forms.ClearableFileInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-uploadfile': _("Upload File") })) self.fields["job_binary_script_name"] = forms.CharField( label=_("Script name"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-newscript': _("Script name") })) self.fields["job_binary_script"] = forms.CharField( label=_("Script text"), required=False, widget=forms.Textarea( attrs={ 'rows': 4, 'class': 'switched', 'data-switch-on': 'jb_internal', 'data-jb_internal-newscript': _("Script text") })) self.fields["job_binary_username"] = forms.CharField( label=_("Username"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('Username') })) self.fields["job_binary_password"] = forms.CharField( label=_("Password"), required=False, widget=forms.PasswordInput( attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'jb_type', 'data-jb_type-swift': _('Password') })) self.fields["job_binary_description"] = ( forms.CharField(label=_("Description"), required=False, widget=forms.Textarea())) self.fields["job_binary_type"].choices =\ [("internal-db", "Internal database"), ("swift", "Swift")] self.fields["job_binary_internal"].choices =\ self.populate_job_binary_internal_choices(request) self.load_form_values()
class AddInterface(forms.SelfHandlingForm): subnet_id = forms.ChoiceField(label=_("Subnet")) ip_address = forms.IPField(label=_("IP Address (optional)"), required=False, initial="", help_text=_( "Specify an IP address for the interface " "created (e.g. 192.168.0.254)."), version=forms.IPv4 | forms.IPv6, mask=False) router_name = forms.CharField( label=_("Router Name"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) router_id = forms.CharField( label=_("Router ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) failure_url = 'horizon:project:routers:detail' def __init__(self, request, *args, **kwargs): super(AddInterface, self).__init__(request, *args, **kwargs) c = self.populate_subnet_id_choices(request) self.fields['subnet_id'].choices = c def populate_subnet_id_choices(self, request): tenant_id = self.request.user.tenant_id networks = [] router_subnet_ids = [] router_id = request.REQUEST.get('router_id', self.initial.get('router_id')) try: networks = api.neutron.network_list_for_tenant(request, tenant_id) if router_id: ports = api.neutron.port_list(request, device_id=router_id) router_subnet_ids = [ fixed_ip["subnet_id"] for port in ports for fixed_ip in port.fixed_ips ] except Exception as e: msg = _('Failed to get network list %s') % e LOG.info(msg) messages.error(request, msg) if router_id: redirect = reverse(self.failure_url, args=[router_id]) else: redirect = reverse('horizon:project:routers:index') exceptions.handle(request, msg, redirect=redirect) return choices = [] for n in networks: net_name = n.name + ': ' if n.name else '' choices += [(subnet.id, '%s%s (%s)' % (net_name, subnet.cidr, subnet.name or subnet.id)) for subnet in n['subnets'] if subnet.id not in router_subnet_ids] if choices: choices.insert(0, ("", _("Select Subnet"))) else: choices.insert(0, ("", _("No subnets available"))) return choices def handle(self, request, data): if data['ip_address']: port = self._add_interface_by_port(request, data) else: port = self._add_interface_by_subnet(request, data) msg = _('Interface added') if port: msg += ' ' + port.fixed_ips[0]['ip_address'] LOG.debug(msg) messages.success(request, msg) return True def _add_interface_by_subnet(self, request, data): router_id = data['router_id'] try: router_inf = api.neutron.router_add_interface( request, router_id, subnet_id=data['subnet_id']) except Exception as e: self._handle_error(request, router_id, e) try: port = api.neutron.port_get(request, router_inf['port_id']) except Exception: # Ignore an error when port_get() since it is just # to get an IP address for the interface. port = None return port def _add_interface_by_port(self, request, data): router_id = data['router_id'] subnet_id = data['subnet_id'] try: subnet = api.neutron.subnet_get(request, subnet_id) except Exception: msg = _('Unable to get subnet "%s"') % subnet_id self._handle_error(request, router_id, msg) try: ip_address = data['ip_address'] body = { 'network_id': subnet.network_id, 'fixed_ips': [{ 'subnet_id': subnet.id, 'ip_address': ip_address }] } port = api.neutron.port_create(request, **body) except Exception as e: self._handle_error(request, router_id, e) try: api.neutron.router_add_interface(request, router_id, port_id=port.id) except Exception as e: self._delete_port(request, port) self._handle_error(request, router_id, e) return port def _handle_error(self, request, router_id, reason): msg = _('Failed to add_interface: %s') % reason LOG.info(msg) redirect = reverse(self.failure_url, args=[router_id]) exceptions.handle(request, msg, redirect=redirect) def _delete_port(self, request, port): try: api.neutron.port_delete(request, port.id) except Exception: msg = _('Failed to delete port %s') % port.id LOG.info(msg) exceptions.handle(request, msg)
class UpdateProviderNetworkRange(forms.SelfHandlingForm): failure_url = 'horizon:admin:datanets:datanets:detail' providernet_id = forms.CharField(widget=forms.HiddenInput()) providernet_range_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField( max_length=255, label=_("Name"), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(max_length=255, label=_("Description"), required=False) minimum = forms.IntegerField(label=_("Minimum"), min_value=1) maximum = forms.IntegerField(label=_("Maximum"), min_value=1) shared = forms.BooleanField(widget=forms.HiddenInput(), required=False) tenant_id = forms.CharField(widget=forms.HiddenInput(), required=False) # VXLAN specific fields mode_widget = forms.TextInput(attrs={'readonly': 'readonly'}) mode = forms.CharField(label=_("mode"), required=False, widget=mode_widget) group_widget = forms.TextInput(attrs={'readonly': 'readonly'}) group = forms.CharField(max_length=255, label=_("Multicast Group Address"), required=False, widget=group_widget) port_widget = forms.RadioSelect(attrs={'disabled': 'disabled'}) port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')), ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')), ('8472', _('Legacy VXLAN UDP port (8472)'))] port = forms.ChoiceField(label=_("UDP Port"), required=False, widget=port_widget, choices=port_choices) ttl_widget = forms.TextInput(attrs={'readonly': 'readonly'}) ttl = forms.IntegerField(label=_("TTL"), required=False, widget=ttl_widget) def __init__(self, request, *args, **kwargs): super(UpdateProviderNetworkRange, self).__init__(request, *args, **kwargs) initial = kwargs['initial'] if 'mode' not in initial: del self.fields["mode"] if 'group' not in initial or initial.get('mode') == 'static': del self.fields["group"] if 'port' not in initial: del self.fields["port"] if 'ttl' not in initial: del self.fields["ttl"] def handle(self, request, data): try: params = { 'description': data['description'], 'minimum': data['minimum'], 'maximum': data['maximum'] } providernet_range = stx_api.neutron.provider_network_range_modify( request, data['providernet_range_id'], **params) msg = (_('Provider network range %s was successfully updated.') % data['providernet_range_id']) LOG.debug(msg) messages.success(request, msg) return providernet_range except neutron_exceptions.NeutronClientException as e: LOG.info(str(e)) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, str(e), redirect=redirect) except Exception: msg = (_('Failed to update provider network range %s') % data['providernet_range_id']) LOG.info(msg) redirect = reverse(self.failure_url, args=[data['providernet_id']]) exceptions.handle(request, msg, redirect=redirect)
class UpdateIpam(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(label=_("Name"), widget=forms.TextInput( attrs={'readonly': 'readonly'}), validators=[validators.validate_slug]) dnsmethod = forms.ChoiceField(label=_('DNS Method'), choices=[('default', _('Default')), ('vdns', _('Virtual DNS')), ('tenantdns', _('Tenant DNS')), ('none', _('None'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'dnsmethod'})) vdns = forms.CharField(label=_("Virtual DNS"), required=False, help_text=_("FQ Name of Virtual DNS i.e. default-domain:vdns"), widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-vdns': _('Virtual DNS')})) tenantdns = fields.IPField(label=_("Tenant DNS Server IP"), required=False, help_text=_("Tenant managed DNS Server's IP Address"), version=fields.IPv4, mask=False, widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-tenantdns': _('Tenant DNS Server IP')})) ntpip = fields.IPField(label=_("NTP Server IP"), required=False, help_text=_("IP Address of the NTP Server"), version=fields.IPv4, mask=False, widget=forms.TextInput()) domainname = forms.CharField(label=_("Domain Name"), required=False, help_text=_("Domain Name i.e. openstack.org"), widget=forms.TextInput()) def __init__(self, *args, **kwargs): ipam_obj = kwargs.pop('ipam_obj', {}) mgmt = ipam_obj.__dict__['_apidict']['mgmt'] super(UpdateIpam, self).__init__(*args, **kwargs) if mgmt['ipam_dns_method'] == 'default-dns-server': self.fields['dnsmethod'].initial = 'default' if mgmt['ipam_dns_method'] == 'tenant-dns-server': self.fields['dnsmethod'].initial = 'tenantdns' if 'ipam_dns_server' in mgmt and \ 'tenant_dns_server_address' in mgmt['ipam_dns_server'] and \ 'ip_address' in mgmt['ipam_dns_server']['tenant_dns_server_address'] : for ip in mgmt['ipam_dns_server']['tenant_dns_server_address']['ip_address']: self.fields['tenantdns'].initial = ip if mgmt['ipam_dns_method'] == 'virtual-dns-server': self.fields['dnsmethod'].initial = 'vdns' if 'ipam_dns_server' in mgmt and \ 'virtual_dns_server_name' in mgmt['ipam_dns_server'] and \ mgmt['ipam_dns_server']['virtual_dns_server_name'] != None : self.fields['vdns'].initial = mgmt['ipam_dns_server']['virtual_dns_server_name'] if mgmt['ipam_dns_method'] == 'none': self.fields['dnsmethod'].initial = 'none' if 'dhcp_option_list' in mgmt: dhcp_list = mgmt['dhcp_option_list'] if dhcp_list and 'dhcp_option' in dhcp_list: for entry in mgmt['dhcp_option_list']['dhcp_option']: if entry['dhcp_option_name'] == '4': self.fields['ntpip'].initial = entry['dhcp_option_value'] if entry['dhcp_option_name'] == '15': self.fields['domainname'].initial = entry['dhcp_option_value'] def clean(self): cleaned_data = super(UpdateIpam, self).clean() name = cleaned_data.get("name") dnsmethod = cleaned_data.get("dnsmethod") vdns = cleaned_data.get("vdns") tenantdns = cleaned_data.get("tenantdns") ntpip = cleaned_data.get("ntpip") domainname = cleaned_data.get("domainname") if dnsmethod == 'vdns' and not len(vdns): msg = _('Virtual DNS : Enter a valid Virtual DNS in FQN format') raise ValidationError(msg) if dnsmethod == 'tenantdns': if not tenantdns: msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) elif not len(tenantdns): msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) return cleaned_data def handle(self, request, data): params = {'mgmt': {'ipam_method': None, 'dhcp_option_list': {'dhcp_option':[]}}} if data['domainname']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '15', 'dhcp_option_value': data['domainname']}) if data['ntpip']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '4', 'dhcp_option_value': data['ntpip']}) params['mgmt']['ipam_dns_server'] = {} params['mgmt']['ipam_dns_server']['tenant_dns_server_address'] = {} params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = None if data['dnsmethod'] == 'default': params['mgmt']['ipam_dns_method'] = 'default-dns-server' if data['dnsmethod'] == 'none': params['mgmt']['ipam_dns_method'] = 'none' if data['dnsmethod'] == 'tenantdns': params['mgmt']['ipam_dns_method'] = 'tenant-dns-server' if data['tenantdns']: params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'] = [] params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'].append(data['tenantdns']) if data['dnsmethod'] == 'vdns': params['mgmt']['ipam_dns_method'] = 'virtual-dns-server' params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = data['vdns'] try: ipam = ipam_modify(request, ipam_id=data['id'], **params) messages.success(request, _('Successfully updated network ipam: %s') % data['name']) return ipam except Exception: redirect = reverse("horizon:project:networking:index") exceptions.handle(request, _('Unable to update network ipam.'), redirect=redirect)
class CreateNetworkIpam(forms.SelfHandlingForm): name = forms.CharField(label=_("Name"), error_messages={ 'required': _('This field is required.'), 'invalid': _("The string may only contain" " ASCII characters and numbers.")}, validators=[validators.validate_slug]) dnsmethod = forms.ChoiceField(label=_('DNS Method'), choices=[('default', _('Default')), ('vdns', _('Virtual DNS')), ('tenantdns', _('Tenant DNS')), ('none', _('None'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'dnsmethod'})) vdns = forms.CharField(label=_("Virtual DNS"), required=False, help_text=_("FQ Name of Virtual DNS i.e. default-domain:vdns"), widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-vdns': _('Virtual DNS')})) tenantdns = fields.IPField(label=_("Tenant DNS Server IP"), required=False, help_text=_("Tenant managed DNS Server's IP Address"), version=fields.IPv4, mask=False, widget=forms.TextInput( attrs={'class': 'switched', 'data-switch-on': 'dnsmethod', 'data-dnsmethod-tenantdns': _('Tenant DNS Server IP')})) ntpip = fields.IPField(label=_("NTP Server IP"), required=False, help_text=_("IP Address of the NTP Server"), version=fields.IPv4, mask=False, widget=forms.TextInput()) domainname = forms.CharField(label=_("Domain Name"), required=False, help_text=_("Domain Name i.e. openstack.org"), widget=forms.TextInput()) def clean(self): cleaned_data = super(CreateNetworkIpam, self).clean() name = cleaned_data.get("name") dnsmethod = cleaned_data.get("dnsmethod") vdns = cleaned_data.get("vdns") tenantdns = cleaned_data.get("tenantdns") ntpip = cleaned_data.get("ntpip") domainname = cleaned_data.get("domainname") if dnsmethod == 'vdns' and not len(vdns): msg = _('Virtual DNS : Enter a valid Virtual DNS in FQN format') raise ValidationError(msg) if dnsmethod == 'tenantdns': if not tenantdns: msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) elif not len(tenantdns): msg = _('Tenant DNS Server IP : Enter Tenant DNS Server IP address') raise ValidationError(msg) return cleaned_data def handle(self, request, data): params = {'name': data['name'], 'mgmt': {'ipam_method': None, 'dhcp_option_list': {'dhcp_option':[]}}} if data['domainname']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '15', 'dhcp_option_value': data['domainname']}) if data['ntpip']: params['mgmt']['dhcp_option_list']['dhcp_option'].append( {'dhcp_option_name': '4', 'dhcp_option_value': data['ntpip']}) params['mgmt']['ipam_dns_server'] = {} params['mgmt']['ipam_dns_server']['tenant_dns_server_address'] = {} params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = None if data['dnsmethod'] == 'default': params['mgmt']['ipam_dns_method'] = 'default-dns-server' if data['dnsmethod'] == 'none': params['mgmt']['ipam_dns_method'] = 'none' if data['dnsmethod'] == 'tenantdns': params['mgmt']['ipam_dns_method'] = 'tenant-dns-server' if data['tenantdns']: params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'] = [] params['mgmt']['ipam_dns_server']['tenant_dns_server_address']['ip_address'].append(data['tenantdns']) if data['dnsmethod'] == 'vdns': params['mgmt']['ipam_dns_method'] = 'virtual-dns-server' params['mgmt']['ipam_dns_server']['virtual_dns_server_name'] = data['vdns'] try: ipam = ipam_create(request, **params) messages.success(request, _('Successfully created network ipam: %s') % data['name']) return ipam except Exception: redirect = reverse("horizon:project:networking:index") exceptions.handle(request, _('Unable to create network ipam.'), redirect=redirect)
class AddMonitorAction(workflows.Action): type = forms.ChoiceField(label=_("Type"), choices=[('ping', _('PING')), ('tcp', _('TCP')), ('http', _('HTTP')), ('https', _('HTTPS'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'type' })) delay = forms.IntegerField( min_value=1, label=_("Delay"), help_text=_("The minimum time in seconds between regular checks " "of a member")) timeout = forms.IntegerField( min_value=1, label=_("Timeout"), help_text=_("The maximum time in seconds for a monitor to wait " "for a reply")) max_retries = forms.IntegerField( max_value=10, min_value=1, label=_("Max Retries (1~10)"), help_text=_("Number of permissible failures before changing " "the status of member to inactive")) http_method = forms.ChoiceField( initial="GET", required=False, choices=[('GET', _('GET'))], label=_("HTTP Method"), help_text=_("HTTP method used to check health status of a member"), widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'type', 'data-type-http': _('HTTP Method'), 'data-type-https': _('HTTP Method') })) url_path = forms.CharField(initial="/", required=False, max_length=80, label=_("URL"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'type', 'data-type-http': _('URL'), 'data-type-https': _('URL') })) expected_codes = forms.RegexField( initial="200", required=False, max_length=80, regex=r'^(\d{3}(\s*,\s*\d{3})*)$|^(\d{3}-\d{3})$', label=_("Expected HTTP Status Codes"), help_text=_("Expected code may be a single value (e.g. 200), " "a list of values (e.g. 200, 202), " "or range of values (e.g. 200-204)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'type', 'data-type-http': _('Expected HTTP Status Codes'), 'data-type-https': _('Expected HTTP Status Codes') })) admin_state_up = forms.BooleanField(label=_("Admin State"), initial=True, required=False) def __init__(self, request, *args, **kwargs): super(AddMonitorAction, self).__init__(request, *args, **kwargs) def clean(self): cleaned_data = super(AddMonitorAction, self).clean() type_opt = cleaned_data.get('type') if type_opt in ['http', 'https']: http_method_opt = cleaned_data.get('http_method') url_path = cleaned_data.get('url_path') expected_codes = cleaned_data.get('expected_codes') if not http_method_opt: msg = _('Please choose a HTTP method') self._errors['http_method'] = self.error_class([msg]) if not url_path: msg = _('Please specify an URL') self._errors['url_path'] = self.error_class([msg]) if not expected_codes: msg = _('Please enter a single value (e.g. 200), ' 'a list of values (e.g. 200, 202), ' 'or range of values (e.g. 200-204)') self._errors['expected_codes'] = self.error_class([msg]) return cleaned_data class Meta: name = _("Add New Monitor") permissions = ('openstack.services.network', ) help_text = _("Create a monitor template.\n\n" "Select type of monitoring. " "Specify delay, timeout, and retry limits " "required by the monitor. " "Specify method, URL path, and expected " "HTTP codes upon success.")
class CreateECStoragePolicy(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), help_text=_("The name of the new policy."), widget=forms.TextInput(attrs={ "ng-model": "name", "not-blank": "" })) partition_power = forms.CharField( max_length=255, label=_("Partiton Power"), help_text=_("If the value is x the num of partitions will be 2^x"), initial=10) time = forms.CharField( max_length=255, label=_("Time"), help_text=_( "Time between moving a partition more than once. In hours"), initial=1) ec_type = forms.CharField( max_length=255, label=_("EC Type"), required=True, help_text=_( "Is chosen from the list of EC backends supported by PyECLib"), initial='liberasurecode_rs_vand') ec_num_data_fragments = forms.CharField( max_length=255, label=_("Num. Data Fragments"), required=True, help_text=_( "The total number of fragments that will be comprised of data."), initial=10) ec_num_parity_fragments = forms.CharField( max_length=255, label=_("Num. Parity Fragments"), required=True, help_text=_( "The total number of fragments that will be comprised of parity."), initial=4) ec_object_segment_size = forms.CharField( max_length=255, label=_("Object Segment Size"), required=True, help_text= _("The amount of data that will be buffered up before feeding a segment into the encoder/decoder." ), initial=1048576) ec_duplication_factor = forms.CharField( max_length=255, label=_("Duplication Factor"), required=True, help_text= _("EC Duplication enables Swift to make duplicated copies of fragments of erasure coded objects." ), initial=1) policy_type = forms.CharField(widget=forms.HiddenInput(), initial='EC') deprecated = forms.CharField(widget=forms.HiddenInput(), initial='False') deployed = forms.CharField(widget=forms.HiddenInput(), initial='False') devices = forms.CharField(widget=forms.HiddenInput(), initial='[]') default = forms.CharField(widget=forms.HiddenInput(), initial='False') def __init__(self, request, *args, **kwargs): super(CreateECStoragePolicy, self).__init__(request, *args, **kwargs) def handle(self, request, data): try: response = api.swift_new_storage_policy(request, data) if 200 <= response.status_code < 300: messages.success(request, _("Storage policy successfully created.")) return data else: raise sdsexception.SdsException(response.text) except Exception as ex: redirect = reverse("horizon:crystal:rings:index") error_message = "Unable to create storage policy.\t %s" % ex.message exceptions.handle(request, _(error_message), redirect=redirect)
class CreateNetworkProfile(forms.SelfHandlingForm): """ Create Network Profile form.""" name = forms.CharField(max_length=255, label=_("Name"), required=True) segment_type = forms.ChoiceField( label=_('Segment Type'), choices=[('vlan', _('VLAN')), ('vxlan', _('VXLAN'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'segtype' })) segment_range = forms.CharField(max_length=255, label=_("Segment Range"), required=True, help_text=_("1-4093 for VLAN")) # TODO(absubram): Update help text for VXLAN segment range value. multicast_ip_range = forms.CharField( max_length=30, label=_("Multicast IP Range"), required=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'segtype', 'data-segtype-vxlan': _("Multicast IP Range") })) 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_id = forms.ChoiceField(label=_("Project"), required=False) def __init__(self, request, *args, **kwargs): super(CreateNetworkProfile, self).__init__(request, *args, **kwargs) self.fields['project_id'].choices = get_tenant_choices(request) def handle(self, request, data): try: LOG.debug(_('request = %(req)s, params = %(params)s'), { 'req': request, 'params': data }) profile = api.neutron.profile_create( request, name=data['name'], segment_type=data['segment_type'], segment_range=data['segment_range'], physical_network=data['physical_network'], multicast_ip_range=data['multicast_ip_range'], tenant_id=data['project_id']) 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 UpdateHostInfoAction(workflows.Action): host_id = forms.CharField(widget=forms.widgets.HiddenInput) personality = forms.ChoiceField( label=_("Personality"), choices=PERSONALITY_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'personality' })) subfunctions = forms.ChoiceField( label=FIELD_LABEL_PERFORMANCE_PROFILE, choices=PERFORMANCE_CHOICES, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Performance Profile") })) hostname = forms.RegexField( label=_("Host Name"), max_length=255, required=False, regex=r'^[\w\.\-]+$', error_messages={ 'invalid': _('Name may only contain letters,' ' numbers, underscores, ' 'periods and hyphens.') }, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Host Name") })) location = forms.CharField(label=_("Location"), initial='location', required=False, help_text=_("Physical location of Host.")) clock_synchronization = forms.ChoiceField( label=_("Clock Synchronization"), choices=stx_api.sysinv.CLOCK_SYNCHRONIZATION_CHOICES) cpuProfile = forms.ChoiceField(label=_("CPU Profile"), required=False) interfaceProfile = forms.ChoiceField(label=_("Interface Profile"), required=False) diskProfile = forms.ChoiceField(label=_("Storage Profile"), required=False) memoryProfile = forms.ChoiceField(label=_("Memory Profile"), required=False) ttys_dcd = forms.BooleanField( label=_("Serial Console Data Carrier Detect"), required=False, help_text=_("Enable serial line data carrier detection. " "When selected, dropping carrier detect on the serial " "port revoke any active session and a new login " "process is initiated when a new connection is detected.")) class Meta(object): name = _("Host Info") help_text = _( "From here you can update the configuration of the current host.\n" "Note: this will not affect the resources allocated to any" " existing" " instances using this host until the host is rebooted.") def __init__(self, request, *args, **kwargs): super(UpdateHostInfoAction, self).__init__(request, *args, **kwargs) # pesonality cannot be storage if ceph is not configured storage_backend = stx_api.sysinv.get_storage_backend(request) if stx_api.sysinv.STORAGE_BACKEND_CEPH not in storage_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can only be controller or worker. systems = stx_api.sysinv.system_list(request) self.system_mode = systems[0].to_dict().get('system_mode') self.system_type = systems[0].to_dict().get('system_type') if self.system_type == constants.TS_AIO: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # hostname cannot be modified once it is set if self.initial['hostname']: self.fields['hostname'].widget.attrs['readonly'] = 'readonly' self.fields['hostname'].required = False # subfunctions cannot be modified once it is set if self.initial['subfunctions']: self.fields['subfunctions'].widget.attrs['readonly'] = 'readonly' self.fields['subfunctions'].required = False # personality cannot be modified once it is set host_id = self.initial['host_id'] personality = self.initial['personality'] mem_profile_configurable = False cpu_profile_configurable = False if personality and self.system_mode != constants.SYSTEM_MODE_SIMPLEX: self.fields['personality'].widget.attrs['readonly'] = 'readonly' self.fields['personality'].required = False self._personality = personality host = stx_api.sysinv.host_get(self.request, host_id) host.nodes = stx_api.sysinv.host_node_list(self.request, host.uuid) host.cpus = stx_api.sysinv.host_cpu_list(self.request, host.uuid) host.ports = stx_api.sysinv.host_port_list(self.request, host.uuid) host.disks = stx_api.sysinv.host_disk_list(self.request, host.uuid) if 'worker' in host.subfunctions: mem_profile_configurable = True cpu_profile_configurable = True host.memory = stx_api.sysinv.host_memory_list( self.request, host.uuid) else: del self.fields['memoryProfile'] if host.nodes and host.cpus and host.ports: # Populate Available Interface Profile Choices try: avail_interface_profile_list = \ stx_api.sysinv.host_interfaceprofile_list(self.request) interface_profile_tuple_list = [ ('', _("Copy from an available interface profile.")) ] for ip in avail_interface_profile_list: if ifprofile_applicable(request, host, ip): interface_profile_tuple_list.append( (ip.profilename, ip.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of interface profiles.')) interface_profile_tuple_list = [] self.fields[ 'interfaceProfile'].choices = interface_profile_tuple_list else: self.fields[ 'interfaceProfile'].widget = forms.widgets.HiddenInput() stor_model = sysinv.get_ceph_storage_model(request) if ((personality == 'storage' or (personality == 'controller' and stor_model == sysinv_const.CEPH_CONTROLLER_MODEL) or 'worker' in host._subfunctions) and host.disks): # Populate Available Disk Profile Choices try: disk_profile_tuple_list = [ ('', _("Copy from an available storage profile.")) ] avail_disk_profile_list = \ stx_api.sysinv.host_diskprofile_list(self.request) for dp in avail_disk_profile_list: if diskprofile_applicable(host, dp): disk_profile_tuple_list.append( (dp.profilename, dp.profilename)) except Exception as e: LOG.exception(e) exceptions.handle( self.request, _('Unable to retrieve list of storage profiles.')) disk_profile_tuple_list = [] self.fields['diskProfile'].choices = disk_profile_tuple_list else: self.fields['diskProfile'].widget = forms.widgets.HiddenInput() # Populate Available Cpu Profile Choices if cpu_profile_configurable and host.nodes and host.memory: try: avail_cpu_profile_list = \ stx_api.sysinv.host_cpuprofile_list(self.request) host_profile = icpu_utils.HostCpuProfile( host.subfunctions, host.cpus, host.nodes) cpu_profile_tuple_list = [ ('', _("Copy from an available cpu profile.")) ] for ip in avail_cpu_profile_list: nodes = stx_api.sysinv.host_node_list( self.request, ip.uuid) cpu_profile = icpu_utils.CpuProfile(ip.cpus, nodes) if host_profile.profile_applicable(cpu_profile): cpu_profile_tuple_list.append( (ip.profilename, ip.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of cpu profiles.')) cpu_profile_tuple_list = [] self.fields['cpuProfile'].choices = cpu_profile_tuple_list else: self.fields['cpuProfile'].widget = forms.widgets.HiddenInput() if mem_profile_configurable and host.nodes and host.memory: # Populate Available Memory Profile Choices try: avail_memory_profile_list = \ stx_api.sysinv.host_memprofile_list(self.request) memory_profile_tuple_list = [ ('', _("Copy from an available memory profile.")) ] for mp in avail_memory_profile_list: if memoryprofile_applicable(host, host._subfunctions, mp): memory_profile_tuple_list.append( (mp.profilename, mp.profilename)) except Exception: exceptions.handle( self.request, _('Unable to retrieve list of memory profiles.')) memory_profile_tuple_list = [] self.fields[ 'memoryProfile'].choices = memory_profile_tuple_list else: self.fields['cpuProfile'].widget = forms.widgets.HiddenInput() self.fields['interfaceProfile'].widget = forms.widgets.HiddenInput( ) self.fields['diskProfile'].widget = forms.widgets.HiddenInput() self.fields['memoryProfile'].widget = forms.widgets.HiddenInput() def clean_location(self): try: host_id = self.cleaned_data['host_id'] host = stx_api.sysinv.host_get(self.request, host_id) location = host._location location['locn'] = self.cleaned_data.get('location') return location except Exception: msg = _('Unable to get host data') exceptions.check_message(["Connection", "refused"], msg) raise def clean(self): cleaned_data = super(UpdateHostInfoAction, self).clean() disabled = self.fields['personality'].widget.attrs.get('disabled') if disabled == 'disabled': if self.system_type == constants.TS_AIO: self._personality = 'controller' cleaned_data['personality'] = self._personality if cleaned_data['personality'] == stx_api.sysinv.PERSONALITY_STORAGE: self._subfunctions = stx_api.sysinv.PERSONALITY_STORAGE cleaned_data['subfunctions'] = self._subfunctions elif cleaned_data['personality'] == \ stx_api.sysinv.PERSONALITY_CONTROLLER: if self.system_type == constants.TS_AIO: self._subfunctions = (stx_api.sysinv.PERSONALITY_CONTROLLER + ',' + stx_api.sysinv.PERSONALITY_WORKER) else: self._subfunctions = stx_api.sysinv.PERSONALITY_CONTROLLER cleaned_data['subfunctions'] = self._subfunctions return cleaned_data
class CreateProviderNetworkRange(forms.SelfHandlingForm): providernet_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField(max_length=255, label=_("Name"), required=False) description = forms.CharField(max_length=255, label=_("Description"), required=False) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False, widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-hide-on-checked': 'true', 'data-slug': 'is_shared' })) tenant_id = forms.ChoiceField( label=_("Project"), required=False, widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'is_shared' })) minimum = forms.IntegerField(label=_("Minimum"), min_value=1) maximum = forms.IntegerField(label=_("Maximum"), min_value=1) # VXLAN specific fields mode_choices = [('dynamic', _('Multicast VXLAN')), ('static', _('Static VXLAN'))] mode = forms.ChoiceField(label=_("Mode"), initial='dynamic', required=False, choices=mode_choices, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'vxlan_mode' })) group_help = (_("Specify the IPv4 or IPv6 multicast address for these " "VXLAN instances")) group = forms.CharField( max_length=255, label=_("Multicast Group Address"), initial="239.0.0.1", required=False, help_text=group_help, widget=forms.TextInput( attrs={ 'class': 'switchable switched', 'data-slug': 'vxlan_group', 'data-switch-on': 'vxlan_mode', 'data-vxlan_mode-dynamic': 'Multicast Group Address' })) port_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')), ('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')), ('8472', _('Legacy VXLAN UDP port (8472)'))] port = forms.ChoiceField(label=_("UDP Port"), required=True, widget=forms.RadioSelect(), choices=port_choices) ttl = forms.IntegerField( label=_("TTL"), required=False, initial=1, min_value=1, max_value=255, help_text=( _("Specify the time-to-live value for these VXLAN instances"))) def __init__(self, request, *args, **kwargs): super(CreateProviderNetworkRange, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) # noqa pylint: disable=unused-variable for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices initial = kwargs['initial'] if 'providernet_type' in initial: providernet_type = initial['providernet_type'] if providernet_type != "vxlan": del self.fields["mode"] del self.fields["group"] del self.fields["port"] del self.fields["ttl"] def handle(self, request, data): try: params = { 'providernet_id': data['providernet_id'], 'name': data['name'], 'description': data['description'], 'minimum': data['minimum'], 'maximum': data['maximum'], 'shared': data['shared'], 'tenant_id': data['tenant_id'] } if not data['tenant_id']: params['shared'] = True if self.initial['providernet_type'] == "vxlan": params['mode'] = data['mode'] if params['mode'] == 'dynamic': params['group'] = data['group'] params['port'] = int(data['port']) params['ttl'] = int(data['ttl']) providernet_range = stx_api.neutron.provider_network_range_create( request, **params) msg = (_('Provider network range %s was successfully created.') % providernet_range['id']) LOG.debug(msg) messages.success(request, msg) return providernet_range except neutron_exceptions.NeutronClientException as e: LOG.info(str(e)) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, str(e), redirect=redirect) except Exception: msg = _('Failed to create a provider' ' network range for network %s') \ % data['providernet_id'] LOG.info(msg) redirect = reverse('horizon:admin:datanets:datanets:' 'detail', args=(data['providernet_id'], )) exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateProviderNetworkRange, self).clean() if not cleaned_data["shared"] and not cleaned_data["tenant_id"]: msg = "Project must be specified for non-shared Segmentation Range" raise forms.ValidationError(msg) if cleaned_data["shared"]: cleaned_data["tenant_id"] = ""
class BoardManagementAction(workflows.Action): FIELD_LABEL_BM_IP = _("Board Management Controller IP Address") FIELD_LABEL_BM_USERNAME = _("Board Management Controller User Name") FIELD_LABEL_BM_PASSWORD = _("Board Management Controller Password") FIELD_LABEL_BM_CONFIRM_PASSWORD = _("Confirm Password") bm_type = forms.ChoiceField(label=_("Board Management Controller Type "), choices=BM_TYPES_CHOICES, required=False, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'bm_type' })) bm_ip = forms.IPField( label=FIELD_LABEL_BM_IP, required=False, help_text=_("IP address of the Board Management Controller" " (e.g. 172.25.0.0)"), version=forms.IPv4 | forms.IPv6, mask=False, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_IP, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_IP, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_IP })) bm_username = forms.CharField( label=FIELD_LABEL_BM_USERNAME, required=False, widget=forms.TextInput( attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_USERNAME, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_USERNAME, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_USERNAME })) bm_password = forms.RegexField( label=FIELD_LABEL_BM_PASSWORD, widget=forms.PasswordInput( render_value=False, attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_PASSWORD }), regex=validators.password_validator(), required=False, error_messages={'invalid': validators.password_validator_msg()}) bm_confirm_password = forms.CharField( label=FIELD_LABEL_BM_CONFIRM_PASSWORD, widget=forms.PasswordInput( render_value=False, attrs={ 'autocomplete': 'off', 'class': 'switched', 'data-switch-on': 'bm_type', 'data-bm_type-' + sysinv.HOST_BM_TYPE_DYNAMIC: FIELD_LABEL_BM_CONFIRM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_IPMI: FIELD_LABEL_BM_CONFIRM_PASSWORD, 'data-bm_type-' + sysinv.HOST_BM_TYPE_REDFISH: FIELD_LABEL_BM_CONFIRM_PASSWORD }), required=False) def clean(self): cleaned_data = super(BoardManagementAction, self).clean() if cleaned_data.get('bm_type') != \ sysinv_const.HOST_BM_TYPE_DEPROVISIONED: if 'bm_ip' not in cleaned_data or not cleaned_data['bm_ip']: raise forms.ValidationError( _('Board management IP address is required.')) if 'bm_username' not in cleaned_data or not \ cleaned_data['bm_username']: raise forms.ValidationError( _('Board management user name is required.')) if 'bm_password' in cleaned_data: if cleaned_data['bm_password'] != cleaned_data.get( 'bm_confirm_password', None): raise forms.ValidationError( _('Board management passwords do not match.')) else: cleaned_data.pop('bm_ip') cleaned_data.pop('bm_username') return cleaned_data # We have to protect the entire "data" dict because it contains the # password and confirm_password strings. @sensitive_variables('data') def handle(self, request, data): # Throw away the password confirmation, we're done with it. data.pop('bm_confirm_password', None)
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField( label=_('Rule'), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'rule_menu' })) description = forms.CharField( label=_('Description'), required=False, max_length=255, widget=forms.Textarea(attrs={'rows': 2}), help_text=_('A brief description of the security group rule ' 'you are adding')) # "direction" field is enabled only when custom mode. # It is because most common rules in local_settings.py is meaningful # when its direction is 'ingress'. direction = forms.ChoiceField( label=_('Direction'), required=False, widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Direction'), 'data-rule_menu-udp': _('Direction'), 'data-rule_menu-icmp': _('Direction'), 'data-rule_menu-custom': _('Direction'), 'data-rule_menu-all_tcp': _('Direction'), 'data-rule_menu-all_udp': _('Direction'), 'data-rule_menu-all_icmp': _('Direction'), })) ip_protocol = forms.IntegerField( label=_('IP Protocol'), required=False, help_text=_("Enter an integer value between 0 and 255."), validators=[utils_validators.validate_ip_protocol], widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-custom': _('IP Protocol') })) port_or_range = forms.ChoiceField( label=_('Open Port'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Open Port'), 'data-rule_menu-udp': _('Open Port') })) port = forms.IntegerField( label=_("Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-port': _('Port') }), validators=[utils_validators.validate_port_range]) from_port = forms.IntegerField( label=_("From Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-range': _('From Port') }), validators=[utils_validators.validate_port_range]) to_port = forms.IntegerField( label=_("To Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'range', 'data-range-range': _('To Port') }), validators=[utils_validators.validate_port_range]) icmp_type = forms.IntegerField( label=_("Type"), required=False, help_text=_("Enter a value for ICMP type " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Type') }), validators=[utils_validators.validate_icmp_type_range]) icmp_code = forms.IntegerField( label=_("Code"), required=False, help_text=_("Enter a value for ICMP code " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Code') }), validators=[utils_validators.validate_icmp_code_range]) remote = forms.ChoiceField( label=_('Remote'), choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))], help_text=_('To specify an allowed IP ' 'range, select "CIDR". ' 'To allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'remote' })) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24, or " "2001:db8::/128)"), version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'remote', 'data-remote-cidr': _('CIDR') })) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-required-when-shown': 'true', 'data-switch-on': 'remote', 'data-remote-sg': _('Security Group') })) # When cidr is used ethertype is determined from IP version of cidr. # When source group, ethertype needs to be specified explicitly. ethertype = forms.ChoiceField(label=_('Ether Type'), required=False, choices=[('IPv4', _('IPv4')), ('IPv6', _('IPv6'))], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-slug': 'ethertype', 'data-switch-on': 'remote', 'data-remote-sg': _('Ether Type') })) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # remote group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices # TODO(amotoki): settings.SECURITY_GROUP_RULES may contains 'backend' # parameter. If 'backend' is used, error message should be emitted. backend = 'neutron' rules_dict = settings.SECURITY_GROUP_RULES common_rules = [(k, rules_dict[k]['name']) for k in rules_dict if rules_dict[k].get('backend', backend) == backend] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule')), ('custom', _('Other Protocol'))] self.fields['rule_menu'].choices = custom_rules + common_rules self.rules = rules_dict self.fields['direction'].choices = [('ingress', _('Ingress')), ('egress', _('Egress'))] self.fields['ip_protocol'].help_text = _( "Enter an integer value between -1 and 255 " "(-1 means wild card).") self.fields['port_or_range'].choices = [ ('port', _('Port')), ('range', _('Port Range')), ('all', _('All ports')), ] if not setting_utils.get_dict_config('OPENSTACK_NEUTRON_NETWORK', 'enable_ipv6'): self.fields['cidr'].version = forms.IPv4 self.fields['ethertype'].widget = forms.TextInput( attrs={'readonly': 'readonly'}) self.fields['ethertype'].initial = 'IPv4' try: is_desc_supported = api.neutron.is_extension_supported( self.request, 'standard-attr-description') except Exception: exceptions.handle( self.request, _('Failed to check if description field is supported.')) is_desc_supported = False if not is_desc_supported: del self.fields['description'] def _update_and_pop_error(self, cleaned_data, key, value): cleaned_data[key] = value self.errors.pop(key, None) def _clean_rule_icmp(self, cleaned_data, rule_menu): icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) if icmp_type == -1 and icmp_code != -1: msg = _('ICMP code is provided but ICMP type is missing.') raise ValidationError(msg) if self.errors.get('icmp_type'): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if self.errors.get('icmp_code'): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) self._update_and_pop_error(cleaned_data, 'from_port', icmp_type) self._update_and_pop_error(cleaned_data, 'to_port', icmp_code) self._update_and_pop_error(cleaned_data, 'port', None) def _clean_rule_tcp_udp(self, cleaned_data, rule_menu): port_or_range = cleaned_data.get("port_or_range") from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if port_or_range == 'all': self._update_and_pop_error(cleaned_data, 'port', None) self._update_and_pop_error(cleaned_data, 'from_port', None) self._update_and_pop_error(cleaned_data, 'to_port', None) elif port_or_range == "port": self._update_and_pop_error(cleaned_data, 'from_port', port) self._update_and_pop_error(cleaned_data, 'to_port', port) if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: self._update_and_pop_error(cleaned_data, 'port', None) if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) def _clean_rule_custom(self, cleaned_data, rule_menu): # custom IP protocol rule so we need to fill unused fields so # the validation works unused_fields = [ 'icmp_code', 'icmp_type', 'from_port', 'to_port', 'port' ] for unused_field in unused_fields: self._update_and_pop_error(cleaned_data, unused_field, None) def _apply_rule_menu(self, cleaned_data, rule_menu): cleaned_data['ip_protocol'] = self.rules[rule_menu]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[rule_menu]['from_port']) cleaned_data['to_port'] = int(self.rules[rule_menu]['to_port']) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']: direction = self.rules[rule_menu].get('direction') cleaned_data['direction'] = direction def _clean_rule_menu(self, cleaned_data): rule_menu = cleaned_data.get('rule_menu') if rule_menu == 'icmp': self._clean_rule_icmp(cleaned_data, rule_menu) elif rule_menu in ('tcp', 'udp'): self._clean_rule_tcp_udp(cleaned_data, rule_menu) elif rule_menu == 'custom': self._clean_rule_custom(cleaned_data, rule_menu) else: self._apply_rule_menu(cleaned_data, rule_menu) def _adjust_ip_protocol_of_icmp(self, data): # Note that this needs to be called after IPv4/IPv6 is determined. try: ip_protocol = int(data['ip_protocol']) except ValueError: # string representation of IP protocol ip_protocol = data['ip_protocol'] is_ipv6 = data['ethertype'] == 'IPv6' if isinstance(ip_protocol, int): # When IP protocol number is specified, we assume a user # knows more detail on IP protocol number, # so a warning message on a mismatch between IP proto number # and IP version is displayed. if is_ipv6 and ip_protocol == 1: msg = _('58 (ipv6-icmp) should be specified for IPv6 ' 'instead of 1.') self._errors['ip_protocol'] = self.error_class([msg]) elif not is_ipv6 and ip_protocol == 58: msg = _('1 (icmp) should be specified for IPv4 ' 'instead of 58.') self._errors['ip_protocol'] = self.error_class([msg]) else: # ICMPv6 uses different an IP protocol name and number. # To allow 'icmp' for both IPv4 and IPv6 in the form, # we need to replace 'icmp' with 'ipv6-icmp' based on IP version. if is_ipv6 and ip_protocol == 'icmp': data['ip_protocol'] = 'ipv6-icmp' def clean(self): cleaned_data = super(AddRule, self).clean() self._clean_rule_menu(cleaned_data) # NOTE(amotoki): There are two cases where cleaned_data['direction'] # is empty: (1) Nova Security Group is used. Since "direction" is # HiddenInput, direction field exists but its value is ''. # (2) Template except all_* is used. In this case, the default value # is None. To make sure 'direction' field has 'ingress' or 'egress', # fill this field here if it is not specified. if not cleaned_data['direction']: cleaned_data['direction'] = 'ingress' remote = cleaned_data.get("remote") if remote == "cidr": self._update_and_pop_error(cleaned_data, 'security_group', None) else: self._update_and_pop_error(cleaned_data, 'cidr', None) # If cleaned_data does not contain a non-empty value, IPField already # has validated it, so skip the further validation for cidr. # In addition cleaned_data['cidr'] is None means source_group is used. if 'cidr' in cleaned_data and cleaned_data['cidr'] is not None: cidr = cleaned_data['cidr'] if not cidr: msg = _('CIDR must be specified.') self._errors['cidr'] = self.error_class([msg]) else: # If cidr is specified, ethertype is determined from IP address # version. It is used only when Neutron is enabled. ip_ver = netaddr.IPNetwork(cidr).version cleaned_data['ethertype'] = 'IPv6' if ip_ver == 6 else 'IPv4' self._adjust_ip_protocol_of_icmp(cleaned_data) return cleaned_data def handle(self, request, data): redirect = reverse("horizon:project:security_groups:detail", args=[data['id']]) params = {} if 'description' in data: params['description'] = data['description'] try: rule = api.neutron.security_group_rule_create( request, filters.get_int_or_uuid(data['id']), data['direction'], data['ethertype'], data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group'], **params) messages.success(request, _('Successfully added rule: %s') % rule) return rule except exceptions.Conflict as error: exceptions.handle(request, error, redirect=redirect) except Exception: exceptions.handle(request, _('Unable to add rule to security group.'), redirect=redirect)
class CreateNetwork(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) tenant_id = forms.ChoiceField(label=_("Project")) if api.neutron.is_port_profiles_supported(): widget = None else: widget = forms.HiddenInput() net_profile_id = forms.ChoiceField(label=_("Network Profile"), required=False, widget=widget) network_type = forms.ChoiceField( label=_("Provider Network Type"), help_text=_("The physical mechanism by which the virtual " "network is implemented."), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'network_type' })) physical_network = forms.CharField( max_length=255, label=_("Physical Network"), help_text=_("The name of the physical network over which the " "virtual network is implemented."), initial='default', widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) segmentation_id = forms.IntegerField( label=_("Segmentation ID"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) admin_state = forms.ChoiceField(choices=[(True, _('UP')), (False, _('DOWN'))], label=_("Admin State")) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) external = forms.BooleanField(label=_("External Network"), initial=False, required=False) @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices if api.neutron.is_port_profiles_supported(): self.fields['net_profile_id'].choices = ( self.get_network_profile_choices(request)) if api.neutron.is_extension_supported(request, 'provider'): neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) self.seg_id_range = SEGMENTATION_ID_RANGE.copy() seg_id_range = neutron_settings.get('segmentation_id_range') if seg_id_range: self.seg_id_range.update(seg_id_range) self.provider_types = PROVIDER_TYPES.copy() extra_provider_types = neutron_settings.get('extra_provider_types') if extra_provider_types: self.provider_types.update(extra_provider_types) self.nettypes_with_seg_id = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_segmentation_id']] self.nettypes_with_physnet = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_physical_network']] supported_provider_types = neutron_settings.get( 'supported_provider_types', DEFAULT_PROVIDER_TYPES) if supported_provider_types == ['*']: supported_provider_types = DEFAULT_PROVIDER_TYPES undefined_provider_types = [ net_type for net_type in supported_provider_types if net_type not in self.provider_types] if undefined_provider_types: LOG.error('Undefined provider network types are found: %s', undefined_provider_types) seg_id_help = [ _("For %(type)s networks, valid IDs are %(min)s to %(max)s.") % {'type': net_type, 'min': self.seg_id_range[net_type][0], 'max': self.seg_id_range[net_type][1]} for net_type in self.nettypes_with_seg_id] self.fields['segmentation_id'].help_text = ' '.join(seg_id_help) # Register network types which require segmentation ID attrs = dict(('data-network_type-%s' % network_type, _('Segmentation ID')) for network_type in self.nettypes_with_seg_id) self.fields['segmentation_id'].widget.attrs.update(attrs) # Register network types which require physical network attrs = dict(('data-network_type-%s' % network_type, _('Physical Network')) for network_type in self.nettypes_with_physnet) self.fields['physical_network'].widget.attrs.update(attrs) network_type_choices = [ (net_type, self.provider_types[net_type]['display_name']) for net_type in supported_provider_types] if len(network_type_choices) == 0: self._hide_provider_network_type() else: self.fields['network_type'].choices = network_type_choices else: self._hide_provider_network_type() def get_network_profile_choices(self, request): profile_choices = [('', _("Select a profile"))] for profile in self._get_profiles(request, 'network'): profile_choices.append((profile.id, profile.name)) return profile_choices def _get_profiles(self, request, type_p): profiles = [] try: profiles = api.neutron.profile_list(request, type_p) except Exception: msg = _('Network Profiles could not be retrieved.') exceptions.handle(request, msg) return profiles def _hide_provider_network_type(self): self.fields['network_type'].widget = forms.HiddenInput() self.fields['physical_network'].widget = forms.HiddenInput() self.fields['segmentation_id'].widget = forms.HiddenInput() self.fields['network_type'].required = False self.fields['physical_network'].required = False self.fields['segmentation_id'].required = False def handle(self, request, data): try: params = {'name': data['name'], 'tenant_id': data['tenant_id'], 'admin_state_up': (data['admin_state'] == 'True'), 'shared': data['shared'], 'router:external': data['external']} if api.neutron.is_port_profiles_supported(): params['net_profile_id'] = data['net_profile_id'] if api.neutron.is_extension_supported(request, 'provider'): network_type = data['network_type'] params['provider:network_type'] = network_type if network_type in self.nettypes_with_physnet: params['provider:physical_network'] = ( data['physical_network']) if network_type in self.nettypes_with_seg_id: params['provider:segmentation_id'] = ( data['segmentation_id']) network = api.neutron.network_create(request, **params) msg = _('Network %s was successfully created.') % data['name'] LOG.debug(msg) messages.success(request, msg) return network except Exception: redirect = reverse('horizon:admin:networks:index') msg = _('Failed to create network %s') % data['name'] exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateNetwork, self).clean() if api.neutron.is_extension_supported(self.request, 'provider'): self._clean_physical_network(cleaned_data) self._clean_segmentation_id(cleaned_data) return cleaned_data def _clean_physical_network(self, data): network_type = data.get('network_type') if ('physical_network' in self._errors and network_type not in self.nettypes_with_physnet): # In this case the physical network is not required, so we can # ignore any errors. del self._errors['physical_network'] def _clean_segmentation_id(self, data): network_type = data.get('network_type') if 'segmentation_id' in self._errors: if network_type not in self.nettypes_with_seg_id: # In this case the segmentation ID is not required, so we can # ignore any errors. del self._errors['segmentation_id'] elif network_type in self.nettypes_with_seg_id: seg_id = data.get('segmentation_id') seg_id_range = {'min': self.seg_id_range[network_type][0], 'max': self.seg_id_range[network_type][1]} if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']: msg = (_('For a %(network_type)s network, valid segmentation ' 'IDs are %(min)s through %(max)s.') % {'network_type': network_type, 'min': seg_id_range['min'], 'max': seg_id_range['max']}) self._errors['segmentation_id'] = self.error_class([msg])
class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField( label=_('Rule'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'rule_menu' })) # "direction" field is enabled only when custom mode. # It is because most common rules in local_settings.py is meaningful # when its direction is 'ingress'. direction = forms.ChoiceField( label=_('Direction'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Direction'), 'data-rule_menu-udp': _('Direction'), 'data-rule_menu-icmp': _('Direction'), 'data-rule_menu-custom': _('Direction'), 'data-rule_menu-all_tcp': _('Direction'), 'data-rule_menu-all_udp': _('Direction'), 'data-rule_menu-all_icmp': _('Direction'), })) ip_protocol = forms.IntegerField( label=_('IP Protocol'), required=False, help_text=_("Enter an integer value between 0 and 255 " "(or -1 which means wildcard)."), validators=[utils_validators.validate_ip_protocol], widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-custom': _('IP Protocol') })) port_or_range = forms.ChoiceField( label=_('Open Port'), choices=[('port', _('Port')), ('range', _('Port Range'))], widget=forms.Select( attrs={ 'class': 'switchable switched', 'data-slug': 'range', 'data-switch-on': 'rule_menu', 'data-rule_menu-tcp': _('Open Port'), 'data-rule_menu-udp': _('Open Port') })) port = forms.IntegerField( label=_("Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-port': _('Port') }), validators=[utils_validators.validate_port_range]) from_port = forms.IntegerField( label=_("From Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('From Port') }), validators=[utils_validators.validate_port_range]) to_port = forms.IntegerField( label=_("To Port"), required=False, help_text=_("Enter an integer value " "between 1 and 65535."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'range', 'data-range-range': _('To Port') }), validators=[utils_validators.validate_port_range]) icmp_type = forms.IntegerField( label=_("Type"), required=False, help_text=_("Enter a value for ICMP type " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Type') }), validators=[utils_validators.validate_port_range]) icmp_code = forms.IntegerField( label=_("Code"), required=False, help_text=_("Enter a value for ICMP code " "in the range (-1: 255)"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'rule_menu', 'data-rule_menu-icmp': _('Code') }), validators=[utils_validators.validate_port_range]) remote = forms.ChoiceField(label=_('Remote'), choices=[('cidr', _('CIDR')), ('sg', _('Security Group'))], help_text=_('To specify an allowed IP ' 'range, select "CIDR". ' 'To allow access from all ' 'members of another security ' 'group select "Security ' 'Group".'), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'remote' })) cidr = forms.IPField(label=_("CIDR"), required=False, initial="0.0.0.0/0", help_text=_("Classless Inter-Domain Routing " "(e.g. 192.168.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-cidr': _('CIDR') })) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'remote', 'data-remote-sg': _('Security ' 'Group') })) # When cidr is used ethertype is determined from IP version of cidr. # When source group, ethertype needs to be specified explicitly. ethertype = forms.ChoiceField(label=_('Ether Type'), required=False, choices=[('IPv4', _('IPv4')), ('IPv6', _('IPv6'))], widget=forms.Select( attrs={ 'class': 'switched', 'data-slug': 'ethertype', 'data-switch-on': 'remote', 'data-remote-sg': _('Ether Type') })) def __init__(self, *args, **kwargs): sg_list = kwargs.pop('sg_list', []) super(AddRule, self).__init__(*args, **kwargs) # Determine if there are security groups available for the # remote group option; add the choices and enable the option if so. if sg_list: security_groups_choices = sg_list else: security_groups_choices = [("", _("No security groups available"))] self.fields['security_group'].choices = security_groups_choices backend = api.network.security_group_backend(self.request) rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', []) common_rules = [(k, rules_dict[k]['name']) for k in rules_dict if rules_dict[k].get('backend', backend) == backend] common_rules.sort() custom_rules = [('tcp', _('Custom TCP Rule')), ('udp', _('Custom UDP Rule')), ('icmp', _('Custom ICMP Rule'))] if backend == 'neutron': custom_rules.append(('custom', _('Other Protocol'))) self.fields['rule_menu'].choices = custom_rules + common_rules self.rules = rules_dict if backend == 'neutron': self.fields['direction'].choices = [('ingress', _('Ingress')), ('egress', _('Egress'))] else: # direction and ethertype are not supported in Nova secgroup. self.fields['direction'].widget = forms.HiddenInput() self.fields['ethertype'].widget = forms.HiddenInput() # ip_protocol field is to specify arbitrary protocol number # and it is available only for neutron security group. self.fields['ip_protocol'].widget = forms.HiddenInput() def _update_and_pop_error(self, cleaned_data, key, value): cleaned_data[key] = value self.errors.pop(key, None) def _clean_rule_icmp(self, cleaned_data, rule_menu): icmp_type = cleaned_data.get("icmp_type", None) icmp_code = cleaned_data.get("icmp_code", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) if icmp_type is None: msg = _('The ICMP type is invalid.') raise ValidationError(msg) if icmp_code is None: msg = _('The ICMP code is invalid.') raise ValidationError(msg) if icmp_type not in range(-1, 256): msg = _('The ICMP type not in range (-1, 255)') raise ValidationError(msg) if icmp_code not in range(-1, 256): msg = _('The ICMP code not in range (-1, 255)') raise ValidationError(msg) self._update_and_pop_error(cleaned_data, 'from_port', icmp_type) self._update_and_pop_error(cleaned_data, 'to_port', icmp_code) self._update_and_pop_error(cleaned_data, 'port', None) def _clean_rule_tcp_udp(self, cleaned_data, rule_menu): port_or_range = cleaned_data.get("port_or_range") from_port = cleaned_data.get("from_port", None) to_port = cleaned_data.get("to_port", None) port = cleaned_data.get("port", None) self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu) self._update_and_pop_error(cleaned_data, 'icmp_code', None) self._update_and_pop_error(cleaned_data, 'icmp_type', None) if port_or_range == "port": self._update_and_pop_error(cleaned_data, 'from_port', port) self._update_and_pop_error(cleaned_data, 'to_port', port) if port is None: msg = _('The specified port is invalid.') raise ValidationError(msg) else: self._update_and_pop_error(cleaned_data, 'port', None) if from_port is None: msg = _('The "from" port number is invalid.') raise ValidationError(msg) if to_port is None: msg = _('The "to" port number is invalid.') raise ValidationError(msg) if to_port < from_port: msg = _('The "to" port number must be greater than ' 'or equal to the "from" port number.') raise ValidationError(msg) def _apply_rule_menu(self, cleaned_data, rule_menu): cleaned_data['ip_protocol'] = self.rules[rule_menu]['ip_protocol'] cleaned_data['from_port'] = int(self.rules[rule_menu]['from_port']) cleaned_data['to_port'] = int(self.rules[rule_menu]['to_port']) if rule_menu not in ['all_tcp', 'all_udp', 'all_icmp']: direction = self.rules[rule_menu].get('direction') cleaned_data['direction'] = direction def _clean_rule_menu(self, cleaned_data): rule_menu = cleaned_data.get('rule_menu') if rule_menu == 'icmp': self._clean_rule_icmp(cleaned_data, rule_menu) elif rule_menu == 'tcp' or rule_menu == 'udp': self._clean_rule_tcp_udp(cleaned_data, rule_menu) elif rule_menu == 'custom': pass else: self._apply_rule_menu(cleaned_data, rule_menu) def clean(self): cleaned_data = super(AddRule, self).clean() self._clean_rule_menu(cleaned_data) # NOTE(amotoki): There are two cases where cleaned_data['direction'] # is empty: (1) Nova Security Group is used. Since "direction" is # HiddenInput, direction field exists but its value is ''. # (2) Template except all_* is used. In this case, the default value # is None. To make sure 'direction' field has 'ingress' or 'egress', # fill this field here if it is not specified. if not cleaned_data['direction']: cleaned_data['direction'] = 'ingress' remote = cleaned_data.get("remote") if remote == "cidr": self._update_and_pop_error(cleaned_data, 'security_group', None) else: self._update_and_pop_error(cleaned_data, 'cidr', None) # If cleaned_data does not contain a non-empty value, IPField already # has validated it, so skip the further validation for cidr. # In addition cleaned_data['cidr'] is None means source_group is used. if 'cidr' in cleaned_data and cleaned_data['cidr'] is not None: cidr = cleaned_data['cidr'] if not cidr: msg = _('CIDR must be specified.') self._errors['cidr'] = self.error_class([msg]) else: # If cidr is specified, ethertype is determined from IP address # version. It is used only when Neutron is enabled. ip_ver = netaddr.IPNetwork(cidr).version cleaned_data['ethertype'] = 'IPv6' if ip_ver == 6 else 'IPv4' return cleaned_data def handle(self, request, data): try: rule = api.network.security_group_rule_create( request, filters.get_int_or_uuid(data['id']), data['direction'], data['ethertype'], data['ip_protocol'], data['from_port'], data['to_port'], data['cidr'], data['security_group']) messages.success( request, _('Successfully added rule: %s') % six.text_type(rule)) return rule except Exception: redirect = reverse( "horizon:project:access_and_security:" "security_groups:detail", args=[data['id']]) exceptions.handle(request, _('Unable to add rule to security group.'), redirect=redirect)
class CreateNetwork(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name"), required=False) tenant_id = forms.ThemableChoiceField(label=_("Project")) network_type = forms.ChoiceField( label=_("Provider Network Type"), help_text=_("The physical mechanism by which the virtual " "network is implemented."), widget=forms.ThemableSelectWidget(attrs={ 'class': 'switchable', 'data-slug': 'network_type' })) physical_network = forms.CharField( max_length=255, label=_("Physical Network"), help_text=_("The name of the physical network over which the " "virtual network is implemented. Specify one of the " "physical networks defined in your neutron deployment."), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) segmentation_id = forms.IntegerField( label=_("Segmentation ID"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'network_type', })) admin_state = forms.BooleanField(label=_("Enable Admin State"), initial=True, required=False) shared = forms.BooleanField(label=_("Shared"), initial=False, required=False) external = forms.BooleanField(label=_("External Network"), initial=False, required=False) with_subnet = forms.BooleanField(label=_("Create Subnet"), widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-slug': 'with_subnet', 'data-hide-tab': 'create_network__' 'createsubnetinfo' 'action,' 'create_network__' 'createsubnetdetail' 'action', 'data-hide-on-checked': 'false' }), initial=True, required=False) az_hints = forms.MultipleChoiceField( label=_("Availability Zone Hints"), required=False, help_text=_("Availability zones where the DHCP agents may be " "scheduled. Leaving this unset is equivalent to " "selecting all availability zones")) @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices try: is_extension_supported = \ api.neutron.is_extension_supported(request, 'provider') except Exception: msg = _("Unable to verify Neutron service providers") exceptions.handle(self.request, msg) self._hide_provider_network_type() is_extension_supported = False if is_extension_supported: neutron_settings = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) self.seg_id_range = SEGMENTATION_ID_RANGE.copy() seg_id_range = neutron_settings.get('segmentation_id_range') if seg_id_range: self.seg_id_range.update(seg_id_range) self.provider_types = PROVIDER_TYPES.copy() extra_provider_types = neutron_settings.get('extra_provider_types') if extra_provider_types: self.provider_types.update(extra_provider_types) self.nettypes_with_seg_id = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_segmentation_id'] ] self.nettypes_with_physnet = [ net_type for net_type in self.provider_types if self.provider_types[net_type]['require_physical_network'] ] supported_provider_types = neutron_settings.get( 'supported_provider_types', DEFAULT_PROVIDER_TYPES) if supported_provider_types == ['*']: supported_provider_types = DEFAULT_PROVIDER_TYPES undefined_provider_types = [ net_type for net_type in supported_provider_types if net_type not in self.provider_types ] if undefined_provider_types: LOG.error('Undefined provider network types are found: %s', undefined_provider_types) seg_id_help = [ _("For %(type)s networks, valid IDs are %(min)s to %(max)s.") % { 'type': net_type, 'min': self.seg_id_range[net_type][0], 'max': self.seg_id_range[net_type][1] } for net_type in self.nettypes_with_seg_id ] self.fields['segmentation_id'].help_text = ' '.join(seg_id_help) # Register network types which require segmentation ID attrs = dict( ('data-network_type-%s' % network_type, _('Segmentation ID')) for network_type in self.nettypes_with_seg_id) self.fields['segmentation_id'].widget.attrs.update(attrs) physical_networks = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get('physical_networks', []) if physical_networks: self.fields['physical_network'] = forms.ThemableChoiceField( label=_("Physical Network"), choices=[(net, net) for net in physical_networks], widget=forms.ThemableSelectWidget( attrs={ 'class': 'switched', 'data-switch-on': 'network_type', }), help_text=_("The name of the physical network over " "which the virtual network is implemented."), ) # Register network types which require physical network attrs = dict( ('data-network_type-%s' % network_type, _('Physical Network')) for network_type in self.nettypes_with_physnet) self.fields['physical_network'].widget.attrs.update(attrs) network_type_choices = [ (net_type, self.provider_types[net_type]['display_name']) for net_type in supported_provider_types ] if not network_type_choices: self._hide_provider_network_type() else: self.fields['network_type'].choices = network_type_choices try: if api.neutron.is_extension_supported(request, 'network_availability_zone'): zones = api.neutron.list_availability_zones( self.request, 'network', 'available') self.fields['az_hints'].choices = [(zone['name'], zone['name']) for zone in zones] else: del self.fields['az_hints'] except Exception: msg = _('Failed to get availability zone list.') messages.warning(request, msg) del self.fields['az_hints'] def _hide_provider_network_type(self): self.fields['network_type'].widget = forms.HiddenInput() self.fields['physical_network'].widget = forms.HiddenInput() self.fields['segmentation_id'].widget = forms.HiddenInput() self.fields['network_type'].required = False self.fields['physical_network'].required = False self.fields['segmentation_id'].required = False def handle(self, request, data): try: params = { 'name': data['name'], 'tenant_id': data['tenant_id'], 'admin_state_up': data['admin_state'], 'shared': data['shared'], 'router:external': data['external'] } if api.neutron.is_extension_supported(request, 'provider'): network_type = data['network_type'] params['provider:network_type'] = network_type if network_type in self.nettypes_with_physnet: params['provider:physical_network'] = ( data['physical_network']) if network_type in self.nettypes_with_seg_id: params['provider:segmentation_id'] = ( data['segmentation_id']) if 'az_hints' in data and data['az_hints']: params['availability_zone_hints'] = data['az_hints'] network = api.neutron.network_create(request, **params) LOG.debug('Network %s was successfully created.', data['name']) return network except Exception: redirect = reverse('horizon:admin:networks:index') msg = _('Failed to create network %s') % data['name'] exceptions.handle(request, msg, redirect=redirect) def clean(self): cleaned_data = super(CreateNetwork, self).clean() if api.neutron.is_extension_supported(self.request, 'provider'): self._clean_physical_network(cleaned_data) self._clean_segmentation_id(cleaned_data) return cleaned_data def _clean_physical_network(self, data): network_type = data.get('network_type') if ('physical_network' in self._errors and network_type not in self.nettypes_with_physnet): # In this case the physical network is not required, so we can # ignore any errors. del self._errors['physical_network'] def _clean_segmentation_id(self, data): network_type = data.get('network_type') if 'segmentation_id' in self._errors: if (network_type not in self.nettypes_with_seg_id and not self.data.get("segmentation_id")): # In this case the segmentation ID is not required, so we can # ignore the field is required error. del self._errors['segmentation_id'] elif network_type in self.nettypes_with_seg_id: seg_id = data.get('segmentation_id') seg_id_range = { 'min': self.seg_id_range[network_type][0], 'max': self.seg_id_range[network_type][1] } if seg_id < seg_id_range['min'] or seg_id > seg_id_range['max']: msg = (_('For a %(network_type)s network, valid segmentation ' 'IDs are %(min)s through %(max)s.') % { 'network_type': network_type, 'min': seg_id_range['min'], 'max': seg_id_range['max'] }) self._errors['segmentation_id'] = self.error_class([msg])
class UpdateIPSecSiteConnection(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ipsecsiteconnection_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(required=False, max_length=80, label=_("Description")) peer_address = forms.IPField( label=_("Peer gateway public IPv4/IPv6 Address or FQDN"), help_text=_("Peer gateway public IPv4/IPv6 address or FQDN for " "the VPN Connection"), version=forms.IPv4 | forms.IPv6, mask=False) peer_id = forms.IPField( label=_("Peer router identity for authentication (Peer ID)"), help_text=_("Peer router identity for authentication. " "Can be IPv4/IPv6 address, e-mail, key ID, or FQDN"), version=forms.IPv4 | forms.IPv6, mask=False) peer_cidrs = forms.MultiIPField(label=_("Remote peer subnet(s)"), help_text=_( "Remote peer subnet(s) address(es) " "with mask(s) in CIDR format " "separated with commas if needed " "(e.g. 20.1.0.0/24, 21.1.0.0/24)"), version=forms.IPv4 | forms.IPv6, mask=True) psk = forms.CharField(widget=forms.PasswordInput(render_value=True), max_length=80, label=_("Pre-Shared Key (PSK) string")) mtu = forms.IntegerField( min_value=68, required=False, label=_("Maximum Transmission Unit size for the connection"), help_text=_("Equal to or greater than 68 if the local subnet is IPv4. " "Equal to or greater than 1280 if the local subnet " "is IPv6.")) dpd_action = forms.ChoiceField(label=_("Dead peer detection actions"), required=False, choices=[('hold', _('hold')), ('clear', _('clear')), ('disabled', _('disabled')), ('restart', _('restart')), ('restart-by-peer', _('restart-by-peer'))]) dpd_interval = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection interval"), help_text=_("Valid integer lesser than the DPD timeout")) dpd_timeout = forms.IntegerField( min_value=1, required=False, label=_("Dead peer detection timeout"), help_text=_("Valid integer greater than the DPD interval")) initiator = forms.ChoiceField(label=_("Initiator state"), required=False, choices=[ ('bi-directional', _('bi-directional')), ('response-only', _('response-only')) ]) admin_state_up = forms.BooleanField(label=_("Enable Admin State"), required=False) failure_url = 'horizon:project:vpn:index' def clean(self): cleaned_data = super(UpdateIPSecSiteConnection, self).clean() interval = cleaned_data.get('dpd_interval') timeout = cleaned_data.get('dpd_timeout') if not interval < timeout: msg = _("DPD Timeout must be greater than DPD Interval") self._errors['dpd_timeout'] = self.error_class([msg]) return cleaned_data def handle(self, request, context): try: data = { 'ipsec_site_connection': { 'name': context['name'], 'description': context['description'], 'peer_address': context['peer_address'], 'peer_id': context['peer_id'], 'peer_cidrs': context['peer_cidrs'].replace(" ", "").split(","), 'psk': context['psk'], 'mtu': context['mtu'], 'dpd': { 'action': context['dpd_action'], 'interval': context['dpd_interval'], 'timeout': context['dpd_timeout'] }, 'initiator': context['initiator'], 'admin_state_up': context['admin_state_up'], } } ipsecsiteconnection = api.vpn.ipsecsiteconnection_update( request, context['ipsecsiteconnection_id'], **data) msg = (_('IPSec Site Connection %s was successfully updated.') % context['name']) messages.success(request, msg) return ipsecsiteconnection except Exception as e: LOG.info('Failed to update IPSec Site Connection %(id)s: %(exc)s', { 'id': context['ipsecsiteconnection_id'], 'exc': e }) msg = (_('Failed to update IPSec Site Connection %s') % context['name']) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class CreateSubnetInfoAction(workflows.Action): subnet_name = forms.CharField(max_length=255, widget=forms.TextInput(attrs={}), label=_("Subnet Name"), required=False) address_source = forms.ChoiceField( required=False, label=_('Network Address Source'), choices=[('manual', _('Enter Network Address manually')), ('subnetpool', _('Allocate Network Address from a pool'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source', })) subnetpool = forms.ChoiceField( label=_("Address pool"), widget=forms.SelectWidget(attrs={ 'class': 'switched switchable', 'data-slug': 'subnetpool', 'data-switch-on': 'source', 'data-source-subnetpool': _('Address pool') }, data_attrs=('name', 'prefixes', 'ip_version', 'min_prefixlen', 'max_prefixlen', 'default_prefixlen'), transform=lambda x: "%s (%s)" % (x.name, ", ".join(x.prefixes)) if 'prefixes' in x else "%s" % (x.name)), required=False) prefixlen = forms.ChoiceField( widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'subnetpool', }), label=_('Network Mask'), required=False) cidr = forms.IPField(label=_("Network Address"), required=False, initial="", widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-manual': _("Network Address"), }), help_text=_("Network address in CIDR format " "(e.g. 192.168.0.0/24, 2001:DB8::/48)"), version=forms.IPv4 | forms.IPv6, mask=True) ip_version = forms.ChoiceField( choices=[(4, 'IPv4'), (6, 'IPv6')], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'ipversion', }), label=_("IP Version"), required=False) gateway_ip = forms.IPField( label=_("Gateway IP"), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'gateway_ip', 'data-source-manual': _("Gateway IP") }), required=False, initial="", help_text=_("IP address of Gateway (e.g. 192.168.0.254) " "The default value is the first IP of the " "network address " "(e.g. 192.168.0.1 for 192.168.0.0/24, " "2001:DB8::1 for 2001:DB8::/48). " "If you use the default, leave blank. " "If you do not want to use a gateway, " "check 'Disable Gateway' below."), version=forms.IPv4 | forms.IPv6, mask=False) no_gateway = forms.BooleanField(label=_("Disable Gateway"), widget=forms.CheckboxInput( attrs={ 'class': 'switchable', 'data-slug': 'gateway_ip', 'data-hide-on-checked': 'true' }), initial=False, required=False) class Meta(object): name = _("Subnet") help_text = _('Creates a subnet associated with the network.' ' You need to enter a valid "Network Address"' ' and "Gateway IP". If you did not enter the' ' "Gateway IP", the first value of a network' ' will be assigned by default. If you do not want' ' gateway please check the "Disable Gateway" checkbox.' ' Advanced configuration is available by clicking on' ' the "Subnet Details" tab.') def __init__(self, request, context, *args, **kwargs): super(CreateSubnetInfoAction, self).__init__(request, context, *args, **kwargs) if 'with_subnet' in context: self.fields['with_subnet'] = forms.BooleanField( initial=context['with_subnet'], required=False, widget=forms.HiddenInput()) if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get( 'enable_ipv6', True): self.fields['ip_version'].widget = forms.HiddenInput() self.fields['ip_version'].initial = 4 try: if api.neutron.is_extension_supported(request, 'subnet_allocation'): self.fields['subnetpool'].choices = \ self.get_subnetpool_choices(request) else: self.hide_subnetpool_choices() except Exception: self.hide_subnetpool_choices() msg = _('Unable to initialize subnetpools') exceptions.handle(request, msg) if len(self.fields['subnetpool'].choices) > 1: # Pre-populate prefixlen choices to satisfy Django # ChoiceField Validation. This is overridden w/data from # subnetpool on select. self.fields['prefixlen'].choices = \ zip(list(range(0, 128 + 1)), list(range(0, 128 + 1))) # Populate data-fields for switching the prefixlen field # when user selects a subnetpool other than # "Provider default pool" for (id, name) in self.fields['subnetpool'].choices: if not len(id): continue key = 'data-subnetpool-' + id self.fields['prefixlen'].widget.attrs[key] = \ _('Network Mask') else: self.hide_subnetpool_choices() def get_subnetpool_choices(self, request): subnetpool_choices = [('', _('Select a pool'))] for subnetpool in api.neutron.subnetpool_list(request): subnetpool_choices.append((subnetpool.id, subnetpool)) return subnetpool_choices def hide_subnetpool_choices(self): self.fields['address_source'].widget = forms.HiddenInput() self.fields['subnetpool'].choices = [] self.fields['subnetpool'].widget = forms.HiddenInput() self.fields['prefixlen'].widget = forms.HiddenInput() def _check_subnet_data(self, cleaned_data, is_create=True): cidr = cleaned_data.get('cidr') ip_version = int(cleaned_data.get('ip_version')) gateway_ip = cleaned_data.get('gateway_ip') no_gateway = cleaned_data.get('no_gateway') address_source = cleaned_data.get('address_source') subnetpool = cleaned_data.get('subnetpool') if not subnetpool and address_source == 'subnetpool': msg = _('Specify "Address pool" or select ' '"Enter Network Address manually" and specify ' '"Network Address".') raise forms.ValidationError(msg) if not cidr and address_source != 'subnetpool': msg = _('Specify "Network Address" or ' 'clear "Create Subnet" checkbox in previous step.') raise forms.ValidationError(msg) if cidr: subnet = netaddr.IPNetwork(cidr) if subnet.version != ip_version: msg = _('Network Address and IP version are inconsistent.') raise forms.ValidationError(msg) if (ip_version == 4 and subnet.prefixlen == 32) or \ (ip_version == 6 and subnet.prefixlen == 128): msg = _("The subnet in the Network Address is " "too small (/%s).") % subnet.prefixlen self._errors['cidr'] = self.error_class([msg]) if not no_gateway and gateway_ip: if netaddr.IPAddress(gateway_ip).version is not ip_version: msg = _('Gateway IP and IP version are inconsistent.') raise forms.ValidationError(msg) if not is_create and not no_gateway and not gateway_ip: msg = _('Specify IP address of gateway or ' 'check "Disable Gateway" checkbox.') raise forms.ValidationError(msg) def clean(self): cleaned_data = super(CreateSubnetInfoAction, self).clean() with_subnet = cleaned_data.get('with_subnet') if not with_subnet: return cleaned_data self._check_subnet_data(cleaned_data) return cleaned_data
class UpdateIKEPolicy(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ikepolicy_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField(required=False, max_length=80, label=_("Description")) # Currently this field has only one choice, so mark it as readonly. auth_algorithm = forms.ChoiceField( label=_("Authorization algorithm"), choices=[('sha1', _('sha1'))], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) encryption_algorithm = forms.ChoiceField(label=_("Encryption algorithm"), choices=[ ('3des', _('3des')), ('aes-128', _('aes-128')), ('aes-192', _('aes-192')), ('aes-256', _('aes-256')) ], required=False) ike_version = forms.ChoiceField(label=_("IKE version"), choices=[('v1', _('v1')), ('v2', _('v2'))], required=False) # Currently this field has only one choice, so mark it as readonly. lifetime_units = forms.ChoiceField( label=_("Lifetime units for IKE keys"), choices=[('seconds', _('seconds'))], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) lifetime_value = forms.IntegerField( min_value=60, label=_("Lifetime value for IKE keys"), help_text=_("Equal to or greater than 60"), required=False) pfs = forms.ChoiceField(label=_("Perfect Forward Secrecy"), choices=[('group2', _('group2')), ('group5', _('group5')), ('group14', _('group14'))], required=False) # Currently this field has only one choice, so mark it as readonly. phase1_negotiation_mode = forms.ChoiceField( label=_("IKE Phase1 negotiation mode"), choices=[('main', 'main')], widget=forms.ThemableSelectWidget(attrs={'readonly': 'readonly'}), required=False) failure_url = 'horizon:project:vpn:index' def handle(self, request, context): try: data = { 'ikepolicy': { 'name': context['name'], 'description': context['description'], 'auth_algorithm': context['auth_algorithm'], 'encryption_algorithm': context['encryption_algorithm'], 'ike_version': context['ike_version'], 'lifetime': { 'units': context['lifetime_units'], 'value': context['lifetime_value'] }, 'pfs': context['pfs'], 'phase1_negotiation_mode': context['phase1_negotiation_mode'], } } ikepolicy = api.vpn.ikepolicy_update(request, context['ikepolicy_id'], **data) msg = (_('IKE Policy %s was successfully updated.') % context['name']) messages.success(request, msg) return ikepolicy except Exception as e: LOG.info('Failed to update IKE Policy %(id)s: %(exc)s', { 'id': context['ikepolicy_id'], 'exc': e }) msg = _('Failed to update IKE Policy %s') % context['name'] redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
class LaunchForm(forms.SelfHandlingForm): name = forms.CharField(label=_("Cluster Name"), max_length=80) datastore = forms.ChoiceField( label=_("Datastore"), help_text=_("Type and version of datastore."), widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'datastore' })) network = forms.ChoiceField(label=_("Network"), help_text=_("Network attached to instance."), required=False) volume = forms.IntegerField(label=_("Volume Size"), min_value=0, initial=1, help_text=_("Size of the volume in GB.")) locality = forms.ChoiceField( label=_("Locality"), choices=[("", "None"), ("affinity", "affinity"), ("anti-affinity", "anti-affinity")], required=False, help_text=_("Specify whether instances in the cluster will " "be created on the same hypervisor (affinity) or on " "different hypervisors (anti-affinity).")) root_password = forms.CharField( label=_("Root Password"), required=False, help_text=_("Password for root user."), widget=forms.PasswordInput(attrs={ 'class': 'switched', 'data-switch-on': 'datastore', })) num_instances_vertica = forms.IntegerField( label=_("Number of Instances"), min_value=3, initial=3, required=False, help_text=_("Number of instances in the cluster. (Read only)"), widget=forms.TextInput( attrs={ 'readonly': 'readonly', 'class': 'switched', 'data-switch-on': 'datastore', })) num_shards = forms.IntegerField( label=_("Number of Shards"), min_value=1, initial=1, required=False, help_text=_("Number of shards. (Read only)"), widget=forms.TextInput( attrs={ 'readonly': 'readonly', 'class': 'switched', 'data-switch-on': 'datastore', })) num_instances = forms.IntegerField( label=_("Number of Instances"), initial=3, required=False, help_text=_("Number of instances in the cluster."), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'datastore', })) # (name of field variable, label) default_fields = [('num_instances', _('Number of Instances'))] mongodb_fields = default_fields + [ ('num_shards', _('Number of Shards')), ] vertica_fields = [ ('num_instances_vertica', ('Number of Instances')), ('root_password', _('Root Password')), ] def __init__(self, request, *args, **kwargs): super(LaunchForm, self).__init__(request, *args, **kwargs) self.fields['datastore'].choices = self.populate_datastore_choices( request) self.fields['network'].choices = self.populate_network_choices(request) def clean(self): datastore_field_value = self.data.get("datastore", None) if datastore_field_value: datastore, datastore_version = ( create_instance.parse_datastore_and_version_text( common_utils.unhexlify(datastore_field_value))) flavor_field_name = self._build_widget_field_name( datastore, datastore_version) if not self.data.get(flavor_field_name, None): msg = _("The flavor must be specified.") self._errors[flavor_field_name] = self.error_class([msg]) if db_capability.is_vertica_datastore(datastore): if not self.data.get("root_password", None): msg = _("Password for root user must be specified.") self._errors["root_password"] = self.error_class([msg]) else: if int(self.data.get("num_instances", 0)) < 1: msg = _("The number of instances must be greater than 1.") self._errors["num_instances"] = self.error_class([msg]) if db_capability.is_mongodb_datastore(datastore): if int(self.data.get("num_shards", 0)) < 1: msg = _("The number of shards must be greater than 1.") self._errors["num_shards"] = self.error_class([msg]) if not self.data.get("locality", None): self.cleaned_data["locality"] = None return self.cleaned_data @memoized.memoized_method def datastore_flavors(self, request, datastore_name, datastore_version): try: return trove_api.trove.datastore_flavors(request, datastore_name, datastore_version) except Exception: LOG.exception("Exception while obtaining flavors list") self._flavors = [] redirect = reverse('horizon:project:database_clusters:index') exceptions.handle(request, _('Unable to obtain flavors.'), redirect=redirect) @memoized.memoized_method def populate_network_choices(self, request): network_list = [] try: if api.base.is_service_enabled(request, 'network'): tenant_id = self.request.user.tenant_id networks = api.neutron.network_list_for_tenant( request, tenant_id) network_list = [(network.id, network.name_or_id) for network in networks] else: self.fields['network'].widget = forms.HiddenInput() except exceptions.ServiceCatalogException: network_list = [] redirect = reverse('horizon:project:database_clusters:index') exceptions.handle(request, _('Unable to retrieve networks.'), redirect=redirect) return network_list @memoized.memoized_method def datastores(self, request): try: return trove_api.trove.datastore_list(request) except Exception: LOG.exception("Exception while obtaining datastores list") self._datastores = [] redirect = reverse('horizon:project:database_clusters:index') exceptions.handle(request, _('Unable to obtain datastores.'), redirect=redirect) def filter_cluster_datastores(self, request): datastores = [] for ds in self.datastores(request): # TODO(michayu): until capabilities lands if db_capability.is_cluster_capable_datastore(ds.name): datastores.append(ds) return datastores @memoized.memoized_method def datastore_versions(self, request, datastore): try: return trove_api.trove.datastore_version_list(request, datastore) except Exception: LOG.exception("Exception while obtaining datastore version list") self._datastore_versions = [] redirect = reverse('horizon:project:database_clusters:index') exceptions.handle(request, _('Unable to obtain datastore versions.'), redirect=redirect) def populate_datastore_choices(self, request): choices = () datastores = self.filter_cluster_datastores(request) if datastores is not None: datastore_flavor_fields = {} for ds in datastores: versions = self.datastore_versions(request, ds.name) if versions: # only add to choices if datastore has at least one version version_choices = () for v in versions: # NOTE(zhaochao): troveclient API resources are lazy # loading objects. When an attribute is not found, the # get() method of the Manager object will be called # with the ID of the resource. However for # datastore_versions, the get() method is expecting two # arguments: datastore and datastore_version(name), so # TypeError will be raised as not enough arguments are # passed. In Python 2.x, hasattr() won't reraise the # exception(which is not correct), but reraise under # Python 3(which should be correct). # Use v.to_dict() to verify the 'active' info instead. if not v.to_dict().get('active', True): continue selection_text = self._build_datastore_display_text( ds.name, v.name) widget_text = self._build_widget_field_name( ds.name, v.name) version_choices = (version_choices + ((widget_text, selection_text), )) k, v = self._add_datastore_flavor_field( request, ds.name, v.name) datastore_flavor_fields[k] = v self._add_attr_to_optional_fields(ds.name, widget_text) choices = choices + version_choices self._insert_datastore_version_fields(datastore_flavor_fields) return choices def _add_datastore_flavor_field(self, request, datastore, datastore_version): name = self._build_widget_field_name(datastore, datastore_version) attr_key = 'data-datastore-' + name field = forms.ChoiceField(label=_("Flavor"), help_text=_("Size of image to launch."), required=False, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'datastore', attr_key: _("Flavor") })) valid_flavors = self.datastore_flavors(request, datastore, datastore_version) if valid_flavors: field.choices = instance_utils.sort_flavor_list( request, valid_flavors) return name, field def _build_datastore_display_text(self, datastore, datastore_version): return datastore + ' - ' + datastore_version def _build_widget_field_name(self, datastore, datastore_version): # Since the fieldnames cannot contain an uppercase character # we generate a hex encoded string representation of the # datastore and version as the fieldname return common_utils.hexlify( self._build_datastore_display_text(datastore, datastore_version)) def _insert_datastore_version_fields(self, datastore_flavor_fields): fields_to_restore_at_the_end = collections.OrderedDict() while True: k, v = self.fields.popitem() if k == 'datastore': self.fields[k] = v break else: fields_to_restore_at_the_end[k] = v for k, v in datastore_flavor_fields.items(): self.fields[k] = v for k in reversed(list(fields_to_restore_at_the_end.keys())): self.fields[k] = fields_to_restore_at_the_end[k] def _add_attr_to_optional_fields(self, datastore, selection_text): if db_capability.is_mongodb_datastore(datastore): fields = self.mongodb_fields elif db_capability.is_vertica_datastore(datastore): fields = self.vertica_fields else: fields = self.default_fields for field in fields: attr_key = 'data-datastore-' + selection_text widget = self.fields[field[0]].widget if attr_key not in widget.attrs: widget.attrs[attr_key] = field[1] def _get_locality(self, data): locality = None if data.get('locality'): locality = data['locality'] return locality @sensitive_variables('data') def handle(self, request, data): try: datastore, datastore_version = ( create_instance.parse_datastore_and_version_text( common_utils.unhexlify(data['datastore']))) flavor_field_name = self._build_widget_field_name( datastore, datastore_version) flavor = data[flavor_field_name] num_instances = data['num_instances'] root_password = None if db_capability.is_vertica_datastore(datastore): root_password = data['root_password'] num_instances = data['num_instances_vertica'] LOG.info( "Launching cluster with parameters " "{name=%s, volume=%s, flavor=%s, " "datastore=%s, datastore_version=%s", "locality=%s", data['name'], data['volume'], flavor, datastore, datastore_version, self._get_locality(data)) trove_api.trove.cluster_create(request, data['name'], data['volume'], flavor, num_instances, datastore=datastore, datastore_version=datastore_version, nics=data['network'], root_password=root_password, locality=self._get_locality(data)) messages.success(request, _('Launched cluster "%s"') % data['name']) return True except Exception: redirect = reverse("horizon:project:database_clusters:index") exceptions.handle(request, _('Unable to launch cluster.'), 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) server_group = nova.server_group_create(request, data['name'], project_id, metadata, policies) return server_group except ValidationError as e: self.api_error(e.messages[0]) return False except Exception: exceptions.handle(request, ignore=True) self.api_error(_("Unable to create server group.")) return False
class CallPluginForm(forms.SelfHandlingForm): uuid = forms.CharField(label=_("Plugin ID"), widget=forms.HiddenInput) name = forms.CharField( label=_('Plugin Name'), widget=forms.TextInput(attrs={'readonly': 'readonly'}) ) board_list = forms.MultipleChoiceField( label=_("Boards List"), widget=forms.SelectMultiple( attrs={'class': 'switchable', 'data-slug': 'slug-call-boards'}), help_text=_("Select boards in this pool ") ) parameters = forms.CharField( label=_("Parameters"), required=False, widget=forms.Textarea( attrs={'class': 'switchable', 'data-slug': 'slug-callplugin-json'}), help_text=_("Plugin parameters") ) def __init__(self, *args, **kwargs): super(CallPluginForm, self).__init__(*args, **kwargs) # input=kwargs.get('initial',{}) boardslist_length = len(kwargs["initial"]["board_list"]) self.fields["board_list"].choices = kwargs["initial"]["board_list"] self.fields["board_list"].max_length = boardslist_length def handle(self, request, data): counter = 0 if not data["parameters"]: data["parameters"] = {} else: data["parameters"] = json.loads(data["parameters"]) for board in data["board_list"]: for key, value in self.fields["board_list"].choices: if key == board: try: plugin = None plugin = iotronic.plugin_action(request, key, data["uuid"], "PluginCall", data["parameters"]) # LOG.debug("API: %s %s", plugin, request) message_text = "Plugin called successfully on board " \ + str(value) + "." messages.success(request, _(message_text)) if counter != len(data["board_list"]) - 1: counter += 2 else: return plugin except Exception: message_text = "Unable to call plugin on board " \ + str(value) + "." exceptions.handle(request, _(message_text)) break
class AddHostInfoAction(workflows.Action): FIELD_LABEL_PERSONALITY = _("Personality") FIELD_LABEL_HOSTNAME = _("Host Name") FIELD_LABEL_MGMT_MAC = _("Management MAC Address") FIELD_LABEL_MGMT_IP = _("Management IP Address") personality = forms.ChoiceField( label=FIELD_LABEL_PERSONALITY, help_text=_("Host Personality"), choices=PERSONALITY_CHOICES, widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'personality' })) subfunctions = forms.ChoiceField( label=FIELD_LABEL_PERFORMANCE_PROFILE, choices=PERFORMANCE_CHOICES, widget=forms.Select( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: _("Personality Sub-Type") })) hostname = forms.RegexField( label=FIELD_LABEL_HOSTNAME, max_length=255, required=False, regex=r'^[\w\.\-]+$', error_messages={ 'invalid': _('Name may only contain letters,' ' numbers, underscores, ' 'periods and hyphens.') }, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: FIELD_LABEL_HOSTNAME, })) mgmt_mac = forms.MACAddressField( label=FIELD_LABEL_MGMT_MAC, widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'personality', 'data-personality-' + stx_api.sysinv.PERSONALITY_WORKER: FIELD_LABEL_MGMT_MAC, 'data-personality-' + stx_api.sysinv.PERSONALITY_CONTROLLER: FIELD_LABEL_MGMT_MAC, 'data-personality-' + stx_api.sysinv.PERSONALITY_STORAGE: FIELD_LABEL_MGMT_MAC, })) class Meta(object): name = _("Host Info") help_text = _( "From here you can add the configuration for a new host.") def __init__(self, request, *arg, **kwargs): super(AddHostInfoAction, self).__init__(request, *arg, **kwargs) # pesonality cannot be storage if ceph is not configured storage_backend = stx_api.sysinv.get_storage_backend(request) if stx_api.sysinv.STORAGE_BACKEND_CEPH not in storage_backend: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE # All-in-one system, personality can be controller or worker. systems = stx_api.sysinv.system_list(request) system_type = systems[0].to_dict().get('system_type') if system_type == constants.TS_AIO: self.fields['personality'].choices = \ PERSONALITY_CHOICES_WITHOUT_STORAGE def clean(self): cleaned_data = super(AddHostInfoAction, self).clean() return cleaned_data
class UpdateIPSecPolicy(forms.SelfHandlingForm): name = forms.CharField(max_length=80, label=_("Name"), required=False) ipsecpolicy_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) description = forms.CharField( required=False, max_length=80, label=_("Description")) # Currently this field has only one choice, so mark it as readonly. auth_algorithm = forms.ChoiceField( label=_("Authorization algorithm"), choices=[('sha1', _('sha1'))], widget=forms.TextInput(attrs={'readonly': 'readonly'})) encapsulation_mode = forms.ChoiceField( label=_("Encapsulation mode"), choices=[('tunnel', _('tunnel')), ('transport', _('transport'))]) encryption_algorithm = forms.ChoiceField( label=_("Encryption algorithm"), choices=[('3des', _('3des')), ('aes-128', _('aes-128')), ('aes-192', _('aes-192')), ('aes-256', _('aes-256'))]) # Currently this field has only one choice, so mark it as readonly. lifetime_units = forms.ChoiceField( label=_("Lifetime units"), choices=[('seconds', _('seconds'))], widget=forms.Select(attrs={'readonly': 'readonly'})) lifetime_value = forms.IntegerField( min_value=60, label=_("Lifetime value"), help_text=_("Equal to or greater than 60")) pfs = forms.ChoiceField( label=_("Perfect Forward Secrecy"), choices=[('group2', _('group2')), ('group5', _('group5')), ('group14', _('group14'))]) transform_protocol = forms.ChoiceField( label=_("Transform Protocol"), choices=[('esp', _('esp')), ('ah', _('ah')), ('ah-esp', _('ah-esp'))]) failure_url = 'horizon:project:vpn:index' def handle(self, request, context): try: data = {'ipsecpolicy': {'name': context['name'], 'description': context['description'], 'auth_algorithm': context['auth_algorithm'], 'encapsulation_mode': context['encapsulation_mode'], 'encryption_algorithm': context['encryption_algorithm'], 'lifetime': {'units': context['lifetime_units'], 'value': context['lifetime_value']}, 'pfs': context['pfs'], 'transform_protocol': context['transform_protocol'], }} ipsecpolicy = api.vpn.ipsecpolicy_update( request, context['ipsecpolicy_id'], **data) msg = (_('IPSec Policy %s was successfully updated.') % context['name']) LOG.debug(msg) messages.success(request, msg) return ipsecpolicy except Exception as e: msg = _('Failed to update IPSec Policy %s') % context['name'] LOG.info('%s: %s' % (msg, e)) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect)
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.utils.functional import cached_property # noqa from django.utils.translation import ugettext_lazy as _ # noqa from horizon import exceptions from horizon import forms from horizon import messages from monitoring import api from monitoring.notifications import constants READONLY_TEXTINPUT = forms.TextInput(attrs={'readonly': 'readonly'}) class BaseNotificationMethodForm(forms.SelfHandlingForm): @classmethod def _instantiate(cls, request, *args, **kwargs): return cls(request, *args, **kwargs) def _init_fields(self, readOnly=False, create=False): required = True textWidget = None selectWidget = None readOnlyTextInput = READONLY_TEXTINPUT readOnlySelectInput = forms.Select(attrs={'disabled': 'disabled'}) if readOnly: required = False