Merge "Create DataNetworks modelling in System Configuration"
This commit is contained in:
commit
91f88e9afc
@ -1,2 +1,2 @@
|
||||
SRC_DIR="cgts-client"
|
||||
TIS_PATCH_VER=61
|
||||
TIS_PATCH_VER=62
|
||||
|
@ -25,6 +25,7 @@ from cgtsclient.v1 import ceph_mon
|
||||
from cgtsclient.v1 import certificate
|
||||
from cgtsclient.v1 import cluster
|
||||
from cgtsclient.v1 import controller_fs
|
||||
from cgtsclient.v1 import datanetwork
|
||||
from cgtsclient.v1 import drbdconfig
|
||||
from cgtsclient.v1 import ethernetport
|
||||
from cgtsclient.v1 import fernet
|
||||
@ -42,6 +43,7 @@ from cgtsclient.v1 import iinterface
|
||||
from cgtsclient.v1 import ilvg
|
||||
from cgtsclient.v1 import imemory
|
||||
from cgtsclient.v1 import inode
|
||||
from cgtsclient.v1 import interface_datanetwork
|
||||
from cgtsclient.v1 import interface_network
|
||||
from cgtsclient.v1 import intp
|
||||
from cgtsclient.v1 import iprofile
|
||||
@ -132,6 +134,9 @@ class Client(http.HTTPClient):
|
||||
self.load = load.LoadManager(self)
|
||||
self.upgrade = upgrade.UpgradeManager(self)
|
||||
self.network = network.NetworkManager(self)
|
||||
self.datanetwork = datanetwork.DataNetworkManager(self)
|
||||
self.interface_datanetwork = \
|
||||
interface_datanetwork.InterfaceDataNetworkManager(self)
|
||||
self.interface_network = interface_network.InterfaceNetworkManager(self)
|
||||
self.service_parameter = service_parameter.ServiceParameterManager(self)
|
||||
self.cluster = cluster.ClusterManager(self)
|
||||
|
78
sysinv/cgts-client/cgts-client/cgtsclient/v1/datanetwork.py
Normal file
78
sysinv/cgts-client/cgts-client/cgtsclient/v1/datanetwork.py
Normal file
@ -0,0 +1,78 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
|
||||
from cgtsclient.common import base
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
|
||||
|
||||
CREATION_ATTRIBUTES = [
|
||||
'network_type', 'name', 'description', 'mtu',
|
||||
'multicast_group', 'port_num', 'ttl', 'mode']
|
||||
|
||||
|
||||
class DataNetwork(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<datanetwork %s>" % self._info
|
||||
|
||||
|
||||
class DataNetworkManager(base.Manager):
|
||||
resource_class = DataNetwork
|
||||
|
||||
def list(self):
|
||||
path = '/v1/datanetworks'
|
||||
return self._list(path, "datanetworks")
|
||||
|
||||
def get(self, datanetwork_id):
|
||||
path = '/v1/datanetworks/%s' % datanetwork_id
|
||||
try:
|
||||
return self._list(path)[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def create(self, **kwargs):
|
||||
path = '/v1/datanetworks'
|
||||
new = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in CREATION_ATTRIBUTES:
|
||||
new[key] = value
|
||||
else:
|
||||
raise exc.InvalidAttribute('%s' % key)
|
||||
return self._create(path, new)
|
||||
|
||||
def update(self, datanetwork_id, patch):
|
||||
path = '/v1/datanetworks/%s' % datanetwork_id
|
||||
return self._update(path, patch)
|
||||
|
||||
def delete(self, datanetwork_id):
|
||||
path = '/v1/datanetworks/%s' % datanetwork_id
|
||||
return self._delete(path)
|
||||
|
||||
|
||||
def _find_datanetwork(cc, datanetwork):
|
||||
if datanetwork.isdigit() and not utils.is_uuid_like(datanetwork):
|
||||
datanetwork_list = cc.datanetwork.list()
|
||||
for n in datanetwork_list:
|
||||
if str(n.id) == datanetwork:
|
||||
return n
|
||||
else:
|
||||
raise exc.CommandError('datanetwork not found: %s' % datanetwork)
|
||||
elif utils.is_uuid_like(datanetwork):
|
||||
try:
|
||||
h = cc.datanetwork.get(datanetwork)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('datanetwork not found: %s' % datanetwork)
|
||||
else:
|
||||
return h
|
||||
else:
|
||||
datanetwork_list = cc.datanetwork.list()
|
||||
for n in datanetwork_list:
|
||||
if n.name == datanetwork:
|
||||
return n
|
||||
else:
|
||||
raise exc.CommandError('datanetwork not found: %s' % datanetwork)
|
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# All Rights Reserved.
|
||||
#
|
||||
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
|
||||
DATANETWORK_TYPE_VXLAN = "vxlan"
|
||||
|
||||
|
||||
def _print_datanetwork_show(obj):
|
||||
fields = ['id', 'uuid', 'name', 'network_type', 'mtu',
|
||||
'description']
|
||||
|
||||
if obj.network_type == DATANETWORK_TYPE_VXLAN:
|
||||
fields.append('multicast_group')
|
||||
fields.append('port_num')
|
||||
fields.append('ttl')
|
||||
fields.append('mode')
|
||||
|
||||
data = [(f, getattr(obj, f, '')) for f in fields]
|
||||
utils.print_tuple_list(data)
|
||||
|
||||
|
||||
@utils.arg('datanetwork_id',
|
||||
metavar='<datanetwork_id>',
|
||||
help="UUID or name of datanetwork")
|
||||
def do_datanetwork_show(cc, args):
|
||||
"""Show datanetwork details."""
|
||||
|
||||
datanetwork = cc.datanetwork.get(args.datanetwork_id)
|
||||
_print_datanetwork_show(datanetwork)
|
||||
|
||||
|
||||
def do_datanetwork_list(cc, args):
|
||||
"""List datanetworks."""
|
||||
|
||||
labels = ['uuid', 'name', 'network_type', 'mtu']
|
||||
fields = ['uuid', 'name', 'network_type', 'mtu']
|
||||
datanetworks = cc.datanetwork.list()
|
||||
utils.print_list(datanetworks, fields, labels, sortby=1)
|
||||
|
||||
|
||||
@utils.arg('name',
|
||||
metavar='<datanetwork_name>',
|
||||
help="Name of the datanetwork [REQUIRED]")
|
||||
@utils.arg('network_type',
|
||||
metavar='<network_type>',
|
||||
choices=['flat', 'vlan', 'vxlan'],
|
||||
help="Type of the datanetwork [REQUIRED]")
|
||||
@utils.arg('-d', '--description',
|
||||
metavar='<description>',
|
||||
help='User description of the datanetwork')
|
||||
@utils.arg('-m', '--mtu',
|
||||
metavar='<mtu>',
|
||||
default=1500,
|
||||
help='MTU of the datanetwork')
|
||||
@utils.arg('-p', '--port_num',
|
||||
metavar='<port_num>',
|
||||
help='port_num of the datanetwork')
|
||||
@utils.arg('-g', '--multicast_group',
|
||||
metavar='<multicast_group>',
|
||||
help='multicast_group of the datanetwork')
|
||||
@utils.arg('-t', '--ttl',
|
||||
metavar='<ttl>',
|
||||
help='time-to-live of the datanetwork')
|
||||
@utils.arg('-M', '--mode',
|
||||
metavar='<mode>',
|
||||
choices=['dynamic', 'static'],
|
||||
default='dynamic',
|
||||
help='mode of the datanetwork')
|
||||
def do_datanetwork_add(cc, args):
|
||||
"""Add a datanetwork."""
|
||||
|
||||
field_list = ['name', 'network_type', 'mtu', 'description',
|
||||
'multicast_group', 'port_num', 'ttl', 'mode']
|
||||
|
||||
# Prune input fields down to required/expected values
|
||||
data = dict((k, v) for (k, v) in vars(args).items()
|
||||
if k in field_list and not (v is None))
|
||||
|
||||
datanetwork = cc.datanetwork.create(**data)
|
||||
uuid = getattr(datanetwork, 'uuid', '')
|
||||
try:
|
||||
datanetwork = cc.datanetwork.get(uuid)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Created DataNetwork UUID not found: %s' % uuid)
|
||||
_print_datanetwork_show(datanetwork)
|
||||
|
||||
|
||||
@utils.arg('datanetwork_id',
|
||||
metavar='<datanetwork_id>',
|
||||
help="Name of the datanetwork [REQUIRED]")
|
||||
@utils.arg('-m', '--mtu',
|
||||
metavar='<mtu>',
|
||||
help='MTU of the datanetwork')
|
||||
@utils.arg('-d', '--description',
|
||||
metavar='<description>',
|
||||
help='User description of the datanetwork')
|
||||
def do_datanetwork_modify(cc, args):
|
||||
"""Modify a datanetwork."""
|
||||
|
||||
rwfields = ['mtu', 'description']
|
||||
|
||||
user_specified_fields = dict((k, v) for (k, v) in vars(args).items()
|
||||
if k in rwfields and not (v is None))
|
||||
|
||||
patch = []
|
||||
for (k, v) in user_specified_fields.items():
|
||||
patch.append({'op': 'replace', 'path': '/' + k, 'value': v})
|
||||
|
||||
datanetwork = cc.datanetwork.update(args.datanetwork_id, patch)
|
||||
_print_datanetwork_show(datanetwork)
|
||||
|
||||
|
||||
@utils.arg('datanetwork_uuid',
|
||||
metavar='<datanetwork_uuid>',
|
||||
help="UUID of datanetwork entry")
|
||||
def do_datanetwork_delete(cc, args):
|
||||
"""Delete a datanetwork."""
|
||||
|
||||
cc.datanetwork.delete(args.datanetwork_uuid)
|
||||
print('Deleted DataNetwork: %s' % args.datanetwork_uuid)
|
@ -14,7 +14,7 @@ from cgtsclient.v1 import port
|
||||
|
||||
CREATION_ATTRIBUTES = ['ifname', 'iftype', 'ihost_uuid', 'imtu', 'ifclass',
|
||||
'networks', 'network_uuid', 'networktype', 'aemode', 'txhashpolicy',
|
||||
'providernetworks', 'providernetworksdict', 'ifcapabilities', 'ports', 'imac',
|
||||
'providernetworks', 'datanetworks', 'ifcapabilities', 'ports', 'imac',
|
||||
'vlan_id', 'uses', 'used_by',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs']
|
||||
|
@ -17,7 +17,7 @@ from cgtsclient.v1 import network as network_utils
|
||||
|
||||
|
||||
def _print_iinterface_show(cc, iinterface):
|
||||
fields = ['ifname', 'iftype', 'ports', 'providernetworks',
|
||||
fields = ['ifname', 'iftype', 'ports', 'datanetworks',
|
||||
'imac', 'imtu', 'ifclass', 'networks',
|
||||
'aemode', 'schedpolicy', 'txhashpolicy',
|
||||
'uuid', 'ihost_uuid',
|
||||
@ -95,9 +95,12 @@ def do_host_if_list(cc, args):
|
||||
attr_str = "%s,accelerated=True" % attr_str
|
||||
setattr(i, 'attrs', attr_str)
|
||||
|
||||
field_labels = ['uuid', 'name', 'class', 'type', 'vlan id', 'ports', 'uses i/f', 'used by i/f', 'attributes', 'provider networks']
|
||||
fields = ['uuid', 'ifname', 'ifclass', 'iftype', 'vlan_id', 'ports', 'uses', 'used_by', 'attrs', 'providernetworks']
|
||||
utils.print_list(iinterfaces, fields, field_labels, sortby=0, no_wrap_fields=['ports'])
|
||||
field_labels = ['uuid', 'name', 'class', 'type', 'vlan id', 'ports',
|
||||
'uses i/f', 'used by i/f', 'attributes', 'data networks']
|
||||
fields = ['uuid', 'ifname', 'ifclass', 'iftype', 'vlan_id', 'ports',
|
||||
'uses', 'used_by', 'attrs', 'datanetworks']
|
||||
utils.print_list(
|
||||
iinterfaces, fields, field_labels, sortby=0, no_wrap_fields=['ports'])
|
||||
|
||||
|
||||
@utils.arg('hostnameorid',
|
||||
@ -125,11 +128,11 @@ def do_host_if_delete(cc, args):
|
||||
choices=['ae', 'vlan'],
|
||||
nargs='?',
|
||||
help="Type of the interface")
|
||||
@utils.arg('providernetworks',
|
||||
metavar='<providernetworks>',
|
||||
@utils.arg('datanetworks',
|
||||
metavar='<datanetworks>',
|
||||
nargs='?',
|
||||
default=None,
|
||||
help=('The provider network attached to the interface '
|
||||
help=('The data network attached to the interface '
|
||||
'(default: %(default)s) '
|
||||
'[REQUIRED when interface class is data or pci-passthrough'))
|
||||
@utils.arg('-a', '--aemode',
|
||||
@ -175,7 +178,7 @@ def do_host_if_add(cc, args):
|
||||
"""Add an interface."""
|
||||
|
||||
field_list = ['ifname', 'iftype', 'imtu', 'ifclass', 'networks', 'aemode',
|
||||
'txhashpolicy', 'providernetworks', 'vlan_id',
|
||||
'txhashpolicy', 'datanetworks', 'vlan_id',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool']
|
||||
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
@ -194,10 +197,10 @@ def do_host_if_add(cc, args):
|
||||
user_specified_fields = dict((k, v) for (k, v) in vars(args).items()
|
||||
if k in field_list and not (v is None))
|
||||
|
||||
if 'providernetworks' in user_specified_fields.keys():
|
||||
user_specified_fields['providernetworks'] = user_specified_fields['providernetworks'].replace(" ", "")
|
||||
if 'none' in user_specified_fields['providernetworks']:
|
||||
del user_specified_fields['providernetworks']
|
||||
if 'datanetworks' in user_specified_fields.keys():
|
||||
user_specified_fields['datanetworks'] = \
|
||||
user_specified_fields['datanetworks'].split(',')
|
||||
|
||||
if 'networks' in user_specified_fields.keys():
|
||||
network = network_utils._find_network(cc, args.networks)
|
||||
user_specified_fields['networks'] = [str(network.id)]
|
||||
@ -230,7 +233,10 @@ def do_host_if_add(cc, args):
|
||||
help='The MTU of the interface')
|
||||
@utils.arg('-p', '--providernetworks',
|
||||
metavar='<providernetworks>',
|
||||
help='The provider network attached to the interface [REQUIRED]')
|
||||
help='[DEPRECATED] The provider network attached to the interface')
|
||||
@utils.arg('-d', '--datanetworks',
|
||||
metavar='<datanetworks>',
|
||||
help='The data network attached to the interface')
|
||||
@utils.arg('-a', '--aemode',
|
||||
metavar='<ae mode>',
|
||||
choices=['balanced', 'active_standby', '802.3ad'],
|
||||
@ -267,7 +273,7 @@ def do_host_if_modify(cc, args):
|
||||
"""Modify interface attributes."""
|
||||
|
||||
rwfields = ['iftype', 'ifname', 'imtu', 'aemode', 'txhashpolicy',
|
||||
'providernetworks', 'ports', 'ifclass', 'networks',
|
||||
'datanetworks', 'providernetworks', 'ports', 'ifclass', 'networks',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs']
|
||||
|
||||
@ -277,7 +283,13 @@ def do_host_if_modify(cc, args):
|
||||
if k in rwfields and not (v is None))
|
||||
|
||||
if 'providernetworks' in user_specified_fields.keys():
|
||||
user_specified_fields['providernetworks'] = user_specified_fields['providernetworks'].replace(" ", "")
|
||||
user_specified_fields['datanetworks'] = \
|
||||
user_specified_fields['providernetworks']
|
||||
del user_specified_fields['providernetworks']
|
||||
|
||||
elif 'datanetworks' in user_specified_fields.keys():
|
||||
user_specified_fields['datanetworks'] = \
|
||||
user_specified_fields['datanetworks']
|
||||
|
||||
interface = _find_interface(cc, ihost, args.ifnameoruuid)
|
||||
fields = interface.__dict__
|
||||
@ -297,7 +309,7 @@ def do_host_if_modify(cc, args):
|
||||
user_specified_fields['ifname'] = p
|
||||
break
|
||||
if interface.ifclass == 'data':
|
||||
user_specified_fields['providernetworks'] = 'none'
|
||||
user_specified_fields['datanetworks'] = 'none'
|
||||
|
||||
patch = []
|
||||
for (k, v) in user_specified_fields.items():
|
||||
|
58
sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_datanetwork.py
Executable file
58
sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_datanetwork.py
Executable file
@ -0,0 +1,58 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
|
||||
from cgtsclient.common import base
|
||||
from cgtsclient import exc
|
||||
|
||||
|
||||
CREATION_ATTRIBUTES = [
|
||||
'interface_uuid', 'datanetwork_uuid'
|
||||
]
|
||||
|
||||
|
||||
class InterfaceDataNetwork(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<interface_datanetwork %s>" % self._info
|
||||
|
||||
|
||||
class InterfaceDataNetworkManager(base.Manager):
|
||||
resource_class = InterfaceDataNetwork
|
||||
|
||||
def list(self):
|
||||
path = '/v1/interface_datanetworks'
|
||||
return self._list(path, "interface_datanetworks")
|
||||
|
||||
def list_by_host(self, host_uuid):
|
||||
path = '/v1/ihosts/%s/interface_datanetworks' % host_uuid
|
||||
return self._list(path, "interface_datanetworks")
|
||||
|
||||
def list_by_interface(self, interface_uuid):
|
||||
path = '/v1/iinterfaces/%s/interface_datanetworks' % interface_uuid
|
||||
return self._list(path, "interface_datanetworks")
|
||||
|
||||
def get(self, interface_datanetwork_uuid):
|
||||
path = '/v1/interface_datanetworks/%s' % interface_datanetwork_uuid
|
||||
try:
|
||||
return self._list(path)[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def assign(self, **kwargs):
|
||||
path = '/v1/interface_datanetworks'
|
||||
new = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in CREATION_ATTRIBUTES:
|
||||
new[key] = value
|
||||
else:
|
||||
raise exc.InvalidAttribute('%s' % key)
|
||||
return self._create(path, new)
|
||||
|
||||
def remove(self, interface_datanetwork_uuid):
|
||||
path = '/v1/interface_datanetworks/%s' % interface_datanetwork_uuid
|
||||
return self._delete(path)
|
102
sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_datanetwork_shell.py
Executable file
102
sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_datanetwork_shell.py
Executable file
@ -0,0 +1,102 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
from cgtsclient.v1 import datanetwork as datanetwork_utils
|
||||
from cgtsclient.v1 import ihost as ihost_utils
|
||||
from cgtsclient.v1 import iinterface as iinterface_utils
|
||||
|
||||
|
||||
def _print_interface_datanetwork_show(cc, obj):
|
||||
fields = ['hostname', 'uuid', 'ifname', 'datanetwork_name']
|
||||
# Add a hostname column using the forihostid field
|
||||
host_id = str(getattr(obj, 'forihostid', ''))
|
||||
ihost = ihost_utils._find_ihost(cc, host_id)
|
||||
setattr(obj, 'hostname', ihost.hostname)
|
||||
data = [(f, getattr(obj, f, '')) for f in fields]
|
||||
utils.print_tuple_list(data)
|
||||
|
||||
|
||||
@utils.arg('hostnameorid',
|
||||
metavar='<hostnameorid>',
|
||||
help="Name or ID of host")
|
||||
@utils.arg('ifnameoruuid',
|
||||
metavar='<ifnameoruuid>',
|
||||
nargs='?',
|
||||
help="Name or UUID of interface")
|
||||
def do_interface_datanetwork_list(cc, args):
|
||||
"""List datanetwork interfaces."""
|
||||
fields = ['hostname', 'uuid', 'ifname', 'datanetwork_name']
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
if args.ifnameoruuid is None:
|
||||
interface_datanetworks = \
|
||||
cc.interface_datanetwork.list_by_host(ihost.uuid)
|
||||
else:
|
||||
interface = \
|
||||
iinterface_utils._find_interface(cc, ihost, args.ifnameoruuid)
|
||||
interface_datanetworks = \
|
||||
cc.interface_datanetwork.list_by_interface(interface.uuid)
|
||||
# Add a hostname column using the forihostid field
|
||||
for i in interface_datanetworks[:]:
|
||||
host_id = str(getattr(i, 'forihostid', ''))
|
||||
ihost = ihost_utils._find_ihost(cc, host_id)
|
||||
setattr(i, 'hostname', ihost.hostname)
|
||||
utils.print_list(interface_datanetworks, fields, fields, sortby=1)
|
||||
|
||||
|
||||
@utils.arg('interface_datanetwork_uuid',
|
||||
metavar='<interface datanetwork uuid>',
|
||||
help="UUID of interface datanetwork entry")
|
||||
def do_interface_datanetwork_show(cc, args):
|
||||
"""Show interface datanetwork details."""
|
||||
interface_datanetwork = \
|
||||
cc.interface_datanetwork.get(args.interface_datanetwork_uuid)
|
||||
_print_interface_datanetwork_show(cc, interface_datanetwork)
|
||||
|
||||
|
||||
@utils.arg('hostnameorid',
|
||||
metavar='<hostnameorid>',
|
||||
help="Name or ID of host [REQUIRED]")
|
||||
@utils.arg('ifnameoruuid',
|
||||
metavar='<ifnameoruuid>',
|
||||
help="Name or UUID of interface [REQUIRED]")
|
||||
@utils.arg('datanetnameoruuid',
|
||||
metavar='<datanetnameoruuid>',
|
||||
help="Name of UUID of datanetwork [REQUIRED]")
|
||||
def do_interface_datanetwork_assign(cc, args):
|
||||
"""Assign a datanetwork to an interface."""
|
||||
# Determine host, interface, and datanetwork using the given arguments
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
interface = \
|
||||
iinterface_utils._find_interface(cc, ihost, args.ifnameoruuid)
|
||||
datanetwork = \
|
||||
datanetwork_utils._find_datanetwork(cc, args.datanetnameoruuid)
|
||||
|
||||
data = dict()
|
||||
data['interface_uuid'] = interface.uuid
|
||||
data['datanetwork_uuid'] = datanetwork.uuid
|
||||
|
||||
interface_datanetwork = cc.interface_datanetwork.assign(**data)
|
||||
uuid = getattr(interface_datanetwork, 'uuid', '')
|
||||
try:
|
||||
interface_datanetwork = cc.interface_datanetwork.get(uuid)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Created Interface DataNetwork '
|
||||
'UUID not found: %s' % uuid)
|
||||
_print_interface_datanetwork_show(cc, interface_datanetwork)
|
||||
|
||||
|
||||
@utils.arg('interface_datanetwork_uuid',
|
||||
metavar='<interface_datanetwork_uuid>',
|
||||
help="UUID of interface datanetwork entry")
|
||||
def do_interface_datanetwork_remove(cc, args):
|
||||
"""Remove an assigned datanetwork from an interface."""
|
||||
cc.interface_datanetwork.remove(args.interface_datanetwork_uuid)
|
||||
print('Deleted Interface DataNetwork: %s' % args.interface_datanetwork_uuid)
|
@ -63,7 +63,7 @@ def get_interfaceconfig(iprofile):
|
||||
for interface in iprofile.interfaces:
|
||||
istr = istr + "%s: %s" % (interface.ifname, interface.networktype)
|
||||
if interface.networktype == 'data':
|
||||
istr = istr + "( %s )" % interface.providernetworks
|
||||
istr = istr + "( %s )" % interface.datanetworks
|
||||
_get_interface_ports_interfaces(iprofile, interface)
|
||||
if interface.ports:
|
||||
istr = istr + " | %s | PORTS = %s" % (interface.iftype, interface.ports)
|
||||
|
@ -13,6 +13,7 @@ from cgtsclient.v1 import ceph_mon_shell
|
||||
from cgtsclient.v1 import certificate_shell
|
||||
from cgtsclient.v1 import cluster_shell
|
||||
from cgtsclient.v1 import controller_fs_shell
|
||||
from cgtsclient.v1 import datanetwork_shell
|
||||
from cgtsclient.v1 import drbdconfig_shell
|
||||
from cgtsclient.v1 import ethernetport_shell
|
||||
from cgtsclient.v1 import firewallrules_shell
|
||||
@ -29,6 +30,7 @@ from cgtsclient.v1 import iinfra_shell
|
||||
from cgtsclient.v1 import iinterface_shell
|
||||
from cgtsclient.v1 import ilvg_shell
|
||||
from cgtsclient.v1 import imemory_shell
|
||||
from cgtsclient.v1 import interface_datanetwork_shell
|
||||
from cgtsclient.v1 import interface_network_shell
|
||||
from cgtsclient.v1 import intp_shell
|
||||
from cgtsclient.v1 import iprofile_shell
|
||||
@ -100,6 +102,8 @@ COMMAND_MODULES = [
|
||||
upgrade_shell,
|
||||
network_shell,
|
||||
interface_network_shell,
|
||||
datanetwork_shell,
|
||||
interface_datanetwork_shell,
|
||||
service_parameter_shell,
|
||||
cluster_shell,
|
||||
lldp_agent_shell,
|
||||
|
@ -1,2 +1,2 @@
|
||||
SRC_DIR="sysinv"
|
||||
TIS_PATCH_VER=295
|
||||
TIS_PATCH_VER=296
|
||||
|
@ -133,7 +133,7 @@
|
||||
<xs:attribute name="mode" type="Ipv6Mode" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="dataNetwork">
|
||||
<xs:complexType name="dataclassNetwork">
|
||||
<xs:sequence>
|
||||
<xs:element name="providerNetworks" type="providerNetworks" minOccurs="1" maxOccurs="unbounded">
|
||||
</xs:element>
|
||||
@ -200,7 +200,7 @@
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="1" maxOccurs="2">
|
||||
<xs:element name="dataNetwork" type="dataNetwork" maxOccurs="1">
|
||||
<xs:element name="dataclassNetwork" type="dataclassNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
<xs:element name="infraNetwork" type="externalNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
@ -237,7 +237,7 @@
|
||||
</xs:element>
|
||||
<xs:element name="oamNetwork" type="externalNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
<xs:element name="dataNetwork" type="dataNetwork" maxOccurs="1">
|
||||
<xs:element name="dataclassNetwork" type="dataclassNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
<xs:element name="infraNetwork" type="externalNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
@ -262,7 +262,7 @@
|
||||
</xs:element>
|
||||
<xs:element name="oamNetwork" type="externalNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
<xs:element name="dataNetwork" type="dataNetwork" maxOccurs="1">
|
||||
<xs:element name="dataclassNetwork" type="dataclassNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
<xs:element name="infraNetwork" type="externalNetwork" maxOccurs="1">
|
||||
</xs:element>
|
||||
|
@ -213,8 +213,8 @@
|
||||
be combined.
|
||||
-->
|
||||
<networks>
|
||||
<dataNetwork>
|
||||
<!--one or more provider network is required for a dataNetwork-->
|
||||
<dataclassNetwork>
|
||||
<!--one or more provider network is required for a dataclassNetwork-->
|
||||
<providerNetworks>
|
||||
<providerNetwork name="group0-data0" />
|
||||
<providerNetwork name="group0-data0b" />
|
||||
@ -232,7 +232,7 @@
|
||||
linkLocal, and
|
||||
static-->
|
||||
<ipv6 mode="link-local"></ipv6>
|
||||
</dataNetwork>
|
||||
</dataclassNetwork>
|
||||
</networks>
|
||||
</ethernetInterface>
|
||||
|
||||
@ -267,13 +267,13 @@
|
||||
<ethernetInterface ifName="data1" mtu="1500" >
|
||||
<port name="eth6" pciAddress="0000:07:00.0" class="Ethernet controller" device="82599ES 10-Gigabit SFI/SFP+ Network Connection" />
|
||||
<networks>
|
||||
<dataNetwork>
|
||||
<dataclassNetwork>
|
||||
<providerNetworks>
|
||||
<providerNetwork name="group0-data1" />
|
||||
</providerNetworks>
|
||||
<ipv4 mode="disabled"></ipv4>
|
||||
<ipv6 mode="disabled"></ipv6>
|
||||
</dataNetwork>
|
||||
</dataclassNetwork>
|
||||
</networks>
|
||||
</ethernetInterface>
|
||||
|
||||
@ -322,8 +322,8 @@
|
||||
|
||||
<vlanInterface ifName="vlan11" interface="ae0" vlanId="11" mtu="1600">
|
||||
<networks>
|
||||
<dataNetwork>
|
||||
<!--This dataNetwork uses ip address pools. See ipv4 and ipv6 tag below
|
||||
<dataclassNetwork>
|
||||
<!--This dataclassNetwork uses ip address pools. See ipv4 and ipv6 tag below
|
||||
-->
|
||||
<providerNetworks>
|
||||
<providerNetwork name="group0-ext0" />
|
||||
@ -336,7 +336,7 @@
|
||||
<ipv6 mode="pool">
|
||||
<pool name="pool-2" />
|
||||
</ipv6>
|
||||
</dataNetwork>
|
||||
</dataclassNetwork>
|
||||
</networks>
|
||||
</vlanInterface>
|
||||
</interfaceProfile>
|
||||
|
@ -29,6 +29,8 @@ from sysinv.api.controllers.v1 import community
|
||||
from sysinv.api.controllers.v1 import controller_fs
|
||||
from sysinv.api.controllers.v1 import cpu
|
||||
from sysinv.api.controllers.v1 import disk
|
||||
from sysinv.api.controllers.v1 import datanetwork
|
||||
from sysinv.api.controllers.v1 import interface_datanetwork
|
||||
from sysinv.api.controllers.v1 import dns
|
||||
from sysinv.api.controllers.v1 import drbdconfig
|
||||
from sysinv.api.controllers.v1 import ethernet_port
|
||||
@ -193,6 +195,12 @@ class V1(base.APIBase):
|
||||
networks = [link.Link]
|
||||
"Links to the network resource"
|
||||
|
||||
datanetworks = [link.Link]
|
||||
"Links to the datanetwork resource"
|
||||
|
||||
interface_datanetworks = [link.Link]
|
||||
"Links to the interface datanetwork resource"
|
||||
|
||||
interface_networks = [link.Link]
|
||||
"Links to the network interface resource"
|
||||
|
||||
@ -751,6 +759,21 @@ class V1(base.APIBase):
|
||||
'apps', '',
|
||||
bookmark=True)]
|
||||
|
||||
v1.datanetworks = [link.Link.make_link('self', pecan.request.host_url,
|
||||
'datanetworks', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url,
|
||||
'datanetworks', '',
|
||||
bookmark=True)]
|
||||
|
||||
v1.interface_datanetworks = [
|
||||
link.Link.make_link('self', pecan.request.host_url,
|
||||
'interface_datanetworks', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url,
|
||||
'interface_datanetworks', '',
|
||||
bookmark=True)]
|
||||
|
||||
return v1
|
||||
|
||||
|
||||
@ -817,6 +840,8 @@ class Controller(rest.RestController):
|
||||
labels = label.LabelController()
|
||||
fernet_repo = fernet_repo.FernetKeyController()
|
||||
apps = kube_app.KubeAppController()
|
||||
datanetworks = datanetwork.DataNetworkController()
|
||||
interface_datanetworks = interface_datanetwork.InterfaceDataNetworkController()
|
||||
|
||||
@wsme_pecan.wsexpose(V1)
|
||||
def get(self):
|
||||
|
365
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/datanetwork.py
Normal file
365
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/datanetwork.py
Normal file
@ -0,0 +1,365 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 UnitedStack Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
|
||||
import jsonpatch
|
||||
import pecan
|
||||
from pecan import rest
|
||||
import six
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from sysinv.api.controllers.v1 import base
|
||||
from sysinv.api.controllers.v1 import collection
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import exception
|
||||
from sysinv.common import utils as cutils
|
||||
from sysinv import objects
|
||||
from sysinv.openstack.common import log
|
||||
from sysinv.openstack.common.gettextutils import _
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
ALLOWED_DATANETWORK_TYPES = [
|
||||
constants.DATANETWORK_TYPE_FLAT,
|
||||
constants.DATANETWORK_TYPE_VLAN,
|
||||
constants.DATANETWORK_TYPE_VXLAN,
|
||||
]
|
||||
|
||||
VXLAN_DYNAMIC_REQUIRED_PARAMS = ['multicast_group', 'port_num', 'ttl']
|
||||
VXLAN_STATIC_REQUIRED_PARAMS = ['port_num', 'ttl']
|
||||
|
||||
|
||||
class DataNetworkPatchType(types.JsonPatchType):
|
||||
@staticmethod
|
||||
def mandatory_attrs():
|
||||
return []
|
||||
|
||||
|
||||
class DataNetwork(base.APIBase):
|
||||
"""API representation of an datanetwork.
|
||||
|
||||
This class enforces type checking and value constraints, and converts
|
||||
between the internal object model and the API representation of a
|
||||
datanetwork.
|
||||
"""
|
||||
|
||||
id = int
|
||||
"Unique ID for this datanetwork"
|
||||
|
||||
uuid = types.uuid
|
||||
"Unique UUID for this datanetwork"
|
||||
|
||||
network_type = wtypes.text
|
||||
"Represent the datanetwork type for the datanetwork"
|
||||
|
||||
name = wtypes.text
|
||||
"Unique name for this datanetwork"
|
||||
|
||||
description = wtypes.text
|
||||
"Represent the user description for the datanetwork"
|
||||
|
||||
mtu = int
|
||||
"Represent the MTU size (bytes) of the datanetwork"
|
||||
|
||||
multicast_group = wtypes.text
|
||||
"Multicast group for this datanetwork. VxLan only"
|
||||
|
||||
port_num = int
|
||||
"Vxlan Port for this datanetwork. VxLan only"
|
||||
|
||||
ttl = int
|
||||
"Time To Live for this datanetwork. VxLan only"
|
||||
|
||||
mode = wtypes.text
|
||||
"Mode for this datanetwork. VxLan only"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = objects.datanetwork.fields.keys()
|
||||
for k in self.fields:
|
||||
if not hasattr(self, k):
|
||||
continue
|
||||
setattr(self, k, kwargs.get(k, wtypes.Unset))
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_datanetwork, expand=True):
|
||||
datanetwork = DataNetwork(**rpc_datanetwork.as_dict())
|
||||
if not expand:
|
||||
datanetwork.unset_fields_except(
|
||||
['id', 'uuid', 'network_type', 'name',
|
||||
'description', 'mtu',
|
||||
'multicast_group', 'port_num', 'ttl', 'mode'])
|
||||
|
||||
return datanetwork
|
||||
|
||||
def _validate_network_type(self):
|
||||
if self.network_type not in ALLOWED_DATANETWORK_TYPES:
|
||||
raise ValueError(_("DataNetwork type %s not supported") %
|
||||
self.network_type)
|
||||
|
||||
def validate_syntax(self):
|
||||
"""
|
||||
Validates the syntax of each field.
|
||||
"""
|
||||
self._validate_network_type()
|
||||
|
||||
|
||||
class DataNetworkCollection(collection.Collection):
|
||||
"""API representation of a collection of datanetworks."""
|
||||
|
||||
datanetworks = [DataNetwork]
|
||||
"A list containing DataNetwork objects"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'datanetworks'
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_datanetworks, limit, url=None,
|
||||
expand=False, **kwargs):
|
||||
collection = DataNetworkCollection()
|
||||
collection.datanetworks = [DataNetwork.convert_with_links(n, expand)
|
||||
for n in rpc_datanetworks]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
|
||||
|
||||
LOCK_NAME = 'DataNetworkController'
|
||||
|
||||
|
||||
class DataNetworkController(rest.RestController):
|
||||
"""REST controller for DataNetworks."""
|
||||
|
||||
def __init__(self, parent=None, **kwargs):
|
||||
self._parent = parent
|
||||
|
||||
def _get_datanetwork_collection(
|
||||
self, marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None, expand=False, resource_url=None):
|
||||
|
||||
limit = utils.validate_limit(limit)
|
||||
sort_dir = utils.validate_sort_dir(sort_dir)
|
||||
marker_obj = None
|
||||
|
||||
if marker:
|
||||
marker_obj = objects.datanetwork.get_by_uuid(
|
||||
pecan.request.context, marker)
|
||||
|
||||
datanetworks = pecan.request.dbapi.datanetworks_get_all(
|
||||
limit=limit, marker=marker_obj,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
return DataNetworkCollection.convert_with_links(
|
||||
datanetworks, limit, url=resource_url, expand=expand,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
def _get_one(self, datanetwork_uuid):
|
||||
rpc_datanetwork = objects.datanetwork.get_by_uuid(
|
||||
pecan.request.context, datanetwork_uuid)
|
||||
return DataNetwork.convert_with_links(rpc_datanetwork)
|
||||
|
||||
@staticmethod
|
||||
def _check_network_type(datanetwork):
|
||||
if 'network_type' not in datanetwork:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_('DataNetwork network_type is required.'))
|
||||
|
||||
network_type = datanetwork['network_type']
|
||||
if network_type not in ALLOWED_DATANETWORK_TYPES:
|
||||
raise ValueError(_("DataNetwork type %s is not supported") %
|
||||
network_type)
|
||||
|
||||
@staticmethod
|
||||
def _check_datanetwork_name(datanetwork):
|
||||
if 'name' not in datanetwork:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_('DataNetwork name is required.'))
|
||||
|
||||
name = datanetwork['name']
|
||||
if name.lower() == constants.DATANETWORK_TYPE_NONE:
|
||||
raise ValueError(_("DataNetwork name '%s' is not allowed") % name)
|
||||
|
||||
@staticmethod
|
||||
def _check_new_datanetwork_mtu_or_set_default(datanetwork):
|
||||
if 'mtu' not in datanetwork:
|
||||
datanetwork['mtu'] = constants.DEFAULT_MTU
|
||||
utils.validate_mtu(datanetwork['mtu'])
|
||||
|
||||
@staticmethod
|
||||
def _check_datanetwork_vxlan(datanetwork):
|
||||
if datanetwork['network_type'] != constants.DATANETWORK_TYPE_VXLAN:
|
||||
return
|
||||
|
||||
mode = datanetwork.get('mode', constants.DATANETWORK_MODE_DYNAMIC)
|
||||
if mode == constants.DATANETWORK_MODE_STATIC:
|
||||
required_vxlan_params = VXLAN_STATIC_REQUIRED_PARAMS
|
||||
else:
|
||||
required_vxlan_params = VXLAN_DYNAMIC_REQUIRED_PARAMS
|
||||
|
||||
missing = set(required_vxlan_params).difference(datanetwork.keys())
|
||||
if missing:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("VxLan parameters '%s' are required for '%s' mode.") %
|
||||
(list(missing), mode))
|
||||
|
||||
multicast_group = datanetwork.get('multicast_group')
|
||||
if mode == constants.DATANETWORK_MODE_STATIC:
|
||||
if multicast_group:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_('VxLan of mode %s does not support multicast_group.') %
|
||||
mode)
|
||||
else:
|
||||
if not cutils.validate_ip_multicast_address(multicast_group):
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("multicast group '%s' is not a valid "
|
||||
"multicast ip address.") %
|
||||
multicast_group)
|
||||
|
||||
def _check_datanetwork(self, datanetwork):
|
||||
self._check_network_type(datanetwork)
|
||||
self._check_datanetwork_name(datanetwork)
|
||||
self._check_new_datanetwork_mtu_or_set_default(datanetwork)
|
||||
self._check_datanetwork_vxlan(datanetwork)
|
||||
|
||||
@staticmethod
|
||||
def _check_update_mtu(rpc_datanetwork):
|
||||
# Check interfaces using this datanetwork
|
||||
ifdns = pecan.request.dbapi.interface_datanetwork_get_by_datanetwork(
|
||||
rpc_datanetwork.uuid)
|
||||
|
||||
for ifdn in ifdns:
|
||||
interface_obj = pecan.request.dbapi.iinterface_get(
|
||||
ifdn.interface_uuid)
|
||||
if interface_obj.imtu < rpc_datanetwork.mtu:
|
||||
msg = _("The datanetwork MTU '%s' must be smaller than "
|
||||
"assigned interface MTU '%s'." %
|
||||
(rpc_datanetwork.mtu, interface_obj.imtu))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
def _create_datanetwork(self, datanetwork):
|
||||
# Perform syntactic validation
|
||||
datanetwork.validate_syntax()
|
||||
|
||||
# Perform semantic validation
|
||||
datanetwork = datanetwork.as_dict()
|
||||
self._check_datanetwork(datanetwork)
|
||||
|
||||
result = pecan.request.dbapi.datanetwork_create(datanetwork)
|
||||
|
||||
return DataNetwork.convert_with_links(result)
|
||||
|
||||
@wsme_pecan.wsexpose(DataNetworkCollection,
|
||||
types.uuid, int, wtypes.text, wtypes.text)
|
||||
def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
|
||||
"""Retrieve a list of DataNetworks."""
|
||||
|
||||
return self._get_datanetwork_collection(marker, limit,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
@wsme_pecan.wsexpose(DataNetwork, wtypes.text)
|
||||
def get_one(self, datanetwork_id):
|
||||
"""Retrieve a single DataNetwork."""
|
||||
|
||||
return self._get_one(datanetwork_id)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(DataNetwork, body=DataNetwork)
|
||||
def post(self, datanetwork):
|
||||
"""Create a new Data Network."""
|
||||
|
||||
return self._create_datanetwork(datanetwork)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme.validate(six.text_type, [DataNetworkPatchType])
|
||||
@wsme_pecan.wsexpose(DataNetwork, six.text_type,
|
||||
body=[DataNetworkPatchType])
|
||||
def patch(self, datanetwork_id, patch):
|
||||
"""Update an existing datanetwork."""
|
||||
|
||||
rpc_datanetwork = \
|
||||
objects.datanetwork.get_by_uuid(
|
||||
pecan.request.context, datanetwork_id)
|
||||
|
||||
utils.validate_patch(patch)
|
||||
patch_obj = jsonpatch.JsonPatch(patch)
|
||||
LOG.info("datanetwork patch_obj=%s" % patch_obj)
|
||||
|
||||
try:
|
||||
datanetwork = DataNetwork(**jsonpatch.apply_patch(
|
||||
rpc_datanetwork.as_dict(), patch_obj))
|
||||
|
||||
except utils.JSONPATCH_EXCEPTIONS as e:
|
||||
raise exception.PatchError(patch=patch, reason=e)
|
||||
|
||||
LOG.info("rpc_datanetwork=%s datanetwork=%s" %
|
||||
(rpc_datanetwork.as_dict(), datanetwork))
|
||||
|
||||
fields = objects.datanetwork.fields
|
||||
|
||||
for field in fields:
|
||||
if (field in rpc_datanetwork and
|
||||
rpc_datanetwork[field] != getattr(datanetwork, field)):
|
||||
rpc_datanetwork[field] = getattr(datanetwork, field)
|
||||
|
||||
delta = rpc_datanetwork.obj_what_changed()
|
||||
if not delta:
|
||||
return DataNetwork.convert_with_links(rpc_datanetwork)
|
||||
|
||||
delta_list = list(delta)
|
||||
|
||||
allowed_updates = ['mtu', 'description']
|
||||
if not set(delta_list).issubset(allowed_updates):
|
||||
extra = set(allowed_updates).difference(delta_list)
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("DataNetwork '%s' attributes '%s' may not be modified ") %
|
||||
(rpc_datanetwork.uuid, extra))
|
||||
|
||||
values = {}
|
||||
if 'mtu' in delta_list:
|
||||
self._check_update_mtu(rpc_datanetwork)
|
||||
values.update({'mtu': rpc_datanetwork.mtu})
|
||||
|
||||
if 'description' in delta_list:
|
||||
values.update({'description': rpc_datanetwork.description})
|
||||
|
||||
rpc_datanetwork.save()
|
||||
|
||||
return DataNetwork.convert_with_links(rpc_datanetwork)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
|
||||
def delete(self, datanetwork_uuid):
|
||||
"""Delete a Data Network."""
|
||||
|
||||
# Only allow delete if there are no associated interfaces
|
||||
ifdns = pecan.request.dbapi.interface_datanetwork_get_by_datanetwork(
|
||||
datanetwork_uuid)
|
||||
if ifdns:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("DataNetwork '%s' is still assigned to interfaces. "
|
||||
"Check interface-datanetwork.") % datanetwork_uuid)
|
||||
|
||||
pecan.request.dbapi.datanetwork_destroy(datanetwork_uuid)
|
@ -80,6 +80,7 @@ from sysinv.api.controllers.v1 import state
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.api.controllers.v1 import interface_network
|
||||
from sysinv.api.controllers.v1 import interface_datanetwork
|
||||
from sysinv.api.controllers.v1 import vim_api
|
||||
from sysinv.api.controllers.v1 import patch_api
|
||||
|
||||
@ -1081,6 +1082,10 @@ class HostController(rest.RestController):
|
||||
parent="ihosts")
|
||||
"Expose interface_networks as a sub-element of ihosts"
|
||||
|
||||
interface_datanetworks = interface_datanetwork.InterfaceDataNetworkController(
|
||||
parent="ihosts")
|
||||
"Expose interface_datanetworks as a sub-element of ihosts"
|
||||
|
||||
_custom_actions = {
|
||||
'detail': ['GET'],
|
||||
'bulk_add': ['POST'],
|
||||
@ -3179,62 +3184,61 @@ class HostController(rest.RestController):
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _semantic_check_interface_providernets(ihost, interface):
|
||||
def _semantic_check_interface_datanets(interface):
|
||||
"""
|
||||
Perform provider network semantics on a specific interface to ensure
|
||||
that any provider networks that have special requirements on the
|
||||
interface has been statisfied.
|
||||
Perform data network semantics on a specific interface to ensure
|
||||
that any data networks that have special requirements on the
|
||||
interface have been satisfied.
|
||||
"""
|
||||
networktype = []
|
||||
if interface.networktype:
|
||||
networktype = [network.strip() for network in interface.networktype.split(",")]
|
||||
if constants.NETWORK_TYPE_DATA not in networktype:
|
||||
|
||||
if interface.ifclass != constants.NETWORK_TYPE_DATA:
|
||||
return
|
||||
# Fetch the list of provider networks from neutron
|
||||
providernets = pecan.request.rpcapi.iinterface_get_providernets(
|
||||
pecan.request.context)
|
||||
# Cleanup the list of provider networks stored on the interface
|
||||
values = interface.providernetworks.strip()
|
||||
values = re.sub(',,+', ',', values)
|
||||
providernet_names = values.split(',')
|
||||
# Check for VXLAN provider networks that require IP addresses
|
||||
for providernet_name in providernet_names:
|
||||
providernet = providernets.get(providernet_name)
|
||||
if not providernet:
|
||||
msg = (_("Interface %(ifname)s is associated to provider "
|
||||
"network %(name)s which does not exist") %
|
||||
{'ifname': interface.ifname, 'name': providernet_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if providernet['type'] != "vxlan":
|
||||
|
||||
ifdatanets = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_by_interface(
|
||||
interface.uuid)
|
||||
|
||||
# Check for VXLAN data networks that require IP addresses
|
||||
for ifdn in ifdatanets:
|
||||
if ifdn.datanetwork_network_type != \
|
||||
constants.DATANETWORK_TYPE_VXLAN:
|
||||
continue
|
||||
for r in providernet['ranges']:
|
||||
if r['vxlan']['group'] is None:
|
||||
continue # static range; fallback to generic check
|
||||
# Check for address family specific ranges
|
||||
address = netaddr.IPAddress(r['vxlan']['group'])
|
||||
if ((address.version == constants.IPV4_FAMILY) and
|
||||
(interface.ipv4_mode == constants.IPV4_DISABLED)):
|
||||
msg = (_("Interface %(ifname)s is associated to VXLAN "
|
||||
"provider network %(name)s which requires an "
|
||||
"IPv4 address") %
|
||||
{'ifname': interface.ifname,
|
||||
'name': providernet_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if ((address.version == constants.IPV6_FAMILY) and
|
||||
(interface.ipv6_mode == constants.IPV6_DISABLED)):
|
||||
msg = (_("Interface %(ifname)s is associated to VXLAN "
|
||||
"provider network %(name)s which requires an "
|
||||
"IPv6 address") %
|
||||
{'ifname': interface.ifname,
|
||||
'name': providernet_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
dn = pecan.request.dbapi.datanetwork_get(ifdn.datanetwork_uuid)
|
||||
if not dn.multicast_group:
|
||||
# static range; fallback to generic check
|
||||
continue
|
||||
|
||||
# Check for address family specific ranges
|
||||
address = netaddr.IPAddress(dn.multicast_group)
|
||||
if ((address.version == constants.IPV4_FAMILY) and
|
||||
(interface.ipv4_mode == constants.IPV4_DISABLED or not
|
||||
interface.ipv4_mode)):
|
||||
msg = (_("Interface %(ifname)s is associated to VXLAN "
|
||||
"data network %(name)s which requires an "
|
||||
"IPv4 address") %
|
||||
{'ifname': interface.ifname,
|
||||
'name': ifdn.datanetwork_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if ((address.version == constants.IPV6_FAMILY) and
|
||||
(interface.ipv6_mode == constants.IPV6_DISABLED or not
|
||||
interface.ipv6_mode)):
|
||||
msg = (_("Interface %(ifname)s is associated to VXLAN "
|
||||
"data network %(name)s which requires an "
|
||||
"IPv6 address") %
|
||||
{'ifname': interface.ifname,
|
||||
'name': ifdn.datanetwork_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# Check for at least 1 address if no ranges exist yet
|
||||
if ((interface.ipv4_mode == constants.IPV4_DISABLED) and
|
||||
(interface.ipv6_mode == constants.IPV6_DISABLED)):
|
||||
(interface.ipv6_mode == constants.IPV6_DISABLED) or
|
||||
(not interface.ipv4_mode and not interface.ipv6_mode)):
|
||||
msg = (_("Interface %(ifname)s is associated to VXLAN "
|
||||
"provider network %(name)s which requires an IP "
|
||||
"data network %(name)s which requires an IP "
|
||||
"address") %
|
||||
{'ifname': interface.ifname, 'name': providernet_name})
|
||||
{'ifname': interface.ifname,
|
||||
'name': ifdn.datanetwork_name})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
@ -3263,11 +3267,11 @@ class HostController(rest.RestController):
|
||||
if ((vswitch_type == constants.VSWITCH_TYPE_OVS_DPDK) and
|
||||
(iif.ifclass == constants.INTERFACE_CLASS_DATA)):
|
||||
self._semantic_check_non_accelerated_interface_support(iif)
|
||||
self._semantic_check_interface_providernets(ihost, iif)
|
||||
self._semantic_check_interface_datanets(iif)
|
||||
self._semantic_check_interface_addresses(ihost, iif)
|
||||
if not iif.networktype:
|
||||
if not iif.ifclass:
|
||||
continue
|
||||
if any(n in [constants.NETWORK_TYPE_DATA] for n in iif.networktype.split(",")):
|
||||
if iif.ifclass == constants.NETWORK_TYPE_DATA:
|
||||
data_interface_configured = True
|
||||
|
||||
if not data_interface_configured:
|
||||
|
@ -16,7 +16,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copyright (c) 2013-2016 Wind River Systems, Inc.
|
||||
# Copyright (c) 2013-2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
|
||||
@ -42,6 +44,7 @@ from sysinv.api.controllers.v1 import route
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.api.controllers.v1 import interface_network
|
||||
from sysinv.api.controllers.v1 import interface_datanetwork
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import exception
|
||||
from sysinv.common import utils as cutils
|
||||
@ -101,8 +104,8 @@ DATA_NETWORK_TYPES = [constants.NETWORK_TYPE_DATA]
|
||||
MAX_IFNAME_LEN = 10
|
||||
MAX_VLAN_ID_LEN = 5
|
||||
|
||||
# Maximum number of characters in provider network list
|
||||
MAX_PROVIDERNETWORK_LEN = 255
|
||||
# Maximum number of characters in data network list
|
||||
MAX_DATANETWORK_LEN = 255
|
||||
|
||||
DEFAULT_MTU = 1500
|
||||
|
||||
@ -152,12 +155,8 @@ class Interface(base.APIBase):
|
||||
txhashpolicy = wtypes.text
|
||||
"Represent the txhashpolicy of the interface"
|
||||
|
||||
providernetworks = wtypes.text
|
||||
"Represent the providernetworks of the interface"
|
||||
|
||||
providernetworksdict = {wtypes.text: utils.ValidTypes(wtypes.text,
|
||||
six.integer_types)}
|
||||
"Represent the providernetworksdict of the interface"
|
||||
datanetworks = [wtypes.text]
|
||||
"Represent the datanetworks of the interface"
|
||||
|
||||
ifcapabilities = {wtypes.text: utils.ValidTypes(wtypes.text,
|
||||
six.integer_types)}
|
||||
@ -219,15 +218,19 @@ class Interface(base.APIBase):
|
||||
# fields = ['uuid', 'address'] if not expand else None
|
||||
# interface = iinterface.from_rpc_object(rpc_interface, fields)
|
||||
|
||||
interface = Interface(**rpc_interface.as_dict())
|
||||
kwargs = rpc_interface.as_dict()
|
||||
datanetworks_list = kwargs.pop('datanetworks')
|
||||
|
||||
interface = Interface(**kwargs)
|
||||
if not expand:
|
||||
interface.unset_fields_except(['uuid', 'ifname', 'iftype',
|
||||
'imac', 'imtu', 'ifclass', 'networktype', 'networks',
|
||||
'ihost_uuid', 'forihostid',
|
||||
'aemode', 'schedpolicy', 'txhashpolicy',
|
||||
'providernetworks', 'ihost_uuid', 'forihostid',
|
||||
'vlan_id', 'uses', 'usesmodify', 'used_by',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs'])
|
||||
'sriov_numvfs',
|
||||
'datanetworks'])
|
||||
|
||||
# never expose the ihost_id attribute
|
||||
interface.ihost_id = wtypes.Unset
|
||||
@ -282,6 +285,13 @@ class Interface(base.APIBase):
|
||||
if interface.ipv6_mode != constants.IPV6_POOL:
|
||||
interface.ipv6_pool = wtypes.Unset
|
||||
|
||||
datanetworks_names_list = []
|
||||
for dn in datanetworks_list:
|
||||
dn = pecan.request.dbapi.datanetwork_get(dn)
|
||||
datanetworks_names_list.append(dn.name)
|
||||
|
||||
interface.datanetworks = datanetworks_names_list
|
||||
|
||||
return interface
|
||||
|
||||
|
||||
@ -323,6 +333,11 @@ class InterfaceController(rest.RestController):
|
||||
parent="iinterfaces")
|
||||
"Expose interface_networks as a sub-element of interface"
|
||||
|
||||
interface_datanetworks = \
|
||||
interface_datanetwork.InterfaceDataNetworkController(
|
||||
parent="iinterfaces")
|
||||
"Expose interface_datanetworks as a sub-element of interface"
|
||||
|
||||
_custom_actions = {
|
||||
'detail': ['GET'],
|
||||
}
|
||||
@ -442,10 +457,13 @@ class InterfaceController(rest.RestController):
|
||||
networks = []
|
||||
networks_to_add = []
|
||||
interface_networks_to_remove = []
|
||||
datanetworks = []
|
||||
datanetworks_to_add = []
|
||||
interface_datanetworks_to_remove = []
|
||||
patches_to_remove = []
|
||||
for p in patch:
|
||||
if '/ifclass' == p['path']:
|
||||
if p['value'] == 'none':
|
||||
if p['value'] == constants.INTERFACE_CLASS_NONE:
|
||||
p['value'] = None
|
||||
elif '/usesmodify' == p['path']:
|
||||
uses = p['value'].split(',')
|
||||
@ -462,6 +480,15 @@ class InterfaceController(rest.RestController):
|
||||
elif '/interface_networks_to_remove' == p['path']:
|
||||
interface_networks_to_remove = p['value'].split(',')
|
||||
patches_to_remove.append(p)
|
||||
elif '/datanetworks' == p['path']:
|
||||
datanetworks = p['value'].split(',')
|
||||
patches_to_remove.append(p)
|
||||
elif '/datanetworks_to_add' == p['path']:
|
||||
datanetworks_to_add = p['value'].split(',')
|
||||
patches_to_remove.append(p)
|
||||
elif '/interface_datanetworks_to_remove' == p['path']:
|
||||
interface_datanetworks_to_remove = p['value'].split(',')
|
||||
patches_to_remove.append(p)
|
||||
|
||||
if uses:
|
||||
patch.append(dict(path='/uses', value=uses, op='replace'))
|
||||
@ -524,6 +551,7 @@ class InterfaceController(rest.RestController):
|
||||
# Process updates
|
||||
vlan_id = None
|
||||
delete_addressing = False
|
||||
delete_ifdn = False
|
||||
|
||||
for p in patch:
|
||||
if '/vlan_id' in p['path']:
|
||||
@ -569,6 +597,7 @@ class InterfaceController(rest.RestController):
|
||||
interface['ipv4_mode'] = None
|
||||
interface['ipv6_mode'] = None
|
||||
delete_addressing = True
|
||||
delete_ifdn = True
|
||||
else:
|
||||
# Otherwise make sure that appropriate defaults are set.
|
||||
interface = _set_defaults(interface)
|
||||
@ -581,7 +610,8 @@ class InterfaceController(rest.RestController):
|
||||
|
||||
interface = _check("modify", interface,
|
||||
ports=ports, ifaces=uses,
|
||||
existing_interface=rpc_interface.as_dict())
|
||||
existing_interface=rpc_interface.as_dict(),
|
||||
datanetworks=datanetworks)
|
||||
|
||||
if uses:
|
||||
# Update MAC address if uses list changed
|
||||
@ -623,6 +653,40 @@ class InterfaceController(rest.RestController):
|
||||
if _is_ipv6_address_mode_updated(interface, rpc_interface):
|
||||
_update_ipv6_address_mode(interface)
|
||||
|
||||
# Update interface-datanetworks
|
||||
if datanetworks_to_add:
|
||||
for datanetwork_id in datanetworks_to_add:
|
||||
values = {'interface_id': interface['id'],
|
||||
'datanetwork_id': datanetwork_id}
|
||||
try:
|
||||
pecan.request.dbapi.interface_datanetwork_create(values)
|
||||
except exception.InterfaceDataNetworkAlreadyExists:
|
||||
pass
|
||||
elif datanetworks:
|
||||
_update_interface_datanetworks(
|
||||
ihost['uuid'], interface, datanetworks, delete_ifdn)
|
||||
|
||||
try:
|
||||
# Remove old datanetworks from the interface
|
||||
if interface_datanetworks_to_remove:
|
||||
for ifdatanet_id in interface_datanetworks_to_remove:
|
||||
pecan.request.dbapi.interface_datanetwork_destroy(
|
||||
ifdatanet_id)
|
||||
elif (orig_ifclass == constants.INTERFACE_CLASS_DATA and
|
||||
(not ifclass or
|
||||
ifclass != constants.INTERFACE_CLASS_DATA)):
|
||||
# data networks apply only for DATA
|
||||
ifdatanets = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_by_interface(
|
||||
rpc_interface['uuid'])
|
||||
for ifdatanet in ifdatanets:
|
||||
pecan.request.dbapi.interface_datanetwork_destroy(ifdatanet.uuid)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
msg = _("Failed to remove interface datanetwork association for "
|
||||
"interface %s" % (interface['ifname']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# Commit operation with neutron
|
||||
if (interface['ifclass'] and
|
||||
interface['ifclass'] in NEUTRON_INTERFACE_CLASS):
|
||||
@ -755,6 +819,65 @@ class InterfaceController(rest.RestController):
|
||||
# UTILS
|
||||
##############
|
||||
|
||||
def _update_interface_datanetworks(host_uuid, interface,
|
||||
datanetworks=None,
|
||||
delete_ifdn=False):
|
||||
|
||||
pns = []
|
||||
|
||||
if datanetworks:
|
||||
# remove 'none' from datanetworks
|
||||
datanetworks = \
|
||||
[x for x in datanetworks if x != constants.DATANETWORK_TYPE_NONE]
|
||||
for datanetwork_id in datanetworks:
|
||||
dn = pecan.request.dbapi.datanetwork_get(datanetwork_id)
|
||||
pns.append(dn.name)
|
||||
elif 'datanetworks' in interface:
|
||||
pns = interface['datanetworks']
|
||||
|
||||
LOG.info("_update_interface_datanetworks interface=%s datanetworks=%s pns=%s" %
|
||||
(interface, datanetworks, pns))
|
||||
|
||||
# remove from the interface datanetworks not in list
|
||||
ifdns = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_by_host(
|
||||
host_uuid)
|
||||
for ifdn in ifdns:
|
||||
# if this is not this interface, continue
|
||||
if_uuid = interface.get('uuid', None)
|
||||
if if_uuid:
|
||||
if if_uuid != ifdn.interface_uuid:
|
||||
continue
|
||||
elif ifdn.ifname != interface.get('ifname'):
|
||||
continue
|
||||
|
||||
LOG.debug("_update_interface_datanetworks host_uuid %s "
|
||||
"interface=%s ifdn=%s" %
|
||||
(host_uuid, interface, ifdn.as_dict()))
|
||||
if (pns and ifdn.datanetwork_name not in pns) or delete_ifdn:
|
||||
LOG.info("interface_datanetwork_destroy %s %s delete_ifdn=%s" %
|
||||
(ifdn.uuid, ifdn.ifname, delete_ifdn))
|
||||
pecan.request.dbapi.interface_datanetwork_destroy(
|
||||
ifdn.uuid)
|
||||
|
||||
for pn in pns:
|
||||
dn = pecan.request.dbapi.datanetwork_get(pn)
|
||||
values = {'interface_id': interface['id'],
|
||||
'datanetwork_id': dn.id}
|
||||
try:
|
||||
ifdn = pecan.request.dbapi.interface_datanetwork_create(values)
|
||||
except exception.InterfaceDataNetworkAlreadyExists:
|
||||
pass
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
msg = _("Failed to create interface datanetwork "
|
||||
"assignment for interface %s" %
|
||||
(interface['ifname']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
return ifdns
|
||||
|
||||
|
||||
def _dynamic_address_allocation():
|
||||
mgmt_network = pecan.request.dbapi.network_get_by_type(
|
||||
constants.NETWORK_TYPE_MGMT)
|
||||
@ -1258,11 +1381,140 @@ def _check_networks(interface):
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
|
||||
def _check_interface_data(op, interface, ihost, existing_interface):
|
||||
def _check_datanetworks(ihost,
|
||||
interface,
|
||||
interface_list,
|
||||
existing_interface,
|
||||
networktypelist,
|
||||
datanetworks=None):
|
||||
|
||||
if 'id' in interface:
|
||||
this_interface_id = interface['id']
|
||||
else:
|
||||
this_interface_id = 0
|
||||
|
||||
ifclass = interface['ifclass']
|
||||
iftype = interface['iftype']
|
||||
|
||||
if not datanetworks:
|
||||
datanetworks = interface.get('datanetworks') or []
|
||||
|
||||
# remove 'none' from datanetworks
|
||||
datanetworks = \
|
||||
[x for x in datanetworks if x != constants.DATANETWORK_TYPE_NONE]
|
||||
|
||||
LOG.debug("_check_datanetworks datanetworks interface=%s datanetworks=%s" %
|
||||
(interface, datanetworks))
|
||||
|
||||
# Get all provisioned datanetworks
|
||||
all_datanetworks = {}
|
||||
db_datanetworks = pecan.request.dbapi.datanetworks_get_all()
|
||||
for db in db_datanetworks:
|
||||
all_datanetworks[db.name] = {
|
||||
'network_type': db.network_type}
|
||||
|
||||
# Ensure a valid datanetwork is specified
|
||||
# Ensure at least one datanetwork is selected for 'data',
|
||||
# and none for 'oam', 'mgmt' and 'infra'
|
||||
# Ensure uniqueness of the datanetworks
|
||||
|
||||
datanetworks_list = []
|
||||
for datanetwork in datanetworks:
|
||||
if datanetwork == constants.DATANETWORK_TYPE_NONE:
|
||||
continue
|
||||
dn = pecan.request.dbapi.datanetwork_get(datanetwork)
|
||||
datanetworks_list.append(dn.name)
|
||||
|
||||
if interface['ifclass'] in NEUTRON_INTERFACE_CLASS:
|
||||
if not datanetworks:
|
||||
msg = _("At least one data network must be selected.")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if len(datanetworks) > MAX_DATANETWORK_LEN:
|
||||
msg = _("Data network list must not exceed %d characters." %
|
||||
MAX_DATANETWORK_LEN)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
for pn in [n.strip() for n in datanetworks_list]:
|
||||
if pn not in all_datanetworks.keys():
|
||||
msg = _("Data network '%s' does not exist." % pn)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if datanetworks_list.count(pn) > 1:
|
||||
msg = (_("Specifying duplicate data network '%(name)s' "
|
||||
"is not permitted") % {'name': pn})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
datanet = all_datanetworks[pn]
|
||||
if iftype == constants.INTERFACE_TYPE_VLAN:
|
||||
if datanet['network_type'] == \
|
||||
constants.DATANETWORK_TYPE_VLAN:
|
||||
msg = _("VLAN based data network '%s' cannot be "
|
||||
"assigned to a VLAN interface" % pn)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# If pxeboot, Mgmt, Infra network types are consolidated
|
||||
# with a data network type on the same interface,
|
||||
# in which case, they would be the primary network
|
||||
# type. Ensure that the only data type that
|
||||
# can be assigned is VLAN.
|
||||
if (datanet['network_type'] != constants.DATANETWORK_TYPE_VLAN and
|
||||
ifclass not in NEUTRON_NETWORK_TYPES):
|
||||
msg = _("Data network '%s' of type '%s' cannot be assigned "
|
||||
"to an interface with interface class '%s'"
|
||||
% (pn, datanet['network_type'], ifclass))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# This ensures that a specific data network type can
|
||||
# only be assigned to 1 data interface. Such as the case of
|
||||
# when only 1 vxlan data is required when SDN is enabled
|
||||
if constants.NETWORK_TYPE_DATA in networktypelist and interface_list:
|
||||
for pn in [n.strip() for n in datanetworks_list]:
|
||||
for i in interface_list:
|
||||
if i.id == this_interface_id:
|
||||
continue
|
||||
if not i.ifclass or not i.datanetworks:
|
||||
continue
|
||||
if constants.NETWORK_TYPE_DATA != i.ifclass:
|
||||
continue
|
||||
|
||||
other_datanetworks = []
|
||||
for datanetwork in i.datanetworks:
|
||||
dn = pecan.request.dbapi.datanetwork_get(datanetwork)
|
||||
other_datanetworks.append(dn.name)
|
||||
if pn in other_datanetworks:
|
||||
msg = _("Data interface %(ifname)s is already "
|
||||
"attached to this Data Network: "
|
||||
"%(datanetwork)s." %
|
||||
{'ifname': i.ifname, 'datanetwork': pn})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
elif (not _neutron_providernet_extension_supported() and
|
||||
any(nt in PCI_NETWORK_TYPES for nt in networktypelist)):
|
||||
# When the neutron implementation is not our own and it does not
|
||||
# support our data network extension we still want to do minimal
|
||||
# validation of the data network list but we cannot do more
|
||||
# complex validation because we do not have any additional information
|
||||
# about the data networks.
|
||||
if not datanetworks:
|
||||
msg = _("At least one data network must be selected.")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
elif (interface['ifclass'] and
|
||||
interface['ifclass'] not in NEUTRON_INTERFACE_CLASS and
|
||||
not existing_interface):
|
||||
if datanetworks:
|
||||
msg = _("Data network(s) not supported "
|
||||
"for non-data interfaces. (%s) (%s)" %
|
||||
(interface['ifclass'], str(existing_interface)))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
elif (_neutron_providernet_extension_supported() or
|
||||
interface['ifclass'] not in NEUTRON_INTERFACE_CLASS):
|
||||
interface['datanetworks'] = None
|
||||
|
||||
|
||||
def _check_interface_data(op, interface, ihost, existing_interface,
|
||||
datanetworks=None):
|
||||
# Get data
|
||||
ihost_id = interface['forihostid']
|
||||
ihost_uuid = interface['ihost_uuid']
|
||||
providernetworks = interface['providernetworks']
|
||||
ifclass = interface['ifclass']
|
||||
networktypelist = []
|
||||
if ifclass == constants.INTERFACE_CLASS_PLATFORM:
|
||||
@ -1274,9 +1526,6 @@ def _check_interface_data(op, interface, ihost, existing_interface):
|
||||
else:
|
||||
networktypelist.append(constants.INTERFACE_CLASS_NONE)
|
||||
|
||||
# Get providernet dict
|
||||
all_providernetworks = _neutron_providernet_list()
|
||||
|
||||
# Check interface name for validity
|
||||
_check_interface_name(op, interface, ihost, existing_interface)
|
||||
|
||||
@ -1448,96 +1697,13 @@ def _check_interface_data(op, interface, ihost, existing_interface):
|
||||
host_port,
|
||||
networktypelist)
|
||||
|
||||
# Ensure a valid providernetwork is specified
|
||||
# Ensure at least one providernetwork is selected for 'data',
|
||||
# or interface (when SDN L3 services are enabled)
|
||||
# and none for 'oam', 'mgmt' and 'infra'
|
||||
# Ensure uniqueness wrt the providernetworks
|
||||
if (_neutron_providernet_extension_supported() and
|
||||
interface['ifclass'] in NEUTRON_INTERFACE_CLASS):
|
||||
if not providernetworks:
|
||||
msg = _("At least one provider network must be selected.")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if len(providernetworks) > MAX_PROVIDERNETWORK_LEN:
|
||||
msg = _("Provider network list must not exceed %d characters." %
|
||||
MAX_PROVIDERNETWORK_LEN)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
providernetworks_list = providernetworks.split(',')
|
||||
for pn in [n.strip() for n in providernetworks_list]:
|
||||
if pn not in all_providernetworks.keys():
|
||||
msg = _("Provider network '%s' does not exist." % pn)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if providernetworks_list.count(pn) > 1:
|
||||
msg = (_("Specifying duplicate provider network '%(name)s' "
|
||||
"is not permitted") % {'name': pn})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
providernet = all_providernetworks[pn]
|
||||
if iftype == constants.INTERFACE_TYPE_VLAN:
|
||||
if providernet['type'] == 'vlan':
|
||||
msg = _("VLAN based provider network '%s' cannot be "
|
||||
"assigned to a VLAN interface" % pn)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# If pxeboot, Mgmt, Infra network types are consolidated
|
||||
# with a data network type on the same interface,
|
||||
# in which case, they would be the primary network
|
||||
# type. Ensure that the only provider type that
|
||||
# can be assigned is VLAN.
|
||||
if (providernet['type'] != constants.NEUTRON_PROVIDERNET_VLAN and
|
||||
ifclass not in NEUTRON_NETWORK_TYPES):
|
||||
msg = _("Provider network '%s' of type '%s' cannot be assigned "
|
||||
"to an interface with interface class '%s'"
|
||||
% (pn, providernet['type'], ifclass))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# This ensures that a specific provider network type can
|
||||
# only be assigned to 1 data interface. Such as the case of
|
||||
# when only 1 vxlan provider is required when SDN is enabled
|
||||
if constants.NETWORK_TYPE_DATA in networktypelist and interface_list:
|
||||
for pn in [n.strip() for n in providernetworks.split(',')]:
|
||||
for i in interface_list:
|
||||
if i.id == this_interface_id:
|
||||
continue
|
||||
if not i.ifclass or not i.providernetworks:
|
||||
continue
|
||||
if constants.NETWORK_TYPE_DATA != i.ifclass:
|
||||
continue
|
||||
other_providernetworks = i.providernetworks.split(',')
|
||||
if pn in other_providernetworks:
|
||||
msg = _("Data interface %(ifname)s is already "
|
||||
"attached to this Provider Network: "
|
||||
"%(network)s." %
|
||||
{'ifname': i.ifname, 'network': pn})
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# Send the interface and provider network details to neutron for
|
||||
# additional validation.
|
||||
_neutron_bind_interface(ihost, interface, test=True)
|
||||
# Send the shared data interface(s) and provider networks details to
|
||||
# neutron for additional validation, if required
|
||||
_update_shared_interface_neutron_bindings(ihost, interface, test=True)
|
||||
|
||||
elif (not _neutron_providernet_extension_supported() and
|
||||
any(nt in PCI_NETWORK_TYPES for nt in networktypelist)):
|
||||
# When the neutron implementation is not our own and it does not
|
||||
# support our provider network extension we still want to do minimal
|
||||
# validation of the provider network list but we cannot do more
|
||||
# complex validation because we do not have any additional information
|
||||
# about the provider networks.
|
||||
if not providernetworks:
|
||||
msg = _("At least one provider network must be selected.")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
elif (interface['ifclass'] and
|
||||
interface['ifclass'] not in NEUTRON_INTERFACE_CLASS and
|
||||
not existing_interface):
|
||||
if providernetworks is not None:
|
||||
msg = _("Provider network(s) not supported "
|
||||
"for non-data interfaces. (%s) (%s)" % (interface['ifclass'], str(existing_interface)))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
elif (_neutron_providernet_extension_supported() or
|
||||
interface['ifclass'] not in NEUTRON_INTERFACE_CLASS):
|
||||
interface['providernetworks'] = None
|
||||
# Check datanetworks (formerly known as providernetworks)
|
||||
_check_datanetworks(ihost,
|
||||
interface,
|
||||
interface_list,
|
||||
existing_interface,
|
||||
networktypelist,
|
||||
datanetworks)
|
||||
|
||||
# check MTU
|
||||
if interface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
||||
@ -1894,31 +2060,6 @@ def _update_host_cluster_address(host, interface):
|
||||
address_name)
|
||||
|
||||
|
||||
def _clean_providernetworks(providernetworks):
|
||||
pn = [','.join(p['name']) for p in providernetworks]
|
||||
return pn
|
||||
|
||||
|
||||
"""
|
||||
Params:
|
||||
pn_all: all providernets stored in neutron
|
||||
pn_names: providernets specified for this interface
|
||||
|
||||
Return:
|
||||
pn_dict: a dictionary of providernets specified
|
||||
for this interface: item format {name:body}
|
||||
"""
|
||||
|
||||
|
||||
def _get_providernetworksdict(pn_all, pn_names):
|
||||
pn_dict = {}
|
||||
if pn_names:
|
||||
for name, body in pn_all.items():
|
||||
if name in pn_names.split(','):
|
||||
pn_dict.update({name: body})
|
||||
return pn_dict
|
||||
|
||||
|
||||
def _get_interface_vlans(ihost_uuid, interface):
|
||||
"""
|
||||
Retrieve the VLAN id values (if any) that are dependent on this
|
||||
@ -2053,6 +2194,38 @@ def _update_shared_interface_neutron_bindings(ihost, interface, test=False):
|
||||
_neutron_bind_interface(ihost, shared_interface, test)
|
||||
|
||||
|
||||
def _datanetworks_get_by_interface(interface_uuid):
|
||||
ifdatanets = pecan.request.dbapi.interface_datanetwork_get_by_interface(
|
||||
interface_uuid)
|
||||
|
||||
LOG.debug("_datanetworks_get_by_interface %s ifdnets=%s" %
|
||||
(interface_uuid, ifdatanets))
|
||||
|
||||
datanetworks = []
|
||||
for ifdatanet in ifdatanets:
|
||||
datanetworks.append(ifdatanet.datanetwork_uuid)
|
||||
|
||||
datanetworks_list = []
|
||||
datanetworks_names_list = []
|
||||
for datanetwork in datanetworks:
|
||||
dn = pecan.request.dbapi.datanetwork_get(datanetwork)
|
||||
datanetwork_dict = \
|
||||
{'name': dn.name,
|
||||
'uuid': dn.uuid,
|
||||
'network_type': dn.network_type,
|
||||
'mtu': dn.mtu}
|
||||
datanetworks_names_list.append(dn.name)
|
||||
if dn.network_type == constants.DATANETWORK_TYPE_VXLAN:
|
||||
datanetwork_dict.update(
|
||||
{'port_num': dn.port_num,
|
||||
'multicast_group': dn.multicast_group,
|
||||
'ttl': dn.ttl,
|
||||
'mode': dn.mode})
|
||||
datanetworks_list.append(datanetwork_dict)
|
||||
|
||||
return datanetworks_names_list, datanetworks_list
|
||||
|
||||
|
||||
def _neutron_bind_interface(ihost, interface, test=False):
|
||||
"""
|
||||
Send a request to neutron to bind the interface to the specified
|
||||
@ -2082,7 +2255,13 @@ def _neutron_bind_interface(ihost, interface, test=False):
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
interface_uuid = interface['uuid']
|
||||
providernetworks = interface.get('providernetworks', '')
|
||||
datanetworks_names_list, _dl = \
|
||||
_datanetworks_get_by_interface(interface_uuid)
|
||||
|
||||
providernetworks = ",".join([str(x) for x in datanetworks_names_list])
|
||||
LOG.info("_neutron_bind_interface uuid=%s datanetworks_names=%s" %
|
||||
(interface_uuid, providernetworks))
|
||||
|
||||
vlans = _get_interface_vlans(ihost_uuid, interface)
|
||||
try:
|
||||
# Send the request to neutron
|
||||
@ -2210,6 +2389,8 @@ def _create(interface, from_profile=False):
|
||||
else:
|
||||
forihostid = ihostId
|
||||
|
||||
datanetworks = interface.get('datanetworks')
|
||||
|
||||
LOG.debug("iinterface post interfaces ihostid: %s" % forihostid)
|
||||
|
||||
interface.update({'forihostid': ihost['id'],
|
||||
@ -2267,6 +2448,9 @@ def _create(interface, from_profile=False):
|
||||
forihostid,
|
||||
interface)
|
||||
|
||||
# Create interface-datanetworks
|
||||
_update_interface_datanetworks(ihost['uuid'], new_interface, datanetworks)
|
||||
|
||||
# Create network-interface
|
||||
try:
|
||||
if (new_interface['ifclass'] and
|
||||
@ -2382,7 +2566,7 @@ def _create(interface, from_profile=False):
|
||||
|
||||
|
||||
def _check(op, interface, ports=None, ifaces=None, from_profile=False,
|
||||
existing_interface=None):
|
||||
existing_interface=None, datanetworks=None):
|
||||
# Semantic checks
|
||||
ihost = pecan.request.dbapi.ihost_get(interface['ihost_uuid']).as_dict()
|
||||
_check_host(ihost)
|
||||
@ -2414,9 +2598,11 @@ def _check(op, interface, ports=None, ifaces=None, from_profile=False,
|
||||
if 'txhashpolicy' not in iface:
|
||||
iface['txhashpolicy'] = None
|
||||
|
||||
_check_interface_data("modify", iface, ihost, existing_iface)
|
||||
_check_interface_data(
|
||||
"modify", iface, ihost, existing_iface, datanetworks)
|
||||
|
||||
interface = _check_interface_data(op, interface, ihost, existing_interface)
|
||||
interface = _check_interface_data(
|
||||
op, interface, ihost, existing_interface, datanetworks)
|
||||
|
||||
return interface
|
||||
|
||||
|
@ -0,0 +1,286 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
# Copyright 2013 UnitedStack Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import uuid
|
||||
import wsme
|
||||
import pecan
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from sysinv.api.controllers.v1 import base
|
||||
from sysinv.api.controllers.v1 import collection
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.common import utils as cutils
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import exception
|
||||
from sysinv.openstack.common.gettextutils import _
|
||||
|
||||
from sysinv import objects
|
||||
|
||||
|
||||
class InterfaceDataNetwork(base.APIBase):
|
||||
|
||||
id = int
|
||||
"Unique ID for this interface data network"
|
||||
|
||||
uuid = types.uuid
|
||||
"Unique UUID for this interface data network"
|
||||
|
||||
forihostid = int
|
||||
"The ID of the host the interface data network belongs to"
|
||||
|
||||
interface_uuid = types.uuid
|
||||
"Unique UUID of the parent interface"
|
||||
|
||||
ifname = wtypes.text
|
||||
"User defined name of the interface"
|
||||
|
||||
datanetwork_id = int
|
||||
"Unique ID of the parent datanetwork"
|
||||
|
||||
datanetwork_uuid = types.uuid
|
||||
"Unique UUID of the parent datanetwork"
|
||||
|
||||
datanetwork_name = wtypes.text
|
||||
"User defined name of the datanetwork"
|
||||
|
||||
network_type = wtypes.text
|
||||
"Represents the type for the datanetwork"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = objects.interface_datanetwork.fields.keys()
|
||||
for k in self.fields:
|
||||
if not hasattr(self, k):
|
||||
continue
|
||||
setattr(self, k, kwargs.get(k, wtypes.Unset))
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_interface_datanetwork, expand=True):
|
||||
interface_datanetwork = InterfaceDataNetwork(
|
||||
**rpc_interface_datanetwork.as_dict())
|
||||
if not expand:
|
||||
interface_datanetwork.unset_fields_except([
|
||||
'forihostid', 'id', 'uuid', 'interface_uuid', 'ifname',
|
||||
'datanetwork_id', 'datanetwork_uuid',
|
||||
'datanetwork_name', 'network_type'
|
||||
])
|
||||
return interface_datanetwork
|
||||
|
||||
|
||||
class InterfaceDataNetworkCollection(collection.Collection):
|
||||
"""API representation of a collection of IP addresses."""
|
||||
|
||||
interface_datanetworks = [InterfaceDataNetwork]
|
||||
"A list containing Interface Data Network objects"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'interface_datanetworks'
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_interface_datanetwork, limit, url=None,
|
||||
expand=False, **kwargs):
|
||||
collection = InterfaceDataNetworkCollection()
|
||||
collection.interface_datanetworks = [
|
||||
InterfaceDataNetwork.convert_with_links(p, expand)
|
||||
for p in rpc_interface_datanetwork]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
|
||||
|
||||
LOCK_NAME = 'InterfaceDataNetworkController'
|
||||
|
||||
|
||||
class InterfaceDataNetworkController(rest.RestController):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self._parent = parent
|
||||
|
||||
def _create_interface_datanetwork(self, interface_datanetwork):
|
||||
interface_datanetwork_dict = interface_datanetwork.as_dict()
|
||||
interface_datanetwork_dict['uuid'] = str(uuid.uuid4())
|
||||
|
||||
# Remove UUIDs from dict to be replaced with IDs
|
||||
interface_uuid = interface_datanetwork_dict.pop('interface_uuid')
|
||||
datanetwork_uuid = interface_datanetwork_dict.pop('datanetwork_uuid')
|
||||
|
||||
interface_id = self._get_interface_id(interface_uuid)
|
||||
|
||||
try:
|
||||
datanetwork_obj = \
|
||||
pecan.request.dbapi.datanetwork_get(datanetwork_uuid)
|
||||
except exception.DataNetworkNotFound:
|
||||
msg = _("DataNetwork with uuid '%s' does not exist. " %
|
||||
datanetwork_uuid)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
datanetwork_id = datanetwork_obj['id']
|
||||
|
||||
interface_datanetwork_dict['interface_id'] = interface_id
|
||||
interface_datanetwork_dict['datanetwork_id'] = datanetwork_id
|
||||
|
||||
interface_obj = pecan.request.dbapi.iinterface_get(interface_uuid)
|
||||
self._check_host(interface_obj.ihost_uuid)
|
||||
|
||||
self._check_interface_class(interface_obj)
|
||||
self._check_interface_mtu(interface_obj, datanetwork_obj)
|
||||
self._check_duplicate_interface_datanetwork(interface_datanetwork_dict)
|
||||
|
||||
result = pecan.request.dbapi.interface_datanetwork_create(
|
||||
interface_datanetwork_dict)
|
||||
|
||||
return InterfaceDataNetwork.convert_with_links(result)
|
||||
|
||||
def _get_interface_datanetwork_collection(
|
||||
self, parent_uuid=None, marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None, expand=False, resource_url=None):
|
||||
limit = utils.validate_limit(limit)
|
||||
sort_dir = utils.validate_sort_dir(sort_dir)
|
||||
marker_obj = None
|
||||
|
||||
if marker:
|
||||
marker_obj = objects.interface_datanetwork.get_by_uuid(
|
||||
pecan.request.context, marker)
|
||||
|
||||
if self._parent == "ihosts":
|
||||
interface_datanetworks = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_by_host(
|
||||
parent_uuid,
|
||||
limit=limit, marker=marker_obj,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
elif self._parent == "iinterfaces":
|
||||
interface_datanetworks = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_by_interface(
|
||||
parent_uuid, limit=limit, marker=marker_obj,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
else:
|
||||
interface_datanetworks = \
|
||||
pecan.request.dbapi.interface_datanetwork_get_all(
|
||||
limit=limit, marker=marker_obj,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
return InterfaceDataNetworkCollection.convert_with_links(
|
||||
interface_datanetworks, limit, url=resource_url, expand=expand,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
@staticmethod
|
||||
def _get_one(interface_datanetwork_uuid):
|
||||
rpc_interface_datanetwork = objects.interface_datanetwork.get_by_uuid(
|
||||
pecan.request.context, interface_datanetwork_uuid)
|
||||
return InterfaceDataNetwork.convert_with_links(
|
||||
rpc_interface_datanetwork)
|
||||
|
||||
@staticmethod
|
||||
def _check_interface_class(interface_obj):
|
||||
if (not interface_obj.ifclass or
|
||||
interface_obj.ifclass == constants.INTERFACE_CLASS_NONE):
|
||||
values = {'ifclass': constants.INTERFACE_CLASS_DATA}
|
||||
pecan.request.dbapi.iinterface_update(interface_obj.uuid, values)
|
||||
return
|
||||
else:
|
||||
# Allow ifclass data to assign another; disallow other ifclass
|
||||
if interface_obj.ifclass != constants.INTERFACE_CLASS_DATA:
|
||||
msg = _("An interface with interface class '%s' "
|
||||
"cannot assign datanetworks." %
|
||||
interface_obj.ifclass)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _check_host(host_uuid):
|
||||
host = pecan.request.dbapi.ihost_get(host_uuid)
|
||||
if host.administrative != constants.ADMIN_LOCKED:
|
||||
msg = _("Operation Rejected: Host '%s' is adminstrative '%s' " %
|
||||
(host.hostname, host.administrative))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _check_interface_mtu(interface_obj, datanetwork_obj):
|
||||
if datanetwork_obj.network_type == constants.DATANETWORK_TYPE_VXLAN:
|
||||
overhead = constants.VXLAN_MTU_OVERHEAD
|
||||
else:
|
||||
overhead = 0
|
||||
|
||||
if interface_obj.imtu < datanetwork_obj.mtu + overhead:
|
||||
msg = _("The interface MTU %s must be larger than the '%s' "
|
||||
"datanetwork MTU requirement." %
|
||||
(interface_obj.imtu, datanetwork_obj.mtu))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _query_interface_datanetwork(interface_datanetwork):
|
||||
try:
|
||||
result = pecan.request.dbapi.interface_datanetwork_query(
|
||||
interface_datanetwork)
|
||||
except exception.InterfaceDataNetworkNotFoundByKeys:
|
||||
return None
|
||||
return result
|
||||
|
||||
def _check_duplicate_interface_datanetwork(self, interface_datanetwork):
|
||||
result = self._query_interface_datanetwork(interface_datanetwork)
|
||||
if not result:
|
||||
return
|
||||
msg = _("Interface '%s' assignment with Data Network '%s' "
|
||||
"already exists."
|
||||
% (interface_datanetwork['interface_id'],
|
||||
interface_datanetwork['datanetwork_id']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _get_interface_id(interface_uuid):
|
||||
interface = pecan.request.dbapi.iinterface_get(interface_uuid)
|
||||
return interface['id']
|
||||
|
||||
@staticmethod
|
||||
def _get_datanetwork_id_and_type(datanetwork_uuid):
|
||||
datanetwork = pecan.request.dbapi.datanetwork_get(datanetwork_uuid)
|
||||
return datanetwork['id'], datanetwork['network_type']
|
||||
|
||||
@wsme_pecan.wsexpose(InterfaceDataNetwork, types.uuid)
|
||||
def get_one(self, interface_datanetwork_uuid):
|
||||
return self._get_one(interface_datanetwork_uuid)
|
||||
|
||||
@wsme_pecan.wsexpose(InterfaceDataNetworkCollection,
|
||||
wtypes.text, types.uuid, int,
|
||||
wtypes.text, wtypes.text)
|
||||
def get_all(self, parent_uuid=None, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc'):
|
||||
return self._get_interface_datanetwork_collection(
|
||||
parent_uuid, marker, limit, sort_key, sort_dir)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(InterfaceDataNetwork, body=InterfaceDataNetwork)
|
||||
def post(self, interface_datanetwork):
|
||||
return self._create_interface_datanetwork(interface_datanetwork)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
|
||||
def delete(self, interface_datanetwork_uuid):
|
||||
ifdn_obj = pecan.request.dbapi.interface_datanetwork_get(
|
||||
interface_datanetwork_uuid)
|
||||
interface_obj = pecan.request.dbapi.iinterface_get(
|
||||
ifdn_obj.interface_uuid)
|
||||
self._check_host(interface_obj.ihost_uuid)
|
||||
pecan.request.dbapi.interface_datanetwork_destroy(
|
||||
interface_datanetwork_uuid)
|
@ -74,7 +74,7 @@ CONF.import_opt('journal_default_size',
|
||||
# Defines the fields that must be copied in/out of interface profiles
|
||||
INTERFACE_PROFILE_FIELDS = ['ifname', 'iftype', 'imtu', 'networktype',
|
||||
'ifclass', 'aemode', 'networks',
|
||||
'txhashpolicy', 'forihostid', 'providernetworks',
|
||||
'txhashpolicy', 'forihostid', 'datanetworks',
|
||||
'vlan_id', 'ipv4_mode', 'ipv6_mode',
|
||||
'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs']
|
||||
@ -1350,7 +1350,7 @@ def _create_if_profile(profile_name, profile_node):
|
||||
'imtu': ethIf.mtu,
|
||||
'networktype': nt,
|
||||
'forihostid': iprofile_id,
|
||||
'providernetworks': providernets,
|
||||
'datanetworks': providernets,
|
||||
'ipv4_mode': ipv4_mode['mode'],
|
||||
'ipv6_mode': ipv6_mode['mode'],
|
||||
'ipv4_pool': ipv4_mode['pool'],
|
||||
@ -1390,7 +1390,7 @@ def _create_if_profile(profile_name, profile_node):
|
||||
'aemode': aeIf.aeMode,
|
||||
'txhashpolicy': aeIf.txPolicy,
|
||||
'forihostid': iprofile_id,
|
||||
'providernetworks': providernets,
|
||||
'datanetworks': providernets,
|
||||
'ipv4_mode': ipv4_mode,
|
||||
'ipv6_mode': ipv6_mode,
|
||||
'ipv4_pool': ipv4_pool,
|
||||
@ -1417,7 +1417,7 @@ def _create_if_profile(profile_name, profile_node):
|
||||
'networktype': nt,
|
||||
'vlan_id': vlanIf.vlanId,
|
||||
'forihostid': iprofile_id,
|
||||
'providernetworks': providernets,
|
||||
'datanetworks': providernets,
|
||||
'ipv4_mode': ipv4_mode,
|
||||
'ipv6_mode': ipv6_mode,
|
||||
'ipv4_pool': ipv4_pool,
|
||||
|
@ -54,13 +54,13 @@ class Network(object):
|
||||
raise InvalidProfileData("At least one provider network must be selected.")
|
||||
|
||||
|
||||
class DataNetwork(Network):
|
||||
class DataclassNetwork(Network):
|
||||
def __init__(self, node):
|
||||
|
||||
super(DataNetwork, self).__init__(node, constants.NETWORK_TYPE_DATA)
|
||||
self.ipv4Mode = DataNetwork.getIpMode(node, "ipv4")
|
||||
self.ipv6Mode = DataNetwork.getIpMode(node, "ipv6")
|
||||
self.routes = DataNetwork.getRoutes(node)
|
||||
super(DataclassNetwork, self).__init__(node, constants.NETWORK_TYPE_DATA)
|
||||
self.ipv4Mode = DataclassNetwork.getIpMode(node, "ipv4")
|
||||
self.ipv6Mode = DataclassNetwork.getIpMode(node, "ipv6")
|
||||
self.routes = DataclassNetwork.getRoutes(node)
|
||||
|
||||
@staticmethod
|
||||
def getRoutes(node):
|
||||
@ -288,7 +288,7 @@ class EthInterface(Interface):
|
||||
|
||||
def getNetworkMap(self):
|
||||
return {
|
||||
'dataNetwork': lambda node: DataNetwork(node),
|
||||
'dataclassNetwork': lambda node: DataclassNetwork(node),
|
||||
'infraNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_INFRA),
|
||||
'oamNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_OAM),
|
||||
'mgmtNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_MGMT),
|
||||
@ -330,7 +330,7 @@ class AeInterface(Interface):
|
||||
|
||||
def getNetworkMap(self):
|
||||
return {
|
||||
'dataNetwork': lambda node: DataNetwork(node),
|
||||
'dataclassNetwork': lambda node: DataclassNetwork(node),
|
||||
'infraNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_INFRA),
|
||||
'oamNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_OAM),
|
||||
'mgmtNetwork': lambda node: ExternalNetwork(node, constants.NETWORK_TYPE_MGMT)
|
||||
@ -365,7 +365,7 @@ class VlanInterface(Interface):
|
||||
|
||||
def getNetworkMap(self):
|
||||
return {
|
||||
'dataNetwork': lambda (node): DataNetwork(node),
|
||||
'dataclassNetwork': lambda (node): DataclassNetwork(node),
|
||||
'infraNetwork': lambda (node): ExternalNetwork(node, constants.NETWORK_TYPE_INFRA),
|
||||
'oamNetwork': lambda (node): ExternalNetwork(node, constants.NETWORK_TYPE_OAM),
|
||||
'mgmtNetwork': lambda (node): ExternalNetwork(node, constants.NETWORK_TYPE_MGMT)
|
||||
|
@ -239,10 +239,42 @@ HWMON_PORT = 2212
|
||||
NEUTRON_HOST_ALIAS = "host"
|
||||
NEUTRON_WRS_PROVIDER_ALIAS = "wrs-provider"
|
||||
|
||||
# Neutron provider networks
|
||||
NEUTRON_PROVIDERNET_FLAT = "flat"
|
||||
NEUTRON_PROVIDERNET_VXLAN = "vxlan"
|
||||
NEUTRON_PROVIDERNET_VLAN = "vlan"
|
||||
# Data Networks
|
||||
DATANETWORK_TYPE_NONE = "none"
|
||||
DATANETWORK_TYPE_FLAT = "flat"
|
||||
DATANETWORK_TYPE_VLAN = "vlan"
|
||||
DATANETWORK_TYPE_VXLAN = "vxlan"
|
||||
|
||||
DATANETWORK_MODE_DYNAMIC = "dynamic"
|
||||
DATANETWORK_MODE_STATIC = "static"
|
||||
|
||||
DATANETWORK_VXLAN_MODES = [
|
||||
DATANETWORK_MODE_DYNAMIC,
|
||||
DATANETWORK_MODE_STATIC
|
||||
]
|
||||
|
||||
# Represents the number of bytes added to a tenant packet when it is carried
|
||||
# by a VXLAN based provider network. We start by assuming a tenant network
|
||||
# with an MTU of 1500 bytes. This means that at the host vswitch the
|
||||
# ethernet frame will be 1514 bytes (+4 if VLAN tagged) not including the FCS
|
||||
# trailer. To get this packet on to the provider network it must be
|
||||
# encapsulated as-is with a {IPv4|IPv6}+UDP+VXLAN headers. The ETH+VLAN
|
||||
# headers are not included because they themselves are not included in the
|
||||
# provider network MTU (i.e., the VXLAN packet must fit within the ethernet
|
||||
# payload of the provider interface).
|
||||
# Therefore the maximum overhead, assuming a VLAN tagged provider network, is:
|
||||
#
|
||||
# IPv4 = 20 + 8 + 8 = 36
|
||||
# IPv6 = 40 + 8 + 8 = 56
|
||||
#
|
||||
# This brings the maximum tenant packet size to:
|
||||
# IPv4 = 36 + 1518 = 1554
|
||||
# IPv6 = 56 + 1518 = 1574
|
||||
#
|
||||
# Therefore to support an tenant MTU of 1500 the underlying physical
|
||||
# interface must support an MTU of 1574 bytes.
|
||||
#
|
||||
VXLAN_MTU_OVERHEAD = 74
|
||||
|
||||
# Supported worker node vswitch types
|
||||
VSWITCH_TYPE_OVS_DPDK = "ovs-dpdk"
|
||||
|
@ -1293,3 +1293,57 @@ class LocalManagementIpNotFound(NotFound):
|
||||
|
||||
class InvalidHelmDockerImageSource(Invalid):
|
||||
message = _("Invalid docker image source: %(source)s. Must be one of %(valid_srcs)s")
|
||||
|
||||
|
||||
# DataNetwork
|
||||
class UnsupportedInterfaceDataNetworkType(Conflict):
|
||||
message = _("Interface with datanetwork type '%(datanetworktype)s' "
|
||||
"is not supported.")
|
||||
|
||||
|
||||
class DataNetworkNotFound(NotFound):
|
||||
message = _("DataNetwork %(datanetwork_uuid)s could not be found.")
|
||||
|
||||
|
||||
class DataNetworkTypeNotFound(NotFound):
|
||||
message = _("DataNetwork of type %(network_type)s could not be found.")
|
||||
|
||||
|
||||
class DataNetworkIDNotFound(NotFound):
|
||||
message = _("DataNetwork with id %(id)s could not be found.")
|
||||
|
||||
|
||||
class DataNetworkNameNotFound(NotFound):
|
||||
message = _("DataNetwork with name %(name)s could not be found.")
|
||||
|
||||
|
||||
class DataNetworkAlreadyExists(Conflict):
|
||||
message = _("DataNetwork of name %(name)s already exists.")
|
||||
|
||||
|
||||
class DataNetworkTypeUnsupported(Conflict):
|
||||
message = _("DataNetwork of type %(network_type)s is not supported.")
|
||||
|
||||
|
||||
class InterfaceDataNetworkNotFound(NotFound):
|
||||
message = _("Interface datanetwork %(uuid)s could not be found.")
|
||||
|
||||
|
||||
class InterfaceDataNetworkAlreadyExists(Conflict):
|
||||
message = _("Interface datanetwork with interface ID %(interface_id)s "
|
||||
"and datanetwork ID %(datanetwork_id)s already exists.")
|
||||
|
||||
|
||||
class InterfaceDataNetworkNotFoundByKeys(NotFound):
|
||||
message = _("Interface datanetwork with interface ID %(interface_id)s "
|
||||
"and datanetwork ID %(datanetwork_id)s not found")
|
||||
|
||||
|
||||
class UnsupportedAssignedInterfaceDataNetworkType(Conflict):
|
||||
message = _("Cannot assign datanetwork with type '%(network_type)s' "
|
||||
"to an interface.")
|
||||
|
||||
|
||||
class UnsupportedRemovedInterfaceDataNetworkType(Conflict):
|
||||
message = _("Cannot remove datanetwork with type '%(network_type)s' "
|
||||
"from an interface.")
|
||||
|
@ -78,7 +78,6 @@ utils_opts = [
|
||||
default=None,
|
||||
help='Explicitly specify the temporary working directory'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(utils_opts)
|
||||
|
||||
@ -425,6 +424,18 @@ def is_valid_ipv6_cidr(address):
|
||||
return False
|
||||
|
||||
|
||||
def validate_ip_multicast_address(address, valid_values=None):
|
||||
"""
|
||||
Validates that an IP address is a multicast address.
|
||||
"""
|
||||
try:
|
||||
return netaddr.IPAddress(address).is_multicast()
|
||||
except Exception:
|
||||
msg = _("'%s' is not a valid multicast IP address") % address
|
||||
LOG.debug(msg)
|
||||
return False
|
||||
|
||||
|
||||
def get_shortened_ipv6(address):
|
||||
addr = netaddr.IPAddress(address, version=6)
|
||||
return str(addr.ipv6())
|
||||
|
@ -451,6 +451,14 @@ class OpenStackOperator(object):
|
||||
(ihost.hostname, agg_add_to))
|
||||
raise
|
||||
|
||||
def _get_interface_datanetworks(self, interface):
|
||||
ifdatanets = self.dbapi.interface_datanetwork_get_by_interface(
|
||||
interface.uuid)
|
||||
names = [x.datanetwork_name for x in ifdatanets]
|
||||
names_csv = ",".join(str(x) for x in names)
|
||||
|
||||
return names_csv
|
||||
|
||||
def nova_host_available(self, ihost_uuid):
|
||||
"""
|
||||
Perform sysinv driven nova operations for an available ihost
|
||||
@ -500,22 +508,22 @@ class OpenStackOperator(object):
|
||||
|
||||
availability_zone = None
|
||||
aggregate_name_prefix = 'provider_'
|
||||
ihost_providernets = []
|
||||
ihost_datanets = []
|
||||
|
||||
ihost_aggset_provider = set()
|
||||
host_aggset_datanet = set()
|
||||
nova_aggset_provider = set()
|
||||
|
||||
# determine which providernets are on this ihost
|
||||
# determine which datanets are on this host
|
||||
try:
|
||||
iinterfaces = self.try_interface_get_by_host(ihost_uuid)
|
||||
for interface in iinterfaces:
|
||||
if interface['ifclass'] == constants.INTERFACE_CLASS_DATA:
|
||||
providernets = interface.providernetworks
|
||||
for providernet in providernets.split(',') if providernets else []:
|
||||
ihost_aggset_provider.add(aggregate_name_prefix +
|
||||
providernet)
|
||||
datanets = self._get_interface_datanetworks(interface)
|
||||
for datanet in datanets.split(',') if datanets else []:
|
||||
host_aggset_datanet.add(aggregate_name_prefix +
|
||||
datanet)
|
||||
|
||||
ihost_providernets = list(ihost_aggset_provider)
|
||||
ihost_datanets = list(host_aggset_datanet)
|
||||
except Exception:
|
||||
LOG.exception("AGG iinterfaces_get failed for %s." % ihost_uuid)
|
||||
|
||||
@ -529,8 +537,8 @@ class OpenStackOperator(object):
|
||||
for aggregate in aggregates:
|
||||
nova_aggset_provider.add(aggregate.name)
|
||||
|
||||
if ihost_providernets:
|
||||
agglist_missing = list(ihost_aggset_provider - nova_aggset_provider)
|
||||
if ihost_datanets:
|
||||
agglist_missing = list(host_aggset_datanet - nova_aggset_provider)
|
||||
LOG.debug("AGG agglist_missing = %s." % agglist_missing)
|
||||
|
||||
for i in agglist_missing:
|
||||
@ -538,8 +546,8 @@ class OpenStackOperator(object):
|
||||
# use None for the availability zone
|
||||
# cs.aggregates.create(args.name, args.availability_zone)
|
||||
try:
|
||||
aggregate = self._get_novaclient().aggregates.create(i,
|
||||
availability_zone)
|
||||
aggregate = self._get_novaclient().aggregates.create(
|
||||
i, availability_zone)
|
||||
aggregates.append(aggregate)
|
||||
LOG.debug("AGG6 aggregate= %s. aggregates= %s" % (aggregate,
|
||||
aggregates))
|
||||
@ -577,7 +585,7 @@ class OpenStackOperator(object):
|
||||
ihost = self.dbapi.ihost_get(ihost_uuid)
|
||||
|
||||
for i in aggregates:
|
||||
if i.name in ihost_providernets:
|
||||
if i.name in ihost_datanets:
|
||||
metadata = self._get_novaclient().aggregates.get(int(i.id))
|
||||
|
||||
nhosts = []
|
||||
@ -596,7 +604,7 @@ class OpenStackOperator(object):
|
||||
% (i.id, ihost.hostname))
|
||||
return False
|
||||
else:
|
||||
LOG.warn("AGG ihost_providernets empty %s." % ihost_uuid)
|
||||
LOG.warn("AGG ihost_datanets empty %s." % ihost_uuid)
|
||||
|
||||
def nova_host_offline(self, ihost_uuid):
|
||||
"""
|
||||
@ -618,22 +626,21 @@ class OpenStackOperator(object):
|
||||
#
|
||||
|
||||
aggregate_name_prefix = 'provider_'
|
||||
ihost_providernets = []
|
||||
ihost_datanets = []
|
||||
|
||||
ihost_aggset_provider = set()
|
||||
host_aggset_datanet = set()
|
||||
nova_aggset_provider = set()
|
||||
|
||||
# determine which providernets are on this ihost
|
||||
# determine which datanets are on this ihost
|
||||
try:
|
||||
iinterfaces = self.try_interface_get_by_host(ihost_uuid)
|
||||
for interface in iinterfaces:
|
||||
if interface['ifclass'] == constants.INTERFACE_CLASS_DATA:
|
||||
providernets = interface.providernetworks
|
||||
for providernet in (
|
||||
providernets.split(',') if providernets else []):
|
||||
ihost_aggset_provider.add(aggregate_name_prefix +
|
||||
providernet)
|
||||
ihost_providernets = list(ihost_aggset_provider)
|
||||
datanets = self._get_interface_datanetworks(interface)
|
||||
for datanet in (datanets.split(',') if datanets else []):
|
||||
host_aggset_datanet.add(aggregate_name_prefix +
|
||||
datanet)
|
||||
ihost_datanets = list(host_aggset_datanet)
|
||||
except Exception:
|
||||
LOG.exception("AGG iinterfaces_get failed for %s." % ihost_uuid)
|
||||
|
||||
@ -643,11 +650,11 @@ class OpenStackOperator(object):
|
||||
self.nova_client = None # password may have updated
|
||||
aggregates = self._get_novaclient().aggregates.list()
|
||||
|
||||
if ihost_providernets:
|
||||
if ihost_datanets:
|
||||
for aggregate in aggregates:
|
||||
nova_aggset_provider.add(aggregate.name)
|
||||
else:
|
||||
LOG.debug("AGG ihost_providernets empty %s." % ihost_uuid)
|
||||
LOG.debug("AGG ihost_datanets empty %s." % ihost_uuid)
|
||||
|
||||
# setup the valid set of storage aggregates for host removal
|
||||
aggset_storage = set([
|
||||
@ -661,7 +668,7 @@ class OpenStackOperator(object):
|
||||
ihost = self.dbapi.ihost_get(ihost_uuid)
|
||||
|
||||
for aggregate in aggregates:
|
||||
if aggregate.name in ihost_providernets or \
|
||||
if aggregate.name in ihost_datanets or \
|
||||
aggregate.name in aggset_storage: # or just do it for all aggs
|
||||
try:
|
||||
LOG.debug("AGG10 remove aggregate id = %s ihost= %s." %
|
||||
|
@ -836,7 +836,6 @@ class Connection(object):
|
||||
'aemode': 'balanced',
|
||||
'schedpolicy': 'xor',
|
||||
'txhashpolicy': 'L2',
|
||||
'providernetworks': 'physnet0, physnet1'
|
||||
'extra': { ... },
|
||||
}
|
||||
:returns: An iinterface.
|
||||
|
@ -404,6 +404,26 @@ def add_interface_filter_by_ihost(query, value):
|
||||
return query.filter(models.ihost.uuid == value)
|
||||
|
||||
|
||||
def add_datanetwork_filter(query, value):
|
||||
"""Adds a datanetwork-specific filter to a query.
|
||||
|
||||
:param query: Initial query to add filter to.
|
||||
:param value: Value for filtering results by.
|
||||
:return: Modified query.
|
||||
"""
|
||||
|
||||
if uuidutils.is_uuid_like(value):
|
||||
return query.filter(or_(models.DataNetworksFlat.uuid == value,
|
||||
models.DataNetworksVlan.uuid == value,
|
||||
models.DataNetworksVXlan.uuid == value))
|
||||
elif utils.is_int_like(value):
|
||||
return query.filter(or_(models.DataNetworksFlat.id == value,
|
||||
models.DataNetworksVlan.id == value,
|
||||
models.DataNetworksVXlan.id == value))
|
||||
else:
|
||||
return add_identity_filter(query, value, use_name=True)
|
||||
|
||||
|
||||
def add_port_filter_by_numa_node(query, nodeid):
|
||||
"""Adds a port-specific numa node filter to a query.
|
||||
|
||||
@ -534,9 +554,9 @@ def add_port_filter_by_host_interface(query, hostid, interfaceid):
|
||||
|
||||
elif utils.is_uuid_like(hostid) and utils.is_uuid_like(interfaceid):
|
||||
query = query.join(models.ihost,
|
||||
models.iinterface)
|
||||
models.Interface)
|
||||
return query.filter(models.ihost.uuid == hostid,
|
||||
models.iinterface.uuid == interfaceid)
|
||||
models.Interface.uuid == interfaceid)
|
||||
|
||||
LOG.debug("port_filter_by_host_iinterface: "
|
||||
"No match for supplied filter ids (%s, %s)"
|
||||
@ -1325,9 +1345,6 @@ class Connection(api.Connection):
|
||||
model_query(models.imemory, read_deleted="no").\
|
||||
filter_by(forihostid=server_id).\
|
||||
delete()
|
||||
model_query(models.iinterface, read_deleted="no").\
|
||||
filter_by(forihostid=server_id).\
|
||||
delete()
|
||||
model_query(models.idisk, read_deleted="no").\
|
||||
filter_by(forihostid=server_id).\
|
||||
delete()
|
||||
@ -2063,7 +2080,6 @@ class Connection(api.Connection):
|
||||
return query.all()
|
||||
|
||||
def _iinterface_get(self, iinterface_id, ihost=None, network=None):
|
||||
# query = model_query(models.iinterface)
|
||||
entity = with_polymorphic(models.Interfaces, '*')
|
||||
query = model_query(entity)
|
||||
query = add_interface_filter(query, iinterface_id)
|
||||
@ -2215,16 +2231,13 @@ class Connection(api.Connection):
|
||||
if obj.id is None:
|
||||
obj.id = temp_id
|
||||
|
||||
# Ensure networktype and providernetworks results are None when they
|
||||
# Ensure networktype results are None when they
|
||||
# are specified as 'none'. Otherwise the 'none' value is written to
|
||||
# the database which causes issues with checks that expects it to be
|
||||
# the None type
|
||||
if getattr(obj, 'networktype', None) == constants.NETWORK_TYPE_NONE:
|
||||
setattr(obj, 'networktype', None)
|
||||
|
||||
if getattr(obj, 'providernetworks', None) == 'none':
|
||||
setattr(obj, 'providernetworks', None)
|
||||
|
||||
try:
|
||||
session.add(obj)
|
||||
session.flush()
|
||||
@ -2301,7 +2314,7 @@ class Connection(api.Connection):
|
||||
for k, v in values.items():
|
||||
if k == 'networktype' and v == constants.NETWORK_TYPE_NONE:
|
||||
v = None
|
||||
if k == 'providernetworks' and v == 'none':
|
||||
if k == 'datanetworks' and v == 'none':
|
||||
v = None
|
||||
if k == 'uses':
|
||||
del obj.uses[:]
|
||||
@ -7579,3 +7592,263 @@ class Connection(api.Connection):
|
||||
except NoResultFound:
|
||||
raise exception.KubeAppNotFound(name)
|
||||
query.delete()
|
||||
|
||||
def _datanetwork_get(self, model_class, datanetwork_id, obj=None):
|
||||
session = None
|
||||
if obj:
|
||||
session = inspect(obj).session
|
||||
query = model_query(model_class, session=session)
|
||||
|
||||
query = add_datanetwork_filter(query, datanetwork_id)
|
||||
|
||||
try:
|
||||
result = query.one()
|
||||
except NoResultFound:
|
||||
raise exception.DataNetworkNotFound(
|
||||
datanetwork_uuid=datanetwork_id)
|
||||
except MultipleResultsFound:
|
||||
raise exception.InvalidParameterValue(
|
||||
err="Multiple entries found for datanetwork %s" % datanetwork_id)
|
||||
return result
|
||||
|
||||
def _datanetwork_get_one(self, datanetwork_id, datanetwork=None):
|
||||
entity = with_polymorphic(models.DataNetworks, '*')
|
||||
query = model_query(entity)
|
||||
query = add_datanetwork_filter(query, datanetwork_id)
|
||||
if datanetwork is not None:
|
||||
query = query.filter_by(network_type=datanetwork)
|
||||
try:
|
||||
result = query.one()
|
||||
except NoResultFound:
|
||||
raise exception.DataNetworkNotFound(
|
||||
datanetwork_uuid=datanetwork_id)
|
||||
except MultipleResultsFound:
|
||||
raise exception.InvalidParameterValue(
|
||||
err="Multiple entries found for datanetwork %s" % datanetwork_id)
|
||||
|
||||
return result
|
||||
|
||||
def _datanetwork_create(self, obj, values):
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
|
||||
with _session_for_write() as session:
|
||||
# The id is null for ae interfaces with more than one member interface
|
||||
temp_id = obj.id
|
||||
obj.update(values)
|
||||
if obj.id is None:
|
||||
obj.id = temp_id
|
||||
|
||||
try:
|
||||
session.add(obj)
|
||||
session.flush()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
LOG.error("Failed to add datanetwork (uuid: %s), "
|
||||
"name %s already exists." %
|
||||
(values['uuid'], values.get('name')))
|
||||
|
||||
raise exception.DataNetworkAlreadyExists(
|
||||
name=values.get('name'))
|
||||
|
||||
return self._datanetwork_get(type(obj), values['uuid'])
|
||||
|
||||
@objects.objectify(objects.datanetwork)
|
||||
def datanetwork_create(self, values):
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
|
||||
network_type = values.get('network_type')
|
||||
if network_type == constants.DATANETWORK_TYPE_FLAT:
|
||||
datanetwork = models.DataNetworksFlat()
|
||||
elif network_type == constants.DATANETWORK_TYPE_VLAN:
|
||||
datanetwork = models.DataNetworksVlan()
|
||||
elif network_type == constants.DATANETWORK_TYPE_VXLAN:
|
||||
datanetwork = models.DataNetworksVXlan()
|
||||
else:
|
||||
raise exception.DataNetworkTypeUnsupported(
|
||||
network_type=network_type)
|
||||
return self._datanetwork_create(datanetwork, values)
|
||||
|
||||
@objects.objectify(objects.datanetwork)
|
||||
def datanetwork_get(self, datanetwork_id):
|
||||
return self._datanetwork_get_one(datanetwork_id)
|
||||
|
||||
def _add_datanetworks_filters(self, query, filters):
|
||||
if filters is None:
|
||||
filters = dict()
|
||||
supported_filters = {'network_type',
|
||||
'name',
|
||||
}
|
||||
unsupported_filters = set(filters).difference(supported_filters)
|
||||
if unsupported_filters:
|
||||
msg = _("SqlAlchemy API does not support "
|
||||
"filtering by %s") % ', '.join(unsupported_filters)
|
||||
raise ValueError(msg)
|
||||
|
||||
for field in supported_filters:
|
||||
if field in filters:
|
||||
query = query.filter_by(**{field: filters[field]})
|
||||
|
||||
return query
|
||||
|
||||
@objects.objectify(objects.datanetwork)
|
||||
def datanetworks_get_all(self, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
|
||||
with _session_for_read() as session:
|
||||
datanetworks = with_polymorphic(models.DataNetworks, '*')
|
||||
query = model_query(datanetworks, session=session)
|
||||
query = self._add_datanetworks_filters(query, filters)
|
||||
|
||||
return _paginate_query(models.DataNetworks, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
@objects.objectify(objects.datanetwork)
|
||||
def datanetwork_update(self, datanetwork_uuid, values):
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.DataNetworks, session=session)
|
||||
query = add_identity_filter(query, datanetwork_uuid)
|
||||
|
||||
count = query.update(values, synchronize_session='fetch')
|
||||
if count != 1:
|
||||
raise exception.DataNetworkNotFound(
|
||||
datanetwork_uuid=datanetwork_uuid)
|
||||
return query.one()
|
||||
|
||||
def datanetwork_destroy(self, datanetwork_uuid):
|
||||
query = model_query(models.DataNetworks)
|
||||
query = add_identity_filter(query, datanetwork_uuid)
|
||||
try:
|
||||
query.one()
|
||||
except NoResultFound:
|
||||
raise exception.DataNetworkNotFound(
|
||||
datanetwork_uuid=datanetwork_uuid)
|
||||
query.delete()
|
||||
|
||||
def _interface_datanetwork_get(self, uuid, session=None):
|
||||
query = model_query(models.InterfaceDataNetworks, session=session)
|
||||
query = add_identity_filter(query, uuid)
|
||||
try:
|
||||
result = query.one()
|
||||
except NoResultFound:
|
||||
raise exception.InterfaceDataNetworkNotFound(uuid=uuid)
|
||||
return result
|
||||
|
||||
def _interface_datanetwork_get_all(
|
||||
self, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
return _paginate_query(
|
||||
models.InterfaceDataNetworks, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def _interface_datanetwork_get_by_host(
|
||||
self, host_uuid, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
query = (query.
|
||||
join(models.Interfaces).
|
||||
join(models.ihost,
|
||||
models.ihost.id == models.Interfaces.forihostid))
|
||||
query, field = add_filter_by_many_identities(
|
||||
query, models.ihost, [host_uuid])
|
||||
return _paginate_query(
|
||||
models.InterfaceDataNetworks, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def _interface_datanetwork_get_by_interface(
|
||||
self, interface_uuid, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
query = (query.join(models.Interfaces))
|
||||
query, field = add_filter_by_many_identities(
|
||||
query, models.Interfaces, [interface_uuid])
|
||||
return _paginate_query(models.InterfaceDataNetworks,
|
||||
limit, marker, sort_key, sort_dir, query)
|
||||
|
||||
def _interface_datanetwork_get_by_datanetwork(
|
||||
self, datanetwork_uuid, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
query = (query.join(models.DataNetworks))
|
||||
query, field = add_filter_by_many_identities(
|
||||
query, models.DataNetworks, [datanetwork_uuid])
|
||||
return _paginate_query(models.InterfaceDataNetworks,
|
||||
limit, marker, sort_key, sort_dir, query)
|
||||
|
||||
def _interface_datanetwork_query(self, values):
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
query = (query.
|
||||
filter(models.InterfaceDataNetworks.interface_id ==
|
||||
values['interface_id']).
|
||||
filter(models.InterfaceDataNetworks.datanetwork_id ==
|
||||
values['datanetwork_id']))
|
||||
try:
|
||||
result = query.one()
|
||||
except NoResultFound:
|
||||
raise exception.InterfaceDataNetworkNotFoundByKeys(
|
||||
interface_id=values['interface_id'],
|
||||
datanetwork_id=values['datanetwork_id'])
|
||||
return result
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_create(self, values):
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
|
||||
interface_datanetwork = models.InterfaceDataNetworks(**values)
|
||||
with _session_for_write() as session:
|
||||
try:
|
||||
session.add(interface_datanetwork)
|
||||
session.flush()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.InterfaceDataNetworkAlreadyExists(
|
||||
interface_id=values['interface_id'],
|
||||
datanetwork_id=values['datanetwork_id'])
|
||||
return self._interface_datanetwork_get(values['uuid'], session)
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_get(self, uuid):
|
||||
return self._interface_datanetwork_get(uuid)
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_get_all(
|
||||
self, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_datanetwork_get_all(
|
||||
limit, marker, sort_key, sort_dir)
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_get_by_host(
|
||||
self, host_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_datanetwork_get_by_host(
|
||||
host_id, limit, marker, sort_key, sort_dir)
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_get_by_interface(
|
||||
self, interface_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_datanetwork_get_by_interface(
|
||||
interface_id, limit, marker, sort_key, sort_dir)
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_get_by_datanetwork(
|
||||
self, datanetwork_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_datanetwork_get_by_datanetwork(
|
||||
datanetwork_id, limit, marker, sort_key, sort_dir)
|
||||
|
||||
def interface_datanetwork_destroy(self, uuid):
|
||||
query = model_query(models.InterfaceDataNetworks)
|
||||
query = add_identity_filter(query, uuid)
|
||||
try:
|
||||
query.one()
|
||||
except NoResultFound:
|
||||
raise exception.InterfaceDataNetworkNotFound(uuid=uuid)
|
||||
query.delete()
|
||||
|
||||
@objects.objectify(objects.interface_datanetwork)
|
||||
def interface_datanetwork_query(self, values):
|
||||
return self._interface_datanetwork_query(values)
|
||||
|
@ -0,0 +1,144 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from sqlalchemy import Column, MetaData, Table
|
||||
from sqlalchemy import DateTime, Integer, String
|
||||
from sqlalchemy import ForeignKey, UniqueConstraint
|
||||
from sysinv.common import constants
|
||||
|
||||
ENGINE = 'InnoDB'
|
||||
CHARSET = 'utf8'
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
Table('interfaces', meta, autoload=True)
|
||||
|
||||
datanetworks = Table(
|
||||
'datanetworks',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('uuid', String(36), unique=True),
|
||||
Column('name', String(255), unique=True),
|
||||
Column('network_type', String(255)),
|
||||
Column('description', String(255)),
|
||||
Column('mtu', Integer, nullable=False),
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
|
||||
datanetworks_flat = Table(
|
||||
'datanetworks_flat',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer,
|
||||
ForeignKey('datanetworks.id', ondelete="CASCADE"),
|
||||
primary_key=True, nullable=False),
|
||||
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
|
||||
datanetworks_vlan = Table(
|
||||
'datanetworks_vlan',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer,
|
||||
ForeignKey('datanetworks.id', ondelete="CASCADE"),
|
||||
primary_key=True, nullable=False),
|
||||
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
|
||||
datanetworks_vxlan = Table(
|
||||
'datanetworks_vxlan',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer,
|
||||
ForeignKey('datanetworks.id', ondelete="CASCADE"),
|
||||
primary_key=True, nullable=False),
|
||||
|
||||
Column('multicast_group', String(64), nullable=True),
|
||||
Column('port_num', Integer, nullable=False),
|
||||
Column('ttl', Integer, nullable=False),
|
||||
Column('mode', String(32), nullable=False,
|
||||
default=constants.DATANETWORK_MODE_DYNAMIC),
|
||||
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
|
||||
interface_datanetworks = Table(
|
||||
'interface_datanetworks',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('uuid', String(36), unique=True),
|
||||
|
||||
Column('interface_id', Integer,
|
||||
ForeignKey('interfaces.id', ondelete='CASCADE')),
|
||||
Column('datanetwork_id', Integer,
|
||||
ForeignKey('datanetworks.id', ondelete='CASCADE')),
|
||||
|
||||
UniqueConstraint('interface_id', 'datanetwork_id',
|
||||
name='u_interface_id@datanetwork_id'),
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
|
||||
tables = (
|
||||
datanetworks,
|
||||
datanetworks_flat,
|
||||
datanetworks_vlan,
|
||||
datanetworks_vxlan,
|
||||
interface_datanetworks,
|
||||
)
|
||||
|
||||
for index, table in enumerate(tables):
|
||||
try:
|
||||
table.create()
|
||||
except Exception:
|
||||
# If an error occurs, drop all tables created so far to return
|
||||
# to the previously existing state.
|
||||
meta.drop_all(tables=tables[:index])
|
||||
raise
|
||||
|
||||
ethernet_interfaces = Table('ethernet_interfaces', meta, autoload=True)
|
||||
ethernet_interfaces.drop_column('providernetworks')
|
||||
ethernet_interfaces.drop_column('providernetworksdict')
|
||||
|
||||
ae_interfaces = Table('ae_interfaces', meta, autoload=True)
|
||||
ae_interfaces.drop_column('providernetworks')
|
||||
ae_interfaces.drop_column('providernetworksdict')
|
||||
|
||||
vlan_interfaces = Table('vlan_interfaces', meta, autoload=True)
|
||||
vlan_interfaces.drop_column('providernetworks')
|
||||
vlan_interfaces.drop_column('providernetworksdict')
|
||||
|
||||
virtual_interfaces = Table('virtual_interfaces', meta, autoload=True)
|
||||
virtual_interfaces.drop_column('providernetworks')
|
||||
virtual_interfaces.drop_column('providernetworksdict')
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
# As per other openstack components, downgrade is
|
||||
# unsupported in this release.
|
||||
raise NotImplementedError('SysInv database downgrade is unsupported.')
|
@ -17,7 +17,8 @@
|
||||
#
|
||||
# Copyright (c) 2013-2018 Wind River Systems, Inc.
|
||||
#
|
||||
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
"""
|
||||
SQLAlchemy models for sysinv data.
|
||||
"""
|
||||
@ -26,6 +27,7 @@ import json
|
||||
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from oslo_config import cfg
|
||||
from oslo_db.sqlalchemy import models
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, BigInteger, Boolean
|
||||
from sqlalchemy import Enum, UniqueConstraint, String, Table, Text, Float
|
||||
@ -35,7 +37,7 @@ from sqlalchemy.ext.declarative import declared_attr
|
||||
from sqlalchemy.types import TypeDecorator, VARCHAR
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
|
||||
from oslo_db.sqlalchemy import models
|
||||
from sysinv.common import constants
|
||||
|
||||
sql_opts = [
|
||||
cfg.StrOpt('mysql_engine',
|
||||
@ -321,32 +323,6 @@ class imemory(Base):
|
||||
UniqueConstraint('forihostid', 'forinodeid', name='u_hostnode')
|
||||
|
||||
|
||||
class iinterface(Base):
|
||||
__tablename__ = 'i_interface'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36))
|
||||
|
||||
ifname = Column(String(255))
|
||||
iftype = Column(String(255))
|
||||
imac = Column(String(255), unique=True)
|
||||
imtu = Column(Integer)
|
||||
networktype = Column(String(255))
|
||||
aemode = Column(String(255)) # e.g. balanced, active_standby
|
||||
aedict = Column(JSONEncodedDict) # e.g. 802.3ad parameters
|
||||
txhashpolicy = Column(String(255)) # e.g. L2, L2L3, L3L4
|
||||
providernetworks = Column(String(255)) # ['physnet0','physnet1']
|
||||
providernetworksdict = Column(JSONEncodedDict)
|
||||
schedpolicy = Column(String(255))
|
||||
ifcapabilities = Column(JSONEncodedDict)
|
||||
sriov_numvfs = Column(Integer)
|
||||
# JSON{'mode':"xor", 'bond':'false'}
|
||||
|
||||
farend = Column(JSONEncodedDict)
|
||||
forihostid = Column(Integer, ForeignKey('i_host.id', ondelete='CASCADE'))
|
||||
UniqueConstraint('ifname', 'forihostid', name='u_ifnameihost')
|
||||
|
||||
|
||||
interfaces_to_interfaces = Table("interfaces_to_interfaces", Base.metadata,
|
||||
Column("used_by_id", Integer, ForeignKey("interfaces.id", ondelete='CASCADE'), primary_key=True),
|
||||
Column("uses_id", Integer, ForeignKey("interfaces.id", ondelete='CASCADE'), primary_key=True)
|
||||
@ -408,8 +384,6 @@ class EthernetCommon(object):
|
||||
|
||||
imac = Column(String(255))
|
||||
imtu = Column(Integer)
|
||||
providernetworks = Column(String(255)) # ['physnet0','physnet1']
|
||||
providernetworksdict = Column(JSONEncodedDict)
|
||||
|
||||
|
||||
class EthernetInterfaces(EthernetCommon, Interfaces):
|
||||
@ -1180,6 +1154,85 @@ class InterfaceNetworks(Base):
|
||||
UniqueConstraint('interface_id', 'network_id', name='u_interface_id@network_id')
|
||||
|
||||
|
||||
class DataNetworks(Base):
|
||||
__tablename__ = 'datanetworks'
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36), unique=True)
|
||||
name = Column(String(255), unique=True)
|
||||
network_type = Column(String(255))
|
||||
description = Column(String(255))
|
||||
mtu = Column(Integer)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'datanetwork',
|
||||
'polymorphic_on': network_type
|
||||
}
|
||||
|
||||
|
||||
class DataNetworksCommon(object):
|
||||
@declared_attr
|
||||
def id(cls):
|
||||
return Column(Integer,
|
||||
ForeignKey('datanetworks.id', ondelete="CASCADE"),
|
||||
primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class DataNetworksFlat(DataNetworksCommon, DataNetworks):
|
||||
__tablename__ = 'datanetworks_flat'
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'flat',
|
||||
}
|
||||
|
||||
|
||||
class DataNetworksVlan(DataNetworksCommon, DataNetworks):
|
||||
__tablename__ = 'datanetworks_vlan'
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'vlan',
|
||||
}
|
||||
|
||||
|
||||
class DataNetworksVXlan(DataNetworksCommon, DataNetworks):
|
||||
__tablename__ = 'datanetworks_vxlan'
|
||||
|
||||
# IP address of the multicast group
|
||||
multicast_group = Column(String(64), nullable=True)
|
||||
|
||||
# Destination DP port for all instances
|
||||
port_num = Column(Integer, nullable=False)
|
||||
|
||||
# Time-to-live value for all instances
|
||||
ttl = Column(Integer, nullable=False)
|
||||
|
||||
# defines dynamic learning with multicast enable/disabled
|
||||
mode = Column(String(32), nullable=False,
|
||||
default=constants.DATANETWORK_MODE_DYNAMIC)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'vxlan',
|
||||
}
|
||||
|
||||
|
||||
class InterfaceDataNetworks(Base):
|
||||
__tablename__ = 'interface_datanetworks'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36), unique=True)
|
||||
|
||||
interface_id = Column(
|
||||
Integer, ForeignKey('interfaces.id', ondelete='CASCADE'))
|
||||
datanetwork_id = Column(
|
||||
Integer, ForeignKey('datanetworks.id', ondelete='CASCADE'))
|
||||
|
||||
interface = relationship(
|
||||
"Interfaces", lazy="joined", backref="interface_datanetworks")
|
||||
datanetwork = relationship(
|
||||
"DataNetworks", lazy="joined", backref="interface_datanetworks")
|
||||
UniqueConstraint(
|
||||
'interface_id', 'datanetwork_id', name='u_interface_id@datanetwork_id')
|
||||
|
||||
|
||||
class SensorGroups(Base):
|
||||
__tablename__ = 'i_sensorgroups'
|
||||
|
||||
|
@ -192,15 +192,17 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
# obtain the assigned bridge for interface
|
||||
brname = 'br-phy%d' % index
|
||||
if brname:
|
||||
providernets = self._get_interface_providernets(iface)
|
||||
for providernet in providernets:
|
||||
datanets = self._get_interface_datanets(iface)
|
||||
for datanet in datanets:
|
||||
LOG.info("_get_dynamic_ovs_agent_config datanet %s" %
|
||||
datanet)
|
||||
address = self._get_interface_primary_address(
|
||||
self.context, host, iface)
|
||||
if address:
|
||||
local_ip = address
|
||||
tunnel_types = constants.NEUTRON_PROVIDERNET_VXLAN
|
||||
tunnel_types = constants.DATANETWORK_TYPE_VXLAN
|
||||
else:
|
||||
bridge_mappings += ('%s:%s,' % (providernet, brname))
|
||||
bridge_mappings += ('%s:%s,' % (datanet, brname))
|
||||
index += 1
|
||||
|
||||
agent = {}
|
||||
@ -217,6 +219,12 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
if bridge_mappings:
|
||||
ovs['bridge_mappings'] = str(bridge_mappings)
|
||||
|
||||
# https://access.redhat.com/documentation/en-us/
|
||||
# red_hat_enterprise_linux_openstack_platform/7/html/
|
||||
# networking_guide/bridge-mappings
|
||||
# required for vlan, not flat, vxlan:
|
||||
# ovs['network_vlan_ranges'] = physnet1:10:20,physnet2:21:25
|
||||
|
||||
return {
|
||||
'agent': agent,
|
||||
'ovs': ovs,
|
||||
@ -230,11 +238,11 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
for iface in sorted(self.dbapi.iinterface_get_by_ihost(host.id),
|
||||
key=self._interface_sort_key):
|
||||
if self._is_sriov_network_type(iface):
|
||||
# obtain the assigned providernets for interface
|
||||
providernets = self._get_interface_providernets(iface)
|
||||
# obtain the assigned datanets for interface
|
||||
datanets = self._get_interface_datanets(iface)
|
||||
port_name = self._get_interface_port_name(iface)
|
||||
for providernet in providernets:
|
||||
physical_device_mappings += ('%s:%s,' % (providernet, port_name))
|
||||
for datanet in datanets:
|
||||
physical_device_mappings += ('%s:%s,' % (datanet, port_name))
|
||||
sriov_nic = {
|
||||
'physical_device_mappings': str(physical_device_mappings),
|
||||
}
|
||||
@ -289,6 +297,15 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
|
||||
return neutron_config
|
||||
|
||||
def _get_ml2_physical_network_mtus(self):
|
||||
ml2_physical_network_mtus = []
|
||||
datanetworks = self.dbapi.datanetworks_get_all()
|
||||
for datanetwork in datanetworks:
|
||||
dn_str = str(datanetwork.name) + ":" + str(datanetwork.mtu)
|
||||
ml2_physical_network_mtus.append(dn_str)
|
||||
|
||||
return ",".join(ml2_physical_network_mtus)
|
||||
|
||||
def _get_neutron_ml2_config(self):
|
||||
ml2_config = {
|
||||
'ml2': {
|
||||
@ -296,12 +313,14 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
'tenant_network_types': 'vlan,vxlan',
|
||||
'mechanism_drivers': 'openvswitch,sriovnicswitch,l2population',
|
||||
'path_mtu': 0,
|
||||
'physical_network_mtus': self._get_ml2_physical_network_mtus()
|
||||
|
||||
},
|
||||
'securitygroup': {
|
||||
'firewall_driver': 'noop',
|
||||
},
|
||||
}
|
||||
LOG.info("_get_neutron_ml2_config=%s" % ml2_config)
|
||||
return ml2_config
|
||||
|
||||
def _is_data_network_type(self, iface):
|
||||
@ -312,14 +331,14 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
||||
networktypelist = utils.get_network_type_list(iface)
|
||||
return bool(any(n in SRIOV_NETWORK_TYPES for n in networktypelist))
|
||||
|
||||
def _get_interface_providernets(self, iface):
|
||||
def _get_interface_datanets(self, iface):
|
||||
"""
|
||||
Return the provider networks of the supplied interface as a list.
|
||||
Return the data networks of the supplied interface as a list.
|
||||
"""
|
||||
providernetworks = iface['providernetworks']
|
||||
if not providernetworks:
|
||||
return []
|
||||
return [x.strip() for x in providernetworks.split(',')]
|
||||
|
||||
ifdatanets = self.dbapi.interface_datanetwork_get_by_interface(
|
||||
iface.uuid)
|
||||
return [ifdn['datanetwork_name'].strip() for ifdn in ifdatanets]
|
||||
|
||||
def _get_interface_port_name(self, iface):
|
||||
"""
|
||||
|
@ -27,6 +27,7 @@ from sysinv.objects import cluster
|
||||
from sysinv.objects import community
|
||||
from sysinv.objects import controller_fs
|
||||
from sysinv.objects import cpu
|
||||
from sysinv.objects import datanetwork
|
||||
from sysinv.objects import disk
|
||||
from sysinv.objects import firewallrules
|
||||
from sysinv.objects import partition
|
||||
@ -41,6 +42,7 @@ from sysinv.objects import network_infra
|
||||
from sysinv.objects import interface
|
||||
from sysinv.objects import interface_ae
|
||||
from sysinv.objects import interface_ethernet
|
||||
from sysinv.objects import interface_datanetwork
|
||||
from sysinv.objects import interface_network
|
||||
from sysinv.objects import interface_virtual
|
||||
from sysinv.objects import interface_vlan
|
||||
@ -128,6 +130,7 @@ ae_interface = interface_ae.AEInterface
|
||||
virtual_interface = interface_virtual.VirtualInterface
|
||||
vlan_interface = interface_vlan.VLANInterface
|
||||
interface_network = interface_network.InterfaceNetwork
|
||||
interface_datanetwork = interface_datanetwork.InterfaceDataNetwork
|
||||
port = port.Port
|
||||
ethernet_port = port_ethernet.EthernetPort
|
||||
disk = disk.Disk
|
||||
@ -183,6 +186,7 @@ storage_ceph_external = storage_ceph_external.StorageCephExternal
|
||||
helm_overrides = helm_overrides.HelmOverrides
|
||||
label = label.Label
|
||||
kube_app = kube_app.KubeApp
|
||||
datanetwork = datanetwork.DataNetwork
|
||||
|
||||
__all__ = (system,
|
||||
cluster,
|
||||
@ -251,6 +255,8 @@ __all__ = (system,
|
||||
storage_ceph_external,
|
||||
helm_overrides,
|
||||
kube_app,
|
||||
datanetwork,
|
||||
interface_network,
|
||||
# alias objects for RPC compatibility
|
||||
ihost,
|
||||
ilvg,
|
||||
|
42
sysinv/sysinv/sysinv/sysinv/objects/datanetwork.py
Normal file
42
sysinv/sysinv/sysinv/sysinv/objects/datanetwork.py
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
#
|
||||
|
||||
from sysinv.db import api as db_api
|
||||
from sysinv.objects import base
|
||||
from sysinv.objects import utils
|
||||
|
||||
|
||||
class DataNetwork(base.SysinvObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {'id': int,
|
||||
'uuid': utils.uuid_or_none,
|
||||
'network_type': utils.str_or_none,
|
||||
'name': utils.str_or_none,
|
||||
'description': utils.str_or_none,
|
||||
'mtu': utils.int_or_none,
|
||||
'multicast_group': utils.str_or_none,
|
||||
'port_num': utils.int_or_none,
|
||||
'ttl': utils.int_or_none,
|
||||
'mode': utils.str_or_none,
|
||||
}
|
||||
|
||||
_optional_fields = {'port_num',
|
||||
'multicast_group',
|
||||
'ttl',
|
||||
'mode'}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
return cls.dbapi.datanetwork_get(uuid)
|
||||
|
||||
def save_changes(self, context, updates):
|
||||
self.dbapi.datanetwork_update(self.uuid, updates)
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2013-2016 Wind River Systems, Inc.
|
||||
# Copyright (c) 2013-2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
@ -88,6 +88,15 @@ def get_networks(field, db_object):
|
||||
return result
|
||||
|
||||
|
||||
def get_datanetworks(field, db_object):
|
||||
result = []
|
||||
if hasattr(db_object, 'interface_datanetworks'):
|
||||
for entry in getattr(db_object, 'interface_datanetworks', []):
|
||||
id_str = str(entry.datanetwork_id)
|
||||
result.append(id_str)
|
||||
return result
|
||||
|
||||
|
||||
class Interface(base.SysinvObject):
|
||||
# VERSION 1.0: Initial version
|
||||
# VERSION 1.1: Added VLAN and uses/used_by interface support
|
||||
@ -110,9 +119,8 @@ class Interface(base.SysinvObject):
|
||||
'aemode': utils.str_or_none,
|
||||
'schedpolicy': utils.str_or_none,
|
||||
'txhashpolicy': utils.str_or_none,
|
||||
'providernetworks': utils.str_or_none,
|
||||
'providernetworksdict': utils.dict_or_none,
|
||||
'networks': utils.list_of_strings_or_none,
|
||||
'datanetworks': utils.list_of_strings_or_none,
|
||||
|
||||
'ifcapabilities': utils.dict_or_none,
|
||||
|
||||
@ -136,7 +144,8 @@ class Interface(base.SysinvObject):
|
||||
'ipv4_pool': get_ipv4_address_pool,
|
||||
'ipv6_pool': get_ipv6_address_pool,
|
||||
'ihost_uuid': get_host_uuid,
|
||||
'networks': get_networks}
|
||||
'networks': get_networks,
|
||||
'datanetworks': get_datanetworks}
|
||||
|
||||
_optional_fields = ['aemode', 'txhashpolicy', 'schedpolicy',
|
||||
'vlan_id', 'vlan_type']
|
||||
|
110
sysinv/sysinv/sysinv/sysinv/objects/interface_datanetwork.py
Normal file
110
sysinv/sysinv/sysinv/sysinv/objects/interface_datanetwork.py
Normal file
@ -0,0 +1,110 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
#
|
||||
|
||||
from sysinv.db import api as db_api
|
||||
from sysinv.objects import base
|
||||
from sysinv.objects import utils
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_mtu(field, db_object):
|
||||
mtu = None
|
||||
datanetwork = getattr(db_object, 'datanetwork', None)
|
||||
if hasattr(datanetwork, 'mtu'):
|
||||
mtu = datanetwork.mtu
|
||||
return mtu
|
||||
|
||||
|
||||
def _get_multicast_group(field, db_object):
|
||||
multicast_group = None
|
||||
datanetwork = getattr(db_object, 'datanetwork', None)
|
||||
if hasattr(datanetwork, 'multicast_group'):
|
||||
multicast_group = datanetwork.multicast_group
|
||||
return multicast_group
|
||||
|
||||
|
||||
def _get_port_num(field, db_object):
|
||||
port_num = None
|
||||
datanetwork = getattr(db_object, 'datanetwork', None)
|
||||
if hasattr(datanetwork, 'port_num'):
|
||||
port_num = datanetwork.port_num
|
||||
return port_num
|
||||
|
||||
|
||||
def _get_ttl(field, db_object):
|
||||
ttl = None
|
||||
datanetwork = getattr(db_object, 'datanetwork', None)
|
||||
if hasattr(datanetwork, 'ttl'):
|
||||
ttl = datanetwork.ttl
|
||||
return ttl
|
||||
|
||||
|
||||
def _get_mode(field, db_object):
|
||||
mode = None
|
||||
datanetwork = getattr(db_object, 'datanetwork', None)
|
||||
if hasattr(datanetwork, 'mode'):
|
||||
mode = datanetwork.mode
|
||||
return mode
|
||||
|
||||
|
||||
class InterfaceDataNetwork(base.SysinvObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': int,
|
||||
'uuid': utils.uuid_or_none,
|
||||
'forihostid': utils.int_or_none,
|
||||
'interface_id': utils.int_or_none,
|
||||
'interface_uuid': utils.uuid_or_none,
|
||||
'ifname': utils.str_or_none,
|
||||
'datanetwork_id': utils.int_or_none,
|
||||
'datanetwork_uuid': utils.uuid_or_none,
|
||||
'datanetwork_name': utils.str_or_none,
|
||||
'datanetwork_network_type': utils.str_or_none,
|
||||
'datanetwork_description': utils.str_or_none,
|
||||
'datanetwork_mtu': utils.int_or_none,
|
||||
'datanetwork_port_num': utils.int_or_none,
|
||||
'datanetwork_multicast_group': utils.str_or_none,
|
||||
'datanetwork_ttl': utils.int_or_none,
|
||||
'datanetwork_mode': utils.str_or_none,
|
||||
}
|
||||
|
||||
_foreign_fields = {
|
||||
'forihostid': 'interface:forihostid',
|
||||
'interface_id': 'interface:id',
|
||||
'interface_uuid': 'interface:uuid',
|
||||
'ifname': 'interface:ifname',
|
||||
'datanetwork_uuid': 'datanetwork:uuid',
|
||||
'datanetwork_id': 'datanetwork:id',
|
||||
'datanetwork_name': 'datanetwork:name',
|
||||
'datanetwork_network_type': 'datanetwork:network_type',
|
||||
'datanetwork_description': 'datanetwork:description',
|
||||
'datanetwork_mtu': _get_mtu,
|
||||
'datanetwork_port_num': _get_port_num,
|
||||
'datanetwork_multicast_group': _get_multicast_group,
|
||||
'datanetwork_ttl': _get_ttl,
|
||||
'datanetwork_mode': _get_mode,
|
||||
}
|
||||
|
||||
_optional_fields = {
|
||||
'datanetwork_port_num',
|
||||
'datanetwork_multicast_group',
|
||||
'datanetwork_ttl',
|
||||
'datanetwork_mode',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
return cls.dbapi.interface_datanetwork_get(uuid)
|
@ -18,8 +18,6 @@ class EthernetInterface(interface_base.InterfaceBase):
|
||||
fields = dict({
|
||||
'imtu': utils.int_or_none,
|
||||
'imac': utils.str_or_none,
|
||||
'providernetworks': utils.str_or_none,
|
||||
'providernetworksdict': utils.dict_or_none,
|
||||
}, **interface_base.InterfaceBase.fields)
|
||||
|
||||
@base.remotable_classmethod
|
||||
|
@ -122,13 +122,14 @@ class InterfacePuppet(base.BasePuppet):
|
||||
'system_mode': self._get_system().system_mode,
|
||||
'ports': self._get_port_interface_id_index(host),
|
||||
'interfaces': self._get_interface_name_index(host),
|
||||
'interfaces_datanets': self._get_interface_name_datanets(host),
|
||||
'devices': self._get_port_pciaddr_index(host),
|
||||
'addresses': self._get_address_interface_name_index(host),
|
||||
'routes': self._get_routes_interface_name_index(host),
|
||||
'networks': self._get_network_type_index(),
|
||||
'gateways': self._get_gateway_index(),
|
||||
'floatingips': self._get_floating_ip_index(),
|
||||
'providernets': self._get_provider_networks(host),
|
||||
'datanets': self._get_datanetworks(host),
|
||||
}
|
||||
return context
|
||||
|
||||
@ -160,6 +161,41 @@ class InterfacePuppet(base.BasePuppet):
|
||||
interfaces = {}
|
||||
for iface in self.dbapi.iinterface_get_by_ihost(host.id):
|
||||
interfaces[iface.ifname] = iface
|
||||
|
||||
return interfaces
|
||||
|
||||
def _get_interface_name_datanets(self, host):
|
||||
"""
|
||||
Builds a dictionary of datanets indexed by interface name.
|
||||
"""
|
||||
interfaces = {}
|
||||
for iface in self.dbapi.iinterface_get_by_ihost(host.id):
|
||||
ifdatanets = self.dbapi.interface_datanetwork_get_by_interface(
|
||||
iface.uuid)
|
||||
|
||||
datanetworks = []
|
||||
for ifdatanet in ifdatanets:
|
||||
datanetworks.append(ifdatanet.datanetwork_uuid)
|
||||
|
||||
datanetworks_list = []
|
||||
for datanetwork in datanetworks:
|
||||
dn = self.dbapi.datanetwork_get(datanetwork)
|
||||
datanetwork_dict = \
|
||||
{'name': dn.name,
|
||||
'uuid': dn.uuid,
|
||||
'network_type': dn.network_type,
|
||||
'mtu': dn.mtu}
|
||||
if dn.network_type == constants.DATANETWORK_TYPE_VXLAN:
|
||||
datanetwork_dict.update(
|
||||
{'multicast_group': dn.multicast_group,
|
||||
'port_num': dn.port_num,
|
||||
'ttl': dn.ttl,
|
||||
'mode': dn.mode})
|
||||
datanetworks_list.append(datanetwork_dict)
|
||||
interfaces[iface.ifname] = datanetworks_list
|
||||
|
||||
LOG.debug("_get_interface_name_datanets ifdatanet=%s" % interfaces)
|
||||
|
||||
return interfaces
|
||||
|
||||
def _get_port_pciaddr_index(self, host):
|
||||
@ -277,17 +313,11 @@ class InterfacePuppet(base.BasePuppet):
|
||||
|
||||
return floating_ips
|
||||
|
||||
def _get_provider_networks(self, host):
|
||||
# TODO(alegacy): this will not work as intended for upgrades of AIO-SX
|
||||
# and -DX. The call to get_providernetworksdict will return an empty
|
||||
# dictionary because the neutron endpoint is not available yet. Since
|
||||
# we do not currently support SDN/OVS over upgrades we will need to
|
||||
# deal with this in a later commit.
|
||||
pnets = {}
|
||||
if (self.openstack and
|
||||
constants.WORKER in utils.get_personalities(host)):
|
||||
pnets = self.openstack.get_providernetworksdict(quiet=True)
|
||||
return pnets
|
||||
def _get_datanetworks(self, host):
|
||||
dnets = {}
|
||||
if constants.WORKER in utils.get_personalities(host):
|
||||
dnets = self.dbapi.datanetworks_get_all()
|
||||
return dnets
|
||||
|
||||
|
||||
def is_platform_network_type(iface):
|
||||
@ -461,14 +491,11 @@ def get_interface_mtu(context, iface):
|
||||
return iface['imtu']
|
||||
|
||||
|
||||
def get_interface_providernets(iface):
|
||||
def get_interface_datanets(context, iface):
|
||||
"""
|
||||
Return the provider networks of the supplied interface as a list.
|
||||
Return the list of data networks of the supplied interface
|
||||
"""
|
||||
providernetworks = iface['providernetworks']
|
||||
if not providernetworks:
|
||||
return []
|
||||
return [x.strip() for x in providernetworks.split(',')]
|
||||
return context['interfaces_datanets'][iface.ifname]
|
||||
|
||||
|
||||
def get_interface_port(context, iface):
|
||||
|
@ -10,6 +10,9 @@ from sysinv.common import utils
|
||||
from sysinv.puppet import interface
|
||||
from sysinv.puppet import openstack
|
||||
|
||||
from oslo_log import log
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class NeutronPuppet(openstack.OpenstackBasePuppet):
|
||||
"""Class to encapsulate puppet operations for neutron configuration"""
|
||||
@ -164,9 +167,14 @@ class NeutronPuppet(openstack.OpenstackBasePuppet):
|
||||
for iface in self.context['interfaces'].values():
|
||||
if (iface['ifclass'] in [constants.INTERFACE_CLASS_PCI_SRIOV]):
|
||||
port = interface.get_interface_port(self.context, iface)
|
||||
providernets = interface.get_interface_providernets(iface)
|
||||
for net in providernets:
|
||||
device_mappings.append("%s:%s" % (net, port['name']))
|
||||
|
||||
datanets = interface.get_interface_datanets(
|
||||
self.context, iface)
|
||||
for dnet in datanets:
|
||||
device_mappings.append(
|
||||
"%s:%s" % (dnet['name'], port['name']))
|
||||
LOG.debug("get_host_config device_mappings=%s" %
|
||||
device_mappings)
|
||||
|
||||
config = {
|
||||
'neutron::agents::ml2::sriov::physical_device_mappings':
|
||||
|
@ -17,6 +17,9 @@ from sysinv.common import utils
|
||||
from sysinv.puppet import openstack
|
||||
from sysinv.puppet import interface
|
||||
|
||||
from oslo_log import log
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
SCHEDULER_FILTERS_COMMON = [
|
||||
'RetryFilter',
|
||||
@ -580,6 +583,13 @@ class NovaPuppet(openstack.OpenstackBasePuppet):
|
||||
return "\"%s\"" % ','.join(
|
||||
"%r:%r" % (node, cpu) for node, cpu in cpu_map.items())
|
||||
|
||||
def _get_datanetwork_names(self, iface):
|
||||
dnets = interface.get_interface_datanets(
|
||||
self.context, iface)
|
||||
dnames_list = [dnet['name'] for dnet in dnets]
|
||||
dnames = ",".join(dnames_list)
|
||||
return dnames
|
||||
|
||||
def _get_pci_pt_whitelist(self, host):
|
||||
# Process all configured PCI passthrough interfaces and add them to
|
||||
# the list of devices to whitelist
|
||||
@ -587,10 +597,13 @@ class NovaPuppet(openstack.OpenstackBasePuppet):
|
||||
for iface in self.context['interfaces'].values():
|
||||
if iface['ifclass'] in [constants.INTERFACE_CLASS_PCI_PASSTHROUGH]:
|
||||
port = interface.get_interface_port(self.context, iface)
|
||||
|
||||
dnames = self._get_datanetwork_names(iface)
|
||||
device = {
|
||||
'address': port['pciaddr'],
|
||||
'physical_network': iface['providernetworks']
|
||||
'physical_network': dnames
|
||||
}
|
||||
LOG.debug("_get_pci_pt_whitelist device=%s" % device)
|
||||
devices.append(device)
|
||||
|
||||
# Process all enabled PCI devices configured for PT and SRIOV and
|
||||
@ -616,11 +629,13 @@ class NovaPuppet(openstack.OpenstackBasePuppet):
|
||||
for iface in self.context['interfaces'].values():
|
||||
if iface['ifclass'] in [constants.INTERFACE_CLASS_PCI_SRIOV]:
|
||||
port = interface.get_interface_port(self.context, iface)
|
||||
dnames = self._get_datanetwork_names(iface)
|
||||
device = {
|
||||
'address': port['pciaddr'],
|
||||
'physical_network': iface['providernetworks'],
|
||||
'physical_network': dnames,
|
||||
'sriov_numvfs': iface['sriov_numvfs']
|
||||
}
|
||||
LOG.info("_get_pci_sriov_whitelist device=%s" % device)
|
||||
devices.append(device)
|
||||
|
||||
return json.dumps(devices) if devices else None
|
||||
|
@ -4,12 +4,15 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from oslo_log import log
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import utils
|
||||
|
||||
from sysinv.puppet import base
|
||||
from sysinv.puppet import interface
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class OVSPuppet(base.BasePuppet):
|
||||
"""Class to encapsulate puppet operations for vswitch configuration"""
|
||||
@ -90,12 +93,10 @@ class OVSPuppet(base.BasePuppet):
|
||||
|
||||
index += 1
|
||||
|
||||
# currently only one provider network is supported per
|
||||
# interface, therefore obtain first entry
|
||||
providernet = interface.get_interface_providernets(iface)[0]
|
||||
datanets = interface.get_interface_datanets(self.context, iface)
|
||||
|
||||
# setup tunnel address if assigned provider network is vxlan
|
||||
if self._is_vxlan_providernet(providernet):
|
||||
if datanets and self._is_vxlan_datanet(datanets[0]):
|
||||
address = interface.get_interface_primary_address(
|
||||
self.context, iface)
|
||||
if address:
|
||||
@ -105,7 +106,7 @@ class OVSPuppet(base.BasePuppet):
|
||||
'prefixlen': address['prefix'],
|
||||
}
|
||||
|
||||
return {
|
||||
ovs_dict = {
|
||||
'platform::vswitch::ovs::devices': ovs_devices,
|
||||
'platform::vswitch::ovs::bridges': ovs_bridges,
|
||||
'platform::vswitch::ovs::ports': ovs_ports,
|
||||
@ -113,6 +114,10 @@ class OVSPuppet(base.BasePuppet):
|
||||
'platform::vswitch::ovs::flows': ovs_flows,
|
||||
}
|
||||
|
||||
LOG.debug("_get_port_config=%s" % ovs_dict)
|
||||
|
||||
return ovs_dict
|
||||
|
||||
def _get_ethernet_device(self, iface):
|
||||
if interface.is_a_mellanox_device(self.context, iface):
|
||||
# Mellanox devices are not bound to the DPDK driver
|
||||
@ -370,6 +375,9 @@ class OVSPuppet(base.BasePuppet):
|
||||
})
|
||||
return config
|
||||
|
||||
def _is_vxlan_datanet(self, datanet):
|
||||
return datanet.get('network_type') == constants.DATANETWORK_TYPE_VXLAN
|
||||
|
||||
def _get_neutron_config(self, host):
|
||||
local_ip = None
|
||||
tunnel_types = set()
|
||||
@ -379,24 +387,29 @@ class OVSPuppet(base.BasePuppet):
|
||||
# obtain the assigned bridge for interface
|
||||
brname = iface.get('_ovs_bridge')
|
||||
if brname:
|
||||
providernets = interface.get_interface_providernets(iface)
|
||||
for providernet in providernets:
|
||||
if self._is_vxlan_providernet(providernet):
|
||||
address = interface.get_interface_primary_address(
|
||||
self.context, iface)
|
||||
datanets = interface.get_interface_datanets(
|
||||
self.context, iface)
|
||||
for datanet in datanets:
|
||||
if self._is_vxlan_datanet(datanet):
|
||||
address = \
|
||||
interface.get_interface_primary_address(
|
||||
self.context, iface)
|
||||
if address:
|
||||
local_ip = address['address']
|
||||
tunnel_types.add(
|
||||
constants.NEUTRON_PROVIDERNET_VXLAN)
|
||||
constants.DATANETWORK_TYPE_VXLAN)
|
||||
else:
|
||||
bridge_mappings.append('%s:%s' %
|
||||
(providernet, brname))
|
||||
(datanet['name'], brname))
|
||||
|
||||
return {
|
||||
neutron_dict = {
|
||||
'neutron::agents::ml2::ovs::local_ip': local_ip,
|
||||
'neutron::agents::ml2::ovs::tunnel_types': list(tunnel_types),
|
||||
'neutron::agents::ml2::ovs::bridge_mappings': bridge_mappings
|
||||
}
|
||||
LOG.debug("OVS get_neutron_config neutron_dict=%s" % neutron_dict)
|
||||
|
||||
return neutron_dict
|
||||
|
||||
def _get_providernet_type(self, name):
|
||||
if name in self.context['providernets']:
|
||||
|
@ -12,6 +12,7 @@ Tests for the API /interfaces/ methods.
|
||||
"""
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
from six.moves import http_client
|
||||
|
||||
from sysinv.api.controllers.v1 import interface as api_if_v1
|
||||
@ -107,7 +108,7 @@ providernet_list = {
|
||||
"port": 8472, "ttl": 10}}],
|
||||
"vlan_transparent": False,
|
||||
"type": "vxlan",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa2",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa4",
|
||||
"name": "group0-ext2"},
|
||||
'group0-ext3': {
|
||||
"status": "ACTIVE", "description": None,
|
||||
@ -121,7 +122,7 @@ providernet_list = {
|
||||
"port": 8472, "ttl": 10}}],
|
||||
"vlan_transparent": False,
|
||||
"type": "vxlan",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa2",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa5",
|
||||
"name": "group0-ext3"},
|
||||
'group0-flat': {
|
||||
"status": "ACTIVE", "description": None,
|
||||
@ -130,13 +131,12 @@ providernet_list = {
|
||||
"id": "72f21b11-6d17-486e-a4e6-4eaf5f00f23e",
|
||||
"name": "group0-flat-r0-0",
|
||||
"tenant_id": None, "maximum": 4,
|
||||
"tenant_id": None, "maximum": 4,
|
||||
"shared": True,
|
||||
"vxlan": {"group": "239.0.2.1",
|
||||
"port": 8472, "ttl": 10}}],
|
||||
"vlan_transparent": False,
|
||||
"type": "flat",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa3",
|
||||
"id": "da9f7bb1-2114-4ffd-8a4c-9ca215d98fa6",
|
||||
"name": "group0-flat"}
|
||||
}
|
||||
|
||||
@ -261,8 +261,26 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
self.worker = host
|
||||
return
|
||||
|
||||
def _create_datanetworks(self):
|
||||
for name, v in providernet_list.items():
|
||||
dn_values = {
|
||||
'name': name,
|
||||
'uuid': v.get('id', None),
|
||||
'network_type': v['type'],
|
||||
'mtu': v['mtu']}
|
||||
if v['type'] == constants.DATANETWORK_TYPE_VXLAN:
|
||||
for r in v['ranges']:
|
||||
dn_values.update(
|
||||
{'multicast_group': r['vxlan'].get('group'),
|
||||
'port_num': r['vxlan'].get('port'),
|
||||
'ttl': r['vxlan'].get('ttl'),
|
||||
'mode': r['vxlan'].get('mode', 'dynamic'),
|
||||
})
|
||||
|
||||
dbutils.create_test_datanetwork(**dn_values)
|
||||
|
||||
def _create_ethernet(self, ifname=None, networktype=None, ifclass=None,
|
||||
providernetworks=None, host=None, expect_errors=False):
|
||||
datanetworks=None, host=None, expect_errors=False):
|
||||
if not isinstance(networktype, list):
|
||||
networktypelist = [networktype]
|
||||
else:
|
||||
@ -308,7 +326,7 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
ifclass=ifclass,
|
||||
networktype=networktype,
|
||||
networks=networks,
|
||||
providernetworks=providernetworks,
|
||||
datanetworks=datanetworks,
|
||||
forihostid=host.id, ihost_uuid=host.uuid)
|
||||
|
||||
response = self._post_and_check(interface, expect_errors)
|
||||
@ -322,7 +340,7 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
return port, interface
|
||||
|
||||
def _create_bond(self, ifname, networktype=None, ifclass=None,
|
||||
providernetworks=None, host=None, expect_errors=False):
|
||||
datanetworks=None, host=None, expect_errors=False):
|
||||
if not isinstance(networktype, list):
|
||||
networktypelist = [networktype]
|
||||
else:
|
||||
@ -357,7 +375,7 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
networks=networks,
|
||||
uses=[iface1['ifname'], iface2['ifname']],
|
||||
txhashpolicy='layer2',
|
||||
providernetworks=providernetworks,
|
||||
datanetworks=datanetworks,
|
||||
forihostid=host.id, ihost_uuid=host.uuid)
|
||||
|
||||
lacp_types = [constants.NETWORK_TYPE_MGMT,
|
||||
@ -378,12 +396,12 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
return interface
|
||||
|
||||
def _create_worker_bond(self, ifname, networktype=None, ifclass=None,
|
||||
providernetworks=None, expect_errors=False):
|
||||
return self._create_bond(ifname, networktype, ifclass, providernetworks,
|
||||
datanetworks=None, expect_errors=False):
|
||||
return self._create_bond(ifname, networktype, ifclass, datanetworks,
|
||||
self.worker, expect_errors)
|
||||
|
||||
def _create_vlan(self, ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface=None, providernetworks=None, host=None,
|
||||
lower_iface=None, datanetworks=None, host=None,
|
||||
expect_errors=False):
|
||||
if not isinstance(networktype, list):
|
||||
networktypelist = [networktype]
|
||||
@ -417,7 +435,7 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
networks=networks,
|
||||
vlan_id=vlan_id,
|
||||
uses=[lower_iface['ifname']],
|
||||
providernetworks=providernetworks,
|
||||
datanetworks=datanetworks,
|
||||
forihostid=host.id, ihost_uuid=host.uuid)
|
||||
|
||||
self._post_and_check(interface, expect_errors)
|
||||
@ -425,11 +443,11 @@ class InterfaceTestCase(base.FunctionalTest):
|
||||
return interface
|
||||
|
||||
def _create_worker_vlan(self, ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface=None, providernetworks=None,
|
||||
lower_iface=None, datanetworks=None,
|
||||
host=None, expect_errors=False):
|
||||
return self._create_vlan(ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface,
|
||||
providernetworks, self.worker, expect_errors)
|
||||
datanetworks, self.worker, expect_errors)
|
||||
|
||||
def _post_and_check_success(self, ndict):
|
||||
response = self.post_json('%s' % self._get_path(), ndict)
|
||||
@ -585,6 +603,7 @@ class InterfaceComputeEthernet(InterfaceTestCase):
|
||||
# Setup a sample configuration where the personality is set to a
|
||||
# worker and all interfaces are ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, admin=constants.ADMIN_UNLOCKED)
|
||||
self._create_datanetworks()
|
||||
self._create_ethernet('oam', constants.NETWORK_TYPE_OAM)
|
||||
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT)
|
||||
self._create_ethernet('infra', constants.NETWORK_TYPE_INFRA)
|
||||
@ -642,6 +661,7 @@ class InterfaceComputeVlanOverEthernet(InterfaceTestCase):
|
||||
# controller and all interfaces are vlan interfaces over ethernet
|
||||
# interfaces.
|
||||
self._create_host(constants.CONTROLLER)
|
||||
self._create_datanetworks()
|
||||
port, iface = self._create_ethernet(
|
||||
'pxeboot', constants.NETWORK_TYPE_PXEBOOT)
|
||||
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
|
||||
@ -663,7 +683,7 @@ class InterfaceComputeVlanOverEthernet(InterfaceTestCase):
|
||||
constants.INTERFACE_CLASS_PLATFORM, 3)
|
||||
self._create_worker_vlan('data', constants.INTERFACE_CLASS_DATA,
|
||||
constants.NETWORK_TYPE_DATA, 5,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
self._create_ethernet('sriov',
|
||||
constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
@ -686,6 +706,7 @@ class InterfaceComputeBond(InterfaceTestCase):
|
||||
# Setup a sample configuration where all platform interfaces are
|
||||
# aggregated ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, admin=constants.ADMIN_UNLOCKED)
|
||||
self._create_datanetworks()
|
||||
self._create_bond('oam', constants.NETWORK_TYPE_OAM)
|
||||
self._create_bond('mgmt', constants.NETWORK_TYPE_MGMT)
|
||||
self._create_bond('infra', constants.NETWORK_TYPE_INFRA)
|
||||
@ -698,7 +719,7 @@ class InterfaceComputeBond(InterfaceTestCase):
|
||||
self._create_worker_bond('data',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
self._create_ethernet('sriov',
|
||||
constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
@ -719,6 +740,7 @@ class InterfaceComputeVlanOverBond(InterfaceTestCase):
|
||||
|
||||
def _setup_configuration(self):
|
||||
self._create_host(constants.CONTROLLER)
|
||||
self._create_datanetworks()
|
||||
bond = self._create_bond('pxeboot', constants.NETWORK_TYPE_PXEBOOT,
|
||||
constants.INTERFACE_CLASS_PLATFORM)
|
||||
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
|
||||
@ -745,7 +767,7 @@ class InterfaceComputeVlanOverBond(InterfaceTestCase):
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
5, bond2,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
|
||||
self._create_worker_bond('bond3', constants.NETWORK_TYPE_NONE)
|
||||
|
||||
@ -769,6 +791,7 @@ class InterfaceComputeVlanOverDataEthernet(InterfaceTestCase):
|
||||
|
||||
def _setup_configuration(self):
|
||||
self._create_host(constants.CONTROLLER)
|
||||
self._create_datanetworks()
|
||||
bond = self._create_bond('pxeboot', constants.NETWORK_TYPE_PXEBOOT)
|
||||
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 1, bond)
|
||||
@ -790,7 +813,7 @@ class InterfaceComputeVlanOverDataEthernet(InterfaceTestCase):
|
||||
host=self.worker)
|
||||
self._create_worker_vlan('data2', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, 5,
|
||||
iface, providernetworks='group0-ext0')
|
||||
iface, datanetworks='group0-ext0')
|
||||
self._create_ethernet('sriov',
|
||||
constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
@ -815,6 +838,7 @@ class InterfaceCpeEthernet(InterfaceTestCase):
|
||||
# ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
self._create_ethernet('oam', constants.NETWORK_TYPE_OAM)
|
||||
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT)
|
||||
self._create_ethernet('infra', constants.NETWORK_TYPE_INFRA)
|
||||
@ -857,6 +881,7 @@ class InterfaceCpeVlanOverEthernet(InterfaceTestCase):
|
||||
# vlan interfaces over ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
port, iface = self._create_ethernet(
|
||||
'pxeboot', constants.NETWORK_TYPE_PXEBOOT)
|
||||
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
|
||||
@ -867,7 +892,7 @@ class InterfaceCpeVlanOverEthernet(InterfaceTestCase):
|
||||
constants.INTERFACE_CLASS_PLATFORM, 3)
|
||||
self._create_ethernet('data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
self._create_ethernet('sriov', constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
'group0-ext1')
|
||||
@ -891,18 +916,19 @@ class InterfaceCpeBond(InterfaceTestCase):
|
||||
self._create_host(constants.CONTROLLER,
|
||||
subfunction=constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
self._create_bond('oam', constants.NETWORK_TYPE_OAM)
|
||||
self._create_bond('mgmt', constants.NETWORK_TYPE_MGMT)
|
||||
self._create_bond('infra', constants.NETWORK_TYPE_INFRA)
|
||||
self._create_bond('data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
self._create_ethernet('sriov', constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
self._create_ethernet('pthru', constants.NETWORK_TYPE_PCI_PASSTHROUGH,
|
||||
constants.INTERFACE_CLASS_PCI_PASSTHROUGH,
|
||||
providernetworks='group0-ext1')
|
||||
datanetworks='group0-ext1')
|
||||
|
||||
def setUp(self):
|
||||
super(InterfaceCpeBond, self).setUp()
|
||||
@ -919,6 +945,7 @@ class InterfaceCpeVlanOverBond(InterfaceTestCase):
|
||||
# vlan interfaces over aggregated ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
bond = self._create_bond('pxeboot', constants.NETWORK_TYPE_PXEBOOT)
|
||||
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 1, bond)
|
||||
@ -930,7 +957,7 @@ class InterfaceCpeVlanOverBond(InterfaceTestCase):
|
||||
self._create_vlan('data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
5, bond2,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
self._create_ethernet('sriov', constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
'group0-ext1')
|
||||
@ -954,6 +981,7 @@ class InterfaceCpeVlanOverDataEthernet(InterfaceTestCase):
|
||||
# vlan interfaces over data ethernet interfaces.
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
port, iface = (
|
||||
self._create_ethernet('data',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
@ -971,15 +999,15 @@ class InterfaceCpeVlanOverDataEthernet(InterfaceTestCase):
|
||||
self._create_vlan('data2', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
5, iface,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=False)
|
||||
self._create_ethernet('sriov', constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
ifclass=constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
expect_errors=False)
|
||||
self._create_ethernet('pthru', constants.NETWORK_TYPE_PCI_PASSTHROUGH,
|
||||
ifclass=constants.INTERFACE_CLASS_PCI_PASSTHROUGH,
|
||||
providernetworks='group0-ext2',
|
||||
datanetworks='group0-ext2',
|
||||
expect_errors=False)
|
||||
|
||||
def setUp(self):
|
||||
@ -1008,6 +1036,7 @@ class TestPatch(InterfaceTestCase):
|
||||
super(TestPatch, self).setUp()
|
||||
self._create_host(constants.CONTROLLER)
|
||||
self._create_host(constants.WORKER, admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
|
||||
def test_modify_ifname(self):
|
||||
interface = dbutils.create_test_interface(forihostid='1')
|
||||
@ -1030,7 +1059,7 @@ class TestPatch(InterfaceTestCase):
|
||||
def test_interface_usesmodify_success(self):
|
||||
data_bond = self._create_bond('data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
host=self.worker)
|
||||
|
||||
port, new_ethernet = self._create_ethernet(
|
||||
@ -1055,7 +1084,7 @@ class TestPatch(InterfaceTestCase):
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
iftype=constants.INTERFACE_TYPE_ETHERNET,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
aemode='balanced',
|
||||
txhashpolicy='layer2',
|
||||
uses=['pxeboot'],
|
||||
@ -1080,7 +1109,7 @@ class TestPatch(InterfaceTestCase):
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
iftype=constants.INTERFACE_TYPE_VLAN,
|
||||
vlan_id=100,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
aemode='balanced',
|
||||
txhashpolicy='layer2',
|
||||
uses=['pxeboot'],
|
||||
@ -1111,6 +1140,7 @@ class TestPost(InterfaceTestCase):
|
||||
super(TestPost, self).setUp()
|
||||
self._create_host(constants.CONTROLLER)
|
||||
self._create_host(constants.WORKER, admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
|
||||
# Expected error: The oam network type is only supported on controller nodes
|
||||
def test_invalid_oam_on_worker(self):
|
||||
@ -1130,28 +1160,28 @@ class TestPost(InterfaceTestCase):
|
||||
def test_invalid_network_type_on_nonworker(self):
|
||||
self._create_ethernet('data0', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Interface name cannot be whitespace.
|
||||
def test_invalid_whitespace_interface_name(self):
|
||||
self._create_ethernet(' ', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Interface name must be in lower case.
|
||||
def test_invalid_uppercase_interface_name(self):
|
||||
self._create_ethernet('miXedCaSe', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Cannot use special characters in interface name.
|
||||
def test_invalid_character_interface_name(self):
|
||||
self._create_ethernet('bad-name', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Interface ___ has name length greater than 10.
|
||||
@ -1163,11 +1193,11 @@ class TestPost(InterfaceTestCase):
|
||||
def test_create_duplicate_interface_name(self):
|
||||
self._create_ethernet('data0', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
host=self.worker)
|
||||
self._create_ethernet('data0', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
host=self.worker,
|
||||
expect_errors=True)
|
||||
|
||||
@ -1303,7 +1333,7 @@ class TestPost(InterfaceTestCase):
|
||||
def test_aemode_invalid_iftype(self):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1317,7 +1347,7 @@ class TestPost(InterfaceTestCase):
|
||||
def test_aemode_no_txhash(self):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1344,7 +1374,7 @@ class TestPost(InterfaceTestCase):
|
||||
def test_aemode_invalid_txhash_none(self):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1355,7 +1385,7 @@ class TestPost(InterfaceTestCase):
|
||||
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1369,7 +1399,7 @@ class TestPost(InterfaceTestCase):
|
||||
def test_aemode_invalid_mgmt(self):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_MGMT,
|
||||
networks=['1'],
|
||||
@ -1385,7 +1415,7 @@ class TestPost(InterfaceTestCase):
|
||||
def test_aemode_invalid_data(self):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
ifname='name',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1460,12 +1490,12 @@ class TestPost(InterfaceTestCase):
|
||||
bond_iface = self._create_worker_bond('bond0',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
port, iface1 = self._create_ethernet()
|
||||
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
ifname='bond1',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1480,7 +1510,7 @@ class TestPost(InterfaceTestCase):
|
||||
self._create_worker_vlan('vlan0', constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
vlan_id=4095,
|
||||
providernetworks='group0-ext0',
|
||||
datanetworks='group0-ext0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: Interface eth0 is already used by another VLAN
|
||||
@ -1490,12 +1520,12 @@ class TestPost(InterfaceTestCase):
|
||||
'vlan0',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
vlan_id=10, providernetworks='group0-ext0')
|
||||
vlan_id=10, datanetworks='group0-ext0')
|
||||
port, iface1 = self._create_ethernet()
|
||||
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
ifname='bond0',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1510,12 +1540,12 @@ class TestPost(InterfaceTestCase):
|
||||
bond_iface = self._create_worker_bond('bond0',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
port, iface1 = self._create_ethernet()
|
||||
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
ihost_uuid=self.worker.uuid,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
ifname='bond1',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1531,13 +1561,13 @@ class TestPost(InterfaceTestCase):
|
||||
vlan_iface = self._create_worker_vlan(
|
||||
'vlan1', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, 1,
|
||||
providernetworks='group0-ext0')
|
||||
datanetworks='group0-ext0')
|
||||
self._create_worker_vlan('vlan2',
|
||||
constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
vlan_id=2,
|
||||
lower_iface=vlan_iface,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: data VLAN cannot be created over a LAG interface with
|
||||
@ -1549,7 +1579,7 @@ class TestPost(InterfaceTestCase):
|
||||
self._create_worker_vlan(
|
||||
'vlan2',
|
||||
constants.NETWORK_TYPE_DATA, constants.INTERFACE_CLASS_DATA, 2,
|
||||
lower_iface=bond_iface, providernetworks='group0-ext1',
|
||||
lower_iface=bond_iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: data VLAN cannot be created over a LAG interface with
|
||||
@ -1561,7 +1591,7 @@ class TestPost(InterfaceTestCase):
|
||||
self._create_worker_vlan(
|
||||
'vlan2', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, 2,
|
||||
lower_iface=bond_iface, providernetworks='group0-ext1',
|
||||
lower_iface=bond_iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: mgmt VLAN cannot be created over a LAG interface with
|
||||
@ -1569,20 +1599,20 @@ class TestPost(InterfaceTestCase):
|
||||
def test_create_mgmt_vlan_over_data_lag(self):
|
||||
bond_iface = self._create_worker_bond(
|
||||
'data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, providernetworks='group0-ext1')
|
||||
constants.INTERFACE_CLASS_DATA, datanetworks='group0-ext1')
|
||||
self._create_worker_vlan(
|
||||
'mgmt', constants.NETWORK_TYPE_MGMT,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 2,
|
||||
lower_iface=bond_iface, providernetworks='group0-ext1',
|
||||
lower_iface=bond_iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# Provider network(s) not supported for non-data interfaces.
|
||||
def test_create_nondata_provider_network(self):
|
||||
# Data network(s) not supported for non-data interfaces.
|
||||
def test_create_nondata_data_network(self):
|
||||
self._create_worker_bond(
|
||||
'pxeboot', constants.NETWORK_TYPE_PXEBOOT,
|
||||
constants.INTERFACE_CLASS_PLATFORM,
|
||||
providernetworks='group0-data0', expect_errors=True)
|
||||
datanetworks='group0-data0', expect_errors=True)
|
||||
|
||||
# Expected message: Name must be unique
|
||||
def test_create_invalid_ae_name(self):
|
||||
@ -1607,7 +1637,7 @@ class TestPost(InterfaceTestCase):
|
||||
self._create_ethernet('shared',
|
||||
networktype=[constants.NETWORK_TYPE_MGMT,
|
||||
constants.NETWORK_TYPE_DATA],
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
host=self.worker,
|
||||
expect_errors=True)
|
||||
|
||||
@ -1618,7 +1648,7 @@ class TestPost(InterfaceTestCase):
|
||||
self._create_ethernet('shared',
|
||||
networktype=[constants.NETWORK_TYPE_DATA,
|
||||
constants.NETWORK_TYPE_PXEBOOT],
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
host=self.worker,
|
||||
expect_errors=True)
|
||||
|
||||
@ -1628,6 +1658,7 @@ class TestCpePost(InterfaceTestCase):
|
||||
super(TestCpePost, self).setUp()
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
|
||||
# Expected message:
|
||||
# Network type list may only contain at most one type
|
||||
@ -1635,7 +1666,7 @@ class TestCpePost(InterfaceTestCase):
|
||||
self._create_bond('bond0',
|
||||
networktype=[constants.NETWORK_TYPE_DATA,
|
||||
constants.NETWORK_TYPE_PXEBOOT],
|
||||
providernetworks='group0-data0', expect_errors=True)
|
||||
datanetworks='group0-data0', expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# Network type list may only contain at most one type
|
||||
@ -1643,7 +1674,7 @@ class TestCpePost(InterfaceTestCase):
|
||||
self._create_bond('shared',
|
||||
networktype=[constants.NETWORK_TYPE_INFRA,
|
||||
constants.NETWORK_TYPE_DATA],
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: oam VLAN cannot be created over an interface with
|
||||
@ -1651,11 +1682,11 @@ class TestCpePost(InterfaceTestCase):
|
||||
def test_create_oam_vlan_over_data_lag(self):
|
||||
bond_iface = self._create_bond(
|
||||
'data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, providernetworks='group0-ext1')
|
||||
constants.INTERFACE_CLASS_DATA, datanetworks='group0-ext1')
|
||||
self._create_vlan(
|
||||
'oam', constants.NETWORK_TYPE_OAM,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 2,
|
||||
lower_iface=bond_iface, providernetworks='group0-ext1',
|
||||
lower_iface=bond_iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: infra VLAN cannot be created over an interface with
|
||||
@ -1663,11 +1694,11 @@ class TestCpePost(InterfaceTestCase):
|
||||
def test_create_infra_vlan_over_data_lag(self):
|
||||
bond_iface = self._create_bond(
|
||||
'data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, providernetworks='group0-ext1')
|
||||
constants.INTERFACE_CLASS_DATA, datanetworks='group0-ext1')
|
||||
self._create_vlan(
|
||||
'infra', constants.NETWORK_TYPE_INFRA,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 2,
|
||||
lower_iface=bond_iface, providernetworks='group0-ext1',
|
||||
lower_iface=bond_iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: mgmt VLAN cannot be created over an interface with
|
||||
@ -1675,11 +1706,11 @@ class TestCpePost(InterfaceTestCase):
|
||||
def test_create_mgmt_vlan_over_data_ethernet(self):
|
||||
port, iface = self._create_ethernet(
|
||||
'data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, providernetworks='group0-ext1')
|
||||
constants.INTERFACE_CLASS_DATA, datanetworks='group0-ext1')
|
||||
self._create_vlan(
|
||||
'mgmt', constants.NETWORK_TYPE_MGMT,
|
||||
constants.INTERFACE_CLASS_PLATFORM, 2,
|
||||
lower_iface=iface, providernetworks='group0-ext1',
|
||||
lower_iface=iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: An interface with \'oam\' network type is already
|
||||
@ -1694,10 +1725,10 @@ class TestCpePost(InterfaceTestCase):
|
||||
port, iface = self._create_ethernet('eth1', constants.NETWORK_TYPE_NONE)
|
||||
self._create_vlan('vlan1', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, vlan_id=1,
|
||||
lower_iface=iface, providernetworks='group0-ext0')
|
||||
lower_iface=iface, datanetworks='group0-ext0')
|
||||
self._create_vlan('vlan2', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, vlan_id=1,
|
||||
lower_iface=iface, providernetworks='group0-ext1',
|
||||
lower_iface=iface, datanetworks='group0-ext1',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected message: Network type list may only contain at most one type
|
||||
@ -1718,13 +1749,14 @@ class TestCpePost(InterfaceTestCase):
|
||||
|
||||
# Expected error: VLAN based provider network group0-data0 cannot be
|
||||
# assigned to a VLAN interface
|
||||
def test_create_invalid_vlan_with_vlan_provider_network(self):
|
||||
def test_create_invalid_vlan_with_vlan_data_network(self):
|
||||
port, lower = self._create_ethernet('eth1', constants.NETWORK_TYPE_NONE)
|
||||
self._create_vlan('vlan2', networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
vlan_id=2, lower_iface=lower, expect_errors=True)
|
||||
|
||||
@testtools.skip("deprecate neutron bind interface")
|
||||
@mock.patch.object(dbsql_api.Connection, 'iinterface_destroy')
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'neutron_bind_interface')
|
||||
def test_create_neutron_bind_failed(self, mock_neutron_bind_interface,
|
||||
@ -1738,7 +1770,7 @@ class TestCpePost(InterfaceTestCase):
|
||||
ndict = dbutils.post_get_test_interface(
|
||||
forihostid=self.controller.id,
|
||||
ihost_uuid=self.controller.uuid,
|
||||
providernetworks='group0-ext1',
|
||||
datanetworks='group0-ext1',
|
||||
ifname='data1',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
@ -1751,39 +1783,39 @@ class TestCpePost(InterfaceTestCase):
|
||||
mock_iinterface_destroy.assert_called_once_with(mock.ANY)
|
||||
|
||||
# Expected error: At least one provider network must be selected.
|
||||
def test_create_invalid_no_provider_network(self):
|
||||
def test_create_invalid_no_data_network(self):
|
||||
self._create_ethernet('data',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Data interface data0 is already attached to this
|
||||
# Provider Network: group0-data0.
|
||||
def test_create_invalid_provider_network_used(self):
|
||||
# Data Network: group0-data0.
|
||||
def test_create_invalid_data_network_used(self):
|
||||
self._create_ethernet('data0',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
self._create_ethernet('data1',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Provider network \'group0-dataXX\' does not exist.
|
||||
def test_create_invalid_provider_network_not_exist(self):
|
||||
# Expected error: Data network \'group0-dataXX\' does not exist.
|
||||
def test_create_invalid_data_network_not_exist(self):
|
||||
self._create_ethernet('data0',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-dataXX',
|
||||
datanetworks='group0-dataXX',
|
||||
expect_errors=True)
|
||||
|
||||
# Expected error: Specifying duplicate provider network 'group0-data1'
|
||||
# is not permitted
|
||||
def test_create_invalid_duplicate_provider_network(self):
|
||||
def test_create_invalid_duplicate_data_network(self):
|
||||
self._create_ethernet('data0',
|
||||
networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data1,group0-data1',
|
||||
datanetworks='group0-data1,group0-data1',
|
||||
expect_errors=True)
|
||||
|
||||
|
||||
@ -1792,20 +1824,22 @@ class TestCpePatch(InterfaceTestCase):
|
||||
super(TestCpePatch, self).setUp()
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
|
||||
def test_create_invalid_infra_data_ethernet(self):
|
||||
self._create_ethernet('shared',
|
||||
networktype=[constants.NETWORK_TYPE_INFRA,
|
||||
constants.NETWORK_TYPE_DATA],
|
||||
providernetworks='group0-data0',
|
||||
datanetworks='group0-data0',
|
||||
expect_errors=True)
|
||||
|
||||
@testtools.skip("deprecate neutron bind interface")
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'neutron_bind_interface')
|
||||
def test_patch_neutron_bind_failed(self, mock_neutron_bind_interface):
|
||||
port, interface = self._create_ethernet(
|
||||
'data0', networktype=constants.NETWORK_TYPE_DATA,
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
providernetworks='group0-data0')
|
||||
datanetworks='group0-data0')
|
||||
|
||||
mock_neutron_bind_interface.side_effect = [
|
||||
None,
|
||||
|
@ -770,7 +770,6 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
'created_at': 'DateTime', 'updated_at': 'DateTime',
|
||||
'ifname': 'String', 'iftype': 'String', 'imac': 'String', 'imtu': 'Integer',
|
||||
'networktype': 'String', 'aemode': 'String', 'txhashpolicy': 'String',
|
||||
'providernetworks': 'String', 'providernetworksdict': 'Text',
|
||||
'schedpolicy': 'String', 'ifcapabilities': 'Text', 'farend': 'Text',
|
||||
'forihostid': 'Integer',
|
||||
}
|
||||
@ -1016,7 +1015,6 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
ethernet_interfaces_col = {
|
||||
'id': 'Integer', 'deleted_at': 'DateTime', 'created_at': 'DateTime',
|
||||
'updated_at': 'DateTime', 'imac': 'String', 'imtu': 'Integer',
|
||||
'providernetworks': 'String', 'providernetworksdict': 'Text',
|
||||
}
|
||||
for col, coltype in ethernet_interfaces_col.items():
|
||||
self.assertTrue(isinstance(ethernet_interfaces.c[col].type,
|
||||
@ -1027,7 +1025,7 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
'id': 'Integer', 'deleted_at': 'DateTime', 'created_at': 'DateTime',
|
||||
'updated_at': 'DateTime', 'aemode': 'String', 'aedict': 'Text',
|
||||
'txhashpolicy': 'String', 'schedpolicy': 'String', 'imac': 'String',
|
||||
'imtu': 'Integer', 'providernetworks': 'String', 'providernetworksdict': 'Text',
|
||||
'imtu': 'Integer',
|
||||
}
|
||||
for col, coltype in ae_interfaces_col.items():
|
||||
self.assertTrue(isinstance(ae_interfaces.c[col].type,
|
||||
@ -1037,8 +1035,7 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
vlan_interfaces_col = {
|
||||
'id': 'Integer', 'deleted_at': 'DateTime', 'created_at': 'DateTime',
|
||||
'updated_at': 'DateTime', 'vlan_id': 'String', 'vlan_type': 'String',
|
||||
'imac': 'String', 'imtu': 'Integer', 'providernetworks': 'String',
|
||||
'providernetworksdict': 'Text',
|
||||
'imac': 'String', 'imtu': 'Integer',
|
||||
}
|
||||
for col, coltype in vlan_interfaces_col.items():
|
||||
self.assertTrue(isinstance(vlan_interfaces.c[col].type,
|
||||
@ -1708,8 +1705,6 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
'id': 'Integer',
|
||||
'imac': 'String',
|
||||
'imtu': 'Integer',
|
||||
'providernetworks': 'String',
|
||||
'providernetworksdict': 'Text',
|
||||
}
|
||||
for col, coltype in virtual_interfaces_col.items():
|
||||
self.assertTrue(isinstance(virtual_interfaces.c[col].type,
|
||||
|
@ -609,6 +609,39 @@ def get_test_ethernet_port(**kw):
|
||||
return ethernet_port
|
||||
|
||||
|
||||
def get_test_datanetwork(**kw):
|
||||
datanetwork = {
|
||||
'uuid': kw.get('uuid', '60d41820-a4a0-4c25-a6a0-2a3b98746640'),
|
||||
'name': kw.get('name'),
|
||||
'network_type': kw.get('network_type', 'vxlan'),
|
||||
'mtu': kw.get('mtu', '1500'),
|
||||
'multicast_group': kw.get('multicast_group', '239.0.2.1'),
|
||||
'port_num': kw.get('port_num', 8472),
|
||||
'ttl': kw.get('ttl', 10),
|
||||
'mode': kw.get('mode', 'dynamic'),
|
||||
}
|
||||
return datanetwork
|
||||
|
||||
|
||||
def create_test_datanetwork(**kw):
|
||||
"""Create test datanetwork entry in DB and return datanetwork DB object.
|
||||
Function to be used to create test datanetwork objects in the database.
|
||||
:param kw: kwargs with overriding values for datanework attributes.
|
||||
:returns: Test datanetwork DB object.
|
||||
"""
|
||||
datanetwork = get_test_datanetwork(**kw)
|
||||
|
||||
if kw['network_type'] != constants.DATANETWORK_TYPE_VXLAN:
|
||||
# Remove DB fields which are specific to VXLAN
|
||||
del datanetwork['multicast_group']
|
||||
del datanetwork['port_num']
|
||||
del datanetwork['ttl']
|
||||
del datanetwork['mode']
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.datanetwork_create(datanetwork)
|
||||
|
||||
|
||||
def create_test_ethernet_port(**kw):
|
||||
"""Create test ethernet port entry in DB and return ethernet port DB object.
|
||||
Function to be used to create test ethernet port objects in the database.
|
||||
@ -624,6 +657,13 @@ def create_test_ethernet_port(**kw):
|
||||
|
||||
|
||||
def post_get_test_interface(**kw):
|
||||
datanetworks = kw.get('datanetworks') or ""
|
||||
|
||||
if datanetworks:
|
||||
datanetworks_list = datanetworks.split(',')
|
||||
else:
|
||||
datanetworks_list = []
|
||||
|
||||
interface = {
|
||||
'forihostid': kw.get('forihostid'),
|
||||
'ihost_uuid': kw.get('ihost_uuid'),
|
||||
@ -636,7 +676,7 @@ def post_get_test_interface(**kw):
|
||||
'networks': kw.get('networks', []),
|
||||
'aemode': kw.get('aemode', 'balanced'),
|
||||
'txhashpolicy': kw.get('txhashpolicy', 'layer2'),
|
||||
'providernetworks': kw.get('providernetworks'),
|
||||
'datanetworks': datanetworks_list,
|
||||
'vlan_id': kw.get('vlan_id'),
|
||||
'uses': kw.get('uses', None),
|
||||
'used_by': kw.get('used_by', []),
|
||||
@ -650,6 +690,13 @@ def post_get_test_interface(**kw):
|
||||
|
||||
|
||||
def get_test_interface(**kw):
|
||||
|
||||
datanetworks = kw.get('datanetworks') or ""
|
||||
if datanetworks:
|
||||
datanetworks_list = datanetworks.split(',')
|
||||
else:
|
||||
datanetworks_list = []
|
||||
|
||||
interface = {
|
||||
'id': kw.get('id'),
|
||||
'uuid': kw.get('uuid'),
|
||||
@ -664,7 +711,7 @@ def get_test_interface(**kw):
|
||||
'networks': kw.get('networks', []),
|
||||
'aemode': kw.get('aemode'),
|
||||
'txhashpolicy': kw.get('txhashpolicy', None),
|
||||
'providernetworks': kw.get('providernetworks'),
|
||||
'datanetworks': datanetworks_list,
|
||||
'vlan_id': kw.get('vlan_id', None),
|
||||
'uses': kw.get('uses', []),
|
||||
'used_by': kw.get('used_by', []),
|
||||
@ -683,13 +730,31 @@ def create_test_interface(**kw):
|
||||
:param kw: kwargs with overriding values for interface's attributes.
|
||||
:returns: Test Interface DB object.
|
||||
"""
|
||||
|
||||
interface = get_test_interface(**kw)
|
||||
datanetworks_list = interface.get('datanetworks') or []
|
||||
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del interface['id']
|
||||
|
||||
if 'datanetworks' in interface:
|
||||
del interface['datanetworks']
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
forihostid = kw.get('forihostid')
|
||||
return dbapi.iinterface_create(forihostid, interface)
|
||||
interface_obj = dbapi.iinterface_create(forihostid, interface)
|
||||
|
||||
# assign the interface to the datanetwork
|
||||
for datanetwork in datanetworks_list:
|
||||
if not datanetwork:
|
||||
continue
|
||||
dn = dbapi.datanetwork_get(datanetwork)
|
||||
values = {'interface_id': interface_obj.id,
|
||||
'datanetwork_id': dn.id}
|
||||
dbapi.interface_datanetwork_create(values)
|
||||
|
||||
return interface_obj
|
||||
|
||||
|
||||
def create_test_interface_network(**kw):
|
||||
|
Loading…
Reference in New Issue
Block a user