Support for plugin NIC attributes
Refactor interface logic: * remove interface_properties * CRUD operations for NIC attributes * default values for NIC meta and attributes Change-Id: I26106f1b55c704a9e79d01fadc48c88a92ccc414 Implements: blueprint nics-and-nodes-attributes-via-plugin
This commit is contained in:
parent
7184b2229e
commit
703290986f
@ -46,19 +46,47 @@ INTERFACES = {
|
||||
}
|
||||
},
|
||||
"pxe": {"type": "boolean"},
|
||||
"interface_properties": {
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"offloading": {
|
||||
"type": "object"},
|
||||
"mtu": {
|
||||
"type": "object"},
|
||||
"sriov": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": base_types.NULLABLE_BOOL,
|
||||
"available": {"type": "boolean"},
|
||||
"sriov_numvfs":
|
||||
base_types.NULLABLE_POSITIVE_INTEGER,
|
||||
"sriov_totalvfs": base_types.NON_NEGATIVE_INTEGER,
|
||||
"pci_id": {"type": "string"},
|
||||
"physnet": {"type": "string"}
|
||||
"enabled": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value":
|
||||
base_types.NULLABLE_BOOL
|
||||
}
|
||||
},
|
||||
"numvfs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value":
|
||||
base_types.NULLABLE_POSITIVE_INTEGER
|
||||
}
|
||||
},
|
||||
"physnet": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dpdk": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value":
|
||||
base_types.NULLABLE_BOOL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ from nailgun.extensions.network_manager.handlers.vip import \
|
||||
ClusterVIPCollectionHandler
|
||||
from nailgun.extensions.network_manager.handlers.vip import ClusterVIPHandler
|
||||
|
||||
from nailgun.extensions.network_manager.handlers.nic import \
|
||||
NodeBondAttributesDefaultsHandler
|
||||
from nailgun.extensions.network_manager.handlers.nic import \
|
||||
NodeCollectionNICsDefaultHandler
|
||||
from nailgun.extensions.network_manager.handlers.nic import \
|
||||
@ -79,6 +81,8 @@ class NetworkManagerExtension(BaseExtension):
|
||||
{'uri': r'/clusters/(?P<cluster_id>\d+)/network_configuration/'
|
||||
r'nova_network/verify/?$',
|
||||
'handler': NovaNetworkConfigurationVerifyHandler},
|
||||
{'uri': r'/nodes/(?P<node_id>\d+)/bonds/attributes/defaults/?$',
|
||||
'handler': NodeBondAttributesDefaultsHandler},
|
||||
{'uri': r'/nodes/interfaces/?$',
|
||||
'handler': NodeCollectionNICsHandler},
|
||||
{'uri': r'/nodes/interfaces/default_assignment/?$',
|
||||
|
@ -157,3 +157,19 @@ class NodeCollectionNICsDefaultHandler(NodeNICsDefaultHandler):
|
||||
nodes = objects.NodeCollection.all()
|
||||
|
||||
return filter(lambda x: x is not None, map(self.get_default, nodes))
|
||||
|
||||
|
||||
class NodeBondAttributesDefaultsHandler(BaseHandler):
|
||||
"""Bond default attributes handler"""
|
||||
|
||||
@handle_errors
|
||||
@validate
|
||||
@serialize
|
||||
def GET(self, node_id):
|
||||
""":returns: JSONized Bond default attributes.
|
||||
|
||||
:http: * 200 (OK)
|
||||
* 404 (node not found in db)
|
||||
"""
|
||||
node = self.get_object_or_404(objects.Node, node_id)
|
||||
return objects.Node.get_bond_default_attributes(node)
|
||||
|
@ -542,48 +542,6 @@ class NetworkManager(object):
|
||||
def clear_bond_configuration(cls, node):
|
||||
objects.Bond.bulk_delete([bond.id for bond in node.bond_interfaces])
|
||||
|
||||
@classmethod
|
||||
def get_default_interface_properties(cls, interface_properties=None):
|
||||
"""Interface properties defaults.
|
||||
|
||||
Some interface's "defaults" properties come from hardware (for
|
||||
example pci_id). If interface_properties parameter is provided
|
||||
than hardware dependent properties would be get from it.
|
||||
|
||||
:param interface_properties: interface properties dict
|
||||
|
||||
"""
|
||||
data = {
|
||||
'mtu': None,
|
||||
'disable_offloading': False,
|
||||
'sriov': {
|
||||
'enabled': False,
|
||||
'sriov_numvfs': None,
|
||||
'physnet': 'physnet2',
|
||||
},
|
||||
'dpdk': {
|
||||
'enabled': False,
|
||||
}
|
||||
}
|
||||
if interface_properties is None:
|
||||
interface_properties = {}
|
||||
|
||||
sriov = interface_properties.get('sriov', {})
|
||||
dpdk = interface_properties.get('dpdk', {})
|
||||
|
||||
hw_default_properties = {
|
||||
'sriov': {
|
||||
'pci_id': sriov.get('pci_id', ''),
|
||||
'available': sriov.get('available', False),
|
||||
'sriov_totalvfs': sriov.get('sriov_totalvfs', 0),
|
||||
},
|
||||
'dpdk': {
|
||||
'available': dpdk.get('available', False),
|
||||
}
|
||||
}
|
||||
|
||||
return nailgun_utils.dict_merge(data, hw_default_properties)
|
||||
|
||||
@classmethod
|
||||
def assign_network_to_interface_by_default(cls, ng):
|
||||
"""Assign network to interface by default for all nodes in node group
|
||||
@ -639,12 +597,9 @@ class NetworkManager(object):
|
||||
), None)
|
||||
for nic in node.nic_interfaces:
|
||||
nic_dict = NodeInterfacesSerializer.serialize(nic)
|
||||
if 'interface_properties' in nic_dict:
|
||||
default_properties = cls.get_default_interface_properties(
|
||||
nic_dict['interface_properties'])
|
||||
nic_dict['interface_properties'] = nailgun_utils.dict_merge(
|
||||
nic_dict['interface_properties'],
|
||||
default_properties)
|
||||
if 'attributes' in nic_dict:
|
||||
default_attributes = objects.NIC.get_default_attributes(nic)
|
||||
nic_dict['attributes'] = default_attributes
|
||||
nic_dict['assigned_networks'] = []
|
||||
|
||||
if to_assign_ids:
|
||||
@ -812,17 +767,17 @@ class NetworkManager(object):
|
||||
in iface['assigned_networks']]
|
||||
objects.NIC.assign_networks(current_iface, nets_to_assign)
|
||||
update = {}
|
||||
if 'interface_properties' in iface:
|
||||
update['interface_properties'] = nailgun_utils.dict_merge(
|
||||
current_iface.interface_properties,
|
||||
iface['interface_properties']
|
||||
)
|
||||
if 'offloading_modes' in iface:
|
||||
update['offloading_modes'] = iface['offloading_modes']
|
||||
if 'attributes' in iface:
|
||||
update['attributes'] = nailgun_utils.dict_merge(
|
||||
current_iface.attributes,
|
||||
iface['attributes']
|
||||
)
|
||||
|
||||
objects.NIC.update(current_iface, update)
|
||||
objects.Node.clear_bonds(node_db)
|
||||
|
||||
objects.Node.clear_bonds(node_db)
|
||||
for bond in bond_interfaces:
|
||||
if bond.get('bond_properties', {}).get('mode'):
|
||||
mode = bond['bond_properties']['mode']
|
||||
@ -834,7 +789,7 @@ class NetworkManager(object):
|
||||
'mode': mode,
|
||||
'mac': bond.get('mac'),
|
||||
'bond_properties': bond.get('bond_properties', {}),
|
||||
'interface_properties': bond.get('interface_properties', {}),
|
||||
'attributes': bond.get('attributes', {})
|
||||
}
|
||||
bond_db = objects.Bond.create(data)
|
||||
|
||||
@ -869,6 +824,7 @@ class NetworkManager(object):
|
||||
except errors.InvalidInterfacesInfo as e:
|
||||
logger.debug("Cannot update interfaces: %s", e.message)
|
||||
return
|
||||
|
||||
pxe_iface_name = cls._get_pxe_iface_name(node)
|
||||
for interface in node.meta["interfaces"]:
|
||||
# set 'pxe' property for appropriate iface
|
||||
@ -1007,20 +963,21 @@ class NetworkManager(object):
|
||||
interface.driver = interface_attrs.get('driver')
|
||||
interface.bus_info = interface_attrs.get('bus_info')
|
||||
interface.pxe = interface_attrs.get('pxe', False)
|
||||
|
||||
interface_properties = nailgun_utils.dict_merge(
|
||||
cls.get_default_interface_properties(),
|
||||
interface.interface_properties or {}
|
||||
meta = nailgun_utils.dict_merge(
|
||||
objects.NIC.get_default_meta(),
|
||||
interface.meta or {}
|
||||
)
|
||||
|
||||
if interface_attrs.get('interface_properties'):
|
||||
interface_properties = nailgun_utils.dict_merge(
|
||||
interface_properties,
|
||||
interface_attrs['interface_properties']
|
||||
meta = nailgun_utils.dict_merge(
|
||||
meta,
|
||||
objects.NIC.get_default_meta(interface_attrs[
|
||||
'interface_properties'])
|
||||
)
|
||||
# update interface_properties in DB only if something was changed
|
||||
if interface.interface_properties != interface_properties:
|
||||
interface.interface_properties = interface_properties
|
||||
if interface_attrs.get('offloading_modes'):
|
||||
meta['offloading_modes'] = interface_attrs['offloading_modes']
|
||||
|
||||
if interface.meta != meta:
|
||||
interface.meta = meta
|
||||
|
||||
@classmethod
|
||||
def __delete_not_found_interfaces(cls, node, interfaces):
|
||||
@ -1407,13 +1364,16 @@ class NetworkManager(object):
|
||||
@classmethod
|
||||
def get_iface_properties(cls, iface):
|
||||
properties = {}
|
||||
if iface.interface_properties.get('mtu'):
|
||||
properties['mtu'] = iface.interface_properties['mtu']
|
||||
if iface.interface_properties.get('disable_offloading'):
|
||||
if iface.attributes.get('mtu', {}).get('value', {}).get('value'):
|
||||
properties['mtu'] = iface.attributes['mtu']['value']['value']
|
||||
if iface.attributes.get('offloading', {}).get('disable', {}).get(
|
||||
'value'):
|
||||
properties['vendor_specific'] = {
|
||||
'disable_offloading':
|
||||
iface.interface_properties['disable_offloading']
|
||||
iface.attributes['offloading']['disable']['value']
|
||||
}
|
||||
|
||||
# TODO(apopovych): rewrite to get offloading data from attributes
|
||||
if iface.offloading_modes:
|
||||
modified_offloading_modes = \
|
||||
cls._get_modified_offloading_modes(iface.offloading_modes)
|
||||
|
@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from nailgun.db import db
|
||||
from nailgun.db.sqlalchemy import models
|
||||
@ -22,6 +23,8 @@ from nailgun.extensions.network_manager.objects.interface import NIC
|
||||
from nailgun.objects import NailgunCollection
|
||||
from nailgun.objects import NailgunObject
|
||||
from nailgun.objects.serializers.base import BasicSerializer
|
||||
from nailgun.plugins.manager import PluginManager
|
||||
from nailgun import utils
|
||||
|
||||
|
||||
class Bond(DPDKMixin, NailgunObject):
|
||||
@ -29,6 +32,13 @@ class Bond(DPDKMixin, NailgunObject):
|
||||
model = models.NodeBondInterface
|
||||
serializer = BasicSerializer
|
||||
|
||||
@classmethod
|
||||
def create(cls, data):
|
||||
bond = super(Bond, cls).create(data)
|
||||
bond = PluginManager.add_plugin_attributes_for_bond(bond)
|
||||
|
||||
return bond
|
||||
|
||||
@classmethod
|
||||
def assign_networks(cls, instance, networks):
|
||||
"""Assigns networks to specified Bond interface.
|
||||
@ -73,6 +83,36 @@ class Bond(DPDKMixin, NailgunObject):
|
||||
aliased=True).filter(models.NetworkGroup.id.in_(networks))
|
||||
return bond_interfaces_query.all()
|
||||
|
||||
@classmethod
|
||||
def get_attributes(cls, instance):
|
||||
"""Get native and plugin attributes for bond.
|
||||
|
||||
:param instance: NodeBondInterface instance
|
||||
:type instance: NodeBondInterface model
|
||||
:returns: dict -- Object of bond attributes
|
||||
"""
|
||||
attributes = copy.deepcopy(instance.attributes)
|
||||
attributes = utils.dict_merge(
|
||||
attributes,
|
||||
PluginManager.get_bond_attributes(instance))
|
||||
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_bond_default_attributes(cls, cluster):
|
||||
"""Get native and plugin default attributes for bond.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: Cluster model
|
||||
:returns: dict -- Object of bond default attributes
|
||||
"""
|
||||
default_attributes = cluster.release.bond_attributes
|
||||
default_attributes = utils.dict_merge(
|
||||
default_attributes,
|
||||
PluginManager.get_bond_default_attributes(cluster))
|
||||
|
||||
return default_attributes
|
||||
|
||||
|
||||
class BondCollection(NailgunCollection):
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import six
|
||||
from sqlalchemy.sql import not_
|
||||
|
||||
@ -23,6 +25,8 @@ from nailgun.objects import Cluster
|
||||
from nailgun.objects import NailgunCollection
|
||||
from nailgun.objects import NailgunObject
|
||||
from nailgun.objects.serializers.base import BasicSerializer
|
||||
from nailgun.plugins.manager import PluginManager
|
||||
from nailgun import utils
|
||||
|
||||
|
||||
class DPDKMixin(object):
|
||||
@ -33,21 +37,24 @@ class DPDKMixin(object):
|
||||
|
||||
@classmethod
|
||||
def dpdk_enabled(cls, instance):
|
||||
dpdk = instance.interface_properties.get('dpdk')
|
||||
return bool(dpdk and dpdk.get('enabled'))
|
||||
return bool(instance.attributes.get('dpdk', {}).get(
|
||||
'enabled', {}).get('value'))
|
||||
|
||||
@classmethod
|
||||
def refresh_interface_dpdk_properties(cls, interface, dpdk_drivers):
|
||||
interface_properties = interface.interface_properties
|
||||
dpdk_properties = interface_properties.get('dpdk', {}).copy()
|
||||
dpdk_properties['available'] = cls.dpdk_available(interface,
|
||||
dpdk_drivers)
|
||||
if (not dpdk_properties['available'] and
|
||||
dpdk_properties.get('enabled')):
|
||||
dpdk_properties['enabled'] = False
|
||||
# update interface_properties in DB only if something was changed
|
||||
if interface_properties.get('dpdk', {}) != dpdk_properties:
|
||||
interface_properties['dpdk'] = dpdk_properties
|
||||
attributes = interface.attributes
|
||||
meta = interface.meta
|
||||
dpdk_attributes = copy.deepcopy(attributes.get('dpdk', {}))
|
||||
dpdk_available = cls.dpdk_available(interface, dpdk_drivers)
|
||||
|
||||
if (not dpdk_available and dpdk_attributes.get(
|
||||
'enabled', {}).get('value')):
|
||||
dpdk_attributes['enabled']['value'] = False
|
||||
# update attributes in DB only if something was changed
|
||||
if attributes.get('dpdk', {}) != dpdk_attributes:
|
||||
attributes['dpdk'] = dpdk_attributes
|
||||
if meta.get('dpdk', {}) != {'available': dpdk_available}:
|
||||
meta['dpdk'] = {'available': dpdk_available}
|
||||
|
||||
|
||||
class NIC(DPDKMixin, NailgunObject):
|
||||
@ -70,7 +77,7 @@ class NIC(DPDKMixin, NailgunObject):
|
||||
|
||||
@classmethod
|
||||
def get_dpdk_driver(cls, instance, dpdk_drivers):
|
||||
pci_id = instance.interface_properties.get('pci_id', '').lower()
|
||||
pci_id = instance.meta.get('pci_id', '').lower()
|
||||
for driver, device_ids in six.iteritems(dpdk_drivers):
|
||||
if pci_id in device_ids:
|
||||
return driver
|
||||
@ -97,8 +104,8 @@ class NIC(DPDKMixin, NailgunObject):
|
||||
|
||||
@classmethod
|
||||
def is_sriov_enabled(cls, instance):
|
||||
sriov = instance.interface_properties.get('sriov')
|
||||
return sriov and sriov['enabled']
|
||||
enabled = instance.attributes.get('sriov', {}).get('enabled')
|
||||
return enabled and enabled['value']
|
||||
|
||||
@classmethod
|
||||
def update_offloading_modes(cls, instance, new_modes, keep_states=False):
|
||||
@ -220,6 +227,142 @@ class NIC(DPDKMixin, NailgunObject):
|
||||
|
||||
return nic
|
||||
|
||||
@classmethod
|
||||
def create_attributes(cls, instance):
|
||||
"""Create attributes for interface with default values.
|
||||
|
||||
:param instance: NodeNICInterface instance
|
||||
:type instance: NodeNICInterface model
|
||||
:returns: None
|
||||
"""
|
||||
instance.attributes = cls._get_default_attributes(instance)
|
||||
PluginManager.add_plugin_attributes_for_interface(instance)
|
||||
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def get_attributes(cls, instance):
|
||||
"""Get all attributes for interface.
|
||||
|
||||
:param instance: NodeNICInterface instance
|
||||
:type instance: NodeNICInterface model
|
||||
:returns: dict -- Object of interface attributes
|
||||
"""
|
||||
attributes = copy.deepcopy(instance.attributes)
|
||||
attributes = utils.dict_merge(
|
||||
attributes,
|
||||
PluginManager.get_nic_attributes(instance))
|
||||
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_default_attributes(cls, instance):
|
||||
"""Get default attributes for interface.
|
||||
|
||||
:param instance: NodeNICInterface instance
|
||||
:type instance: NodeNICInterface model
|
||||
:returns: dict -- Dict object of NIC attributes
|
||||
"""
|
||||
attributes = cls._get_default_attributes(instance)
|
||||
attributes = utils.dict_merge(
|
||||
attributes,
|
||||
PluginManager.get_nic_attributes(instance))
|
||||
attributes = utils.dict_merge(
|
||||
attributes,
|
||||
PluginManager.get_nic_default_attributes(
|
||||
instance.node.cluster))
|
||||
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def update(cls, instance, data):
|
||||
"""Update data of native and plugin attributes for interface.
|
||||
|
||||
:param instance: NodeNICInterface instance
|
||||
:type instance: NodeNICInterface model
|
||||
:param data: Data to update
|
||||
:type data: dict
|
||||
:returns: None
|
||||
"""
|
||||
attributes = data.pop('attributes', None)
|
||||
if attributes:
|
||||
PluginManager.update_nic_attributes(attributes)
|
||||
instance.attributes = utils.dict_merge(
|
||||
instance.attributes, attributes)
|
||||
|
||||
return super(NIC, cls).update(instance, data)
|
||||
|
||||
@classmethod
|
||||
def offloading_modes_as_flat_dict(cls, modes):
|
||||
"""Represents multilevel structure of offloading modes as flat dict
|
||||
|
||||
This is done to ease merging
|
||||
:param modes: list of offloading modes
|
||||
:return: flat dictionary {mode['name']: mode['state']}
|
||||
"""
|
||||
result = dict()
|
||||
if modes is None:
|
||||
return result
|
||||
for mode in modes:
|
||||
result[mode["name"]] = mode["state"]
|
||||
if mode["sub"]:
|
||||
result.update(cls.offloading_modes_as_flat_dict(mode["sub"]))
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _get_default_attributes(cls, instance):
|
||||
attributes = copy.deepcopy(
|
||||
instance.node.cluster.release.nic_attributes)
|
||||
|
||||
mac = instance.mac
|
||||
meta = instance.node.meta
|
||||
name = instance.name
|
||||
offloading_modes = []
|
||||
properties = {}
|
||||
|
||||
for interface in meta.get('interfaces', []):
|
||||
if interface['name'] == name and interface['mac'] == mac:
|
||||
properties = interface.get('interface_properties', {})
|
||||
offloading_modes = interface.get('offloading_modes', [])
|
||||
|
||||
attributes['mtu']['value']['value'] = properties.get('mtu')
|
||||
attributes['sriov']['enabled']['value'] = \
|
||||
properties.get('sriov', {}).get('enabled', False)
|
||||
attributes['sriov']['numvfs']['value'] = \
|
||||
properties.get('sriov', {}).get('sriov_numvfs')
|
||||
attributes['sriov']['physnet']['value'] = \
|
||||
properties.get('sriov', {}).get('physnet', 'physnet2')
|
||||
attributes['dpdk']['enabled']['value'] = \
|
||||
properties.get('dpdk', {}).get('enabled', False)
|
||||
|
||||
offloading_modes_values = cls.offloading_modes_as_flat_dict(
|
||||
offloading_modes)
|
||||
attributes['offloading']['disable']['value'] = \
|
||||
properties.get('disable_offloading', False)
|
||||
attributes['offloading']['modes']['value'] = offloading_modes_values
|
||||
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_default_meta(cls, interface_properties={}):
|
||||
return {
|
||||
'offloading_modes': [],
|
||||
'sriov': {
|
||||
'available': interface_properties.get(
|
||||
'sriov', {}).get('available', False),
|
||||
'pci_id': interface_properties.get(
|
||||
'sriov', {}).get('pci_id', ''),
|
||||
'totalvfs': interface_properties.get(
|
||||
'sriov', {}).get('sriov_totalvfs', 0)
|
||||
},
|
||||
'dpdk': {
|
||||
'available': interface_properties.get(
|
||||
'dpdk', {}).get('available', False)
|
||||
},
|
||||
'pci_id': interface_properties.get('pci_id', ''),
|
||||
'numa_node': interface_properties.get('numa_node')
|
||||
}
|
||||
|
||||
|
||||
class NICCollection(NailgunCollection):
|
||||
|
||||
|
@ -29,13 +29,13 @@ class NodeInterfacesSerializer(BasicSerializer):
|
||||
'mac',
|
||||
'name',
|
||||
'type',
|
||||
'interface_properties',
|
||||
'state',
|
||||
'current_speed',
|
||||
'max_speed',
|
||||
'assigned_networks',
|
||||
'driver',
|
||||
'bus_info',
|
||||
'meta',
|
||||
'offloading_modes',
|
||||
'pxe'
|
||||
)
|
||||
@ -85,31 +85,31 @@ class NodeInterfacesSerializer(BasicSerializer):
|
||||
|
||||
@classmethod
|
||||
def serialize_nic_interface(cls, instance, fields=None):
|
||||
from nailgun.extensions.network_manager.objects.interface import NIC
|
||||
if not fields:
|
||||
if StrictVersion(cls._get_env_version(instance)) < \
|
||||
StrictVersion('6.1'):
|
||||
fields = cls.nic_fields_60
|
||||
else:
|
||||
fields = cls.nic_fields
|
||||
return BasicSerializer.serialize(
|
||||
instance,
|
||||
fields=fields
|
||||
)
|
||||
data_dict = BasicSerializer.serialize(instance, fields=fields)
|
||||
data_dict['attributes'] = NIC.get_attributes(instance)
|
||||
|
||||
return data_dict
|
||||
|
||||
@classmethod
|
||||
def serialize_bond_interface(cls, instance, fields=None):
|
||||
from nailgun.extensions.network_manager.objects.bond import Bond
|
||||
if not fields:
|
||||
if StrictVersion(cls._get_env_version(instance)) < \
|
||||
StrictVersion('6.1'):
|
||||
fields = cls.bond_fields_60
|
||||
else:
|
||||
fields = cls.bond_fields
|
||||
data_dict = BasicSerializer.serialize(
|
||||
instance,
|
||||
fields=fields
|
||||
)
|
||||
data_dict['slaves'] = [{'name': slave.name}
|
||||
for slave in instance.slaves]
|
||||
data_dict = BasicSerializer.serialize(instance, fields=fields)
|
||||
data_dict['slaves'] = [{'name': s.name} for s in instance.slaves]
|
||||
data_dict['attributes'] = Bond.get_attributes(instance)
|
||||
|
||||
return data_dict
|
||||
|
||||
@classmethod
|
||||
|
@ -192,10 +192,11 @@ class NetworkDeploymentSerializer(object):
|
||||
'name': iface.name,
|
||||
'interfaces': sorted(x['name'] for x in iface.slaves),
|
||||
}
|
||||
if iface.interface_properties.get('mtu'):
|
||||
bond['mtu'] = iface.interface_properties['mtu']
|
||||
if iface.attributes.get('mtu', {}).get('value', {}).get('value'):
|
||||
bond['mtu'] = iface.attributes['mtu']['value']['value']
|
||||
if parameters:
|
||||
bond.update(parameters)
|
||||
|
||||
return bond
|
||||
|
||||
@classmethod
|
||||
@ -214,4 +215,5 @@ class NetworkDeploymentSerializer(object):
|
||||
patch['provider'] = provider
|
||||
if mtu:
|
||||
patch['mtu'] = mtu
|
||||
|
||||
return patch
|
||||
|
@ -1536,7 +1536,7 @@ class SriovSerializerMixin90(object):
|
||||
for n in cluster.nodes:
|
||||
for nic in n.nic_interfaces:
|
||||
if objects.NIC.is_sriov_enabled(nic):
|
||||
pci_ids.add(nic.interface_properties['sriov']['pci_id'])
|
||||
pci_ids.add(nic.meta['sriov']['pci_id'])
|
||||
if pci_ids:
|
||||
attrs['supported_pci_vendor_devs'] = list(pci_ids)
|
||||
return attrs
|
||||
@ -1561,10 +1561,10 @@ class NeutronNetworkDeploymentSerializer90(
|
||||
# add ports with SR-IOV settings for SR-IOV enabled NICs
|
||||
if (not iface.bond and iface.name not in nets_by_ifaces and
|
||||
objects.NIC.is_sriov_enabled(iface)):
|
||||
sriov = iface.interface_properties['sriov']
|
||||
sriov = iface.attributes['sriov']
|
||||
config = {
|
||||
'sriov_numvfs': sriov['sriov_numvfs'],
|
||||
'physnet': sriov['physnet']
|
||||
'sriov_numvfs': sriov['numvfs']['value'],
|
||||
'physnet': sriov['physnet']['value']
|
||||
}
|
||||
transformations.append(cls.add_port(
|
||||
iface.name, bridge=None, provider='sriov',
|
||||
|
@ -1060,24 +1060,6 @@ class TestNetworkManager(BaseIntegrationTest):
|
||||
for iface in node.interfaces:
|
||||
self.assertEqual([], iface.assigned_networks_list)
|
||||
|
||||
def test_get_default_interface_properties(self):
|
||||
defaults = self.env.network_manager.get_default_interface_properties()
|
||||
hw_data = {
|
||||
'sriov': {
|
||||
'pci_id': '123:456',
|
||||
'available': True,
|
||||
'sriov_totalvfs': 100500,
|
||||
},
|
||||
'dpdk': {
|
||||
'available': True,
|
||||
}
|
||||
}
|
||||
expected = nailgun.utils.dict_merge(defaults, hw_data)
|
||||
test = self.env.network_manager.get_default_interface_properties(
|
||||
hw_data
|
||||
)
|
||||
self.assertEqual(test, expected)
|
||||
|
||||
|
||||
class TestNovaNetworkManager(BaseIntegrationTest):
|
||||
|
||||
|
@ -467,15 +467,16 @@ class TestNodeHandlers(BaseIntegrationTest):
|
||||
|
||||
class TestNodeNICsSerialization(BaseIntegrationTest):
|
||||
|
||||
versions = [('2014.1', False),
|
||||
('2014.1.1-5.0.1', False),
|
||||
('2014.1.1-5.1', False),
|
||||
('2014.1.1-5.1.1', False),
|
||||
('2014.2-6.0', False),
|
||||
('2014.2-6.0.1', False),
|
||||
('2014.2-6.1', True)]
|
||||
versions = [('2014.1', True),
|
||||
('2014.1.1-5.0.1', True),
|
||||
('2014.1.1-5.1', True),
|
||||
('2014.1.1-5.1.1', True),
|
||||
('2014.2-6.0', True),
|
||||
('2014.2-6.0.1', True),
|
||||
('2014.2-6.1', True),
|
||||
('newton-10.0', True)]
|
||||
|
||||
def check_nics_interface_properties(self, handler):
|
||||
def check_nics_interface_attributes(self, handler):
|
||||
for ver, present in self.versions:
|
||||
cluster = self.env.create(
|
||||
release_kwargs={'version': ver},
|
||||
@ -492,7 +493,7 @@ class TestNodeNICsSerialization(BaseIntegrationTest):
|
||||
headers=self.default_headers
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual('interface_properties' in resp.json_body[0],
|
||||
self.assertEqual('attributes' in resp.json_body[0],
|
||||
present)
|
||||
objects.Node.delete(node)
|
||||
objects.Cluster.delete(cluster)
|
||||
@ -500,10 +501,10 @@ class TestNodeNICsSerialization(BaseIntegrationTest):
|
||||
self.env.clusters = []
|
||||
|
||||
def test_interface_properties_in_default_nic_information(self):
|
||||
self.check_nics_interface_properties('NodeNICsDefaultHandler')
|
||||
self.check_nics_interface_attributes('NodeNICsDefaultHandler')
|
||||
|
||||
def test_interface_properties_in_current_nic_information(self):
|
||||
self.check_nics_interface_properties('NodeNICsHandler')
|
||||
self.check_nics_interface_attributes('NodeNICsHandler')
|
||||
|
||||
|
||||
class TestNodeNICAdminAssigning(BaseIntegrationTest):
|
||||
|
@ -15,6 +15,7 @@
|
||||
# under the License.
|
||||
|
||||
from mock import patch
|
||||
import uuid
|
||||
|
||||
from copy import deepcopy
|
||||
from oslo_serialization import jsonutils
|
||||
@ -241,9 +242,13 @@ class TestHandlers(BaseIntegrationTest):
|
||||
|
||||
def test_NIC_updates_by_agent(self):
|
||||
meta = self.env.default_metadata()
|
||||
self.env.set_interfaces_in_meta(meta, [
|
||||
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
||||
'state': 'up'}])
|
||||
self.env.set_interfaces_in_meta(meta, [{
|
||||
'name': 'eth0',
|
||||
'mac': '00:00:00:00:00:00',
|
||||
'current_speed': 1,
|
||||
'state': 'up'}
|
||||
])
|
||||
|
||||
node = self.env.create_node(api=True, meta=meta)
|
||||
new_meta = self.env.default_metadata()
|
||||
self.env.set_interfaces_in_meta(new_meta, [
|
||||
@ -268,7 +273,8 @@ class TestHandlers(BaseIntegrationTest):
|
||||
jsonutils.dumps(node_data),
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
# add node into cluster
|
||||
self.env.create_cluster(nodes=[node['id']])
|
||||
resp = self.app.get(
|
||||
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
||||
headers=self.default_headers)
|
||||
@ -281,23 +287,56 @@ class TestHandlers(BaseIntegrationTest):
|
||||
self.assertEqual(resp_nic['max_speed'], nic['max_speed'])
|
||||
self.assertEqual(resp_nic['state'], nic['state'])
|
||||
self.assertEqual(
|
||||
resp_nic['interface_properties']['sriov'],
|
||||
resp_nic['attributes']['sriov'],
|
||||
{
|
||||
'enabled': False,
|
||||
'sriov_numvfs': None,
|
||||
'sriov_totalvfs': 8,
|
||||
'available': True,
|
||||
'pci_id': '1234:5678',
|
||||
'physnet': 'physnet2'
|
||||
'metadata': {
|
||||
'label': 'SR-IOV',
|
||||
'weight': 30
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'SR-IOV enabled',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
},
|
||||
'numvfs': {
|
||||
'label': 'Virtual functions',
|
||||
'weight': 20,
|
||||
'type': 'number',
|
||||
'min': 0,
|
||||
'value': None
|
||||
},
|
||||
'physnet': {
|
||||
'label': 'Physical network',
|
||||
'weight': 30,
|
||||
'type': 'text',
|
||||
'value': 'physnet2'
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
resp_nic['interface_properties']['dpdk'],
|
||||
resp_nic['meta']['sriov'],
|
||||
{
|
||||
'enabled': False,
|
||||
'available': False
|
||||
'available': True,
|
||||
'pci_id': '1234:5678',
|
||||
'totalvfs': 8
|
||||
})
|
||||
for conn in ('assigned_networks', ):
|
||||
self.assertEqual(resp_nic[conn], [])
|
||||
|
||||
self.assertEqual(
|
||||
resp_nic['attributes']['dpdk'],
|
||||
{
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'DPDK enabled',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
resp_nic['meta']['dpdk'], {'available': False})
|
||||
|
||||
def create_cluster_and_node_with_dpdk_support(self, segment_type,
|
||||
drivers_mock):
|
||||
@ -339,17 +378,27 @@ class TestHandlers(BaseIntegrationTest):
|
||||
self.assertEqual(len(resp.json_body), 1)
|
||||
resp_nic = resp.json_body[0]
|
||||
self.assertEqual(
|
||||
resp_nic['interface_properties']['dpdk'],
|
||||
resp_nic['attributes']['dpdk'],
|
||||
{
|
||||
'enabled': False,
|
||||
'available': dpdk_available
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'DPDK enabled',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
resp_nic['meta']['dpdk'], {'available': dpdk_available})
|
||||
return resp.json_body
|
||||
|
||||
def check_put_request_passes_without_dpdk_section(self, node, nics):
|
||||
# remove 'dpdk' section from all interfaces
|
||||
for nic in nics:
|
||||
nic['interface_properties'].pop('dpdk', None)
|
||||
nic['attributes'].pop('dpdk', None)
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler", kwargs={"node_id": node['id']}),
|
||||
jsonutils.dumps(nics),
|
||||
@ -553,7 +602,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||
|
||||
@patch.dict('nailgun.api.v1.handlers.version.settings.VERSION', {
|
||||
'release': '6.1'})
|
||||
def test_interface_properties_after_update_by_agent(self):
|
||||
def test_interface_attributes_after_update_by_agent(self):
|
||||
meta = self.env.default_metadata()
|
||||
self.env.set_interfaces_in_meta(meta, [
|
||||
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
||||
@ -571,12 +620,20 @@ class TestHandlers(BaseIntegrationTest):
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
nic = resp.json_body[0]
|
||||
self.assertEqual(
|
||||
nic['interface_properties'],
|
||||
self.env.network_manager.get_default_interface_properties()
|
||||
)
|
||||
|
||||
# change mtu
|
||||
nic['interface_properties']['mtu'] = 1500
|
||||
nic['attributes']['mtu'] = {
|
||||
'metadata': {
|
||||
'label': 'MTU',
|
||||
'weight': 20
|
||||
},
|
||||
'value': {
|
||||
'label': 'MTU',
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': 1500
|
||||
}
|
||||
}
|
||||
nodes_list = [{'id': node['id'], 'interfaces': [nic]}]
|
||||
resp_put = self.app.put(
|
||||
reverse('NodeCollectionNICsHandler'),
|
||||
@ -596,7 +653,18 @@ class TestHandlers(BaseIntegrationTest):
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
resp_nic = resp.json_body[0]
|
||||
self.assertEqual(resp_nic['interface_properties']['mtu'], 1500)
|
||||
self.assertEqual(resp_nic['attributes']['mtu'], {
|
||||
'metadata': {
|
||||
'label': 'MTU',
|
||||
'weight': 20
|
||||
},
|
||||
'value': {
|
||||
'label': 'MTU',
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': 1500
|
||||
}
|
||||
})
|
||||
|
||||
def test_nic_adds_by_agent(self):
|
||||
meta = self.env.default_metadata()
|
||||
@ -894,27 +962,22 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
return resp.json_body
|
||||
|
||||
def test_get_update_sriov_properties(self):
|
||||
sriov_default = \
|
||||
self.env.network_manager.get_default_interface_properties()[
|
||||
'sriov']
|
||||
# Use NIC #2 as SR-IOV can be enabled only on NICs that have no
|
||||
# assigned networks. First two NICs have assigned networks.
|
||||
self.assertEqual(self.nics[2]['interface_properties']['sriov'],
|
||||
sriov_default)
|
||||
|
||||
# change NIC properties in DB as SR-IOV parameters can be set up only
|
||||
# for NICs that have hardware SR-IOV support
|
||||
nic = self.env.nodes[0].nic_interfaces[2]
|
||||
nic.interface_properties['sriov']['available'] = True
|
||||
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
||||
nic.interface_properties.changed()
|
||||
nic.meta['sriov']['available'] = True
|
||||
nic.meta['sriov']['totalvfs'] = 8
|
||||
nic.meta.changed()
|
||||
|
||||
nics = self.get_node_interfaces()
|
||||
nics[2]['interface_properties']['sriov'].update({
|
||||
'enabled': True,
|
||||
'sriov_numvfs': 8,
|
||||
'physnet': 'new_physnet'
|
||||
})
|
||||
sriov = nics[2]['attributes']['sriov']
|
||||
sriov['enabled']['value'] = True
|
||||
sriov['numvfs']['value'] = 8
|
||||
sriov['physnet']['value'] = 'new_physnet'
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
kwargs={"node_id": self.env.nodes[0].id}),
|
||||
@ -922,13 +985,13 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
sriov = resp.json_body[2]['interface_properties']['sriov']
|
||||
self.assertEqual(sriov['enabled'], True)
|
||||
self.assertEqual(sriov['sriov_numvfs'], 8)
|
||||
self.assertEqual(sriov['physnet'], 'new_physnet')
|
||||
sriov = resp.json_body[2]['attributes']['sriov']
|
||||
self.assertEqual(sriov['enabled']['value'], True)
|
||||
self.assertEqual(sriov['numvfs']['value'], 8)
|
||||
self.assertEqual(sriov['physnet']['value'], 'new_physnet')
|
||||
|
||||
def test_update_readonly_sriov_properties_failed(self):
|
||||
self.nics[0]['interface_properties']['sriov']['available'] = True
|
||||
self.nics[0]['meta']['sriov']['available'] = True
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -944,7 +1007,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
self.env.nodes[0].id, self.nics[0]['name']))
|
||||
|
||||
def test_enable_sriov_failed_when_not_available(self):
|
||||
self.nics[0]['interface_properties']['sriov']['enabled'] = True
|
||||
self.nics[0]['attributes']['sriov']['enabled']['value'] = True
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -960,7 +1023,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
self.env.nodes[0].id, self.nics[0]['name']))
|
||||
|
||||
def test_set_sriov_numvfs_failed(self):
|
||||
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 8
|
||||
self.nics[0]['attributes']['sriov']['numvfs']['value'] = 8
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -976,7 +1039,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
self.env.nodes[0].id, self.nics[0]['name']))
|
||||
|
||||
def test_set_sriov_numvfs_failed_negative_value(self):
|
||||
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = -40
|
||||
self.nics[0]['attributes']['sriov']['numvfs']['value'] = -40
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -991,7 +1054,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
)
|
||||
|
||||
def test_set_sriov_numvfs_failed_float_value(self):
|
||||
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 2.5
|
||||
self.nics[0]['attributes']['sriov']['numvfs']['value'] = 2.5
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -1006,7 +1069,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
)
|
||||
|
||||
def test_set_sriov_numvfs_zero_value(self):
|
||||
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 0
|
||||
self.nics[0]['attributes']['sriov']['numvfs']['value'] = 0
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -1024,12 +1087,12 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
# change NIC properties in DB as SR-IOV parameters can be set up only
|
||||
# for NICs that have hardware SR-IOV support
|
||||
nic = objects.NIC.get_by_uid(self.env.nodes[0].nic_interfaces[0].id)
|
||||
nic.interface_properties['sriov']['available'] = True
|
||||
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
||||
nic.interface_properties.changed()
|
||||
nic.meta['sriov']['available'] = True
|
||||
nic.meta['sriov']['totalvfs'] = 8
|
||||
nic.meta.changed()
|
||||
|
||||
nics = self.get_node_interfaces()
|
||||
nics[0]['interface_properties']['sriov']['enabled'] = True
|
||||
nics[0]['attributes']['sriov']['enabled']['value'] = True
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -1067,7 +1130,7 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
nics = resp.json_body
|
||||
nics[0]['interface_properties']['sriov']['enabled'] = True
|
||||
nics[0]['attributes']['sriov']['enabled']['value'] = True
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -1083,13 +1146,13 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
|
||||
def test_enable_sriov_failed_when_nic_has_networks_assigned(self):
|
||||
nic = self.env.nodes[0].nic_interfaces[0]
|
||||
nic.interface_properties['sriov']['available'] = True
|
||||
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
||||
nic.interface_properties.changed()
|
||||
nic.meta['sriov']['available'] = True
|
||||
nic.meta['sriov']['totalvfs'] = 8
|
||||
nic.meta.changed()
|
||||
|
||||
nics = self.get_node_interfaces()
|
||||
nics[0]['interface_properties']['sriov']['enabled'] = True
|
||||
nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 8
|
||||
nics[0]['attributes']['sriov']['enabled']['value'] = True
|
||||
nics[0]['attributes']['sriov']['numvfs']['value'] = 8
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
@ -1103,3 +1166,181 @@ class TestSriovHandlers(BaseIntegrationTest):
|
||||
"Node '{0}' interface '{1}': SR-IOV cannot be enabled when "
|
||||
"networks are assigned to the interface".format(
|
||||
self.env.nodes[0].id, nics[0]['name']))
|
||||
|
||||
|
||||
class TestNICAttributesHandlers(BaseIntegrationTest):
|
||||
|
||||
EXPECTED_ATTRIBUTES = {
|
||||
'offloading': {
|
||||
'disable': {
|
||||
'value': False,
|
||||
'label': 'Disable offloading',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'Offloading',
|
||||
'weight': 10
|
||||
},
|
||||
'modes': {
|
||||
'description': 'Offloading modes',
|
||||
'value': {},
|
||||
'label': 'Offloading modes',
|
||||
'type': 'offloading_modes',
|
||||
'weight': 20
|
||||
}
|
||||
},
|
||||
'mtu': {
|
||||
'value': {
|
||||
'value': None,
|
||||
'label': 'MTU',
|
||||
'type': 'number',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'MTU',
|
||||
'weight': 20
|
||||
}
|
||||
},
|
||||
'sriov': {
|
||||
'enabled': {
|
||||
'value': False,
|
||||
'label': 'SR-IOV enabled',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'physnet': {
|
||||
'value': 'physnet2',
|
||||
'label': 'Physical network',
|
||||
'type': 'text',
|
||||
'weight': 30
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'SR-IOV',
|
||||
'weight': 30
|
||||
},
|
||||
'numvfs': {
|
||||
'value': None,
|
||||
'label': 'Virtual functions',
|
||||
'weight': 20,
|
||||
'type': 'number',
|
||||
'min': 0
|
||||
},
|
||||
},
|
||||
'dpdk': {
|
||||
'enabled': {
|
||||
'value': False,
|
||||
'label': 'DPDK enabled',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
}
|
||||
},
|
||||
'plugin_a_with_nic_attributes': {
|
||||
'plugin_name_text': {
|
||||
'value': 'value',
|
||||
'weight': 25,
|
||||
'type': 'text',
|
||||
'label': 'label',
|
||||
'description': 'Some description'
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'Test plugin',
|
||||
'class': 'plugin'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OFFLOADING_MODES = {
|
||||
'eth0': {
|
||||
'rx-checksumming': None,
|
||||
'tx-checksum-sctp': False,
|
||||
'tx-checksumming': True,
|
||||
'tx-checksum-ipv4': None,
|
||||
'tx-checksum-ipv6': True
|
||||
},
|
||||
'eth1': {
|
||||
'tx-checksum-sctp': None,
|
||||
'rx-checksumming': None,
|
||||
'tx-checksumming': None,
|
||||
'tx-checksum-ipv6': False,
|
||||
'tx-checksum-ipv4': None
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestNICAttributesHandlers, self).setUp()
|
||||
self.cluster = self.env.create(
|
||||
release_kwargs={
|
||||
'name': uuid.uuid4().get_hex(),
|
||||
'version': 'newton-10.0',
|
||||
'operating_system': 'Ubuntu',
|
||||
'modes': [consts.CLUSTER_MODES.ha_compact]},
|
||||
nodes_kwargs=[{'roles': ['controller']}])
|
||||
self.node = self.env.nodes[0]
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
cluster=self.cluster,
|
||||
nic_attributes_metadata=self.env.get_default_plugin_nic_config())
|
||||
|
||||
def test_get_nic_attributes(self):
|
||||
resp = self.app.get(
|
||||
reverse("NodeNICsHandler", kwargs={"node_id": self.node.id}),
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
expected_attributes = deepcopy(self.EXPECTED_ATTRIBUTES)
|
||||
|
||||
for nic in resp.json_body:
|
||||
expected_attributes['offloading']['modes']['value'] = \
|
||||
self.OFFLOADING_MODES[nic['name']]
|
||||
del nic['attributes']['plugin_a_with_nic_attributes'][
|
||||
'metadata']['nic_plugin_id']
|
||||
self.assertEqual(expected_attributes, nic['attributes'])
|
||||
|
||||
def test_put_nic_attributes(self):
|
||||
resp = self.app.get(
|
||||
reverse("NodeNICsHandler", kwargs={"node_id": self.node.id}),
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
nics = deepcopy(resp.json_body)
|
||||
expected_attributes = deepcopy(self.EXPECTED_ATTRIBUTES)
|
||||
|
||||
for nic in nics:
|
||||
nic['attributes']['mtu']['value']['value'] = 1000
|
||||
nic['attributes']['plugin_a_with_nic_attributes'][
|
||||
'plugin_name_text']['value'] = 'test'
|
||||
|
||||
resp = self.app.put(
|
||||
reverse("NodeNICsHandler",
|
||||
kwargs={"node_id": self.node.id}),
|
||||
jsonutils.dumps(nics),
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
for nic in resp.json_body:
|
||||
expected_attributes['offloading']['modes']['value'] = \
|
||||
self.OFFLOADING_MODES[nic['name']]
|
||||
expected_attributes['mtu']['value']['value'] = 1000
|
||||
expected_attributes['plugin_a_with_nic_attributes'][
|
||||
'plugin_name_text']['value'] = 'test'
|
||||
del nic['attributes']['plugin_a_with_nic_attributes'][
|
||||
'metadata']['nic_plugin_id']
|
||||
self.assertEqual(expected_attributes, nic['attributes'])
|
||||
|
||||
def test_get_default_attributes(self):
|
||||
resp = self.app.get(
|
||||
reverse("NodeNICsDefaultHandler",
|
||||
kwargs={"node_id": self.node.id}),
|
||||
headers=self.default_headers)
|
||||
expected_attributes = deepcopy(self.EXPECTED_ATTRIBUTES)
|
||||
|
||||
for nic in resp.json_body:
|
||||
expected_attributes['offloading']['modes']['value'] = \
|
||||
self.OFFLOADING_MODES[nic['name']]
|
||||
del nic['attributes']['plugin_a_with_nic_attributes'][
|
||||
'metadata']['nic_plugin_id']
|
||||
self.assertEqual(expected_attributes, nic['attributes'])
|
||||
|
@ -15,11 +15,14 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import uuid
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from nailgun.consts import BOND_MODES
|
||||
from nailgun.consts import BOND_TYPES
|
||||
from nailgun.consts import BOND_XMIT_HASH_POLICY
|
||||
from nailgun.consts import CLUSTER_MODES
|
||||
from nailgun.consts import HYPERVISORS
|
||||
from nailgun.consts import NETWORK_INTERFACE_TYPES
|
||||
from nailgun.db import db
|
||||
@ -125,7 +128,7 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
self.admin_nic = nic
|
||||
elif net_names:
|
||||
self.other_nic = nic
|
||||
elif nic['interface_properties']['sriov']['available']:
|
||||
elif nic['meta']['sriov']['available']:
|
||||
self.sriov_nic = nic
|
||||
else:
|
||||
self.empty_nic = nic
|
||||
@ -169,7 +172,7 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
"lacp_rate": "slow",
|
||||
"type__": bond_type
|
||||
},
|
||||
"interface_properties": iface_props,
|
||||
"attributes": iface_props,
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
{"name": self.empty_nic["name"]}],
|
||||
@ -257,9 +260,10 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
def test_nics_lnx_bond_create_failed_with_dpdk(self, m_dpdk_available):
|
||||
m_dpdk_available.return_value = True
|
||||
bond_name = 'bond0'
|
||||
self.prepare_bond_w_props(bond_name=bond_name,
|
||||
self.prepare_bond_w_props(
|
||||
bond_name=bond_name,
|
||||
bond_type=BOND_TYPES.linux,
|
||||
iface_props={'dpdk': {'enabled': True}})
|
||||
iface_props={'dpdk': {'enabled': {'value': True}}})
|
||||
self.node_nics_put_check_error("Bond interface '{0}': DPDK can be"
|
||||
" enabled only for 'dpdkovs' bond type".
|
||||
format(bond_name))
|
||||
@ -294,7 +298,7 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
self.data = self.env.node_nics_get(node.id).json_body
|
||||
bond = [iface for iface in self.data
|
||||
if iface['type'] == NETWORK_INTERFACE_TYPES.bond][0]
|
||||
bond['interface_properties'] = {'dpdk': {'enabled': True}}
|
||||
bond['attributes'] = {'dpdk': {'enabled': {'value': True}}}
|
||||
self.node_nics_put_check_error(
|
||||
"Bond interface '{0}': DPDK can be enabled only for 'dpdkovs' bond"
|
||||
" type".format(bond_name))
|
||||
@ -766,8 +770,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
{"name": self.sriov_nic["name"]}],
|
||||
"assigned_networks": self.sriov_nic["assigned_networks"]
|
||||
})
|
||||
self.sriov_nic['interface_properties']['sriov']['enabled'] = True
|
||||
self.sriov_nic['interface_properties']['sriov']['sriov_numvfs'] = 2
|
||||
self.sriov_nic['attributes']['sriov']['enabled']['value'] = True
|
||||
self.sriov_nic['attributes']['sriov']['numvfs']['value'] = 2
|
||||
cluster_db = self.env.clusters[-1]
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
cluster_attrs['common']['libvirt_type']['value'] = HYPERVISORS.kvm
|
||||
@ -860,3 +864,89 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||
"type: '{3}'".format(
|
||||
self.env.nodes[0]["id"], BOND_MODES.balance_rr, BOND_TYPES.ovs,
|
||||
allowed_modes))
|
||||
|
||||
|
||||
class TestBondAttributesDefaultsHandler(BaseIntegrationTest):
|
||||
|
||||
EXPECTED_ATTRIBUTES = {
|
||||
'mode': {
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'select',
|
||||
'value': 'balance-rr',
|
||||
'label': 'Mode',
|
||||
'values': [
|
||||
{'data': 'balance-rr', 'label': 'balance-rr'}
|
||||
]
|
||||
},
|
||||
'metadata': {
|
||||
'weight': 10,
|
||||
'label': 'Mode'
|
||||
}
|
||||
},
|
||||
'offloading': {
|
||||
'metadata': {
|
||||
'weight': 20,
|
||||
'label': 'Offloading'
|
||||
},
|
||||
'disable': {
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False,
|
||||
'label': 'Disable offloading'
|
||||
},
|
||||
'modes': {
|
||||
'weight': 20,
|
||||
'type': 'offloading_modes',
|
||||
'value': {},
|
||||
'label': 'Offloading modes',
|
||||
'description': 'Offloading modes'
|
||||
}
|
||||
},
|
||||
'mtu': {
|
||||
'metadata': {
|
||||
'weight': 30,
|
||||
'label': 'MTU'
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': None,
|
||||
'label': 'MTU'
|
||||
}
|
||||
},
|
||||
'plugin_a_with_bond_attributes': {
|
||||
'plugin_name_text': {
|
||||
'weight': 25,
|
||||
'type': 'text',
|
||||
'value': 'value',
|
||||
'label': 'label',
|
||||
'description': 'Some description'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestBondAttributesDefaultsHandler, self).setUp()
|
||||
self.cluster = self.env.create(
|
||||
release_kwargs={
|
||||
'name': uuid.uuid4().get_hex(),
|
||||
'version': 'newton-10.0',
|
||||
'operating_system': 'Ubuntu',
|
||||
'modes': [CLUSTER_MODES.ha_compact]},
|
||||
nodes_kwargs=[{'roles': ['controller']}])
|
||||
self.node = self.env.nodes[0]
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
cluster=self.cluster,
|
||||
bond_attributes_metadata=self.env.get_default_plugin_bond_config())
|
||||
|
||||
def test_get_bond_default_attributes(self):
|
||||
resp = self.app.get(
|
||||
reverse(
|
||||
"NodeBondAttributesDefaultsHandler",
|
||||
kwargs={"node_id": self.node.id}),
|
||||
headers=self.default_headers)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertDictEqual(self.EXPECTED_ATTRIBUTES, resp.json_body)
|
||||
|
@ -91,16 +91,31 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
||||
objects.NIC.assign_networks(nic_3, [])
|
||||
|
||||
dpdk_settings = {
|
||||
'dpdk': {'enabled': True,
|
||||
'available': True},
|
||||
'pci_id': 'test_id:2',
|
||||
'attributes': {
|
||||
'dpdk': {
|
||||
'enabled': {'value': True}},
|
||||
},
|
||||
'meta': {
|
||||
'dpdk': {
|
||||
'available': True,
|
||||
},
|
||||
'pci_id': "test_id:2"
|
||||
}
|
||||
objects.NIC.update(nic_2, {'interface_properties': utils.dict_merge(
|
||||
nic_2.interface_properties, dpdk_settings)})
|
||||
}
|
||||
objects.NIC.update(nic_2, {
|
||||
'attributes': utils.dict_merge(
|
||||
nic_2.attributes, dpdk_settings['attributes']),
|
||||
'meta': utils.dict_merge(
|
||||
nic_2.meta, dpdk_settings['meta'])
|
||||
})
|
||||
|
||||
dpdk_settings['dpdk']['enabled'] = False
|
||||
objects.NIC.update(nic_3, {'interface_properties': utils.dict_merge(
|
||||
nic_3.interface_properties, dpdk_settings)})
|
||||
dpdk_settings['attributes']['dpdk']['enabled']['value'] = False
|
||||
objects.NIC.update(nic_3, {
|
||||
'attributes': utils.dict_merge(
|
||||
nic_3.attributes, dpdk_settings['attributes']),
|
||||
'meta': utils.dict_merge(
|
||||
nic_3.meta, dpdk_settings['meta'])
|
||||
})
|
||||
|
||||
self.cluster_attrs = objects.Cluster.get_editable_attributes(
|
||||
self.cluster_db)
|
||||
@ -138,17 +153,17 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
||||
|
||||
def test_change_pci_id(self):
|
||||
iface = self.data['interfaces'][1]
|
||||
iface['interface_properties']['pci_id'] = '123:345'
|
||||
iface['meta']['pci_id'] = '123:345'
|
||||
self.check_fail("PCI-ID .* can't be changed manually")
|
||||
|
||||
def test_wrong_mtu(self):
|
||||
iface = self.data['interfaces'][1]
|
||||
iface['interface_properties']['mtu'] = 1501
|
||||
iface['attributes']['mtu']['value']['value'] = 1501
|
||||
self.check_fail("MTU size must be less than 1500 bytes")
|
||||
|
||||
def test_non_default_mtu(self):
|
||||
iface = self.data['interfaces'][1]
|
||||
iface['interface_properties']['mtu'] = 1500
|
||||
iface['attributes']['mtu']['value']['value'] = 1500
|
||||
self.assertNotRaises(errors.InvalidData,
|
||||
self.validator, self.data)
|
||||
|
||||
@ -158,18 +173,56 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
||||
|
||||
nets = nic_2['assigned_networks']
|
||||
nic_2['assigned_networks'] = []
|
||||
nic_3['interface_properties']['dpdk']['enabled'] = True
|
||||
nic_3['attributes']['dpdk']['enabled']['value'] = True
|
||||
|
||||
bond_data = {
|
||||
'type': "bond",
|
||||
'name': "ovs-bond0",
|
||||
'mode': "active-backup",
|
||||
'assigned_networks': nets,
|
||||
'interface_properties': {
|
||||
'mtu': None,
|
||||
'disable_offloading': True,
|
||||
'attributes': {
|
||||
'offloading': {
|
||||
'disable': {
|
||||
'value': True,
|
||||
'label': 'Disable offloading',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'Offloading',
|
||||
'weight': 10
|
||||
},
|
||||
'offloading_modes': {
|
||||
'description': 'Offloading modes',
|
||||
'value': {},
|
||||
'label': 'Offloading modes',
|
||||
'type': 'offloading_modes',
|
||||
'weight': 20
|
||||
}
|
||||
},
|
||||
'mtu': {
|
||||
'value': {
|
||||
'value': None,
|
||||
'label': 'MTU',
|
||||
'type': 'number',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'MTU',
|
||||
'weight': 20
|
||||
}
|
||||
},
|
||||
'dpdk': {
|
||||
'enabled': True,
|
||||
'enabled': {
|
||||
'value': True,
|
||||
'label': 'DPDK enabled',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
}
|
||||
}
|
||||
},
|
||||
'bond_properties': {
|
||||
@ -194,9 +247,12 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
||||
self._create_bond_data()
|
||||
nic_3 = self.node.nic_interfaces[2]
|
||||
|
||||
dpdk_settings = {'dpdk': {'available': False}, 'pci_id': ''}
|
||||
objects.NIC.update(nic_3, {'interface_properties': utils.dict_merge(
|
||||
nic_3.interface_properties, dpdk_settings)})
|
||||
meta = {
|
||||
'dpdk': {'available': False},
|
||||
'pci_id': ''
|
||||
}
|
||||
objects.NIC.update(nic_3, {
|
||||
'meta': utils.dict_merge(nic_3.meta, meta)})
|
||||
|
||||
self.assertRaisesRegexp(
|
||||
errors.InvalidData,
|
||||
|
@ -538,13 +538,14 @@ class NetAssignmentValidator(BasicValidator):
|
||||
|
||||
@classmethod
|
||||
def _verify_sriov_properties(cls, iface, data, db_node):
|
||||
non_changeable = ['sriov_totalvfs', 'available', 'pci_id']
|
||||
sriov_data = data['interface_properties']['sriov']
|
||||
sriov_db = iface.interface_properties['sriov']
|
||||
non_changeable = ['totalvfs', 'available', 'pci_id']
|
||||
sriov_data = data['attributes']['sriov']
|
||||
sriov_db = iface.attributes['sriov']
|
||||
sriov_meta = iface.meta['sriov']
|
||||
sriov_new = sriov_db.copy()
|
||||
sriov_new.update(sriov_data)
|
||||
|
||||
if sriov_new['enabled']:
|
||||
if sriov_new['enabled']['value']:
|
||||
# check hypervisor type
|
||||
h_type = objects.Cluster.get_editable_attributes(
|
||||
db_node.cluster)['common']['libvirt_type']['value']
|
||||
@ -552,36 +553,39 @@ class NetAssignmentValidator(BasicValidator):
|
||||
raise errors.InvalidData(
|
||||
'Only KVM hypervisor works with SR-IOV.')
|
||||
|
||||
if 'meta' in data:
|
||||
sriov_meta_data = data['meta']['sriov']
|
||||
for param_name in non_changeable:
|
||||
if sriov_db[param_name] != sriov_new[param_name]:
|
||||
if sriov_meta[param_name] != sriov_meta_data[param_name]:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}' interface '{1}': SR-IOV parameter '{2}' cannot"
|
||||
" be changed through API".format(
|
||||
"Node '{0}' interface '{1}': SR-IOV parameter '{2}' "
|
||||
"cannot be changed through API".format(
|
||||
db_node.id, iface.name, param_name),
|
||||
log_message=True
|
||||
)
|
||||
if not sriov_db['available'] and sriov_new['enabled']:
|
||||
|
||||
if not sriov_meta['available'] and sriov_new['enabled']['value']:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}' interface '{1}': SR-IOV cannot be enabled as it is"
|
||||
" not available".format(db_node.id, iface.name),
|
||||
log_message=True
|
||||
)
|
||||
if not sriov_new['sriov_numvfs'] and sriov_new['enabled']:
|
||||
if not sriov_new['numvfs']['value'] and sriov_new['enabled']['value']:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}' interface '{1}': virtual functions can not be"
|
||||
" enabled for interface when 'sriov_numfs' option is not"
|
||||
" specified!".format(db_node.id, iface.name),
|
||||
log_message=True
|
||||
)
|
||||
if sriov_db['sriov_totalvfs'] < sriov_new['sriov_numvfs']:
|
||||
if sriov_meta['totalvfs'] < sriov_new['numvfs']['value']:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}' interface '{1}': '{2}' virtual functions was"
|
||||
" requested but just '{3}' are available".format(
|
||||
db_node.id, iface.name, sriov_new['sriov_numvfs'],
|
||||
sriov_db['sriov_totalvfs']),
|
||||
db_node.id, iface.name, sriov_new['numvfs']['value'],
|
||||
sriov_meta['totalvfs']),
|
||||
log_message=True
|
||||
)
|
||||
if (sriov_new['enabled'] and
|
||||
if (sriov_new['enabled']['value'] and
|
||||
data.get('assigned_networks', iface.assigned_networks)):
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}' interface '{1}': SR-IOV cannot be enabled when "
|
||||
@ -656,10 +660,7 @@ class NetAssignmentValidator(BasicValidator):
|
||||
hw_available &= objects.NIC.dpdk_available(
|
||||
slave_iface, dpdk_drivers)
|
||||
|
||||
interface_properties = iface.get('interface_properties', {})
|
||||
enabled = interface_properties.get('dpdk', {}).get(
|
||||
'enabled', False)
|
||||
|
||||
attributes = iface.get('attributes', {})
|
||||
bond_type = iface.get('bond_properties', {}).get('type__')
|
||||
else:
|
||||
if iface['type'] == consts.NETWORK_INTERFACE_TYPES.ether:
|
||||
@ -669,13 +670,13 @@ class NetAssignmentValidator(BasicValidator):
|
||||
bond_type = iface.get('bond_properties', {}).get(
|
||||
'type__', db_iface.bond_properties.get('type__'))
|
||||
hw_available = iface_cls.dpdk_available(db_iface, dpdk_drivers)
|
||||
|
||||
interface_properties = utils.dict_merge(
|
||||
db_iface.interface_properties,
|
||||
iface.get('interface_properties', {})
|
||||
attributes = utils.dict_merge(
|
||||
db_iface.attributes,
|
||||
iface.get('attributes', {})
|
||||
)
|
||||
enabled = interface_properties.get('dpdk', {}).get(
|
||||
'enabled', False)
|
||||
|
||||
enabled = attributes.get('dpdk', {}).get('enabled', {}).get(
|
||||
'value', False)
|
||||
|
||||
# check basic parameters
|
||||
if not hw_available and enabled:
|
||||
@ -696,9 +697,10 @@ class NetAssignmentValidator(BasicValidator):
|
||||
log_message=True
|
||||
)
|
||||
|
||||
if db_iface is not None:
|
||||
pci_id = interface_properties.get('pci_id')
|
||||
db_pci_id = db_iface.interface_properties.get('pci_id')
|
||||
if (db_iface is not None and iface['type'] !=
|
||||
consts.NETWORK_INTERFACE_TYPES.bond):
|
||||
pci_id = iface.get('meta', {}).get('pci_id', '')
|
||||
db_pci_id = db_iface.meta.get('pci_id', '')
|
||||
|
||||
if pci_id != db_pci_id:
|
||||
raise errors.InvalidData(
|
||||
@ -718,7 +720,7 @@ class NetAssignmentValidator(BasicValidator):
|
||||
|
||||
# check mtu <= 1500
|
||||
# github.com/openvswitch/ovs/blob/master/INSTALL.DPDK.md#restrictions
|
||||
mtu = interface_properties.get('mtu')
|
||||
mtu = attributes.get('mtu', {}).get('value', {}).get('value')
|
||||
if enabled and mtu is not None and mtu > 1500:
|
||||
raise errors.InvalidData(
|
||||
"For interface '{}' with enabled DPDK MTU"
|
||||
@ -794,7 +796,7 @@ class NetAssignmentValidator(BasicValidator):
|
||||
iface['name']),
|
||||
log_message=True
|
||||
)
|
||||
if iface.get('interface_properties', {}).get('sriov'):
|
||||
if iface.get('attributes', {}).get('sriov'):
|
||||
cls._verify_sriov_properties(db_iface, iface, db_node)
|
||||
|
||||
elif iface['type'] == consts.NETWORK_INTERFACE_TYPES.bond:
|
||||
@ -824,9 +826,10 @@ class NetAssignmentValidator(BasicValidator):
|
||||
)
|
||||
cur_iface = interfaces_by_name.get(slave['name'], {})
|
||||
iface_props = utils.dict_merge(
|
||||
db_slave.interface_properties,
|
||||
cur_iface.get('interface_properties', {}))
|
||||
if iface_props.get('sriov', {}).get('enabled'):
|
||||
db_slave.attributes,
|
||||
cur_iface.get('attributes', {}))
|
||||
if iface_props.get('sriov', {}).get('enabled').get(
|
||||
'value'):
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}': bond '{1}' cannot contain SRIOV "
|
||||
"enabled interface '{2}'".format(
|
||||
|
@ -435,8 +435,8 @@
|
||||
bonding:
|
||||
availability:
|
||||
- dpdkovs: "'experimental' in version:feature_groups and interface:pxe == false and
|
||||
interface:interface_properties.dpdk.enabled and not interface:interface_properties.sriov.enabled"
|
||||
- linux: "not interface:interface_properties.sriov.enabled"
|
||||
interface:attributes.dpdk.enabled.value and not interface:attributes.sriov.enabled.value"
|
||||
- linux: "not interface:attributes.sriov.enabled.value"
|
||||
properties:
|
||||
linux:
|
||||
mode:
|
||||
@ -1151,7 +1151,6 @@
|
||||
group: "security"
|
||||
weight: 20
|
||||
type: "radio"
|
||||
|
||||
public_network_assignment:
|
||||
metadata:
|
||||
weight: 10
|
||||
@ -2104,6 +2103,98 @@
|
||||
- hypervisor
|
||||
- network
|
||||
- storage
|
||||
nic_attributes:
|
||||
offloading:
|
||||
metadata:
|
||||
label: "Offloading"
|
||||
weight: 10
|
||||
disable:
|
||||
label: "Disable offloading"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
modes:
|
||||
label: "Offloading modes"
|
||||
weight: 20
|
||||
description: "Offloading modes"
|
||||
type: "offloading_modes"
|
||||
value: {}
|
||||
mtu:
|
||||
metadata:
|
||||
label: "MTU"
|
||||
weight: 20
|
||||
value:
|
||||
label: "MTU"
|
||||
weight: 10
|
||||
type: "number"
|
||||
value: null
|
||||
sriov:
|
||||
metadata:
|
||||
label: "SR-IOV"
|
||||
weight: 30
|
||||
enabled:
|
||||
label: "SR-IOV enabled"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
numvfs:
|
||||
label: "Virtual functions"
|
||||
weight: 20
|
||||
type: "number"
|
||||
min: 0
|
||||
value: null
|
||||
physnet:
|
||||
label: "Physical network"
|
||||
weight: 30
|
||||
type: "text"
|
||||
value: ''
|
||||
dpdk:
|
||||
metadata:
|
||||
label: "DPDK"
|
||||
weight: 40
|
||||
enabled:
|
||||
label: "DPDK enabled"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
bond_attributes:
|
||||
mode:
|
||||
metadata:
|
||||
label: "Mode"
|
||||
weight: 10
|
||||
value:
|
||||
label: "Mode"
|
||||
weight: 10
|
||||
type: "select"
|
||||
values:
|
||||
-
|
||||
label: balance-rr
|
||||
data: balance-rr
|
||||
value: balance-rr
|
||||
offloading:
|
||||
metadata:
|
||||
label: "Offloading"
|
||||
weight: 20
|
||||
disable:
|
||||
label: "Disable offloading"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
modes:
|
||||
label: "Offloading modes"
|
||||
weight: 20
|
||||
description: "Offloading modes"
|
||||
type: "offloading_modes"
|
||||
value: {}
|
||||
mtu:
|
||||
metadata:
|
||||
label: "MTU"
|
||||
weight: 30
|
||||
value:
|
||||
label: "MTU"
|
||||
weight: 10
|
||||
type: "number"
|
||||
value: null
|
||||
modes: ['ha_compact']
|
||||
extensions: ['volume_manager', 'network_manager']
|
||||
- pk: 1
|
||||
|
@ -77,10 +77,12 @@ from nailgun.objects.master_node_settings import MasterNodeSettings
|
||||
from nailgun.objects.node_group import NodeGroup
|
||||
from nailgun.objects.node_group import NodeGroupCollection
|
||||
|
||||
from nailgun.objects.plugin import ClusterPlugin
|
||||
from nailgun.objects.plugin import NodeBondInterfaceClusterPlugin
|
||||
from nailgun.objects.plugin import NodeClusterPlugin
|
||||
from nailgun.objects.plugin import NodeNICInterfaceClusterPlugin
|
||||
from nailgun.objects.plugin import Plugin
|
||||
from nailgun.objects.plugin import PluginCollection
|
||||
from nailgun.objects.plugin import ClusterPlugin
|
||||
from nailgun.objects.plugin import NodeClusterPlugin
|
||||
|
||||
from nailgun.objects.plugin_link import PluginLink
|
||||
from nailgun.objects.plugin_link import PluginLinkCollection
|
||||
|
@ -696,6 +696,7 @@ class Cluster(NailgunObject):
|
||||
objects.Node.assign_group(node)
|
||||
net_manager.assign_networks_by_default(node)
|
||||
objects.Node.set_default_attributes(node)
|
||||
objects.Node.create_nic_attributes(node)
|
||||
objects.Node.refresh_dpdk_properties(node)
|
||||
cls.update_nodes_network_template(instance, nodes_to_add)
|
||||
db().flush()
|
||||
|
@ -528,7 +528,6 @@ class Node(NailgunObject):
|
||||
try:
|
||||
network_manager = Cluster.get_network_manager(instance.cluster)
|
||||
network_manager.update_interfaces_info(instance)
|
||||
|
||||
db().refresh(instance)
|
||||
except errors.InvalidInterfacesInfo as exc:
|
||||
logger.warning(
|
||||
@ -964,6 +963,7 @@ class Node(NailgunObject):
|
||||
cls.add_pending_change(instance, consts.CLUSTER_CHANGES.interfaces)
|
||||
cls.set_network_template(instance)
|
||||
cls.set_default_attributes(instance)
|
||||
cls.create_nic_attributes(instance)
|
||||
|
||||
@classmethod
|
||||
def set_network_template(cls, instance):
|
||||
@ -1220,34 +1220,6 @@ class Node(NailgunObject):
|
||||
vm['created'] = False
|
||||
node.vms_conf.changed()
|
||||
|
||||
@classmethod
|
||||
def set_default_attributes(cls, instance):
|
||||
if not instance.cluster_id:
|
||||
logger.warning(
|
||||
u"Attempting to update attributes of node "
|
||||
u"'{0}' which isn't added to any cluster".format(
|
||||
instance.full_name))
|
||||
return
|
||||
|
||||
instance.attributes = copy.deepcopy(
|
||||
instance.cluster.release.node_attributes)
|
||||
NodeAttributes.set_default_hugepages(instance)
|
||||
PluginManager.add_plugin_attributes_for_node(instance)
|
||||
|
||||
@classmethod
|
||||
def dpdk_enabled(cls, instance):
|
||||
network_manager = Cluster.get_network_manager(instance.cluster)
|
||||
if not hasattr(network_manager, 'dpdk_enabled_for_node'):
|
||||
return False
|
||||
return network_manager.dpdk_enabled_for_node(instance)
|
||||
|
||||
@classmethod
|
||||
def sriov_enabled(cls, instance):
|
||||
for iface in instance.nic_interfaces:
|
||||
if NIC.is_sriov_enabled(iface):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_attributes(cls, instance):
|
||||
attributes = copy.deepcopy(instance.attributes)
|
||||
@ -1281,6 +1253,34 @@ class Node(NailgunObject):
|
||||
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def set_default_attributes(cls, instance):
|
||||
if not instance.cluster_id:
|
||||
logger.warning(
|
||||
u"Attempting to get default attributes of node "
|
||||
u"'{0}' which isn't added to any cluster".format(
|
||||
instance.full_name))
|
||||
return
|
||||
|
||||
instance.attributes = copy.deepcopy(
|
||||
instance.cluster.release.node_attributes)
|
||||
NodeAttributes.set_default_hugepages(instance)
|
||||
PluginManager.add_plugin_attributes_for_node(instance)
|
||||
|
||||
@classmethod
|
||||
def dpdk_enabled(cls, instance):
|
||||
network_manager = Cluster.get_network_manager(instance.cluster)
|
||||
if not hasattr(network_manager, 'dpdk_enabled_for_node'):
|
||||
return False
|
||||
return network_manager.dpdk_enabled_for_node(instance)
|
||||
|
||||
@classmethod
|
||||
def sriov_enabled(cls, instance):
|
||||
for iface in instance.nic_interfaces:
|
||||
if NIC.is_sriov_enabled(iface):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def refresh_dpdk_properties(cls, instance):
|
||||
if not instance.cluster:
|
||||
@ -1319,6 +1319,29 @@ class Node(NailgunObject):
|
||||
nm = Cluster.get_network_manager(instance.cluster)
|
||||
return nm.dpdk_nics(instance)
|
||||
|
||||
@classmethod
|
||||
def get_bond_default_attributes(cls, instance):
|
||||
if not instance.cluster_id:
|
||||
logger.warning(
|
||||
u"Attempting to get bond default attributes of node "
|
||||
u"'{0}' which isn't added to any cluster".format(
|
||||
instance.full_name))
|
||||
return
|
||||
|
||||
return Bond.get_bond_default_attributes(instance.cluster)
|
||||
|
||||
@classmethod
|
||||
def create_nic_attributes(cls, instance):
|
||||
if not instance.cluster_id:
|
||||
logger.warning(
|
||||
u"Attempting to create NIC attributes of node "
|
||||
u"'{0}' which isn't added to any cluster".format(
|
||||
instance.full_name))
|
||||
return
|
||||
|
||||
for nic_interface in instance.nic_interfaces:
|
||||
NIC.create_attributes(nic_interface)
|
||||
|
||||
|
||||
class NodeCollection(NailgunCollection):
|
||||
"""Node collection"""
|
||||
@ -1434,7 +1457,7 @@ class NodeAttributes(object):
|
||||
for nic in dpdk_nics:
|
||||
# NIC may have numa_node equal to null, in that case
|
||||
# we assume that it belongs to first NUMA
|
||||
nics_numas.append(nic.interface_properties.get('numa_node') or 0)
|
||||
nics_numas.append(nic.meta.get('numa_node') or 0)
|
||||
|
||||
return cpu_distribution.distribute_node_cpus(
|
||||
numa_nodes, components, nics_numas)
|
||||
|
@ -259,7 +259,14 @@ class ClusterPlugin(NailgunObject):
|
||||
'enabled': False,
|
||||
'attributes': plugin_attributes
|
||||
})
|
||||
NodeClusterPlugin.add_nodes_for_cluster_plugin(cluster_plugin)
|
||||
# Set default attributes for Nodes, NICs and Bonds during
|
||||
# plugin installation
|
||||
NodeClusterPlugin.add_nodes_for_cluster_plugin(
|
||||
cluster_plugin)
|
||||
NodeNICInterfaceClusterPlugin.add_node_nics_for_cluster_plugin(
|
||||
cluster_plugin)
|
||||
NodeBondInterfaceClusterPlugin.add_node_bonds_for_cluster_plugin(
|
||||
cluster_plugin)
|
||||
|
||||
db().flush()
|
||||
|
||||
@ -477,3 +484,186 @@ class NodeClusterPlugin(BasicNodeClusterPlugin):
|
||||
})
|
||||
|
||||
db().flush()
|
||||
|
||||
|
||||
class NodeNICInterfaceClusterPlugin(BasicNodeClusterPlugin):
|
||||
|
||||
model = models.NodeNICInterfaceClusterPlugin
|
||||
|
||||
@classmethod
|
||||
def get_all_enabled_attributes_by_interface(cls, interface):
|
||||
"""Returns plugin enabled attributes for specific NIC.
|
||||
|
||||
:param interface: Interface instance
|
||||
:type interface: models.node.NodeNICInterface
|
||||
:returns: dict -- Dict object with plugin NIC attributes
|
||||
"""
|
||||
nic_attributes = {}
|
||||
nic_plugin_attributes_query = db().query(
|
||||
cls.model.id,
|
||||
models.Plugin.name,
|
||||
models.Plugin.title,
|
||||
cls.model.attributes
|
||||
).join(
|
||||
models.ClusterPlugin,
|
||||
).filter(
|
||||
cls.model.interface_id == interface.id
|
||||
).filter(
|
||||
models.ClusterPlugin.enabled.is_(True)).all()
|
||||
|
||||
for nic_plugin_id, name, title, attributes \
|
||||
in nic_plugin_attributes_query:
|
||||
nic_attributes[name] = {
|
||||
'metadata': {
|
||||
'label': title,
|
||||
'nic_plugin_id': nic_plugin_id,
|
||||
'class': 'plugin'
|
||||
}
|
||||
}
|
||||
# TODO(apopovych): resolve conflicts of same attribute names
|
||||
# for different plugins
|
||||
nic_attributes[name].update(attributes)
|
||||
|
||||
return nic_attributes
|
||||
|
||||
@classmethod
|
||||
def add_node_nics_for_cluster_plugin(cls, cluster_plugin):
|
||||
"""Populates 'node_nic_interface_cluster_plugins' table.
|
||||
|
||||
:param cluster_plugin: ClusterPlugin instance
|
||||
:type cluster_plugin: models.cluster.ClusterPlugin
|
||||
:returns: None
|
||||
"""
|
||||
nic_attributes = dict(
|
||||
cluster_plugin.plugin.nic_attributes_metadata)
|
||||
for node in cluster_plugin.cluster.nodes:
|
||||
for interface in node.nic_interfaces:
|
||||
if nic_attributes:
|
||||
cls.create({
|
||||
'cluster_plugin_id': cluster_plugin.id,
|
||||
'interface_id': interface.id,
|
||||
'node_id': node.id,
|
||||
'attributes': nic_attributes
|
||||
})
|
||||
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def add_cluster_plugins_for_node_nic(cls, interface):
|
||||
"""Populates 'node_nic_interface_cluster_plugins' table.
|
||||
|
||||
:param interface: Interface instance
|
||||
:type interface: models.node.NodeNICInterface
|
||||
:returns: None
|
||||
"""
|
||||
node = interface.node
|
||||
# remove old relations for interfaces
|
||||
nic_cluster_plugin_ids = set(
|
||||
item.id for item in node.node_nic_interface_cluster_plugins
|
||||
if item.interface_id == interface.id)
|
||||
cls.bulk_delete(nic_cluster_plugin_ids)
|
||||
|
||||
for cluster_plugin in node.cluster.cluster_plugins:
|
||||
nic_attributes = dict(
|
||||
cluster_plugin.plugin.nic_attributes_metadata)
|
||||
if nic_attributes:
|
||||
cls.create({
|
||||
'cluster_plugin_id': cluster_plugin.id,
|
||||
'interface_id': interface.id,
|
||||
'node_id': node.id,
|
||||
'attributes': nic_attributes
|
||||
})
|
||||
|
||||
db().flush()
|
||||
|
||||
|
||||
class NodeBondInterfaceClusterPlugin(BasicNodeClusterPlugin):
|
||||
|
||||
model = models.NodeBondInterfaceClusterPlugin
|
||||
|
||||
@classmethod
|
||||
def get_all_enabled_attributes_by_bond(cls, bond):
|
||||
"""Returns plugin enabled attributes for specific Bond.
|
||||
|
||||
:param interface: Bond instance
|
||||
:type interface: models.node.NodeBondInterface
|
||||
:returns: dict -- Dict object with plugin Bond attributes
|
||||
"""
|
||||
bond_attributes = {}
|
||||
bond_plugin_attributes_query = db().query(
|
||||
cls.model.id,
|
||||
models.Plugin.name,
|
||||
models.Plugin.title,
|
||||
cls.model.attributes
|
||||
).join(
|
||||
models.ClusterPlugin,
|
||||
models.Plugin
|
||||
).filter(
|
||||
cls.model.bond_id == bond.id
|
||||
).filter(
|
||||
models.ClusterPlugin.enabled.is_(True))
|
||||
|
||||
for bond_plugin_id, name, title, attributes \
|
||||
in bond_plugin_attributes_query:
|
||||
bond_attributes[name] = {
|
||||
'metadata': {
|
||||
'label': title,
|
||||
'bond_plugin_id': bond_plugin_id,
|
||||
'class': 'plugin'
|
||||
}
|
||||
}
|
||||
# TODO(apopovych): resolve conflicts of same attribute names
|
||||
# for different plugins
|
||||
bond_attributes[name].update(attributes)
|
||||
|
||||
return bond_attributes
|
||||
|
||||
@classmethod
|
||||
def add_node_bonds_for_cluster_plugin(cls, cluster_plugin):
|
||||
"""Populates 'node_bond_interface_cluster_plugins' table with bonds.
|
||||
|
||||
:param cluster_plugin: ClusterPlugin instance
|
||||
:type cluster_plugin: models.cluster.ClusterPlugin
|
||||
:returns: None
|
||||
"""
|
||||
bond_attributes = dict(
|
||||
cluster_plugin.plugin.bond_attributes_metadata)
|
||||
for node in cluster_plugin.cluster.nodes:
|
||||
for bond in node.bond_interfaces:
|
||||
if bond_attributes:
|
||||
cls.create({
|
||||
'cluster_plugin_id': cluster_plugin.id,
|
||||
'bond_id': bond.id,
|
||||
'node_id': node.id,
|
||||
'attributes': bond_attributes
|
||||
})
|
||||
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def add_cluster_plugins_for_node_bond(cls, bond):
|
||||
"""Populates 'node_bond_interface_cluster_plugins' table.
|
||||
|
||||
:param interface: Bond instance
|
||||
:type interface: models.node.NodeBondInterface
|
||||
:returns: None
|
||||
"""
|
||||
node = bond.node
|
||||
# remove old relations for bonds
|
||||
bond_cluster_plugin_ids = set(
|
||||
item.id for item in node.node_bond_interface_cluster_plugins
|
||||
if item.bond_id == bond.id)
|
||||
cls.bulk_delete(bond_cluster_plugin_ids)
|
||||
|
||||
for cluster_plugin in node.cluster.cluster_plugins:
|
||||
bond_attributes = dict(
|
||||
cluster_plugin.plugin.bond_attributes_metadata)
|
||||
if bond_attributes:
|
||||
cls.create({
|
||||
'cluster_plugin_id': cluster_plugin.id,
|
||||
'bond_id': bond.id,
|
||||
'node_id': node.id,
|
||||
'attributes': bond_attributes
|
||||
})
|
||||
|
||||
db().flush()
|
||||
|
@ -186,7 +186,7 @@ class PluginAdapterBase(object):
|
||||
|
||||
@property
|
||||
def nic_attributes_metadata(self):
|
||||
return self.plugin.bond_attributes_metadata
|
||||
return self.plugin.nic_attributes_metadata
|
||||
|
||||
@property
|
||||
def node_attributes_metadata(self):
|
||||
|
@ -28,7 +28,9 @@ from nailgun import consts
|
||||
from nailgun import errors
|
||||
from nailgun.logger import logger
|
||||
from nailgun.objects.plugin import ClusterPlugin
|
||||
from nailgun.objects.plugin import NodeBondInterfaceClusterPlugin
|
||||
from nailgun.objects.plugin import NodeClusterPlugin
|
||||
from nailgun.objects.plugin import NodeNICInterfaceClusterPlugin
|
||||
from nailgun.objects.plugin import Plugin
|
||||
from nailgun.objects.plugin import PluginCollection
|
||||
from nailgun.settings import settings
|
||||
@ -499,6 +501,115 @@ class PluginManager(object):
|
||||
NodeClusterPlugin.add_cluster_plugins_for_node(node)
|
||||
|
||||
# ENTRY POINT
|
||||
@classmethod
|
||||
def get_bond_default_attributes(cls, cluster):
|
||||
"""Get plugin bond attributes metadata for cluster.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: Cluster model
|
||||
:returns: dict -- Object with bond attributes
|
||||
"""
|
||||
plugins_bond_metadata = {}
|
||||
enabled_plugins = ClusterPlugin.get_enabled(cluster.id)
|
||||
for plugin_adapter in six.moves.map(wrap_plugin, enabled_plugins):
|
||||
metadata = plugin_adapter.bond_attributes_metadata
|
||||
if metadata:
|
||||
plugins_bond_metadata[plugin_adapter.name] = metadata
|
||||
|
||||
return plugins_bond_metadata
|
||||
|
||||
@classmethod
|
||||
def get_bond_attributes(cls, bond):
|
||||
"""Return plugin related attributes for Bond.
|
||||
|
||||
:param interface: A BOND instance
|
||||
:type interface: Bond model
|
||||
"""
|
||||
return NodeBondInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_bond(bond)
|
||||
|
||||
@classmethod
|
||||
def add_plugin_attributes_for_bond(cls, bond):
|
||||
"""Add plugin related attributes for Bond.
|
||||
|
||||
:param interface: A BOND instance
|
||||
:type interface: Bond model
|
||||
:returns: object -- Bond model instance
|
||||
"""
|
||||
NodeBondInterfaceClusterPlugin.\
|
||||
add_cluster_plugins_for_node_bond(bond)
|
||||
|
||||
return bond
|
||||
|
||||
@classmethod
|
||||
def update_bond_attributes(cls, attributes):
|
||||
plugins = []
|
||||
for k in list(attributes):
|
||||
if cls.is_plugin_data(attributes[k]):
|
||||
plugins.append(attributes.pop(k))
|
||||
|
||||
for plugin in plugins:
|
||||
metadata = plugin.pop('metadata')
|
||||
NodeNICInterfaceClusterPlugin.\
|
||||
set_attributes(
|
||||
metadata['bond_plugin_id'],
|
||||
plugin
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_nic_default_attributes(cls, cluster):
|
||||
"""Get default plugin nic attributes for cluster.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: Cluster model
|
||||
:returns: dict -- Object with nic attributes
|
||||
"""
|
||||
plugins_nic_metadata = {}
|
||||
enabled_plugins = ClusterPlugin.get_enabled(cluster.id)
|
||||
for plugin_adapter in six.moves.map(wrap_plugin, enabled_plugins):
|
||||
metadata = plugin_adapter.nic_attributes_metadata
|
||||
if metadata:
|
||||
plugins_nic_metadata[plugin_adapter.name] = metadata
|
||||
|
||||
return plugins_nic_metadata
|
||||
|
||||
@classmethod
|
||||
def get_nic_attributes(cls, interface):
|
||||
"""Return plugin related attributes for NIC.
|
||||
|
||||
:param interface: A NIC instance
|
||||
:type interface: Interface model
|
||||
:returns:
|
||||
"""
|
||||
return NodeNICInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_interface(interface)
|
||||
|
||||
@classmethod
|
||||
def update_nic_attributes(cls, attributes):
|
||||
plugins = []
|
||||
for k in list(attributes):
|
||||
if cls.is_plugin_data(attributes[k]):
|
||||
plugins.append(attributes.pop(k))
|
||||
|
||||
for plugin in plugins:
|
||||
metadata = plugin.pop('metadata')
|
||||
NodeNICInterfaceClusterPlugin.\
|
||||
set_attributes(
|
||||
metadata['nic_plugin_id'],
|
||||
plugin
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def add_plugin_attributes_for_interface(cls, interface):
|
||||
"""Add plugin related attributes for NIC.
|
||||
|
||||
:param interface: A NIC instance
|
||||
:type interface: Interface model
|
||||
:returns: None
|
||||
"""
|
||||
NodeNICInterfaceClusterPlugin.\
|
||||
add_cluster_plugins_for_node_nic(interface)
|
||||
|
||||
@classmethod
|
||||
def sync_plugins_metadata(cls, plugin_ids=None):
|
||||
"""Sync or install metadata for plugins by given IDs.
|
||||
|
@ -630,9 +630,9 @@ class EnvironmentManager(object):
|
||||
deployment_tasks = plugin_data.pop('deployment_tasks', None)
|
||||
tasks = plugin_data.pop('tasks', None)
|
||||
components = plugin_data.pop('components', None)
|
||||
nic_config = plugin_data.pop('nic_config', None)
|
||||
bond_config = plugin_data.pop('bond_config', None)
|
||||
node_config = plugin_data.pop('node_config', None)
|
||||
nic_config = plugin_data.pop('nic_attributes_metadata', None)
|
||||
bond_config = plugin_data.pop('bond_attributes_metadata', None)
|
||||
node_config = plugin_data.pop('node_attributes_metadata', None)
|
||||
|
||||
mocked_metadata = {
|
||||
'metadata.*': plugin_data,
|
||||
@ -1028,7 +1028,11 @@ class EnvironmentManager(object):
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': 'mitaka-9.0', 'os': 'ubuntu',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': 'newton-10.0', 'os': 'ubuntu',
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': 'newton-10.0', 'os': 'ubuntu',
|
||||
@ -1323,7 +1327,8 @@ class EnvironmentManager(object):
|
||||
)
|
||||
|
||||
def make_bond_via_api(self, bond_name, bond_mode, nic_names, node_id=None,
|
||||
bond_properties=None, interface_properties=None):
|
||||
bond_properties=None, interface_properties=None,
|
||||
attrs=None):
|
||||
if not node_id:
|
||||
node_id = self.nodes[0]["id"]
|
||||
resp = self.app.get(
|
||||
@ -1351,7 +1356,8 @@ class EnvironmentManager(object):
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"mode": bond_mode,
|
||||
"slaves": slaves,
|
||||
"assigned_networks": assigned_nets
|
||||
"assigned_networks": assigned_nets,
|
||||
"attributes": attrs or {}
|
||||
}
|
||||
if bond_properties:
|
||||
bond_dict["bond_properties"] = bond_properties
|
||||
|
@ -623,9 +623,9 @@ class TestNovaNetworkOrchestratorSerializer61(OrchestratorSerializerTestBase):
|
||||
self.move_network(node.id, 'management', 'eth0', 'eth1')
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', ['eth1', 'eth2'], node.id,
|
||||
bond_properties={'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux}
|
||||
)
|
||||
bond_properties={
|
||||
'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux})
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
for node in facts:
|
||||
@ -671,9 +671,9 @@ class TestNovaNetworkOrchestratorSerializer61(OrchestratorSerializerTestBase):
|
||||
self.move_network(node.id, 'fixed', 'eth0', 'eth1')
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', ['eth1', 'eth2'], node.id,
|
||||
bond_properties={'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux}
|
||||
)
|
||||
bond_properties={
|
||||
'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux})
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
for node in facts:
|
||||
@ -757,13 +757,10 @@ class TestNeutronOrchestratorSerializer61(OrchestratorSerializerTestBase):
|
||||
self.assertEquals(200, resp.status_code)
|
||||
interfaces = jsonutils.loads(resp.body)
|
||||
for iface in interfaces:
|
||||
self.assertEqual(
|
||||
iface['interface_properties'],
|
||||
self.env.network_manager.get_default_interface_properties()
|
||||
)
|
||||
if iface['name'] == 'eth0':
|
||||
iface['interface_properties']['mtu'] = 1500
|
||||
iface['interface_properties']['disable_offloading'] = True
|
||||
iface['attributes']['mtu']['value']['value'] = 1500
|
||||
iface['attributes']['offloading'][
|
||||
'disable']['value'] = True
|
||||
nodes_list.append({'id': node.id, 'interfaces': interfaces})
|
||||
resp_put = self.app.put(
|
||||
reverse('NodeCollectionNICsHandler'),
|
||||
@ -949,8 +946,11 @@ class TestNeutronOrchestratorSerializer61(OrchestratorSerializerTestBase):
|
||||
self.move_network(node.id, 'storage', 'eth0', 'eth1')
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', ['eth1', 'eth2'], node.id,
|
||||
bond_properties={'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux},
|
||||
bond_properties={
|
||||
'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux
|
||||
},
|
||||
attrs={'mtu': {'value': {'value': 9000}}},
|
||||
interface_properties={'mtu': 9000}
|
||||
)
|
||||
serializer = self.create_serializer(cluster)
|
||||
@ -1091,19 +1091,16 @@ class TestNeutronOrchestratorSerializer61(OrchestratorSerializerTestBase):
|
||||
for node in cluster.nodes:
|
||||
self.move_network(node.id, 'storage', 'eth0', 'eth1')
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond',
|
||||
'',
|
||||
['eth1', 'eth2'],
|
||||
node.id,
|
||||
'lnx_bond', '', ['eth1', 'eth2'], node.id,
|
||||
bond_properties={
|
||||
'mode': consts.BOND_MODES.l_802_3ad,
|
||||
'xmit_hash_policy': consts.BOND_XMIT_HASH_POLICY.layer2,
|
||||
'lacp_rate': consts.BOND_LACP_RATES.slow,
|
||||
'type__': consts.BOND_TYPES.linux
|
||||
},
|
||||
interface_properties={
|
||||
'mtu': 9000
|
||||
})
|
||||
attrs={'mtu': {'value': {'value': 9000}}},
|
||||
interface_properties={'mtu': 9000}
|
||||
)
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
for node in facts:
|
||||
|
@ -97,13 +97,13 @@ class TestDeploymentAttributesSerialization90(
|
||||
objects.NIC.assign_networks(other_nic, other_nets)
|
||||
objects.NIC.assign_networks(dpdk_nic, dpdk_nets)
|
||||
|
||||
objects.NIC.update(dpdk_nic,
|
||||
{'interface_properties':
|
||||
{
|
||||
'dpdk': {'enabled': True,
|
||||
'available': True},
|
||||
objects.NIC.update(dpdk_nic, {
|
||||
'meta': {
|
||||
'dpdk': {'available': True},
|
||||
'pci_id': 'test_id:2'
|
||||
}})
|
||||
},
|
||||
'attributes': {'dpdk': {'enabled': {'value': True}}}
|
||||
})
|
||||
|
||||
def _create_cluster_with_vxlan(self):
|
||||
release_id = self.cluster_db.release.id
|
||||
@ -214,8 +214,8 @@ class TestDeploymentAttributesSerialization90(
|
||||
node.cluster, {'editable': cluster_attrs})
|
||||
|
||||
for iface in node.interfaces:
|
||||
iface['interface_properties'].update({'pci_id': 'test_id:1'})
|
||||
iface['interface_properties']['dpdk']['available'] = True
|
||||
iface['meta'].update({'pci_id': 'test_id:1'})
|
||||
iface['meta']['dpdk']['available'] = True
|
||||
|
||||
interfaces = self.env.node_nics_get(node.id).json_body
|
||||
first_nic = interfaces[0]
|
||||
@ -233,7 +233,7 @@ class TestDeploymentAttributesSerialization90(
|
||||
'slaves': nics_for_bond,
|
||||
'assigned_networks': networks_for_bond,
|
||||
'bond_properties': bond_properties,
|
||||
'interface_properties': {'dpdk': {'enabled': True}}}
|
||||
'attributes': {'dpdk': {'enabled': {'value': True}}}}
|
||||
interfaces.append(bond_interface)
|
||||
self.env.node_nics_put(node.id, interfaces)
|
||||
objects.Cluster.prepare_for_deployment(self.cluster_db)
|
||||
@ -969,16 +969,18 @@ class TestSriovSerialization90(
|
||||
for nic in self.env.nodes[0].nic_interfaces:
|
||||
if not nic.assigned_networks_list:
|
||||
nic_sriov = nic
|
||||
nic.interface_properties['sriov'] = {
|
||||
'enabled': True,
|
||||
'sriov_numvfs': 8,
|
||||
'sriov_totalvfs': 8,
|
||||
nic.attributes['sriov'] = {
|
||||
'enabled': {'value': True},
|
||||
'numvfs': {'value': 8},
|
||||
'physnet': {'value': 'new_physnet'}
|
||||
}
|
||||
nic.meta['sriov'] = {
|
||||
'available': True,
|
||||
'pci_id': '1234:5678',
|
||||
'physnet': 'new_physnet'
|
||||
'totalvfs': 8,
|
||||
'pci_id': '1234:5678'
|
||||
}
|
||||
objects.NIC.update(
|
||||
nic, {'interface_properties': nic.interface_properties})
|
||||
nic, {'attributes': nic.attributes, 'meta': nic.meta})
|
||||
break
|
||||
else:
|
||||
self.fail('NIC without assigned networks was not found')
|
||||
|
@ -570,6 +570,7 @@ class TestClusterPluginIntegration(base.BaseTestCase):
|
||||
|
||||
|
||||
class TestNodeClusterPluginIntegration(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNodeClusterPluginIntegration, self).setUp()
|
||||
|
||||
@ -712,3 +713,61 @@ class TestNodeClusterPluginIntegration(base.BaseTestCase):
|
||||
},
|
||||
attributes
|
||||
)
|
||||
|
||||
|
||||
class TestNICIntegration(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNICIntegration, self).setUp()
|
||||
|
||||
self.cluster = self.env.create(
|
||||
release_kwargs={
|
||||
'operating_system': consts.RELEASE_OS.ubuntu,
|
||||
'version': '2015.1-8.0'})
|
||||
|
||||
self.env.create_plugin(
|
||||
name='plugin_a',
|
||||
cluster=self.cluster,
|
||||
enabled=True,
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata={'attr_a': {'value': 'test_a'}})
|
||||
self.node = self.env.create_nodes_w_interfaces_count(
|
||||
1, 1, **{"cluster_id": self.cluster.id})[0]
|
||||
self.interface = self.node.nic_interfaces[0]
|
||||
|
||||
def test_get_nic_default_attributes(self):
|
||||
self.env.create_plugin(
|
||||
name='plugin_b',
|
||||
cluster=self.cluster,
|
||||
enabled=True,
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata={'attr_b': {'value': 'test_b'}})
|
||||
default_attributes = PluginManager.get_nic_default_attributes(
|
||||
self.cluster)
|
||||
self.assertDictEqual({
|
||||
'plugin_a': {'attr_a': {'value': 'test_a'}},
|
||||
'plugin_b': {'attr_b': {'value': 'test_b'}}
|
||||
}, default_attributes)
|
||||
|
||||
def test_get_nic_plugin_atributes(self):
|
||||
attributes = PluginManager.get_nic_attributes(self.interface)
|
||||
del attributes['plugin_a']['metadata']['nic_plugin_id']
|
||||
self.assertDictEqual(
|
||||
{'plugin_a': {
|
||||
'attr_a': {'value': 'test_a'},
|
||||
'metadata': {
|
||||
'class': 'plugin',
|
||||
'label': 'Test plugin'}}}, attributes)
|
||||
|
||||
def test_update_nic_attributes(self):
|
||||
new_attrs = PluginManager.get_nic_attributes(self.interface)
|
||||
new_attrs['plugin_a']['attr_a']['value'] = {}
|
||||
PluginManager.update_nic_attributes(new_attrs)
|
||||
attributes = PluginManager.get_nic_attributes(self.interface)
|
||||
del attributes['plugin_a']['metadata']['nic_plugin_id']
|
||||
self.assertDictEqual(
|
||||
{'plugin_a': {
|
||||
'attr_a': {'value': {}},
|
||||
'metadata': {
|
||||
'class': 'plugin',
|
||||
'label': 'Test plugin'}}}, attributes)
|
||||
|
@ -477,8 +477,8 @@ class TestProvisioningSerializer90(BaseIntegrationTest):
|
||||
)
|
||||
|
||||
sriov_nic = self.env.nodes[0].nic_interfaces[0]
|
||||
sriov_nic.interface_properties['sriov']['available'] = True
|
||||
sriov_nic.interface_properties['sriov']['enabled'] = True
|
||||
sriov_nic.meta['sriov']['available'] = True
|
||||
sriov_nic.attributes['sriov']['enabled']['value'] = True
|
||||
objects.NIC.update(sriov_nic, {})
|
||||
|
||||
serialized_node = self.serializer.serialize(
|
||||
|
@ -55,7 +55,7 @@ class TestNetworkVerification(BaseTestCase):
|
||||
dpdk_iface = iface
|
||||
network_config.append({'name': ng.name, 'vlans': vlans})
|
||||
|
||||
dpdk_iface.interface_properties['dpdk']['enabled'] = True
|
||||
dpdk_iface.attributes['dpdk']['enabled']['value'] = True
|
||||
|
||||
task = VerifyNetworksTask(None, network_config)
|
||||
task.get_ifaces_on_deployed_node(node, node_json, [])
|
||||
@ -107,7 +107,7 @@ class TestNetworkVerification(BaseTestCase):
|
||||
if ng.name == consts.NETWORKS.private:
|
||||
private_ifaces[node.name] = iface.name
|
||||
|
||||
dpdk_iface.interface_properties['dpdk']['enabled'] = True
|
||||
dpdk_iface.attributes['dpdk']['enabled']['value'] = True
|
||||
|
||||
task = models.Task(
|
||||
name=consts.TASK_NAMES.check_networks,
|
||||
@ -147,8 +147,8 @@ class TestNetworkVerification(BaseTestCase):
|
||||
continue
|
||||
network_config.append({'name': ng.name, 'vlans': vlans})
|
||||
|
||||
dpdk_iface.interface_properties['dpdk'].update({'available': True,
|
||||
'enabled': True})
|
||||
dpdk_iface.attributes['dpdk']['enabled']['value'] = True
|
||||
dpdk_iface.meta['dpdk']['available'] = True
|
||||
private_iface.assigned_networks_list = [private_ng]
|
||||
|
||||
task = VerifyNetworksTask(None, network_config)
|
||||
|
@ -37,8 +37,8 @@ class TestNodeAttributes(base.BaseUnitTest):
|
||||
'components': comp_entity
|
||||
}
|
||||
m_dpdk_nics.return_value = [
|
||||
mock.Mock(interface_properties={'numa_node': None}),
|
||||
mock.Mock(interface_properties={'numa_node': 1}),
|
||||
mock.Mock(meta={'numa_node': None}),
|
||||
mock.Mock(meta={'numa_node': 1}),
|
||||
]
|
||||
|
||||
node = mock.Mock(
|
||||
|
@ -22,7 +22,9 @@ from oslo_serialization import jsonutils
|
||||
|
||||
from nailgun import consts
|
||||
from nailgun.objects import ClusterPlugin
|
||||
from nailgun.objects import NodeBondInterfaceClusterPlugin
|
||||
from nailgun.objects import NodeClusterPlugin
|
||||
from nailgun.objects import NodeNICInterfaceClusterPlugin
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.objects import PluginCollection
|
||||
from nailgun.test import base
|
||||
@ -31,29 +33,19 @@ from nailgun.test import base
|
||||
class ExtraFunctions(base.BaseTestCase):
|
||||
|
||||
def _create_test_plugins(self):
|
||||
plugin_ids = []
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1', '3.0.0', '4.0.0', '5.0.0']:
|
||||
plugin_data = self.env.get_default_plugin_metadata(
|
||||
self.env.create_plugin(
|
||||
version=version,
|
||||
name='multiversion_plugin')
|
||||
plugin = Plugin.create(plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
single_plugin_data = self.env.get_default_plugin_metadata(
|
||||
self.env.create_plugin(
|
||||
name='single_plugin')
|
||||
plugin = Plugin.create(single_plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
incompatible_plugin_data = self.env.get_default_plugin_metadata(
|
||||
self.env.create_plugin(
|
||||
name='incompatible_plugin',
|
||||
releases=[]
|
||||
)
|
||||
plugin = Plugin.create(incompatible_plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
releases=[])
|
||||
|
||||
return plugin_ids
|
||||
return [p.id for p in self.env.plugins]
|
||||
|
||||
def _create_test_cluster(self):
|
||||
def _create_test_cluster(self, nodes=[]):
|
||||
return self.env.create(
|
||||
cluster_kwargs={'mode': consts.CLUSTER_MODES.multinode},
|
||||
release_kwargs={
|
||||
@ -61,17 +53,16 @@ class ExtraFunctions(base.BaseTestCase):
|
||||
'version': '2015.1-8.0',
|
||||
'operating_system': 'Ubuntu',
|
||||
'modes': [consts.CLUSTER_MODES.multinode,
|
||||
consts.CLUSTER_MODES.ha_compact]})
|
||||
consts.CLUSTER_MODES.ha_compact]},
|
||||
nodes_kwargs=nodes)
|
||||
|
||||
|
||||
class TestPluginCollection(ExtraFunctions):
|
||||
|
||||
def test_all_newest(self):
|
||||
self._create_test_plugins()
|
||||
|
||||
newest_plugins = PluginCollection.all_newest()
|
||||
self.assertEqual(len(newest_plugins), 3)
|
||||
|
||||
single_plugin = filter(
|
||||
lambda p: p.name == 'single_plugin',
|
||||
newest_plugins)
|
||||
@ -81,7 +72,6 @@ class TestPluginCollection(ExtraFunctions):
|
||||
|
||||
self.assertEqual(len(single_plugin), 1)
|
||||
self.assertEqual(len(multiversion_plugin), 1)
|
||||
|
||||
self.assertEqual(multiversion_plugin[0].version, '5.0.0')
|
||||
|
||||
def test_get_by_uids(self):
|
||||
@ -358,3 +348,174 @@ class TestNodeClusterPlugin(ExtraFunctions):
|
||||
self.assertDictEqual(
|
||||
attributes,
|
||||
jsonutils.loads(node_attributes_cluster_plugin[1]))
|
||||
|
||||
|
||||
class TestNodeNICInterfaceClusterPlugin(ExtraFunctions):
|
||||
|
||||
def test_get_all_attributes_by_interface_with_enabled_plugin(self):
|
||||
nic_config = self.env.get_default_plugin_nic_config()
|
||||
plugin = self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata=nic_config)
|
||||
cluster = self._create_test_cluster()
|
||||
# create node with 1 interface for easy testing
|
||||
node = self.env.create_nodes_w_interfaces_count(
|
||||
1, 1, **{"cluster_id": cluster.id})[0]
|
||||
interface = node.nic_interfaces[0]
|
||||
nic_plugin_id = node.node_nic_interface_cluster_plugins[0].id
|
||||
ClusterPlugin.set_attributes(cluster.id, plugin.id, enabled=True)
|
||||
|
||||
attributes = NodeNICInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_interface(interface)
|
||||
expected_attributes = {
|
||||
'plugin_a_with_nic_attributes': {
|
||||
'metadata': {
|
||||
'label': 'Test plugin',
|
||||
'nic_plugin_id': nic_plugin_id,
|
||||
'class': 'plugin'}}}
|
||||
expected_attributes['plugin_a_with_nic_attributes'].update(nic_config)
|
||||
|
||||
self.assertEqual(expected_attributes, attributes)
|
||||
|
||||
def test_get_all_attributes_by_interface_with_disabled_plugin(self):
|
||||
nic_config = self.env.get_default_plugin_nic_config()
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata=nic_config)
|
||||
cluster = self._create_test_cluster()
|
||||
node = self.env.create_nodes_w_interfaces_count(
|
||||
1, 1, **{"cluster_id": cluster.id})[0]
|
||||
interface = node.nic_interfaces[0]
|
||||
|
||||
attributes = NodeNICInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_interface(interface)
|
||||
|
||||
self.assertDictEqual({}, attributes)
|
||||
|
||||
def test_populate_nic_with_plugin_attributes(self):
|
||||
# create cluster with 2 nodes
|
||||
# install plugin with nic attributes which compatible with cluster
|
||||
meta = base.reflect_db_metadata()
|
||||
nic_config = self.env.get_default_plugin_nic_config()
|
||||
self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata=nic_config)
|
||||
|
||||
node_nic_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_nic_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(4, len(node_nic_interface_cluster_plugins))
|
||||
for item in node_nic_interface_cluster_plugins:
|
||||
self.assertDictEqual(nic_config, jsonutils.loads(item.attributes))
|
||||
|
||||
def test_populate_nic_with_empty_plugin_attributes(self):
|
||||
# create cluster with 2 nodes
|
||||
# install plugin without nic attributes which compatible with cluster
|
||||
meta = base.reflect_db_metadata()
|
||||
self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
self.env.create_plugin(
|
||||
name='plugin_b_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata={})
|
||||
|
||||
node_nic_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_nic_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(0, len(node_nic_interface_cluster_plugins))
|
||||
|
||||
def test_add_cluster_plugin_for_node_nic(self):
|
||||
# install plugins compatible with cluster
|
||||
# populate cluster with node
|
||||
meta = base.reflect_db_metadata()
|
||||
nic_config = self.env.get_default_plugin_nic_config()
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata=nic_config)
|
||||
self.env.create_plugin(
|
||||
name='plugin_b_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata={})
|
||||
self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
|
||||
node_nic_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_nic_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(4, len(node_nic_interface_cluster_plugins))
|
||||
for item in node_nic_interface_cluster_plugins:
|
||||
self.assertDictEqual(nic_config, jsonutils.loads(item.attributes))
|
||||
|
||||
def test_set_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
nic_config = self.env.get_default_plugin_nic_config()
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_nic_attributes',
|
||||
package_version='5.0.0',
|
||||
nic_attributes_metadata=nic_config)
|
||||
cluster = self._create_test_cluster()
|
||||
self.env.create_nodes_w_interfaces_count(
|
||||
1, 1, **{"cluster_id": cluster.id})[0]
|
||||
|
||||
node_nic_interface_cluster_plugin = self.db.execute(
|
||||
meta.tables['node_nic_interface_cluster_plugins'].select()
|
||||
).fetchall()[0]
|
||||
|
||||
_id = node_nic_interface_cluster_plugin.id
|
||||
attributes = {'test_attr': 'a'}
|
||||
NodeNICInterfaceClusterPlugin.set_attributes(_id, attributes)
|
||||
|
||||
node_nic_interface_cluster_plugin = self.db.execute(
|
||||
meta.tables['node_nic_interface_cluster_plugins'].select()
|
||||
).fetchall()[0]
|
||||
|
||||
self.assertDictEqual(
|
||||
attributes,
|
||||
jsonutils.loads(node_nic_interface_cluster_plugin[1]))
|
||||
|
||||
|
||||
class TestNodeBondInterfaceClusterPlugin(ExtraFunctions):
|
||||
|
||||
def test_set_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
bond_config = self.env.get_default_plugin_bond_config()
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
bond_attributes_metadata=bond_config)
|
||||
cluster = self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}])
|
||||
|
||||
for node in cluster.nodes:
|
||||
nic_names = [iface.name for iface in node.nic_interfaces]
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', nic_names, node.id,
|
||||
bond_properties={
|
||||
'type__': consts.BOND_TYPES.linux,
|
||||
'mode': consts.BOND_MODES.balance_rr},
|
||||
attrs=bond_config)
|
||||
|
||||
node_bond_interface_cluster_plugin = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
).fetchall()[0]
|
||||
|
||||
_id = node_bond_interface_cluster_plugin.id
|
||||
attributes = {'test_attr': 'a'}
|
||||
NodeBondInterfaceClusterPlugin.set_attributes(_id, attributes)
|
||||
|
||||
node_bond_interface_cluster_plugin = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
).fetchall()[0]
|
||||
|
||||
self.assertDictEqual(
|
||||
attributes,
|
||||
jsonutils.loads(node_bond_interface_cluster_plugin[1]))
|
||||
|
@ -423,14 +423,20 @@ class TestCheckBeforeDeploymentTask(BaseTestCase):
|
||||
)
|
||||
|
||||
def test_sriov_is_enabled_with_non_kvm_hypervisor(self):
|
||||
objects.NIC.update(self.node.nic_interfaces[0],
|
||||
{'interface_properties':
|
||||
{
|
||||
'sriov': {'enabled': True,
|
||||
'sriov_totalvfs': 4,
|
||||
'sriov_numfs': 2,
|
||||
'available': True},
|
||||
}})
|
||||
objects.NIC.update(self.node.nic_interfaces[0], {
|
||||
'attributes': {
|
||||
'sriov': {
|
||||
'enabled': {'value': True},
|
||||
'numfs': {'value': 2}
|
||||
}
|
||||
},
|
||||
'meta': {
|
||||
'sriov': {
|
||||
'available': True,
|
||||
'totalvfs': 4,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaisesRegexp(
|
||||
errors.InvalidData,
|
||||
|
Loading…
Reference in New Issue
Block a user