Bond attributes in interface API
Remove restrictions on set of BOND properties. Now BOND can have any attributes. Remove redundant `bond_properties`. Change-Id: I60d1a0628d84c5ba49bb5b45824d660297dacccc Implements: blueprint nics-and-nodes-attributes-via-plugin
This commit is contained in:
parent
040c1b4544
commit
1b9dff2dac
|
@ -208,15 +208,6 @@ BOND_MODES = Enum(
|
|||
)
|
||||
)
|
||||
|
||||
BOND_PROPERTIES = Enum(
|
||||
'mode',
|
||||
'xmit_hash_policy',
|
||||
'lacp_rate',
|
||||
'lacp',
|
||||
# not for orchestrator input
|
||||
'type__'
|
||||
)
|
||||
|
||||
BOND_XMIT_HASH_POLICY = Enum(
|
||||
'layer2',
|
||||
'layer2+3',
|
||||
|
|
|
@ -36,6 +36,7 @@ from nailgun.logger import logger
|
|||
from nailgun import objects
|
||||
from nailgun.settings import settings
|
||||
from nailgun import utils as nailgun_utils
|
||||
from nailgun.utils import get_in
|
||||
from nailgun.utils.restrictions import RestrictionBase
|
||||
|
||||
|
||||
|
@ -778,8 +779,13 @@ class NetworkManager(object):
|
|||
objects.NIC.update(current_iface, update)
|
||||
|
||||
objects.Node.clear_bonds(node_db)
|
||||
|
||||
for bond in bond_interfaces:
|
||||
if bond.get('bond_properties', {}).get('mode'):
|
||||
# updated via API
|
||||
if get_in(bond, 'attributes', 'mode', 'value', 'value'):
|
||||
mode = bond['attributes']['mode']['value']['value']
|
||||
# updated via network templates
|
||||
elif get_in(bond, 'bond_properties', 'mode'):
|
||||
mode = bond['bond_properties']['mode']
|
||||
else:
|
||||
mode = bond['mode']
|
||||
|
@ -787,9 +793,7 @@ class NetworkManager(object):
|
|||
'node': node_db,
|
||||
'name': bond['name'],
|
||||
'mode': mode,
|
||||
'mac': bond.get('mac'),
|
||||
'bond_properties': bond.get('bond_properties', {}),
|
||||
'attributes': bond.get('attributes', {})
|
||||
'mac': bond.get('mac')
|
||||
}
|
||||
bond_db = objects.Bond.create(data)
|
||||
|
||||
|
@ -802,9 +806,19 @@ class NetworkManager(object):
|
|||
node_nics = {nic['name']: nic for nic in node_db.nic_interfaces}
|
||||
slaves = [node_nics[n['name']] for n in bond['slaves']]
|
||||
|
||||
bond_attributes = bond.get('attributes', {})
|
||||
nailgun_utils.remove_key_from_dict(
|
||||
bond_attributes, 'bond_plugin_id')
|
||||
|
||||
bond_attributes = nailgun_utils.dict_merge(
|
||||
objects.Bond.get_attributes(bond_db),
|
||||
bond_attributes
|
||||
)
|
||||
|
||||
update = {
|
||||
'slaves': slaves,
|
||||
'offloading_modes': bond.get('offloading_modes', {})
|
||||
'offloading_modes': bond.get('offloading_modes', {}),
|
||||
'attributes': bond_attributes
|
||||
}
|
||||
objects.Bond.update(bond_db, update)
|
||||
|
||||
|
@ -1354,20 +1368,33 @@ class NetworkManager(object):
|
|||
|
||||
@classmethod
|
||||
def get_lnx_bond_properties(cls, bond):
|
||||
properties = {'mode': bond.mode}
|
||||
properties.update(bond.bond_properties)
|
||||
to_drop = [k for k in properties.keys() if k.endswith('__')]
|
||||
for prop in to_drop:
|
||||
properties.pop(prop)
|
||||
properties = {}
|
||||
attributes = nailgun_utils.dict_merge(
|
||||
{'mode': {'value': {'value': bond.mode}}}, bond.attributes)
|
||||
|
||||
def set_property(*args):
|
||||
if get_in(attributes, *args):
|
||||
temp_attrs = attributes
|
||||
for arg in args:
|
||||
value = temp_attrs[arg]
|
||||
temp_attrs = value
|
||||
properties[args[0]] = value
|
||||
|
||||
keys = (('mode', 'value', 'value'),
|
||||
('lacp', 'value', 'value'),
|
||||
('lacp_rate', 'value', 'value'),
|
||||
('xmit_hash_policy', 'value', 'value'))
|
||||
for key in keys:
|
||||
set_property(*key)
|
||||
|
||||
return properties
|
||||
|
||||
@classmethod
|
||||
def get_iface_properties(cls, iface):
|
||||
properties = {}
|
||||
if iface.attributes.get('mtu', {}).get('value', {}).get('value'):
|
||||
if get_in(iface.attributes, 'mtu', 'value', 'value'):
|
||||
properties['mtu'] = iface.attributes['mtu']['value']['value']
|
||||
if iface.attributes.get('offloading', {}).get('disable', {}).get(
|
||||
'value'):
|
||||
if get_in(iface.attributes, 'offloading', 'disable', 'value'):
|
||||
properties['vendor_specific'] = {
|
||||
'disable_offloading':
|
||||
iface.attributes['offloading']['disable']['value']
|
||||
|
|
|
@ -59,8 +59,15 @@ class Bond(DPDKMixin, NailgunObject):
|
|||
:param data: dictionary of key-value pairs as object fields
|
||||
:returns: instance of an object (model)
|
||||
"""
|
||||
instance.update(data)
|
||||
attributes = data.pop('attributes', None)
|
||||
if attributes:
|
||||
PluginManager.update_bond_attributes(attributes)
|
||||
instance.attributes = utils.dict_merge(
|
||||
instance.attributes, attributes)
|
||||
|
||||
instance = super(Bond, cls).update(instance, data)
|
||||
instance.offloading_modes = data.get('offloading_modes', {})
|
||||
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
|
@ -99,7 +106,7 @@ class Bond(DPDKMixin, NailgunObject):
|
|||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_bond_default_attributes(cls, cluster):
|
||||
def get_default_attributes(cls, cluster):
|
||||
"""Get native and plugin default attributes for bond.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
|
|
|
@ -287,7 +287,7 @@ class NIC(DPDKMixin, NailgunObject):
|
|||
:type instance: NodeNICInterface model
|
||||
:param data: Data to update
|
||||
:type data: dict
|
||||
:returns: None
|
||||
:returns: instance of an object (model)
|
||||
"""
|
||||
attributes = data.pop('attributes', None)
|
||||
if attributes:
|
||||
|
|
|
@ -43,9 +43,7 @@ class NodeInterfacesSerializer(BasicSerializer):
|
|||
'mac',
|
||||
'name',
|
||||
'type',
|
||||
'interface_properties',
|
||||
'mode',
|
||||
'bond_properties',
|
||||
'state',
|
||||
'assigned_networks',
|
||||
'offloading_modes'
|
||||
|
@ -68,7 +66,6 @@ class NodeInterfacesSerializer(BasicSerializer):
|
|||
'name',
|
||||
'type',
|
||||
'mode',
|
||||
'bond_properties',
|
||||
'state',
|
||||
'assigned_networks'
|
||||
)
|
||||
|
|
|
@ -881,14 +881,14 @@ class TestPublicNetworkAssigment(BaseIntegrationTest):
|
|||
3)
|
||||
self.env.make_bond_via_api(
|
||||
'ovsbond0', consts.BOND_MODES.balance_tcp, ['eth1', 'eth2'],
|
||||
node1['id'], bond_properties={'type__': consts.BOND_TYPES.ovs})
|
||||
node1['id'], attrs={'type__': {'value': consts.BOND_TYPES.ovs}})
|
||||
|
||||
node2, macs2 = self.create_node_with_preset_macs(cluster,
|
||||
['cinder'],
|
||||
3)
|
||||
self.env.make_bond_via_api(
|
||||
'ovsbond0', consts.BOND_MODES.balance_tcp, ['eth1', 'eth2'],
|
||||
node2['id'], bond_properties={'type__': consts.BOND_TYPES.ovs})
|
||||
node2['id'], attrs={'type__': {'value': consts.BOND_TYPES.ovs}})
|
||||
|
||||
self.check_network_assigments(node1, {
|
||||
'eth0': self.default_networks,
|
||||
|
|
|
@ -294,45 +294,57 @@ class TestHandlers(BaseIntegrationTest):
|
|||
'weight': 30
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'SR-IOV enabled',
|
||||
'label': 'Enable SR-IOV',
|
||||
'description': 'Single-root I/O Virtualization (SR-IOV) '
|
||||
'is a specification that, when implemented '
|
||||
'by a physical PCIe device, enables it to '
|
||||
'appear as multiple separate PCIe devices. '
|
||||
'This enables multiple virtualized guests '
|
||||
'to share direct access to the physical '
|
||||
'device, offering improved performance '
|
||||
'over an equivalent virtual device.',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
'value': False,
|
||||
'restrictions': [{
|
||||
"settings:common.libvirt_type.value != 'kvm'":
|
||||
"Only KVM hypervisor works with SR-IOV"
|
||||
}]
|
||||
},
|
||||
'numvfs': {
|
||||
'label': 'Virtual functions',
|
||||
'label': 'Number of Virtual Functions',
|
||||
'weight': 20,
|
||||
'type': 'number',
|
||||
'min': 0,
|
||||
'value': None
|
||||
'min': 1,
|
||||
'value': None,
|
||||
'restrictions': [
|
||||
"nic_attributes:sriov.enabled.value == false"]
|
||||
},
|
||||
'physnet': {
|
||||
'label': 'Physical network',
|
||||
'label': 'Physical Network Name',
|
||||
'weight': 30,
|
||||
'type': 'text',
|
||||
'value': 'physnet2'
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
resp_nic['meta']['sriov'],
|
||||
{
|
||||
'available': True,
|
||||
'pci_id': '1234:5678',
|
||||
'totalvfs': 8
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
resp_nic['attributes']['dpdk'],
|
||||
{
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'DPDK enabled',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
'value': 'physnet2',
|
||||
'regex': {
|
||||
'source': "^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$",
|
||||
'error': "Invalid physical network name"
|
||||
},
|
||||
'restrictions': [
|
||||
"nic_attributes:sriov.enabled.value == false",
|
||||
{
|
||||
'condition': "nic_attributes:sriov.physnet.value "
|
||||
"!= 'physnet2'",
|
||||
'message': "Only \"physnet2\" will be configured "
|
||||
"by Fuel in Neutron. Configuration of "
|
||||
"other physical networks is up to "
|
||||
"Operator or plugin. Fuel will just "
|
||||
"configure appropriate "
|
||||
"pci_passthrough_whitelist option in "
|
||||
"nova.conf for such interface and "
|
||||
"physical networks.",
|
||||
'action': "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
|
@ -385,10 +397,17 @@ class TestHandlers(BaseIntegrationTest):
|
|||
'weight': 40
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'DPDK enabled',
|
||||
'label': 'Enable DPDK',
|
||||
'description': 'The Data Plane Development Kit (DPDK) '
|
||||
'provides high-performance packet '
|
||||
'processing libraries and user space '
|
||||
'drivers.',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False
|
||||
'value': False,
|
||||
'restrictions': [{
|
||||
"settings:common.libvirt_type.value != 'kvm'":
|
||||
"Only KVM hypervisor works with DPDK"}]
|
||||
}
|
||||
})
|
||||
self.assertEqual(
|
||||
|
@ -631,7 +650,9 @@ class TestHandlers(BaseIntegrationTest):
|
|||
'label': 'MTU',
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': 1500
|
||||
'value': 1500,
|
||||
'min': 42,
|
||||
'max': 65536
|
||||
}
|
||||
}
|
||||
nodes_list = [{'id': node['id'], 'interfaces': [nic]}]
|
||||
|
@ -662,7 +683,9 @@ class TestHandlers(BaseIntegrationTest):
|
|||
'label': 'MTU',
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': 1500
|
||||
'value': 1500,
|
||||
'min': 42,
|
||||
'max': 65536
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1174,7 +1197,7 @@ class TestNICAttributesHandlers(BaseIntegrationTest):
|
|||
'offloading': {
|
||||
'disable': {
|
||||
'value': False,
|
||||
'label': 'Disable offloading',
|
||||
'label': 'Disable Offloading',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
|
@ -1183,9 +1206,8 @@ class TestNICAttributesHandlers(BaseIntegrationTest):
|
|||
'weight': 10
|
||||
},
|
||||
'modes': {
|
||||
'description': 'Offloading modes',
|
||||
'value': {},
|
||||
'label': 'Offloading modes',
|
||||
'label': 'Offloading Modes',
|
||||
'type': 'offloading_modes',
|
||||
'weight': 20
|
||||
}
|
||||
|
@ -1195,7 +1217,9 @@ class TestNICAttributesHandlers(BaseIntegrationTest):
|
|||
'value': None,
|
||||
'label': 'MTU',
|
||||
'type': 'number',
|
||||
'weight': 10
|
||||
'weight': 10,
|
||||
'min': 42,
|
||||
'max': 65536
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'MTU',
|
||||
|
@ -1203,40 +1227,81 @@ class TestNICAttributesHandlers(BaseIntegrationTest):
|
|||
}
|
||||
},
|
||||
'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
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'Enable SR-IOV',
|
||||
'description': 'Single-root I/O Virtualization (SR-IOV) '
|
||||
'is a specification that, when implemented '
|
||||
'by a physical PCIe device, enables it to '
|
||||
'appear as multiple separate PCIe devices. '
|
||||
'This enables multiple virtualized guests '
|
||||
'to share direct access to the physical '
|
||||
'device, offering improved performance '
|
||||
'over an equivalent virtual device.',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False,
|
||||
'restrictions': [{
|
||||
"settings:common.libvirt_type.value != 'kvm'":
|
||||
"Only KVM hypervisor works with SR-IOV"
|
||||
}]
|
||||
},
|
||||
'numvfs': {
|
||||
'value': None,
|
||||
'label': 'Virtual functions',
|
||||
'label': 'Number of Virtual Functions',
|
||||
'weight': 20,
|
||||
'type': 'number',
|
||||
'min': 0
|
||||
'min': 1,
|
||||
'value': None,
|
||||
'restrictions': [
|
||||
"nic_attributes:sriov.enabled.value == false"]
|
||||
},
|
||||
'physnet': {
|
||||
'label': 'Physical Network Name',
|
||||
'weight': 30,
|
||||
'type': 'text',
|
||||
'value': 'physnet2',
|
||||
'regex': {
|
||||
'source': "^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$",
|
||||
'error': "Invalid physical network name"
|
||||
},
|
||||
'restrictions': [
|
||||
"nic_attributes:sriov.enabled.value == false",
|
||||
{
|
||||
'condition': "nic_attributes:sriov.physnet.value "
|
||||
"!= 'physnet2'",
|
||||
'message': "Only \"physnet2\" will be configured "
|
||||
"by Fuel in Neutron. Configuration of "
|
||||
"other physical networks is up to "
|
||||
"Operator or plugin. Fuel will just "
|
||||
"configure appropriate "
|
||||
"pci_passthrough_whitelist option in "
|
||||
"nova.conf for such interface and "
|
||||
"physical networks.",
|
||||
'action': "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'dpdk': {
|
||||
'enabled': {
|
||||
'value': False,
|
||||
'label': 'DPDK enabled',
|
||||
'type': 'checkbox',
|
||||
'weight': 10
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
},
|
||||
'enabled': {
|
||||
'label': 'Enable DPDK',
|
||||
'description': 'The Data Plane Development Kit (DPDK) '
|
||||
'provides high-performance packet '
|
||||
'processing libraries and user space '
|
||||
'drivers.',
|
||||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False,
|
||||
'restrictions': [{
|
||||
"settings:common.libvirt_type.value != 'kvm'":
|
||||
"Only KVM hypervisor works with DPDK"}]
|
||||
}
|
||||
},
|
||||
'plugin_a_with_nic_attributes': {
|
||||
|
|
|
@ -163,20 +163,23 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
if iface_props is None:
|
||||
iface_props = {}
|
||||
|
||||
attributes = {
|
||||
'mode': {'value': {'value': bond_mode}},
|
||||
'xmit_hash_policy': {
|
||||
'value': {'value': BOND_XMIT_HASH_POLICY.layer2_3}},
|
||||
'lacp_rate': {'value': {'value': 'slow'}},
|
||||
'type__': {'value': bond_type}
|
||||
}
|
||||
attributes.update(iface_props)
|
||||
|
||||
self.data.append({
|
||||
"name": bond_name,
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": bond_mode,
|
||||
"xmit_hash_policy": BOND_XMIT_HASH_POLICY.layer2_3,
|
||||
"lacp_rate": "slow",
|
||||
"type__": bond_type
|
||||
},
|
||||
"attributes": iface_props,
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
{"name": self.empty_nic["name"]}],
|
||||
"assigned_networks": self.other_nic["assigned_networks"]
|
||||
'name': bond_name,
|
||||
'type': NETWORK_INTERFACE_TYPES.bond,
|
||||
'attributes': attributes,
|
||||
'slaves': [
|
||||
{'name': self.other_nic['name']},
|
||||
{'name': self.empty_nic['name']}],
|
||||
'assigned_networks': self.other_nic['assigned_networks']
|
||||
})
|
||||
self.other_nic["assigned_networks"] = []
|
||||
|
||||
|
@ -279,8 +282,8 @@ 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['bond_properties']['type__'] = BOND_TYPES.dpdkovs
|
||||
bond['bond_properties']['mode'] = BOND_MODES.balance_tcp
|
||||
bond['attributes']['type__']['value'] = BOND_TYPES.dpdkovs
|
||||
bond['attributes']['mode']['value']['value'] = BOND_MODES.balance_tcp
|
||||
self.node_nics_put_check_error(
|
||||
"Bond interface '{0}': DPDK should be enabled for 'dpdkovs' bond"
|
||||
" type".format(bond_name))
|
||||
|
@ -298,7 +301,9 @@ 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['attributes'] = {'dpdk': {'enabled': {'value': True}}}
|
||||
bond['attributes'] = {
|
||||
'type__': {'value': BOND_TYPES.linux},
|
||||
'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))
|
||||
|
@ -372,8 +377,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"mode": "unknown",
|
||||
"bond_properties": {
|
||||
'type__': BOND_TYPES.linux
|
||||
"attributes": {
|
||||
'type__': {'value': BOND_TYPES.linux}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -391,8 +396,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
'type__': BOND_TYPES.linux
|
||||
"attributes": {
|
||||
'type__': {'value': BOND_TYPES.linux}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -409,9 +414,10 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"xmit_hash_policy": BOND_XMIT_HASH_POLICY.layer2_3,
|
||||
'type__': BOND_TYPES.linux
|
||||
"attributes": {
|
||||
'xmit_hash_policy': {
|
||||
'value': {'value': BOND_XMIT_HASH_POLICY.layer2_3}},
|
||||
'type__': {'value': BOND_TYPES.linux}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -428,9 +434,9 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": 'unknown',
|
||||
'type__': BOND_TYPES.linux
|
||||
"attributes": {
|
||||
'type__': {'value': BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': 'unknown'}}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -443,26 +449,6 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
"Node '{0}': bond interface 'bond0' has unknown mode "
|
||||
"'unknown'".format(self.env.nodes[0]["id"]))
|
||||
|
||||
def test_nics_bond_create_failed_unknown_property(self):
|
||||
self.data.append({
|
||||
"name": 'bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": BOND_MODES.balance_xor,
|
||||
'type__': BOND_TYPES.linux,
|
||||
"policy": BOND_XMIT_HASH_POLICY.layer2_3
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
{"name": self.empty_nic["name"]}],
|
||||
"assigned_networks": self.other_nic["assigned_networks"]
|
||||
})
|
||||
self.other_nic["assigned_networks"] = []
|
||||
|
||||
self.node_nics_put_check_error(
|
||||
"Node '{0}', interface 'bond0': unknown bond property "
|
||||
"'policy'".format(self.env.nodes[0]["id"]))
|
||||
|
||||
def test_nics_bond_create_failed_no_slaves(self):
|
||||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
|
@ -497,8 +483,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"slaves": [
|
||||
|
@ -516,8 +502,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"slaves": [
|
||||
|
@ -537,8 +523,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -559,8 +545,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.other_nic["name"]},
|
||||
|
@ -578,8 +564,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"slaves": [
|
||||
|
@ -598,8 +584,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"slaves": [
|
||||
|
@ -622,8 +608,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'lnx-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.linux
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.linux}
|
||||
},
|
||||
"mode": mode,
|
||||
"slaves": [
|
||||
|
@ -648,8 +634,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": mode,
|
||||
"slaves": [
|
||||
|
@ -673,8 +659,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": mode,
|
||||
"slaves": [
|
||||
|
@ -697,8 +683,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": mode,
|
||||
"slaves": [
|
||||
|
@ -761,8 +747,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"type__": BOND_TYPES.ovs
|
||||
"attributes": {
|
||||
"type__": {'value': BOND_TYPES.ovs}
|
||||
},
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"slaves": [
|
||||
|
@ -789,8 +775,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": BOND_MODES.balance_slb
|
||||
"attributes": {
|
||||
"mode": {'value': BOND_MODES.balance_slb}
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.admin_nic["name"]},
|
||||
|
@ -799,9 +785,9 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
})
|
||||
self.node_nics_put_check_error(
|
||||
"Node '{0}', bond interface 'ovs-bond0': doesn't have "
|
||||
"bond_properties.type__".format(self.env.nodes[0]["id"]))
|
||||
"attributes.type__".format(self.env.nodes[0]["id"]))
|
||||
|
||||
def test_nics_bond_create_failed_without_bond_properties(self):
|
||||
def test_nics_bond_create_failed_without_attributes(self):
|
||||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
|
@ -813,15 +799,15 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
})
|
||||
self.node_nics_put_check_error(
|
||||
"Node '{0}', bond interface 'ovs-bond0': doesn't have "
|
||||
"bond_properties".format(self.env.nodes[0]["id"]))
|
||||
"attributes".format(self.env.nodes[0]["id"]))
|
||||
|
||||
def test_nics_bond_create_failed_with_unexpected_type__(self):
|
||||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": BOND_MODES.balance_slb,
|
||||
"type__": 'unexpected_type',
|
||||
"attributes": {
|
||||
"mode": {'value': {'value': BOND_MODES.balance_slb}},
|
||||
"type__": {'value': 'unexpected_type'},
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.admin_nic["name"]},
|
||||
|
@ -844,9 +830,9 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
self.data.append({
|
||||
"name": 'ovs-bond0',
|
||||
"type": NETWORK_INTERFACE_TYPES.bond,
|
||||
"bond_properties": {
|
||||
"mode": BOND_MODES.balance_rr,
|
||||
"type__": BOND_TYPES.ovs,
|
||||
"attributes": {
|
||||
"mode": {'value': {'value': BOND_MODES.balance_rr}},
|
||||
"type__": {'value': BOND_TYPES.ovs},
|
||||
},
|
||||
"slaves": [
|
||||
{"name": self.admin_nic["name"]},
|
||||
|
@ -869,15 +855,18 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
|||
class TestBondAttributesDefaultsHandler(BaseIntegrationTest):
|
||||
|
||||
EXPECTED_ATTRIBUTES = {
|
||||
'type__': {
|
||||
'value': None,
|
||||
'type': 'hidden'
|
||||
},
|
||||
'mode': {
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'select',
|
||||
'value': 'balance-rr',
|
||||
'label': 'Mode',
|
||||
'values': [
|
||||
{'data': 'balance-rr', 'label': 'balance-rr'}
|
||||
]
|
||||
'value': '',
|
||||
'label': 'Mode'
|
||||
},
|
||||
'metadata': {
|
||||
'weight': 10,
|
||||
|
@ -893,13 +882,13 @@ class TestBondAttributesDefaultsHandler(BaseIntegrationTest):
|
|||
'weight': 10,
|
||||
'type': 'checkbox',
|
||||
'value': False,
|
||||
'label': 'Disable offloading'
|
||||
'label': 'Disable Offloading'
|
||||
},
|
||||
'modes': {
|
||||
'weight': 20,
|
||||
'type': 'offloading_modes',
|
||||
'value': {},
|
||||
'label': 'Offloading modes',
|
||||
'label': 'Offloading Modes',
|
||||
'description': 'Offloading modes'
|
||||
}
|
||||
},
|
||||
|
@ -908,6 +897,84 @@ class TestBondAttributesDefaultsHandler(BaseIntegrationTest):
|
|||
'weight': 30,
|
||||
'label': 'MTU'
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
'value': None,
|
||||
'label': 'MTU',
|
||||
'min': 42,
|
||||
'max': 65536
|
||||
}
|
||||
},
|
||||
'dpdk': {
|
||||
'enabled': {
|
||||
'value': False,
|
||||
'label': 'Enable DPDK',
|
||||
'description': 'The Data Plane Development Kit (DPDK) '
|
||||
'provides high-performance packet processing '
|
||||
'libraries and user space drivers.',
|
||||
'type': 'checkbox',
|
||||
'weight': 10,
|
||||
'restrictions': [{
|
||||
"settings:common.libvirt_type.value != 'kvm'":
|
||||
"Only KVM hypervisor works with DPDK"
|
||||
}]
|
||||
},
|
||||
'metadata': {
|
||||
'label': 'DPDK',
|
||||
'weight': 40
|
||||
}
|
||||
},
|
||||
'lacp': {
|
||||
'metadata': {
|
||||
'weight': 50,
|
||||
'label': 'Lacp'
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'select',
|
||||
'value': '',
|
||||
'label': 'Lacp'
|
||||
}
|
||||
},
|
||||
'lacp_rate': {
|
||||
'metadata': {
|
||||
'weight': 60,
|
||||
'value': '',
|
||||
'label': 'Lacp'
|
||||
}
|
||||
},
|
||||
'lacp_rate': {
|
||||
'metadata': {
|
||||
'weight': 60,
|
||||
'label': 'Lacp rate'
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'select',
|
||||
'value': '',
|
||||
'label': 'Lacp rate'
|
||||
}
|
||||
},
|
||||
'xmit_hash_policy': {
|
||||
'metadata': {
|
||||
'weight': 70,
|
||||
'label': 'Xmit hash policy'
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'select',
|
||||
'value': '',
|
||||
'label': 'Xmit hash policy'
|
||||
}
|
||||
},
|
||||
'plugin_a_with_bond_attributes': {
|
||||
'plugin_name_text': {
|
||||
'weight': 25,
|
||||
'type': 'text',
|
||||
'value': 'value',
|
||||
'label': 'label',
|
||||
},
|
||||
'value': {
|
||||
'weight': 10,
|
||||
'type': 'number',
|
||||
|
|
|
@ -181,6 +181,22 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
|||
'mode': "active-backup",
|
||||
'assigned_networks': nets,
|
||||
'attributes': {
|
||||
'type__': {
|
||||
'type': 'hidden',
|
||||
'value': 'dpdkovs'
|
||||
},
|
||||
'mode': {
|
||||
'metadata': {
|
||||
'label': "Mode",
|
||||
'weight': 10
|
||||
},
|
||||
'value': {
|
||||
'label': "Mode",
|
||||
'weight': 10,
|
||||
'type': "select",
|
||||
'value': 'active-backup'
|
||||
}
|
||||
},
|
||||
'offloading': {
|
||||
'disable': {
|
||||
'value': True,
|
||||
|
@ -225,10 +241,6 @@ class TestDPDKValidation(BaseNetAssignmentValidatorTest):
|
|||
}
|
||||
}
|
||||
},
|
||||
'bond_properties': {
|
||||
'mode': "active-backup",
|
||||
'type__': "dpdkovs"
|
||||
},
|
||||
'slaves': [
|
||||
{'name': nic_2['name']},
|
||||
{'name': nic_3['name']}
|
||||
|
|
|
@ -389,28 +389,21 @@ class NetAssignmentValidator(BasicValidator):
|
|||
"must have name".format(node['id'], iface['name']),
|
||||
log_message=True
|
||||
)
|
||||
if 'bond_properties' not in iface:
|
||||
if 'attributes' not in iface:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}', bond interface '{1}': doesn't have "
|
||||
"bond_properties".format(node['id'], iface['name']),
|
||||
"attributes".format(node['id'], iface['name']),
|
||||
log_message=True
|
||||
)
|
||||
for k in iface['bond_properties']:
|
||||
if k not in consts.BOND_PROPERTIES:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}', interface '{1}': unknown bond "
|
||||
"property '{2}'".format(
|
||||
node['id'], iface['name'], k),
|
||||
log_message=True
|
||||
)
|
||||
if 'type__' not in iface['bond_properties']:
|
||||
if 'type__' not in iface['attributes']:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}', bond interface '{1}': doesn't have "
|
||||
"bond_properties.type__".format(
|
||||
"attributes.type__".format(
|
||||
node['id'], iface['name']),
|
||||
log_message=True
|
||||
)
|
||||
bond_type = iface['bond_properties']['type__']
|
||||
|
||||
bond_type = iface['attributes']['type__']['value']
|
||||
if bond_type not in consts.BOND_TYPES:
|
||||
raise errors.InvalidData(
|
||||
"Node '{0}', interface '{1}': unknown type__ '{2}'. "
|
||||
|
@ -487,8 +480,8 @@ class NetAssignmentValidator(BasicValidator):
|
|||
bond_mode = None
|
||||
if 'mode' in iface:
|
||||
bond_mode = iface['mode']
|
||||
if 'mode' in iface.get('bond_properties', {}):
|
||||
bond_mode = iface['bond_properties']['mode']
|
||||
if 'mode' in iface.get('attributes', {}):
|
||||
bond_mode = iface['attributes']['mode']['value']['value']
|
||||
return bond_mode
|
||||
|
||||
@classmethod
|
||||
|
@ -647,6 +640,8 @@ class NetAssignmentValidator(BasicValidator):
|
|||
if db_iface is None:
|
||||
db_iface = cls._get_iface_by_name(iface['name'], db_interfaces)
|
||||
|
||||
attributes = iface.get('attributes', {})
|
||||
bond_type = attributes.get('type__', {}).get('value')
|
||||
if db_iface is None:
|
||||
# looks like user creates new bond
|
||||
# let's check every slave in input data
|
||||
|
@ -659,21 +654,16 @@ class NetAssignmentValidator(BasicValidator):
|
|||
|
||||
hw_available &= objects.NIC.dpdk_available(
|
||||
slave_iface, dpdk_drivers)
|
||||
|
||||
attributes = iface.get('attributes', {})
|
||||
bond_type = iface.get('bond_properties', {}).get('type__')
|
||||
else:
|
||||
if iface['type'] == consts.NETWORK_INTERFACE_TYPES.ether:
|
||||
iface_cls = objects.NIC
|
||||
elif iface['type'] == consts.NETWORK_INTERFACE_TYPES.bond:
|
||||
iface_cls = objects.Bond
|
||||
bond_type = iface.get('bond_properties', {}).get(
|
||||
'type__', db_iface.bond_properties.get('type__'))
|
||||
bond_type = bond_type or db_iface.attributes.get(
|
||||
'type__', {}).get('value')
|
||||
hw_available = iface_cls.dpdk_available(db_iface, dpdk_drivers)
|
||||
attributes = utils.dict_merge(
|
||||
db_iface.attributes,
|
||||
iface.get('attributes', {})
|
||||
)
|
||||
db_iface.attributes, attributes)
|
||||
|
||||
enabled = attributes.get('dpdk', {}).get('enabled', {}).get(
|
||||
'value', False)
|
||||
|
|
|
@ -435,8 +435,8 @@
|
|||
bonding:
|
||||
availability:
|
||||
- dpdkovs: "'experimental' in version:feature_groups and interface:pxe == false and
|
||||
interface:attributes.dpdk.enabled.value and not interface:attributes.sriov.enabled.value"
|
||||
- linux: "not interface:attributes.sriov.enabled.value"
|
||||
nic_attributes:dpdk.enabled.value and not nic_attributes:sriov.enabled.value"
|
||||
- linux: "not nic_attributes:sriov.enabled.value"
|
||||
properties:
|
||||
linux:
|
||||
mode:
|
||||
|
@ -2110,14 +2110,13 @@
|
|||
label: "Offloading"
|
||||
weight: 10
|
||||
disable:
|
||||
label: "Disable offloading"
|
||||
label: "Disable Offloading"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
modes:
|
||||
label: "Offloading modes"
|
||||
label: "Offloading Modes"
|
||||
weight: 20
|
||||
description: "Offloading modes"
|
||||
type: "offloading_modes"
|
||||
value: {}
|
||||
mtu:
|
||||
|
@ -2129,36 +2128,57 @@
|
|||
weight: 10
|
||||
type: "number"
|
||||
value: null
|
||||
min: 42
|
||||
max: 65536
|
||||
sriov:
|
||||
metadata:
|
||||
label: "SR-IOV"
|
||||
weight: 30
|
||||
enabled:
|
||||
label: "SR-IOV enabled"
|
||||
label: "Enable SR-IOV"
|
||||
description: 'Single-root I/O Virtualization (SR-IOV) is a specification that, when implemented by a physical PCIe device, enables it to appear as multiple separate PCIe devices. This enables multiple virtualized guests to share direct access to the physical device, offering improved performance over an equivalent virtual device.'
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
restrictions:
|
||||
- "settings:common.libvirt_type.value != 'kvm'": "Only KVM hypervisor works with SR-IOV"
|
||||
numvfs:
|
||||
label: "Virtual functions"
|
||||
label: "Number of Virtual Functions"
|
||||
weight: 20
|
||||
type: "number"
|
||||
min: 0
|
||||
min: 1
|
||||
value: null
|
||||
restrictions:
|
||||
- "nic_attributes:sriov.enabled.value == false"
|
||||
physnet:
|
||||
label: "Physical network"
|
||||
label: "Physical Network Name"
|
||||
weight: 30
|
||||
type: "text"
|
||||
value: ''
|
||||
value: ""
|
||||
regex:
|
||||
source: "^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$"
|
||||
error: "Invalid physical network name"
|
||||
restrictions:
|
||||
- "nic_attributes:sriov.enabled.value == false"
|
||||
- condition: "nic_attributes:sriov.physnet.value != 'physnet2'"
|
||||
message: "Only \"physnet2\" will be configured by Fuel in Neutron. Configuration of other physical networks is up to Operator or plugin. Fuel will just configure appropriate pci_passthrough_whitelist option in nova.conf for such interface and physical networks."
|
||||
action: "none"
|
||||
dpdk:
|
||||
metadata:
|
||||
label: "DPDK"
|
||||
weight: 40
|
||||
enabled:
|
||||
label: "DPDK enabled"
|
||||
label: "Enable DPDK"
|
||||
description: 'The Data Plane Development Kit (DPDK) provides high-performance packet processing libraries and user space drivers.'
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
restrictions:
|
||||
- "settings:common.libvirt_type.value != 'kvm'": "Only KVM hypervisor works with DPDK"
|
||||
bond_attributes:
|
||||
type__:
|
||||
type: 'hidden'
|
||||
value: null
|
||||
mode:
|
||||
metadata:
|
||||
label: "Mode"
|
||||
|
@ -2167,22 +2187,18 @@
|
|||
label: "Mode"
|
||||
weight: 10
|
||||
type: "select"
|
||||
values:
|
||||
-
|
||||
label: balance-rr
|
||||
data: balance-rr
|
||||
value: balance-rr
|
||||
value: ''
|
||||
offloading:
|
||||
metadata:
|
||||
label: "Offloading"
|
||||
weight: 20
|
||||
disable:
|
||||
label: "Disable offloading"
|
||||
label: "Disable Offloading"
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
modes:
|
||||
label: "Offloading modes"
|
||||
label: "Offloading Modes"
|
||||
weight: 20
|
||||
description: "Offloading modes"
|
||||
type: "offloading_modes"
|
||||
|
@ -2196,6 +2212,47 @@
|
|||
weight: 10
|
||||
type: "number"
|
||||
value: null
|
||||
min: 42
|
||||
max: 65536
|
||||
dpdk:
|
||||
metadata:
|
||||
label: "DPDK"
|
||||
weight: 40
|
||||
enabled:
|
||||
label: "Enable DPDK"
|
||||
description: 'The Data Plane Development Kit (DPDK) provides high-performance packet processing libraries and user space drivers.'
|
||||
weight: 10
|
||||
type: "checkbox"
|
||||
value: False
|
||||
restrictions:
|
||||
- "settings:common.libvirt_type.value != 'kvm'": "Only KVM hypervisor works with DPDK"
|
||||
lacp:
|
||||
metadata:
|
||||
label: "Lacp"
|
||||
weight: 50
|
||||
value:
|
||||
label: "Lacp"
|
||||
weight: 10
|
||||
type: "select"
|
||||
value: ''
|
||||
lacp_rate:
|
||||
metadata:
|
||||
label: "Lacp rate"
|
||||
weight: 60
|
||||
value:
|
||||
label: "Lacp rate"
|
||||
weight: 10
|
||||
type: "select"
|
||||
value: ''
|
||||
xmit_hash_policy:
|
||||
metadata:
|
||||
label: "Xmit hash policy"
|
||||
weight: 70
|
||||
value:
|
||||
label: "Xmit hash policy"
|
||||
weight: 10
|
||||
type: "select"
|
||||
value: ''
|
||||
modes: ['ha_compact']
|
||||
extensions: ['volume_manager', 'network_manager']
|
||||
- pk: 1
|
||||
|
|
|
@ -1332,7 +1332,7 @@ class Node(NailgunObject):
|
|||
instance.full_name))
|
||||
return
|
||||
|
||||
return Bond.get_bond_default_attributes(instance.cluster)
|
||||
return Bond.get_default_attributes(instance.cluster)
|
||||
|
||||
@classmethod
|
||||
def create_nic_attributes(cls, instance):
|
||||
|
|
|
@ -539,6 +539,7 @@ class NodeNICInterfaceClusterPlugin(BasicNodeClusterPlugin):
|
|||
cls.model.attributes
|
||||
).join(
|
||||
models.ClusterPlugin,
|
||||
models.Plugin
|
||||
).filter(
|
||||
cls.model.interface_id == interface.id
|
||||
).filter(
|
||||
|
|
|
@ -500,7 +500,6 @@ 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.
|
||||
|
@ -550,7 +549,7 @@ class PluginManager(object):
|
|||
|
||||
for plugin in plugins:
|
||||
metadata = plugin.pop('metadata')
|
||||
NodeNICInterfaceClusterPlugin.\
|
||||
NodeBondInterfaceClusterPlugin.\
|
||||
set_attributes(
|
||||
metadata['bond_plugin_id'],
|
||||
plugin
|
||||
|
|
|
@ -1328,9 +1328,8 @@ class EnvironmentManager(object):
|
|||
"Nothing to verify - try creating cluster"
|
||||
)
|
||||
|
||||
def make_bond_via_api(self, bond_name, bond_mode, nic_names, node_id=None,
|
||||
bond_properties=None, interface_properties=None,
|
||||
attrs=None):
|
||||
def make_bond_via_api(self, bond_name, bond_mode, nic_names,
|
||||
node_id=None, attrs=None):
|
||||
if not node_id:
|
||||
node_id = self.nodes[0]["id"]
|
||||
resp = self.app.get(
|
||||
|
@ -1361,15 +1360,6 @@ class EnvironmentManager(object):
|
|||
"assigned_networks": assigned_nets,
|
||||
"attributes": attrs or {}
|
||||
}
|
||||
if bond_properties:
|
||||
bond_dict["bond_properties"] = bond_properties
|
||||
else:
|
||||
bond_dict["bond_properties"] = {}
|
||||
|
||||
if interface_properties:
|
||||
bond_dict["interface_properties"] = interface_properties
|
||||
else:
|
||||
bond_dict["interface_properties"] = {}
|
||||
data.append(bond_dict)
|
||||
resp = self.node_nics_put(node_id, data)
|
||||
self.tester.assertEqual(resp.status_code, 200)
|
||||
|
|
|
@ -623,9 +623,10 @@ 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})
|
||||
attrs={
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {
|
||||
'value': {'value': consts.BOND_MODES.balance_rr}}})
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
for node in facts:
|
||||
|
@ -671,9 +672,10 @@ 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})
|
||||
attrs={
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {
|
||||
'value': {'value': consts.BOND_MODES.balance_rr}}})
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
for node in facts:
|
||||
|
@ -946,12 +948,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
|
||||
},
|
||||
attrs={'mtu': {'value': {'value': 9000}}},
|
||||
interface_properties={'mtu': 9000}
|
||||
attrs={
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mtu': {'value': {'value': 9000}},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}
|
||||
}
|
||||
)
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
|
@ -1092,14 +1093,22 @@ 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.l_802_3ad,
|
||||
'xmit_hash_policy': consts.BOND_XMIT_HASH_POLICY.layer2,
|
||||
'lacp_rate': consts.BOND_LACP_RATES.slow,
|
||||
'type__': consts.BOND_TYPES.linux
|
||||
},
|
||||
attrs={'mtu': {'value': {'value': 9000}}},
|
||||
interface_properties={'mtu': 9000}
|
||||
attrs={
|
||||
'mtu': {
|
||||
'value': {
|
||||
'value': 9000}},
|
||||
'mode': {
|
||||
'value': {
|
||||
'value': consts.BOND_MODES.l_802_3ad}},
|
||||
'xmit_hash_policy': {
|
||||
'value': {
|
||||
'value': consts.BOND_XMIT_HASH_POLICY.layer2}},
|
||||
'lacp_rate': {
|
||||
'value': {
|
||||
'value': consts.BOND_LACP_RATES.slow}},
|
||||
'type__': {
|
||||
'value': consts.BOND_TYPES.linux}
|
||||
}
|
||||
)
|
||||
serializer = self.create_serializer(cluster)
|
||||
facts = serializer.serialize(cluster, cluster.nodes)['nodes']
|
||||
|
@ -2235,13 +2244,9 @@ class TestNeutronOrchestratorSerializerBonds(OrchestratorSerializerTestBase):
|
|||
def check_bond_with_mode(self, mode, bond_type):
|
||||
cluster = self.create_env()
|
||||
for node in cluster.nodes:
|
||||
self.env.make_bond_via_api('ovsbond0',
|
||||
mode,
|
||||
['eth1', 'eth2'],
|
||||
node.id,
|
||||
bond_properties={
|
||||
'type__': bond_type}
|
||||
)
|
||||
self.env.make_bond_via_api(
|
||||
'ovsbond0', mode, ['eth1', 'eth2'], node.id,
|
||||
attrs={'type__': {'value': bond_type}})
|
||||
facts = self.serialize(cluster)
|
||||
for node in facts['nodes']:
|
||||
transforms = node['network_scheme']['transformations']
|
||||
|
|
|
@ -202,7 +202,7 @@ class TestDeploymentAttributesSerialization90(
|
|||
self._check_dpdk_serializing(has_vlan_tag=True)
|
||||
|
||||
@mock.patch('nailgun.objects.Release.get_supported_dpdk_drivers')
|
||||
def _check_dpdk_bond_serializing(self, bond_properties, drivers_mock):
|
||||
def _check_dpdk_bond_serializing(self, attributes, drivers_mock):
|
||||
drivers_mock.return_value = {
|
||||
'driver_1': ['test_id:1', 'test_id:2']
|
||||
}
|
||||
|
@ -237,13 +237,14 @@ class TestDeploymentAttributesSerialization90(
|
|||
if net['name'] == 'private':
|
||||
networks_for_bond.append(first_nic_networks.pop(i))
|
||||
break
|
||||
attributes.update(
|
||||
{'dpdk': {'enabled': {'value': True}}})
|
||||
bond_interface = {
|
||||
'name': bond_interface_name,
|
||||
'type': consts.NETWORK_INTERFACE_TYPES.bond,
|
||||
'slaves': nics_for_bond,
|
||||
'assigned_networks': networks_for_bond,
|
||||
'bond_properties': bond_properties,
|
||||
'attributes': {'dpdk': {'enabled': {'value': True}}}}
|
||||
'attributes': attributes}
|
||||
interfaces.append(bond_interface)
|
||||
self.env.node_nics_put(node.id, interfaces)
|
||||
objects.Cluster.prepare_for_deployment(self.cluster_db)
|
||||
|
@ -271,52 +272,58 @@ class TestDeploymentAttributesSerialization90(
|
|||
transformations)
|
||||
self.assertEqual(len(dpdk_bonds), 1)
|
||||
self.assertEqual(dpdk_bonds[0]['bridge'], br_name)
|
||||
self.assertEqual(private_br.get('vendor_specific'),
|
||||
vendor_specific)
|
||||
self.assertEqual(dpdk_bonds[0].get('provider'),
|
||||
consts.NEUTRON_L23_PROVIDERS.dpdkovs)
|
||||
self.assertEqual(dpdk_bonds[0].get('bond_properties').get('mode'),
|
||||
bond_interface['bond_properties'].get('mode'))
|
||||
self.assertEqual(
|
||||
private_br.get('vendor_specific'),
|
||||
vendor_specific)
|
||||
self.assertEqual(
|
||||
dpdk_bonds[0].get('provider'),
|
||||
consts.NEUTRON_L23_PROVIDERS.dpdkovs)
|
||||
self.assertEqual(
|
||||
dpdk_bonds[0].get('bond_properties').get('mode'),
|
||||
attributes.get('mode', {}).get('value', {}).get('value'))
|
||||
interfaces = serialised_node['network_scheme']['interfaces']
|
||||
|
||||
for iface in nics_for_bond:
|
||||
dpdk_interface = interfaces[iface['name']]
|
||||
vendor_specific = dpdk_interface.get('vendor_specific', {})
|
||||
self.assertEqual(vendor_specific.get('dpdk_driver'), 'driver_1')
|
||||
|
||||
def test_serialization_with_dpdk_on_bond(self):
|
||||
bond_properties = {
|
||||
'mode': consts.BOND_MODES.balance_slb,
|
||||
'type__': consts.BOND_TYPES.dpdkovs,
|
||||
attributes = {
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_slb}},
|
||||
'type__': {'value': consts.BOND_TYPES.dpdkovs}
|
||||
}
|
||||
|
||||
self._check_dpdk_bond_serializing(bond_properties)
|
||||
self._check_dpdk_bond_serializing(attributes)
|
||||
|
||||
def test_serialization_with_dpdk_on_lacp_bond(self):
|
||||
bond_properties = {
|
||||
'mode': consts.BOND_MODES.balance_tcp,
|
||||
'lacp': 'active',
|
||||
'lacp_rate': 'fast',
|
||||
'xmit_hash_policy': 'layer2',
|
||||
'type__': consts.BOND_TYPES.dpdkovs}
|
||||
self._check_dpdk_bond_serializing(bond_properties)
|
||||
attributes = {
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_tcp}},
|
||||
'lacp': {'value': {'value': 'active'}},
|
||||
'lacp_rate': {'value': {'value': 'fast'}},
|
||||
'xmit_hash_policy': {'value': {'value': 'layer2'}},
|
||||
'type__': {'value': consts.BOND_TYPES.dpdkovs}
|
||||
}
|
||||
self._check_dpdk_bond_serializing(attributes)
|
||||
|
||||
def test_serialization_with_vxlan_dpdk_on_bond(self):
|
||||
self._create_cluster_with_vxlan()
|
||||
bond_properties = {
|
||||
'mode': consts.BOND_MODES.balance_slb,
|
||||
'type__': consts.BOND_TYPES.dpdkovs,
|
||||
attributes = {
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_slb}},
|
||||
'type__': {'value': consts.BOND_TYPES.dpdkovs}
|
||||
}
|
||||
self._check_dpdk_bond_serializing(bond_properties)
|
||||
self._check_dpdk_bond_serializing(attributes)
|
||||
|
||||
def test_serialization_with_vxlan_dpdk_on_lacp_bond(self):
|
||||
self._create_cluster_with_vxlan()
|
||||
bond_properties = {
|
||||
'mode': consts.BOND_MODES.balance_tcp,
|
||||
'lacp': 'active',
|
||||
'lacp_rate': 'fast',
|
||||
'xmit_hash_policy': 'layer2',
|
||||
'type__': consts.BOND_TYPES.dpdkovs}
|
||||
self._check_dpdk_bond_serializing(bond_properties)
|
||||
attributes = {
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_tcp}},
|
||||
'lacp': {'value': {'value': 'active'}},
|
||||
'lacp_rate': {'value': {'value': 'fast'}},
|
||||
'xmit_hash_policy': {'value': {'value': 'layer2'}},
|
||||
'type__': {'value': consts.BOND_TYPES.dpdkovs}
|
||||
}
|
||||
self._check_dpdk_bond_serializing(attributes)
|
||||
|
||||
def test_attributes_cpu_pinning(self):
|
||||
numa_nodes = [
|
||||
|
|
|
@ -209,14 +209,11 @@ class TestProvisioningSerializer(BaseIntegrationTest):
|
|||
# get node from db
|
||||
node_db = objects.Node.get_by_uid(node['id'])
|
||||
# bond admin iface
|
||||
self.env.make_bond_via_api('lnx_bond',
|
||||
'',
|
||||
['eth1', 'eth4'],
|
||||
node['id'],
|
||||
bond_properties={
|
||||
'mode': consts.BOND_MODES.balance_rr,
|
||||
'type__': consts.BOND_TYPES.linux
|
||||
})
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', ['eth1', 'eth4'], node['id'],
|
||||
attrs={
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}})
|
||||
# check serialized data
|
||||
serialized_node = ps.serialize(self.cluster_db, [node_db])['nodes'][0]
|
||||
out_mac = serialized_node['kernel_options']['netcfg/choose_interface']
|
||||
|
|
|
@ -289,7 +289,7 @@ class TestNetworkVerificationWithBonds(BaseIntegrationTest):
|
|||
self.env.make_bond_via_api(
|
||||
"ovs-bond0", consts.BOND_MODES.balance_slb,
|
||||
[other_nic["name"], empty_nic["name"]], node["id"],
|
||||
bond_properties={'type__': consts.BOND_TYPES.ovs})
|
||||
attrs={'type__': {'value': consts.BOND_TYPES.ovs}})
|
||||
self.verify_bonds(node)
|
||||
|
||||
def verify_nics(self, node):
|
||||
|
|
|
@ -261,7 +261,7 @@ class TestInstallationInfo(BaseTestCase):
|
|||
self.env.make_bond_via_api(
|
||||
'bond0', consts.BOND_MODES.active_backup,
|
||||
['eth1', 'eth2'], node_id=self.env.nodes[0].id,
|
||||
bond_properties={'type__': consts.BOND_TYPES.linux})
|
||||
attrs={'type__': {'value': consts.BOND_TYPES.linux}})
|
||||
nodes_info = info.get_nodes_info(self.env.nodes)
|
||||
self.assertEquals(len(self.env.nodes), len(nodes_info))
|
||||
for idx, node in enumerate(self.env.nodes):
|
||||
|
|
|
@ -485,6 +485,138 @@ class TestNodeNICInterfaceClusterPlugin(ExtraFunctions):
|
|||
|
||||
class TestNodeBondInterfaceClusterPlugin(ExtraFunctions):
|
||||
|
||||
def test_get_all_attributes_by_bond_with_enabled_plugin(self):
|
||||
plugin_bond_config = self.env.get_default_plugin_bond_config()
|
||||
plugin = self.env.create_plugin(
|
||||
name='plugin_a_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
bond_attributes_metadata=plugin_bond_config)
|
||||
cluster = self._create_test_cluster()
|
||||
node = self.env.create_nodes_w_interfaces_count(
|
||||
1, 2, **{"cluster_id": cluster.id})[0]
|
||||
bond_config = {
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}}
|
||||
nic_names = [iface.name for iface in node.nic_interfaces]
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', nic_names, node.id, attrs=bond_config)
|
||||
bond = node.bond_interfaces[0]
|
||||
bond_plugin_id = node.node_bond_interface_cluster_plugins[0].id
|
||||
ClusterPlugin.set_attributes(cluster.id, plugin.id, enabled=True)
|
||||
|
||||
attributes = NodeBondInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_bond(bond)
|
||||
expected_attributes = {
|
||||
'plugin_a_with_bond_attributes': {
|
||||
'metadata': {
|
||||
'label': 'Test plugin',
|
||||
'bond_plugin_id': bond_plugin_id,
|
||||
'class': 'plugin'}}}
|
||||
expected_attributes['plugin_a_with_bond_attributes'].update(
|
||||
plugin_bond_config)
|
||||
|
||||
self.assertEqual(expected_attributes, attributes)
|
||||
|
||||
def test_get_all_attributes_by_bond_with_disabled_plugin(self):
|
||||
plugin_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=plugin_bond_config)
|
||||
cluster = self._create_test_cluster()
|
||||
node = self.env.create_nodes_w_interfaces_count(
|
||||
1, 2, **{"cluster_id": cluster.id})[0]
|
||||
bond_config = {
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}}
|
||||
nic_names = [iface.name for iface in node.nic_interfaces]
|
||||
self.env.make_bond_via_api(
|
||||
'lnx_bond', '', nic_names, node.id, attrs=bond_config)
|
||||
bond = node.bond_interfaces[0]
|
||||
|
||||
attributes = NodeBondInterfaceClusterPlugin.\
|
||||
get_all_enabled_attributes_by_bond(bond)
|
||||
|
||||
self.assertDictEqual({}, attributes)
|
||||
|
||||
def test_populate_bond_with_plugin_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
plugin_bond_config = self.env.get_default_plugin_bond_config()
|
||||
cluster = self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
bond_config = {
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}}
|
||||
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, attrs=bond_config)
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
bond_attributes_metadata=plugin_bond_config)
|
||||
|
||||
node_bond_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(2, len(node_bond_interface_cluster_plugins))
|
||||
for item in node_bond_interface_cluster_plugins:
|
||||
self.assertDictEqual(
|
||||
plugin_bond_config, jsonutils.loads(item.attributes))
|
||||
|
||||
def test_populate_bond_with_empty_plugin_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
cluster = self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
bond_config = {
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}}
|
||||
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, attrs=bond_config)
|
||||
self.env.create_plugin(
|
||||
name='plugin_a_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
bond_attributes_metadata={})
|
||||
|
||||
node_bond_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(0, len(node_bond_interface_cluster_plugins))
|
||||
|
||||
def test_add_cluster_plugin_for_node_bond(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
plugin_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=plugin_bond_config)
|
||||
self.env.create_plugin(
|
||||
name='plugin_b_with_bond_attributes',
|
||||
package_version='5.0.0',
|
||||
bond_attributes_metadata={})
|
||||
cluster = self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}, {'roles': ['compute']}])
|
||||
bond_config = {
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}}
|
||||
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, attrs=bond_config)
|
||||
|
||||
node_bond_interface_cluster_plugins = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
).fetchall()
|
||||
|
||||
self.assertEqual(2, len(node_bond_interface_cluster_plugins))
|
||||
for item in node_bond_interface_cluster_plugins:
|
||||
self.assertDictEqual(
|
||||
plugin_bond_config, jsonutils.loads(item.attributes))
|
||||
|
||||
def test_set_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
bond_config = self.env.get_default_plugin_bond_config()
|
||||
|
@ -495,14 +627,13 @@ class TestNodeBondInterfaceClusterPlugin(ExtraFunctions):
|
|||
cluster = self._create_test_cluster(
|
||||
nodes=[{'roles': ['controller']}])
|
||||
|
||||
bond_config.update({
|
||||
'type__': {'value': consts.BOND_TYPES.linux},
|
||||
'mode': {'value': {'value': consts.BOND_MODES.balance_rr}}})
|
||||
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)
|
||||
'lnx_bond', '', nic_names, node.id, attrs=bond_config)
|
||||
|
||||
node_bond_interface_cluster_plugin = self.db.execute(
|
||||
meta.tables['node_bond_interface_cluster_plugins'].select()
|
||||
|
|
|
@ -27,6 +27,7 @@ from nailgun.utils import flatten
|
|||
from nailgun.utils import get_lines
|
||||
from nailgun.utils import grouper
|
||||
from nailgun.utils import parse_bool
|
||||
from nailgun.utils import remove_key_from_dict
|
||||
from nailgun.utils import text_format_safe
|
||||
from nailgun.utils import traverse
|
||||
|
||||
|
@ -151,6 +152,19 @@ class TestUtils(base.BaseIntegrationTest):
|
|||
self.assertRaises(ValueError, parse_bool, 'tru')
|
||||
self.assertRaises(ValueError, parse_bool, 'fals')
|
||||
|
||||
def test_remove_key_from_dict(self):
|
||||
input_dict = {
|
||||
'spam': {
|
||||
'egg': {
|
||||
'key_to_remove': 'a'
|
||||
}
|
||||
},
|
||||
'key_to_remove': 'b'
|
||||
}
|
||||
output_dict = remove_key_from_dict(input_dict, 'key_to_remove')
|
||||
|
||||
self.assertDictEqual({'spam': {'egg': {}}}, output_dict)
|
||||
|
||||
|
||||
class TestTraverse(base.BaseUnitTest):
|
||||
|
||||
|
|
|
@ -378,3 +378,23 @@ def is_feature_supported(rel_version, support_version):
|
|||
return StrictVersion(version) >= StrictVersion(support_version)
|
||||
except (AttributeError, ValueError):
|
||||
return False
|
||||
|
||||
|
||||
def remove_key_from_dict(target_dict, key):
|
||||
"""Recursively remove specific key from dict
|
||||
:param target_dict: target dict to remove key in
|
||||
:type target_dict: dict
|
||||
:param key: key to remove
|
||||
:type key: string
|
||||
"returns: dict -- target_dict without key
|
||||
"""
|
||||
try:
|
||||
del target_dict[key]
|
||||
except KeyError:
|
||||
pass
|
||||
for v in target_dict.values():
|
||||
if isinstance(v, dict):
|
||||
remove_key_from_dict(v, key)
|
||||
|
||||
return target_dict
|
||||
|
||||
|
|
Loading…
Reference in New Issue