# -*- coding: utf8 -*- # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # 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. import json import logging import django.forms from django.utils.datastructures import SortedDict from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.forms import horizon.messages from tuskar_ui import api import tuskar_ui.forms from tuskar_ui.utils import utils LOG = logging.getLogger(__name__) VIRT_TYPE_CHOICES = [ ('kvm', _("Baremetal (kvm)")), ('qemu', _("Virtualized (qemu)")), ] NEUTRON_PUBLIC_INTERFACE_CHOICES = [ ('em2', _("Baremetal (em2)")), ('eth0', _("Virtualized (eth0)")), ] CINDER_ISCSI_HELPER_CHOICES = [ ('tgtadm', _('tgtadm')), ('lioadm', _('lioadm')), ] class ParameterAwareMixin(object): parameter = None def parameter_fields(request, prefix=None, read_only=False): fields = SortedDict() plan = api.tuskar.Plan.get_the_plan(request) parameters = plan.parameter_list(include_key_parameters=False) for p in parameters: if prefix and not p.name.startswith(prefix): continue Field = django.forms.CharField field_kwargs = {} widget = None if read_only: if p.hidden: widget = tuskar_ui.forms.StaticTextPasswordWidget else: widget = tuskar_ui.forms.StaticTextWidget else: if p.hidden: widget = django.forms.PasswordInput(render_value=True) elif p.parameter_type == 'number': Field = django.forms.IntegerField elif p.parameter_type == 'boolean': Field = django.forms.BooleanField elif (p.parameter_type == 'string' and p.get_constraint_by_type('allowed_values')): Field = django.forms.ChoiceField field_kwargs['choices'] = [ (choice, choice) for choice in p.get_constraint_by_type('allowed_values')['definition']] elif (p.parameter_type in ['json', 'comma_delimited_list'] or 'Certificate' in p.name): widget = django.forms.Textarea fields[p.name] = Field( required=False, label=_parameter_label(p), initial=p.value, widget=widget, **field_kwargs ) fields[p.name].__class__ = type('ParameterAwareField', (ParameterAwareMixin, Field), {}) fields[p.name].parameter = p return fields def _parameter_label(parameter): return tuskar_ui.forms.label_with_tooltip( parameter.label or utils.de_camel_case(parameter.stripped_name), parameter.description) class ServiceConfig(horizon.forms.SelfHandlingForm): def __init__(self, *args, **kwargs): super(ServiceConfig, self).__init__(*args, **kwargs) self.fields.update(parameter_fields(self.request, read_only=True)) def global_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='^(?!.*::)') def controller_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='Controller-1') def compute_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='Compute-1') def block_storage_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='Cinder-Storage-1') def object_storage_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='Swift-Storage-1') def ceph_storage_fieldset(self): return tuskar_ui.forms.fieldset(self, prefix='Ceph-Storage-1') def handle(): pass class AdvancedEditServiceConfig(ServiceConfig): def __init__(self, *args, **kwargs): super(AdvancedEditServiceConfig, self).__init__(*args, **kwargs) self.fields.update(parameter_fields(self.request)) def handle(self, request, data): plan = api.tuskar.Plan.get_the_plan(self.request) # TODO(bcrochet): Commenting this out. # For advanced config, we should have a whitelist of which params # must be synced across roles. # data = self._sync_common_params_across_roles(plan, data) try: plan.patch(request, plan.uuid, data) except Exception as e: horizon.exceptions.handle( request, _("Unable to update the service configuration.")) LOG.exception(e) return False else: horizon.messages.success( request, _("Service configuration updated.")) return True @staticmethod def _sync_common_params_across_roles(plan, parameters_dict): for (p_key, p_value) in parameters_dict.iteritems(): for role in plan.role_list: role_parameter_key = (role.parameter_prefix + api.tuskar.strip_prefix(p_key)) if role_parameter_key in parameters_dict: parameters_dict[role_parameter_key] = p_value return parameters_dict class SimpleEditServiceConfig(horizon.forms.SelfHandlingForm): virt_type = django.forms.ChoiceField( label=_("Deployment Type"), choices=VIRT_TYPE_CHOICES, required=True, help_text=_('If you are testing OpenStack in a virtual machine, ' 'you must configure Compute to use qemu without KVM ' 'and hardware virtualization.')) neutron_public_interface = django.forms.ChoiceField( label=_("Public Interface"), choices=NEUTRON_PUBLIC_INTERFACE_CHOICES, required=True, help_text=_('What interface to bridge onto br-ex for network nodes. ' 'If you are testing OpenStack in a virtual machine' 'you must configure interface to eth0.')) snmp_password = django.forms.CharField( label=_("SNMP Password"), required=True, help_text=_('The user password for SNMPd with readonly ' 'rights running on all Overcloud nodes'), widget=django.forms.PasswordInput(render_value=True)) cloud_name = django.forms.CharField( label=_("Cloud name"), required=True, initial="overcloud", help_text=_('The DNS name of this cloud. ' 'E.g. ci-overcloud.tripleo.org')) cinder_iscsi_helper = django.forms.ChoiceField( label=_("Cinder ISCSI helper"), choices=CINDER_ISCSI_HELPER_CHOICES, required=True, help_text=_('The iSCSI helper to use with cinder.')) ntp_server = django.forms.CharField( label=_("NTP server"), required=False, initial="", help_text=_('Address of the NTP server. If blank, public NTP servers ' 'will be used.')) extra_config = django.forms.CharField( label=_("Extra Config"), required=False, widget=django.forms.Textarea(attrs={'rows': 2}), help_text=("Additional configuration to inject into the cluster." "The data format of this field is JSON." "See http://git.io/PuwLXQ for more information.")) def clean_extra_config(self): data = self.cleaned_data['extra_config'] try: json.loads(data) except Exception as json_error: raise django.forms.ValidationError( _("%(err_msg)s"), params={'err_msg': json_error.message}) return data @staticmethod def _load_additional_parameters(plan, data, form_key, param_name): params = {} param_value = data.get(form_key) # Set the same parameter and value in all roles. for role in plan.role_list: key = role.parameter_prefix + param_name if key in [parameter.name for parameter in role.parameter_list(plan)]: params[key] = param_value return params def handle(self, request, data): plan = api.tuskar.Plan.get_the_plan(self.request) compute_prefix = plan.get_role_by_name('Compute').parameter_prefix controller_prefix = plan.get_role_by_name( 'Controller').parameter_prefix cinder_prefix = plan.get_role_by_name( 'Cinder-Storage').parameter_prefix virt_type = data.get('virt_type') neutron_public_interface = data.get('neutron_public_interface') cloud_name = data.get('cloud_name') cinder_iscsi_helper = data.get('cinder_iscsi_helper') ntp_server = data.get('ntp_server') parameters = { compute_prefix + 'NovaComputeLibvirtType': virt_type, controller_prefix + 'CinderISCSIHelper': cinder_iscsi_helper, cinder_prefix + 'CinderISCSIHelper': cinder_iscsi_helper, controller_prefix + 'CloudName': cloud_name, controller_prefix + 'NeutronPublicInterface': neutron_public_interface, compute_prefix + 'NeutronPublicInterface': neutron_public_interface, controller_prefix + 'NtpServer': ntp_server, compute_prefix + 'NtpServer': ntp_server, } parameters.update(self._load_additional_parameters( plan, data, 'snmp_password', 'SnmpdReadonlyUserPassword')) parameters.update(self._load_additional_parameters( plan, data, 'extra_config', 'ExtraConfig')) try: plan.patch(request, plan.uuid, parameters) except Exception as e: horizon.exceptions.handle( request, _("Unable to update the service configuration.")) LOG.exception(e) return False else: horizon.messages.success( request, _("Service configuration updated.")) return True