# # Copyright (c) 2013-2016 Wind River Systems, Inc. # # The right to copy, distribute, modify, or otherwise make use # of this software may be licensed only pursuant to the terms # of an applicable Wind River license agreement. # # vim: tabstop=4 shiftwidth=4 softtabstop=4 import logging from compiler.ast import flatten import netaddr from cgtsclient import exc from django.core.urlresolvers import reverse # noqa from django import shortcuts from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages from openstack_dashboard import api LOG = logging.getLogger(__name__) def _get_ipv4_pool_choices(pools): choices = [] for p in pools: address = netaddr.IPAddress(p.network) if address.version == 4: choices.append((p.uuid, p.name)) return choices def _get_ipv6_pool_choices(pools): choices = [] for p in pools: address = netaddr.IPAddress(p.network) if address.version == 6: choices.append((p.uuid, p.name)) return choices class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple): """Custom checkbox select widget that will render a text string with an hidden input if there are no choices. """ def __init__(self, attrs=None, choices=(), empty_value=''): super(CheckboxSelectMultiple, self).__init__(attrs, choices) self.empty_value = empty_value def render(self, name, value, attrs=None, choices=()): if self.choices: return super(CheckboxSelectMultiple, self).render(name, value, attrs, choices) else: hi = forms.HiddenInput(self.attrs) hi.is_hidden = False # ensure text is rendered return mark_safe(self.empty_value + hi.render(name, None, attrs)) class MultipleChoiceField(forms.MultipleChoiceField): """Custom multiple choice field that only validates if a value was provided. """ def valid_value(self, value): if not self.required and not value: return True return super(MultipleChoiceField, self).valid_value(value) class AddInterfaceProfile(forms.SelfHandlingForm): host_id = forms.CharField(widget=forms.widgets.HiddenInput) profilename = forms.CharField(label=_("Interface Profile Name"), required=True) failure_url = 'horizon:admin:inventory:detail' def __init__(self, *args, **kwargs): super(AddInterfaceProfile, self).__init__(*args, **kwargs) def clean(self): cleaned_data = super(AddInterfaceProfile, self).clean() # host_id = cleaned_data.get('host_id') # interfaceProfileName = cleaned_data.get('hostname') return cleaned_data def handle(self, request, data): host_id = data['host_id'] interfaceProfileName = data['profilename'] try: interfaceProfile = api.sysinv.host_interfaceprofile_create(request, **data) msg = _( 'Interface Profile "%s" was ' 'successfully created.') % interfaceProfileName LOG.debug(msg) messages.success(request, msg) return interfaceProfile except exc.ClientException as ce: # Allow REST API error message to appear on UI messages.error(request, ce) LOG.error(ce) # Redirect to failure pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception: msg = _( 'Failed to create interface' ' profile "%s".') % interfaceProfileName LOG.info(msg) redirect = reverse(self.failure_url, args=[data['host_id']]) exceptions.handle(request, msg, redirect=redirect) class AddInterface(forms.SelfHandlingForm): NETWORK_TYPE_CHOICES = ( ('none', _("none")), ('mgmt', _("mgmt")), ('oam', _("oam")), ('data', _("data")), ('data-external', _("data-external")), ('control', _("control")), ('infra', _("infra")), ('pxeboot', _("pxeboot")), ) INTERFACE_TYPE_CHOICES = ( (None, _("")), ('ethernet', _("ethernet")), ('ae', _("aggregated ethernet")), ('vlan', _("vlan")), ) id = forms.CharField(widget=forms.widgets.HiddenInput) networktype = forms.MultipleChoiceField( label=_("Network Type"), help_text=_("Note: The network type of an interface cannot be changed " "without first being reset back to 'none'"), required=True, widget=forms.CheckboxSelectMultiple( attrs={ 'class': 'switchable', 'data-slug': 'network_type'})) sriov_numvfs = forms.IntegerField( label=_("Virtual Functions"), required=False, min_value=0, help_text=_("Virtual Functions for pci-sriov."), widget=forms.TextInput( attrs={ 'class': 'switched', 'data-switch-on': 'network_type', 'data-slug': 'num_vfs', 'data-network_type-pci-sriov': 'Num VFs'})) sriov_totalvfs = forms.IntegerField( label=_("Maximum Virtual Functions"), required=False, widget=forms.widgets.TextInput( attrs={ 'class': 'switched', 'readonly': 'readonly', 'data-switch-on': 'network_type', 'data-network_type-pci-sriov': 'Max VFs'})) iftypedata = forms.ChoiceField( label=_("Interface Type"), choices=INTERFACE_TYPE_CHOICES, widget=forms.HiddenInput) def __init__(self, *args, **kwargs): super(UpdateInterface, self).__init__(*args, **kwargs) networktype_val = kwargs['initial']['networktype'] host_uuid = kwargs['initial']['ihost_uuid'] # Get the SDN configuration sdn_enabled = kwargs['initial']['sdn_enabled'] sdn_l3_mode = kwargs['initial']['sdn_l3_mode_enabled'] this_interface_id = kwargs['initial']['id'] iftype_val = kwargs['initial']['iftype'] if 'mgmt' in networktype_val: self.fields['aemode'].choices = self.MGMT_AE_MODE_CHOICES else: self.fields['aemode'].choices = self.AE_MODE_CHOICES # Populate Address Pool selections pools = api.sysinv.address_pool_list(self.request) self.fields['ipv4_pool'].choices = _get_ipv4_pool_choices(pools) self.fields['ipv6_pool'].choices = _get_ipv6_pool_choices(pools) self.fields['ipv4_pool'].initial = kwargs['initial'].get('ipv4_pool') self.fields['ipv6_pool'].initial = kwargs['initial'].get('ipv6_pool') # Setting field to read-only doesn't actually work so we're making # it disabled. This has the effect of not allowing the data through # to the form submission, so we require a hidden field to carry the # actual value through (iftype data) self.fields['iftype'].widget.attrs['disabled'] = 'disabled' self.fields['iftype'].required = False self.fields['iftype'].choices = self.INTERFACE_TYPE_CHOICES self.fields['iftypedata'].initial = kwargs['initial'].get('iftype') self.fields['iftype'].initial = kwargs['initial'].get('iftype') # Load the networktype choices networktype_choices = [] used_choices = [] if networktype_val: for network in networktype_val: label = "{}".format(network) net_type = (str(network), label) used_choices.append(str(network)) networktype_choices.append(net_type) else: label = "{}".format("none") net_type = ("none", label) networktype_choices.append(net_type) used_choices.append("none") # if SDN L3 mode is enabled, then we may allow # updating an interface network type to 'data-external' data_choices = ['data', 'control'] if (sdn_enabled and sdn_l3_mode): data_choices.append('data-external') if iftype_val == 'ethernet': choices_list = ['none', 'infra', 'oam', 'mgmt', 'pci-passthrough', data_choices, 'pci-sriov', 'pxeboot'] elif iftype_val == 'ae': choices_list = ['none', 'infra', 'oam', 'mgmt', data_choices, 'pxeboot'] else: choices_list = ['infra', 'oam', 'mgmt', data_choices] choices_list = flatten(choices_list) for choice in choices_list: if choice not in used_choices: label = "{}".format(choice) net_type = (str(choice), label) networktype_choices.append(net_type) self.fields['networktype'].choices = networktype_choices if not networktype_val: del kwargs['initial']['networktype'] self.fields['networktype'].initial = ('none', 'none') # Get the total possible number of VFs for SRIOV network type port_list = api.sysinv.host_port_list(self.request, host_uuid) for p in port_list: if p.interface_uuid == this_interface_id: if p.sriov_totalvfs: self.fields['sriov_totalvfs'].initial = p.sriov_totalvfs else: self.fields['sriov_totalvfs'].initial = 0 break initial_numvfs = kwargs['initial']['sriov_numvfs'] if initial_numvfs: self.fields['sriov_numvfs'].initial = initial_numvfs else: self.fields['sriov_numvfs'].initial = 0 def clean(self): cleaned_data = super(UpdateInterface, self).clean() cleaned_data['iftype'] = cleaned_data.get('iftypedata') cleaned_data.pop('iftypedata', None) return cleaned_data def handle(self, request, data): host_id = data['host_id'] interface_id = data['id'] host_uuid = data['ihost_uuid'] try: if data['ports']: del data['uses'] else: uses = data['uses'][:] data['usesmodify'] = ','.join(uses) del data['ports'] del data['uses'] del data['id'] del data['host_id'] del data['ihost_uuid'] if not data['vlan_id'] or data['iftype'] != 'vlan': del data['vlan_id'] else: data['vlan_id'] = unicode(data['vlan_id']) data['imtu'] = unicode(data['imtu']) if data['iftype'] != 'ae': del data['txhashpolicy'] del data['aemode'] elif data['aemode'] == 'active_standby': del data['txhashpolicy'] if 'none' in data['networktype']: avail_port_list = api.sysinv.host_port_list( self.request, host_uuid) current_interface = api.sysinv.host_interface_get( self.request, interface_id) if data['iftype'] != 'ae' or data['iftype'] != 'vlan': for p in avail_port_list: if p.interface_uuid == current_interface.uuid: data['ifname'] = p.get_port_display_name() break if any(nt in ['data', 'data-external'] for nt in [str(current_interface.networktype).split(",")]): data['providernetworks'] = 'none' if not data['providernetworks']: del data['providernetworks'] if 'sriov_numvfs' in data: data['sriov_numvfs'] = unicode(data['sriov_numvfs']) # Explicitly set iftype when user selects pci-pt or pci-sriov network_type = \ flatten(list(nt) for nt in self.fields['networktype'].choices) if 'pci-passthrough' in network_type or \ ('pci-sriov' in network_type and data['sriov_numvfs']): current_interface = api.sysinv.host_interface_get( self.request, interface_id) if current_interface.iftype != 'ethernet': # Only ethernet interfaces can be pci-sriov msg = _('pci-passthrough or pci-sriov can only' ' be set on ethernet interfaces') messages.error(request, msg) LOG.error(msg) # Redirect to failure pg redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) else: data['iftype'] = current_interface.iftype del data['sriov_totalvfs'] if 'pci-sriov' not in data['networktype']: del data['sriov_numvfs'] if data['networktype']: data['networktype'] = ",".join(data['networktype']) interface = api.sysinv.host_interface_update(request, interface_id, **data) # FIXME: this should be done under # the interface update API of sysinv # Update Ports' iinterface_uuid attribute # port_list = api.sysinv.host_port_list(request, host_uuid) # for p in port_list: # if p.uuid in ports: # pdata = { 'interface_uuid' : interface.uuid } # api.sysinv.host_port_update(request, p.uuid, **pdata) # elif p.interface_uuid == interface.uuid: # pdata = { 'interface_uuid' : '0' } # api.sysinv.host_port_update(request, p.uuid, **pdata) msg = _('Interface "%s" was' ' successfully updated.') % data['ifname'] LOG.debug(msg) messages.success(request, msg) return interface except exc.ClientException as ce: # Allow REST API error message to appear on UI messages.error(request, ce) LOG.error(ce) # Redirect to failure page redirect = reverse(self.failure_url, args=[host_id]) return shortcuts.redirect(redirect) except Exception: msg = _('Failed to update interface "%s".') % data['ifname'] LOG.info(msg) redirect = reverse(self.failure_url, args=[host_id]) exceptions.handle(request, msg, redirect=redirect)