887 lines
32 KiB
Python
Executable File
887 lines
32 KiB
Python
Executable File
#
|
|
# 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, _("<Select interface type>")),
|
|
('ae', _("aggregated ethernet")),
|
|
('vlan', _("vlan")),
|
|
)
|
|
|
|
AE_MODE_CHOICES = (
|
|
('active_standby', _("active/standby")),
|
|
('balanced', _("balanced")),
|
|
('802.3ad', _("802.3ad")),
|
|
)
|
|
|
|
AE_XMIT_HASH_POLICY_CHOICES = (
|
|
('layer3+4', _("layer3+4")),
|
|
('layer2+3', _("layer2+3")),
|
|
('layer2', _("layer2")),
|
|
)
|
|
|
|
IPV4_MODE_CHOICES = (
|
|
('disabled', _("Disabled")),
|
|
('static', _("Static")),
|
|
('pool', _("Pool")),
|
|
)
|
|
|
|
IPV6_MODE_CHOICES = (
|
|
('disabled', _("Disabled")),
|
|
('static', _("Static")),
|
|
('pool', _("Pool")),
|
|
('auto', _("Automatic Assignment")),
|
|
('link-local', _("Link Local")),
|
|
)
|
|
|
|
ihost_uuid = forms.CharField(
|
|
label=_("ihost_uuid"),
|
|
initial='ihost_uuid',
|
|
required=False,
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
host_id = forms.CharField(
|
|
label=_("host_id"),
|
|
initial='host_id',
|
|
required=False,
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
# don't enforce a max length in ifname form field as
|
|
# this will be validated by the SysInv REST call.
|
|
# This ensures that both cgsclient and Dashboard
|
|
# have the same length constraints.
|
|
ifname = forms.RegexField(
|
|
label=_("Interface Name"),
|
|
required=True,
|
|
regex=r'^[\w\.\-]+$',
|
|
error_messages={
|
|
'invalid':
|
|
_('Name may only contain letters, numbers, underscores, '
|
|
'periods and hyphens.')})
|
|
|
|
networktype = forms.MultipleChoiceField(
|
|
label=_("Network Type"),
|
|
required=True,
|
|
choices=NETWORK_TYPE_CHOICES,
|
|
widget=forms.CheckboxSelectMultiple(
|
|
attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'network_type'}))
|
|
|
|
iftype = forms.ChoiceField(
|
|
label=_("Interface Type"),
|
|
required=True,
|
|
choices=INTERFACE_TYPE_CHOICES,
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switchable switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-none': 'Interface Type',
|
|
'data-network_type-infra': 'Interface Type',
|
|
'data-network_type-data': 'Interface Type',
|
|
'data-network_type-data-external': 'Interface Type',
|
|
'data-network_type-control': 'Interface Type',
|
|
'data-network_type-mgmt': 'Interface Type',
|
|
'data-network_type-oam': 'Interface Type',
|
|
'data-network_type-pxeboot': 'Interface Type',
|
|
'data-slug': 'interface_type'}))
|
|
|
|
aemode = forms.ChoiceField(
|
|
label=_("Aggregated Ethernet - Mode"),
|
|
required=False,
|
|
choices=AE_MODE_CHOICES,
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switchable switched',
|
|
'data-slug': 'ae_mode',
|
|
'data-switch-on': 'interface_type',
|
|
'data-interface_type-ae': 'Aggregated Ethernet - Mode'}))
|
|
|
|
txhashpolicy = forms.ChoiceField(
|
|
label=_("Aggregated Ethernet - Tx Policy"),
|
|
required=False,
|
|
choices=AE_XMIT_HASH_POLICY_CHOICES,
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'ae_mode',
|
|
'data-ae_mode-balanced': 'Aggregated Ethernet - Tx Policy',
|
|
'data-ae_mode-802.3ad': 'Aggregated Ethernet - Tx Policy'}))
|
|
|
|
vlan_id = forms.IntegerField(
|
|
label=_("Vlan ID"),
|
|
initial=1,
|
|
min_value=1,
|
|
max_value=4094,
|
|
required=False,
|
|
help_text=_("Virtual LAN tag."),
|
|
error_messages={'invalid': _('Vlan ID must be '
|
|
'between 1 and 4094.')},
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'interface_type',
|
|
'data-slug': 'vlanid',
|
|
'data-interface_type-vlan': 'Vlan ID'}))
|
|
|
|
uses = forms.MultipleChoiceField(
|
|
label=_("Interface(s)"),
|
|
required=False,
|
|
widget=forms.CheckboxSelectMultiple(),
|
|
help_text=_("Interface(s) of Interface."))
|
|
|
|
ports = forms.CharField(
|
|
label=_("Port(s)"),
|
|
required=False,
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
providernetworks_data = MultipleChoiceField(
|
|
label=_("Provider Network(s)"),
|
|
required=False,
|
|
widget=CheckboxSelectMultiple(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-data': ''},
|
|
empty_value=_("No provider networks available for network type.")))
|
|
|
|
providernetworks_data_external = MultipleChoiceField(
|
|
label=_("Provider Network(s)"),
|
|
required=False,
|
|
widget=CheckboxSelectMultiple(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-data-external': ''},
|
|
empty_value=_("No provider networks available for network type.")))
|
|
|
|
providernetworks_pci = MultipleChoiceField(
|
|
label=_("Provider Network(s)"),
|
|
required=False,
|
|
widget=CheckboxSelectMultiple(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-pci-passthrough': ''},
|
|
empty_value=_("No provider networks available for network type.")))
|
|
|
|
providernetworks_sriov = MultipleChoiceField(
|
|
label=_("Provider Network(s)"),
|
|
required=False,
|
|
widget=CheckboxSelectMultiple(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-pci-sriov': ''},
|
|
empty_value=_("No provider networks available for network type.")))
|
|
|
|
imtu = forms.IntegerField(
|
|
label=_("MTU"),
|
|
initial=1500,
|
|
min_value=576,
|
|
max_value=9216,
|
|
required=False,
|
|
help_text=_("Maximum Transmit Unit."),
|
|
error_messages={'invalid': _('MTU must be '
|
|
'between 576 and 9216 bytes.')},
|
|
widget=forms.TextInput(attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-none': _('MTU'),
|
|
'data-network_type-data': _('MTU'),
|
|
'data-network_type-data-external': _('MTU'),
|
|
'data-network_type-control': _('MTU'),
|
|
'data-network_type-mgmt': _('MTU'),
|
|
'data-network_type-infra': _('MTU'),
|
|
'data-network_type-pci-passthrough': _('MTU'),
|
|
'data-network_type-pci-sriov': _('MTU'),
|
|
'data-network_type-oam': _('MTU'),
|
|
'data-network_type-pxeboot': _('MTU')}))
|
|
|
|
ipv4_mode = forms.ChoiceField(
|
|
label=_("IPv4 Addressing Mode"),
|
|
required=False,
|
|
initial='disabled',
|
|
choices=IPV4_MODE_CHOICES,
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switchable switched',
|
|
'data-slug': 'ipv4_mode',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-data': 'IPv4 Addressing Mode',
|
|
'data-network_type-control': 'IPv4 Addressing Mode'}))
|
|
|
|
ipv4_pool = forms.ChoiceField(
|
|
label=_("IPv4 Address Pool"),
|
|
required=False,
|
|
initial='',
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'ipv4_mode',
|
|
'data-ipv4_mode-pool': 'IPv4 Address Pool'}))
|
|
|
|
ipv6_mode = forms.ChoiceField(
|
|
label=_("IPv6 Addressing Mode"),
|
|
required=False,
|
|
initial='disabled',
|
|
choices=IPV6_MODE_CHOICES,
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switchable switched',
|
|
'data-slug': 'ipv6_mode',
|
|
'data-switch-on': 'network_type',
|
|
'data-network_type-data': 'IPv6 Addressing Mode',
|
|
'data-network_type-control': 'IPv6 Addressing Mode'}))
|
|
|
|
ipv6_pool = forms.ChoiceField(
|
|
label=_("IPv6 Address Pool"),
|
|
required=False,
|
|
initial='',
|
|
widget=forms.Select(
|
|
attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'ipv6_mode',
|
|
'data-ipv6_mode-pool': 'IPv6 Address Pool'}))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddInterface, self).__init__(*args, **kwargs)
|
|
|
|
# Populate Available Port Choices
|
|
# Only include ports that are not already part of other interfaces
|
|
this_interface_id = 0
|
|
|
|
current_interface = None
|
|
if (type(self) is UpdateInterface):
|
|
this_interface_id = kwargs['initial']['id']
|
|
current_interface = api.sysinv.host_interface_get(
|
|
self.request, this_interface_id)
|
|
else:
|
|
self.fields['providernetworks_sriov'].widget = \
|
|
forms.widgets.HiddenInput()
|
|
self.fields['providernetworks_pci'].widget = \
|
|
forms.widgets.HiddenInput()
|
|
|
|
host_uuid = kwargs['initial']['ihost_uuid']
|
|
|
|
# Retrieve SDN configuration
|
|
sdn_enabled = kwargs['initial']['sdn_enabled']
|
|
sdn_l3_mode = kwargs['initial']['sdn_l3_mode_enabled']
|
|
|
|
# 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)
|
|
|
|
# Populate Provider Network Choices by querying Neutron
|
|
self.extras = {}
|
|
interfaces = api.sysinv.host_interface_list(self.request, host_uuid)
|
|
|
|
used_providernets = []
|
|
for i in interfaces:
|
|
networktypelist = []
|
|
if i.networktype:
|
|
networktypelist = [i.networktype.split(",")]
|
|
if 'data' in networktypelist and \
|
|
i.providernetworks and \
|
|
i.uuid != this_interface_id:
|
|
used_providernets = used_providernets + \
|
|
i.providernetworks.split(",")
|
|
|
|
providernet_choices = []
|
|
providernet_filtered = []
|
|
providernet_flat = None
|
|
providernets = api.neutron.provider_network_list(self.request)
|
|
for provider in providernets:
|
|
label = "{} (mtu={})".format(provider.name, provider.mtu)
|
|
providernet = (str(provider.name), label)
|
|
providernet_choices.append(providernet)
|
|
if provider.name not in used_providernets:
|
|
providernet_filtered.append(providernet)
|
|
if provider.type == 'flat':
|
|
providernet_flat = providernet
|
|
|
|
self.fields['providernetworks_data'].choices = providernet_filtered
|
|
if (type(self) is UpdateInterface):
|
|
self.fields['providernetworks_pci'].choices = providernet_choices
|
|
self.fields['providernetworks_sriov'].choices = providernet_choices
|
|
|
|
if not (sdn_enabled and sdn_l3_mode):
|
|
self.fields['providernetworks_data_external'].widget = \
|
|
forms.widgets.HiddenInput()
|
|
nt_choices = self.fields['networktype'].choices
|
|
self.fields['networktype'].choices = [i for i in nt_choices
|
|
if i[0] != 'data-external']
|
|
else:
|
|
# Support a 'data-external' network type and allow
|
|
# its Provider Network configuration (FLAT only)
|
|
self.fields['providernetworks_data_external'].choices = \
|
|
[providernet_flat]
|
|
|
|
if current_interface:
|
|
# update operation
|
|
if not current_interface.uses:
|
|
# update default interfaces
|
|
self.fields['uses'].widget = forms.widgets.HiddenInput()
|
|
avail_port_list = api.sysinv.host_port_list(
|
|
self.request, host_uuid)
|
|
for p in avail_port_list:
|
|
if p.interface_uuid == this_interface_id:
|
|
self.fields['ports'].initial = p.uuid
|
|
else:
|
|
# update non default interfaces
|
|
avail_interface_list = api.sysinv.host_interface_list(
|
|
self.request, host_uuid)
|
|
interface_tuple_list = []
|
|
for i in avail_interface_list:
|
|
if i.uuid != current_interface.uuid:
|
|
interface_tuple_list.append(
|
|
(i.uuid, "%s (%s, %s)" %
|
|
(i.ifname, i.imac, i.networktype)))
|
|
|
|
uses_initial = [i.uuid for i in avail_interface_list if
|
|
i.ifname in current_interface.uses]
|
|
|
|
self.fields['uses'].initial = uses_initial
|
|
self.fields['uses'].choices = interface_tuple_list
|
|
|
|
if current_interface.vlan_id:
|
|
self.fields['vlan_id'].initial = current_interface.vlan_id
|
|
|
|
else:
|
|
# add operation
|
|
avail_interface_list = api.sysinv.host_interface_list(
|
|
self.request, host_uuid)
|
|
interface_tuple_list = []
|
|
for i in avail_interface_list:
|
|
interface_tuple_list.append(
|
|
(i.uuid, "%s (%s, %s)" %
|
|
(i.ifname, i.imac, i.networktype)))
|
|
self.fields['uses'].choices = interface_tuple_list
|
|
self.fields['networktype'].initial = ('none', 'none')
|
|
|
|
def clean(self):
|
|
cleaned_data = super(AddInterface, self).clean()
|
|
|
|
networktype = cleaned_data.get('networktype', 'none')
|
|
|
|
if ('data' not in networktype and
|
|
'control' not in networktype):
|
|
cleaned_data.pop('ipv4_mode', None)
|
|
cleaned_data.pop('ipv6_mode', None)
|
|
|
|
if cleaned_data.get('ipv4_mode') != 'pool':
|
|
cleaned_data.pop('ipv4_pool', None)
|
|
|
|
if cleaned_data.get('ipv6_mode') != 'pool':
|
|
cleaned_data.pop('ipv6_pool', None)
|
|
|
|
if 'data' in networktype:
|
|
providernetworks = filter(
|
|
None, cleaned_data.get('providernetworks_data', []))
|
|
elif 'data-external' in networktype:
|
|
# 'data' and 'data-external' nts cannot be consolidated
|
|
# on same interface
|
|
providernetworks = filter(
|
|
None, cleaned_data.get('providernetworks_data_external', []))
|
|
elif 'pci-passthrough' in networktype:
|
|
providernetworks = filter(None, cleaned_data.get(
|
|
'providernetworks_pci', []))
|
|
elif 'pci-sriov' in networktype:
|
|
providernetworks = filter(
|
|
None,
|
|
cleaned_data.get('providernetworks_sriov', []))
|
|
else:
|
|
providernetworks = []
|
|
|
|
# providernetwork selection is required for 'data', 'pci-passthrough'
|
|
# and 'pci-sriov'. It is NOT required for any other network type
|
|
if not providernetworks:
|
|
|
|
# Note that 1 of 3 different controls may be used to select
|
|
# provider network, make sure to set the error on the appropriate
|
|
# control
|
|
if any(network in ['data', 'pci-passthrough', 'pci-sriov']
|
|
for network in networktype):
|
|
raise forms.ValidationError(_(
|
|
"You must specify a Provider Network"))
|
|
|
|
cleaned_data['providernetworks'] = ",".join(providernetworks)
|
|
if 'providernetworks_data' in cleaned_data:
|
|
del cleaned_data['providernetworks_data']
|
|
if 'providernetworks_data_external' in cleaned_data:
|
|
del cleaned_data['providernetworks_data_external']
|
|
if 'providernetworks_pci' in cleaned_data:
|
|
del cleaned_data['providernetworks_pci']
|
|
if 'providernetworks_sriov' in cleaned_data:
|
|
del cleaned_data['providernetworks_sriov']
|
|
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
host_id = data['host_id']
|
|
|
|
try:
|
|
del data['host_id']
|
|
|
|
if data['ports']:
|
|
del data['uses']
|
|
else:
|
|
uses = data['uses'][:]
|
|
data['uses'] = uses
|
|
del data['ports']
|
|
|
|
if not data['providernetworks']:
|
|
del data['providernetworks']
|
|
|
|
if not data['vlan_id'] or data['iftype'] != 'vlan':
|
|
del data['vlan_id']
|
|
else:
|
|
data['vlan_id'] = unicode(data['vlan_id'])
|
|
|
|
if any(network in data['networktype'] for network in
|
|
['mgmt', 'infra', 'oam']):
|
|
del data['imtu']
|
|
else:
|
|
data['imtu'] = unicode(data['imtu'])
|
|
|
|
if data['networktype']:
|
|
data['networktype'] = ",".join(data['networktype'])
|
|
|
|
if data['iftype'] != 'ae':
|
|
del data['txhashpolicy']
|
|
del data['aemode']
|
|
elif data['aemode'] == 'active_standby':
|
|
del data['txhashpolicy']
|
|
|
|
interface = api.sysinv.host_interface_create(request, **data)
|
|
msg = _('Interface "%s" was successfully'
|
|
' created.') % 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 create interface "%s".') % data['ifname']
|
|
LOG.info(msg)
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
exceptions.handle(request, msg, redirect=redirect)
|
|
|
|
|
|
class UpdateInterface(AddInterface):
|
|
MGMT_AE_MODE_CHOICES = (
|
|
('active_standby', _("active/standby")),
|
|
('802.3ad', _("802.3ad")),
|
|
)
|
|
|
|
INTERFACE_TYPE_CHOICES = (
|
|
(None, _("<Select interface type>")),
|
|
('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)
|