Remove offloading modes from data model
Change-Id: I4869f3a08bb1fd60cceb324d2b66a31553e90bd4 Implements: blueprint nics-and-nodes-attributes-via-plugin
This commit is contained in:
parent
4ed6b4570a
commit
95321932af
|
@ -16,5 +16,3 @@ from nailgun.api.v1.validators.json_schema \
|
||||||
import cluster as cluster_schema
|
import cluster as cluster_schema
|
||||||
from nailgun.api.v1.validators.json_schema \
|
from nailgun.api.v1.validators.json_schema \
|
||||||
import node as node_schema
|
import node as node_schema
|
||||||
from nailgun.api.v1.validators.json_schema \
|
|
||||||
import interface as iface_schema
|
|
||||||
|
|
|
@ -199,7 +199,6 @@ def upload_fixture(fileobj, loader=None):
|
||||||
# UGLY HACK for testing
|
# UGLY HACK for testing
|
||||||
if new_obj.__class__.__name__ == 'Node':
|
if new_obj.__class__.__name__ == 'Node':
|
||||||
objects.Node.update_interfaces(new_obj)
|
objects.Node.update_interfaces(new_obj)
|
||||||
objects.Node.update_interfaces_offloading_modes(new_obj)
|
|
||||||
fire_callback_on_node_create(new_obj)
|
fire_callback_on_node_create(new_obj)
|
||||||
db().commit()
|
db().commit()
|
||||||
|
|
||||||
|
|
|
@ -768,8 +768,6 @@ class NetworkManager(object):
|
||||||
in iface['assigned_networks']]
|
in iface['assigned_networks']]
|
||||||
objects.NIC.assign_networks(current_iface, nets_to_assign)
|
objects.NIC.assign_networks(current_iface, nets_to_assign)
|
||||||
update = {}
|
update = {}
|
||||||
if 'offloading_modes' in iface:
|
|
||||||
update['offloading_modes'] = iface['offloading_modes']
|
|
||||||
if 'attributes' in iface:
|
if 'attributes' in iface:
|
||||||
update['attributes'] = nailgun_utils.dict_merge(
|
update['attributes'] = nailgun_utils.dict_merge(
|
||||||
current_iface.attributes,
|
current_iface.attributes,
|
||||||
|
@ -814,10 +812,8 @@ class NetworkManager(object):
|
||||||
objects.Bond.get_attributes(bond_db),
|
objects.Bond.get_attributes(bond_db),
|
||||||
bond_attributes
|
bond_attributes
|
||||||
)
|
)
|
||||||
|
|
||||||
update = {
|
update = {
|
||||||
'slaves': slaves,
|
'slaves': slaves,
|
||||||
'offloading_modes': bond.get('offloading_modes', {}),
|
|
||||||
'attributes': bond_attributes
|
'attributes': bond_attributes
|
||||||
}
|
}
|
||||||
objects.Bond.update(bond_db, update)
|
objects.Bond.update(bond_db, update)
|
||||||
|
@ -1400,27 +1396,14 @@ class NetworkManager(object):
|
||||||
iface.attributes['offloading']['disable']['value']
|
iface.attributes['offloading']['disable']['value']
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO(apopovych): rewrite to get offloading data from attributes
|
if iface.attributes.get('offloading', {}).get(
|
||||||
if iface.offloading_modes:
|
'modes', {}).get('value'):
|
||||||
modified_offloading_modes = \
|
properties['ethtool'] = {
|
||||||
cls._get_modified_offloading_modes(iface.offloading_modes)
|
'offload': iface.attributes['offloading']['modes']['value']
|
||||||
if modified_offloading_modes:
|
}
|
||||||
properties['ethtool'] = {}
|
|
||||||
properties['ethtool']['offload'] = \
|
|
||||||
modified_offloading_modes
|
|
||||||
|
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_modified_offloading_modes(cls, offloading_modes):
|
|
||||||
result = dict()
|
|
||||||
for mode in offloading_modes:
|
|
||||||
if mode['state'] is not None:
|
|
||||||
result[mode['name']] = mode['state']
|
|
||||||
if mode['sub'] and mode['state'] is not False:
|
|
||||||
result.update(cls._get_modified_offloading_modes(mode['sub']))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_nic_assoc_with_ng(cls, node, network_group):
|
def find_nic_assoc_with_ng(cls, node, network_group):
|
||||||
"""Will find iface on node that is associated with network_group.
|
"""Will find iface on node that is associated with network_group.
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
import copy
|
|
||||||
|
|
||||||
from sqlalchemy import Boolean
|
from sqlalchemy import Boolean
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column
|
||||||
from sqlalchemy.dialects import postgresql as psql
|
from sqlalchemy.dialects import postgresql as psql
|
||||||
|
@ -29,7 +27,6 @@ from nailgun import consts
|
||||||
from nailgun.db.sqlalchemy.models.base import Base
|
from nailgun.db.sqlalchemy.models.base import Base
|
||||||
from nailgun.db.sqlalchemy.models.fields import JSON
|
from nailgun.db.sqlalchemy.models.fields import JSON
|
||||||
from nailgun.db.sqlalchemy.models.mutable import MutableDict
|
from nailgun.db.sqlalchemy.models.mutable import MutableDict
|
||||||
from nailgun.db.sqlalchemy.models.mutable import MutableList
|
|
||||||
|
|
||||||
|
|
||||||
class IPAddr(Base):
|
class IPAddr(Base):
|
||||||
|
@ -152,10 +149,6 @@ class NodeNICInterface(Base):
|
||||||
driver = Column(Text)
|
driver = Column(Text)
|
||||||
bus_info = Column(Text)
|
bus_info = Column(Text)
|
||||||
pxe = Column(Boolean, default=False, nullable=False)
|
pxe = Column(Boolean, default=False, nullable=False)
|
||||||
|
|
||||||
offloading_modes = Column(MutableList.as_mutable(JSON),
|
|
||||||
default=[], nullable=False,
|
|
||||||
server_default='[]')
|
|
||||||
attributes = Column(
|
attributes = Column(
|
||||||
MutableDict.as_mutable(JSON),
|
MutableDict.as_mutable(JSON),
|
||||||
default={}, server_default='{}', nullable=False)
|
default={}, server_default='{}', nullable=False)
|
||||||
|
@ -178,24 +171,6 @@ class NodeNICInterface(Base):
|
||||||
def assigned_networks(self, value):
|
def assigned_networks(self, value):
|
||||||
self.assigned_networks_list = value
|
self.assigned_networks_list = value
|
||||||
|
|
||||||
# TODO(fzhadaev): move to object
|
|
||||||
@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
|
|
||||||
|
|
||||||
|
|
||||||
class NodeBondInterface(Base):
|
class NodeBondInterface(Base):
|
||||||
__tablename__ = 'node_bond_interfaces'
|
__tablename__ = 'node_bond_interfaces'
|
||||||
|
@ -252,57 +227,3 @@ class NodeBondInterface(Base):
|
||||||
@assigned_networks.setter
|
@assigned_networks.setter
|
||||||
def assigned_networks(self, value):
|
def assigned_networks(self, value):
|
||||||
self.assigned_networks_list = value
|
self.assigned_networks_list = value
|
||||||
|
|
||||||
@property
|
|
||||||
def offloading_modes(self):
|
|
||||||
tmp = None
|
|
||||||
intersection_dict = {}
|
|
||||||
for interface in self.slaves:
|
|
||||||
modes = interface.offloading_modes
|
|
||||||
if tmp is None:
|
|
||||||
tmp = modes
|
|
||||||
intersection_dict = \
|
|
||||||
interface.offloading_modes_as_flat_dict(tmp)
|
|
||||||
continue
|
|
||||||
intersection_dict = self._intersect_offloading_dicts(
|
|
||||||
intersection_dict,
|
|
||||||
interface.offloading_modes_as_flat_dict(modes)
|
|
||||||
)
|
|
||||||
|
|
||||||
return self._apply_intersection(tmp, intersection_dict)
|
|
||||||
|
|
||||||
@offloading_modes.setter
|
|
||||||
def offloading_modes(self, new_modes):
|
|
||||||
new_modes_dict = \
|
|
||||||
NodeNICInterface.offloading_modes_as_flat_dict(new_modes)
|
|
||||||
for interface in self.slaves:
|
|
||||||
self._update_modes(interface.offloading_modes, new_modes_dict)
|
|
||||||
interface.offloading_modes.changed()
|
|
||||||
|
|
||||||
def _update_modes(self, modes, update_dict):
|
|
||||||
for mode in modes:
|
|
||||||
if mode['name'] in update_dict:
|
|
||||||
mode['state'] = update_dict[mode['name']]
|
|
||||||
if mode['sub']:
|
|
||||||
self._update_modes(mode['sub'], update_dict)
|
|
||||||
|
|
||||||
def _intersect_offloading_dicts(self, dict1, dict2):
|
|
||||||
result = dict()
|
|
||||||
for mode in dict1:
|
|
||||||
if mode in dict2:
|
|
||||||
result[mode] = dict1[mode] and dict2[mode]
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _apply_intersection(self, modes, intersection_dict):
|
|
||||||
result = list()
|
|
||||||
if modes is None:
|
|
||||||
return result
|
|
||||||
for mode in copy.deepcopy(modes):
|
|
||||||
if mode["name"] not in intersection_dict:
|
|
||||||
continue
|
|
||||||
mode["state"] = intersection_dict[mode["name"]]
|
|
||||||
if mode["sub"]:
|
|
||||||
mode["sub"] = \
|
|
||||||
self._apply_intersection(mode["sub"], intersection_dict)
|
|
||||||
result.append(mode)
|
|
||||||
return result
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ class DPDKMixin(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_interface_dpdk_properties(cls, interface, dpdk_drivers):
|
def refresh_interface_dpdk_properties(cls, interface, dpdk_drivers):
|
||||||
attributes = interface.attributes
|
attributes = interface.attributes
|
||||||
meta = interface.meta
|
|
||||||
dpdk_attributes = copy.deepcopy(attributes.get('dpdk', {}))
|
dpdk_attributes = copy.deepcopy(attributes.get('dpdk', {}))
|
||||||
dpdk_available = cls.dpdk_available(interface, dpdk_drivers)
|
dpdk_available = cls.dpdk_available(interface, dpdk_drivers)
|
||||||
|
|
||||||
|
@ -53,8 +52,10 @@ class DPDKMixin(object):
|
||||||
# update attributes in DB only if something was changed
|
# update attributes in DB only if something was changed
|
||||||
if attributes.get('dpdk', {}) != dpdk_attributes:
|
if attributes.get('dpdk', {}) != dpdk_attributes:
|
||||||
attributes['dpdk'] = dpdk_attributes
|
attributes['dpdk'] = dpdk_attributes
|
||||||
if meta.get('dpdk', {}) != {'available': dpdk_available}:
|
if 'meta' in interface:
|
||||||
meta['dpdk'] = {'available': dpdk_available}
|
meta = interface.meta
|
||||||
|
if meta.get('dpdk', {}) != {'available': dpdk_available}:
|
||||||
|
meta['dpdk'] = {'available': dpdk_available}
|
||||||
|
|
||||||
|
|
||||||
class NIC(DPDKMixin, NailgunObject):
|
class NIC(DPDKMixin, NailgunObject):
|
||||||
|
@ -107,32 +108,6 @@ class NIC(DPDKMixin, NailgunObject):
|
||||||
enabled = instance.attributes.get('sriov', {}).get('enabled')
|
enabled = instance.attributes.get('sriov', {}).get('enabled')
|
||||||
return enabled and enabled['value']
|
return enabled and enabled['value']
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_offloading_modes(cls, instance, new_modes, keep_states=False):
|
|
||||||
"""Update information about offloading modes for the interface.
|
|
||||||
|
|
||||||
:param instance: Interface object
|
|
||||||
:param new_modes: New offloading modes
|
|
||||||
:param keep_states: If True, information about available modes will be
|
|
||||||
updated, but states configured by user will not be overwritten.
|
|
||||||
"""
|
|
||||||
def set_old_states(modes):
|
|
||||||
"""Set old state for offloading modes
|
|
||||||
|
|
||||||
:param modes: List of offloading modes
|
|
||||||
"""
|
|
||||||
for mode in modes:
|
|
||||||
if mode['name'] in old_modes_states:
|
|
||||||
mode['state'] = old_modes_states[mode['name']]
|
|
||||||
if mode.get('sub'):
|
|
||||||
set_old_states(mode['sub'])
|
|
||||||
|
|
||||||
if keep_states:
|
|
||||||
old_modes_states = instance.offloading_modes_as_flat_dict(
|
|
||||||
instance.offloading_modes)
|
|
||||||
set_old_states(new_modes)
|
|
||||||
instance.offloading_modes = new_modes
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_nic_interfaces_for_all_nodes(cls, cluster, networks=None):
|
def get_nic_interfaces_for_all_nodes(cls, cluster, networks=None):
|
||||||
nic_interfaces_query = db().query(
|
nic_interfaces_query = db().query(
|
||||||
|
|
|
@ -36,7 +36,6 @@ class NodeInterfacesSerializer(BasicSerializer):
|
||||||
'driver',
|
'driver',
|
||||||
'bus_info',
|
'bus_info',
|
||||||
'meta',
|
'meta',
|
||||||
'offloading_modes',
|
|
||||||
'pxe'
|
'pxe'
|
||||||
)
|
)
|
||||||
bond_fields = (
|
bond_fields = (
|
||||||
|
@ -45,8 +44,7 @@ class NodeInterfacesSerializer(BasicSerializer):
|
||||||
'type',
|
'type',
|
||||||
'mode',
|
'mode',
|
||||||
'state',
|
'state',
|
||||||
'assigned_networks',
|
'assigned_networks'
|
||||||
'offloading_modes'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
nic_fields_60 = (
|
nic_fields_60 = (
|
||||||
|
|
|
@ -477,7 +477,8 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
self.assertEqual(len(resp.json_body), 1)
|
self.assertEqual(len(resp.json_body), 1)
|
||||||
resp_nic = resp.json_body[0]
|
resp_nic = resp.json_body[0]
|
||||||
nic = new_meta['interfaces'][0]
|
nic = new_meta['interfaces'][0]
|
||||||
self.assertEqual(resp_nic['offloading_modes'], nic['offloading_modes'])
|
self.assertEqual(
|
||||||
|
nic['offloading_modes'], resp_nic['meta']['offloading_modes'])
|
||||||
|
|
||||||
def test_NIC_change_offloading_modes(self):
|
def test_NIC_change_offloading_modes(self):
|
||||||
meta = self.env.default_metadata()
|
meta = self.env.default_metadata()
|
||||||
|
@ -518,7 +519,8 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
self.assertEqual(len(resp.json_body), 1)
|
self.assertEqual(len(resp.json_body), 1)
|
||||||
resp_nic = resp.json_body[0]
|
resp_nic = resp.json_body[0]
|
||||||
nic = new_meta['interfaces'][0]
|
nic = new_meta['interfaces'][0]
|
||||||
self.assertEqual(resp_nic['offloading_modes'], nic['offloading_modes'])
|
self.assertEqual(
|
||||||
|
nic['offloading_modes'], resp_nic['meta']['offloading_modes'])
|
||||||
|
|
||||||
resp = self.app.get(
|
resp = self.app.get(
|
||||||
reverse('NodeCollectionHandler', kwargs={'node_id': node['id']}),
|
reverse('NodeCollectionHandler', kwargs={'node_id': node['id']}),
|
||||||
|
@ -548,8 +550,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
self.env.set_interfaces_in_meta(resp_node["meta"], [
|
self.env.set_interfaces_in_meta(resp_node["meta"], [new_nic])
|
||||||
new_nic])
|
|
||||||
|
|
||||||
resp_node.pop('group_id')
|
resp_node.pop('group_id')
|
||||||
|
|
||||||
|
@ -566,8 +567,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
self.assertEqual(len(resp.json_body), 1)
|
self.assertEqual(len(resp.json_body), 1)
|
||||||
resp_nic = resp.json_body[0]
|
resp_nic = resp.json_body[0]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
resp_nic['offloading_modes'],
|
new_nic['offloading_modes'], resp_nic['meta']['offloading_modes'])
|
||||||
new_nic['offloading_modes'])
|
|
||||||
|
|
||||||
def test_NIC_locking_on_update_by_agent(self):
|
def test_NIC_locking_on_update_by_agent(self):
|
||||||
lock_vs_status = (
|
lock_vs_status = (
|
||||||
|
|
|
@ -168,7 +168,8 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||||
'xmit_hash_policy': {
|
'xmit_hash_policy': {
|
||||||
'value': {'value': BOND_XMIT_HASH_POLICY.layer2_3}},
|
'value': {'value': BOND_XMIT_HASH_POLICY.layer2_3}},
|
||||||
'lacp_rate': {'value': {'value': 'slow'}},
|
'lacp_rate': {'value': {'value': 'slow'}},
|
||||||
'type__': {'value': bond_type}
|
'type__': {'value': bond_type},
|
||||||
|
'offloading': {'modes': {'value': {'mode_common': None}}}
|
||||||
}
|
}
|
||||||
attributes.update(iface_props)
|
attributes.update(iface_props)
|
||||||
|
|
||||||
|
@ -195,13 +196,9 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||||
resp.json_body)
|
resp.json_body)
|
||||||
self.assertEqual(len(bonds), 1)
|
self.assertEqual(len(bonds), 1)
|
||||||
self.assertEqual(bonds[0]["name"], bond_name)
|
self.assertEqual(bonds[0]["name"], bond_name)
|
||||||
bond_offloading_modes = bonds[0]['offloading_modes']
|
modes = bonds[0]['attributes']['offloading']['modes']['value']
|
||||||
self.assertEqual(len(bond_offloading_modes), 1)
|
|
||||||
self.assertDictEqual(
|
self.assertDictEqual(
|
||||||
bond_offloading_modes[0],
|
modes, {'mode_common': None})
|
||||||
{'name': 'mode_common',
|
|
||||||
'state': None,
|
|
||||||
'sub': []})
|
|
||||||
|
|
||||||
def nics_bond_remove(self, put_func):
|
def nics_bond_remove(self, put_func):
|
||||||
resp = self.env.node_nics_get(self.env.nodes[0]["id"])
|
resp = self.env.node_nics_get(self.env.nodes[0]["id"])
|
||||||
|
@ -716,15 +713,14 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||||
body)
|
body)
|
||||||
self.assertEqual(1, len(bonds))
|
self.assertEqual(1, len(bonds))
|
||||||
|
|
||||||
bond_offloading_modes = bonds[0]['offloading_modes']
|
bond_offloading_modes = bonds[0]['attributes'][
|
||||||
|
'offloading']['modes']['value']
|
||||||
self.assertEqual(len(bond_offloading_modes), 1)
|
self.assertEqual(len(bond_offloading_modes), 1)
|
||||||
slaves = bonds[0]['slaves']
|
slaves = bonds[0]['slaves']
|
||||||
|
|
||||||
self.assertEqual(2, len(slaves))
|
self.assertEqual(2, len(slaves))
|
||||||
|
self.assertIsNone(bond_offloading_modes['mode_common'])
|
||||||
self.assertIsNone(bond_offloading_modes[0]['state'])
|
bond_offloading_modes['mode_common'] = True
|
||||||
bond_offloading_modes[0]['state'] = True
|
|
||||||
self.assertTrue(bond_offloading_modes[0]['state'])
|
|
||||||
|
|
||||||
resp = self.env.node_nics_put(
|
resp = self.env.node_nics_put(
|
||||||
self.env.nodes[0]["id"],
|
self.env.nodes[0]["id"],
|
||||||
|
@ -736,12 +732,13 @@ class TestNodeNICsBonding(BaseIntegrationTest):
|
||||||
body)
|
body)
|
||||||
self.assertEqual(1, len(bonds))
|
self.assertEqual(1, len(bonds))
|
||||||
|
|
||||||
bond_offloading_modes = bonds[0]['offloading_modes']
|
bond_offloading_modes = bonds[0]['attributes'][
|
||||||
|
'offloading']['modes']['value']
|
||||||
self.assertEqual(len(bond_offloading_modes), 1)
|
self.assertEqual(len(bond_offloading_modes), 1)
|
||||||
slaves = bonds[0]['slaves']
|
slaves = bonds[0]['slaves']
|
||||||
|
|
||||||
self.assertEqual(2, len(slaves))
|
self.assertEqual(2, len(slaves))
|
||||||
self.assertTrue(bond_offloading_modes[0]['state'])
|
self.assertTrue(bond_offloading_modes['mode_common'])
|
||||||
|
|
||||||
def test_nics_bond_cannot_contain_sriov_enabled_interfaces(self):
|
def test_nics_bond_cannot_contain_sriov_enabled_interfaces(self):
|
||||||
self.data.append({
|
self.data.append({
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
|
@ -96,16 +97,19 @@ class TestBondObject(BaseTestCase):
|
||||||
'name': 'bond0',
|
'name': 'bond0',
|
||||||
'slaves': self.node.nic_interfaces,
|
'slaves': self.node.nic_interfaces,
|
||||||
'node': self.node,
|
'node': self.node,
|
||||||
|
'attributes': {
|
||||||
|
'offloading': {
|
||||||
|
'modes': {'value': {'test_mode': 'mode'}}}}
|
||||||
}
|
}
|
||||||
bond = objects.Bond.create(data)
|
bond = objects.Bond.create(data)
|
||||||
offloading_modes = bond.offloading_modes
|
new_data = {
|
||||||
offloading_modes[0]['state'] = 'test'
|
'attributes': {
|
||||||
|
'offloading': {
|
||||||
data = {
|
'modes': {'value': {'test_mode': 'test'}}}}
|
||||||
'offloading_modes': offloading_modes
|
|
||||||
}
|
}
|
||||||
objects.Bond.update(bond, data)
|
|
||||||
self.assertEqual(data['offloading_modes'], bond.offloading_modes)
|
objects.Bond.update(bond, copy.deepcopy(new_data))
|
||||||
|
self.assertEqual(new_data['attributes'], bond['attributes'])
|
||||||
|
|
||||||
def test_get_bond_interfaces_for_all_nodes(self):
|
def test_get_bond_interfaces_for_all_nodes(self):
|
||||||
node = self.env.nodes[0]
|
node = self.env.nodes[0]
|
||||||
|
@ -120,6 +124,71 @@ class TestBondObject(BaseTestCase):
|
||||||
|
|
||||||
class TestNICObject(BaseTestCase):
|
class TestNICObject(BaseTestCase):
|
||||||
|
|
||||||
|
changed_modes = [
|
||||||
|
{
|
||||||
|
'name': 'mode_1',
|
||||||
|
'state': True,
|
||||||
|
'sub': [
|
||||||
|
{
|
||||||
|
'name': 'sub_mode_1',
|
||||||
|
'state': None,
|
||||||
|
'sub': []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'mode_2',
|
||||||
|
'state': None,
|
||||||
|
'sub': [
|
||||||
|
{
|
||||||
|
'name': 'sub_mode_2',
|
||||||
|
'state': False,
|
||||||
|
'sub': []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
expected_result = {
|
||||||
|
'mode_1': True,
|
||||||
|
'sub_mode_1': None,
|
||||||
|
'mode_2': None,
|
||||||
|
'sub_mode_2': False
|
||||||
|
}
|
||||||
|
|
||||||
|
deep_structure = [
|
||||||
|
{
|
||||||
|
'name': 'level_1',
|
||||||
|
'state': True,
|
||||||
|
'sub': [
|
||||||
|
{
|
||||||
|
'name': 'level_2',
|
||||||
|
'state': None,
|
||||||
|
'sub': [
|
||||||
|
{
|
||||||
|
'name': 'level_3',
|
||||||
|
'state': None,
|
||||||
|
'sub': [
|
||||||
|
{
|
||||||
|
'name': 'level_4',
|
||||||
|
'state': False,
|
||||||
|
'sub': []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
expected_result_deep = {
|
||||||
|
'level_1': True,
|
||||||
|
'level_2': None,
|
||||||
|
'level_3': None,
|
||||||
|
'level_4': False
|
||||||
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestNICObject, self).setUp()
|
super(TestNICObject, self).setUp()
|
||||||
|
|
||||||
|
@ -127,6 +196,16 @@ class TestNICObject(BaseTestCase):
|
||||||
cluster_kwargs={'api': False},
|
cluster_kwargs={'api': False},
|
||||||
nodes_kwargs=[{'role': 'controller'}])
|
nodes_kwargs=[{'role': 'controller'}])
|
||||||
|
|
||||||
|
def test_offloading_modes_as_flat_dict(self):
|
||||||
|
self.assertDictEqual(
|
||||||
|
self.expected_result,
|
||||||
|
objects.NIC.offloading_modes_as_flat_dict(
|
||||||
|
self.changed_modes))
|
||||||
|
self.assertDictEqual(
|
||||||
|
self.expected_result_deep,
|
||||||
|
objects.NIC.offloading_modes_as_flat_dict(
|
||||||
|
self.deep_structure))
|
||||||
|
|
||||||
def test_replace_assigned_networks(self):
|
def test_replace_assigned_networks(self):
|
||||||
node = self.env.nodes[0]
|
node = self.env.nodes[0]
|
||||||
nic_1 = node.interfaces[0]
|
nic_1 = node.interfaces[0]
|
||||||
|
@ -166,44 +245,6 @@ class TestNICObject(BaseTestCase):
|
||||||
self.env.clusters[0])
|
self.env.clusters[0])
|
||||||
self.assertEqual(len(nic_interfaces), len(interfaces))
|
self.assertEqual(len(nic_interfaces), len(interfaces))
|
||||||
|
|
||||||
def test_update_offloading_modes(self):
|
|
||||||
node = self.env.nodes[0]
|
|
||||||
new_modes = [
|
|
||||||
{'state': True, 'name': 'tx-checksumming', 'sub': [
|
|
||||||
{'state': False, 'name': 'tx-checksum-sctp', 'sub': []},
|
|
||||||
{'state': True, 'name': 'tx-checksum-ipv6', 'sub': []},
|
|
||||||
{'state': None, 'name': 'tx-checksum-ipv4', 'sub': []}]},
|
|
||||||
{'state': None, 'name': 'rx-checksumming', 'sub': []},
|
|
||||||
{'state': True, 'name': 'new_offloading_mode', 'sub': []}]
|
|
||||||
objects.NIC.update_offloading_modes(node.interfaces[0], new_modes)
|
|
||||||
self.assertListEqual(node.interfaces[0].offloading_modes, new_modes)
|
|
||||||
|
|
||||||
def test_update_offloading_modes_keep_states(self):
|
|
||||||
node = self.env.nodes[0]
|
|
||||||
old_modes = [
|
|
||||||
{'state': True, 'name': 'tx-checksumming', 'sub': [
|
|
||||||
{'state': False, 'name': 'tx-checksum-sctp', 'sub': []},
|
|
||||||
{'state': True, 'name': 'tx-checksum-ipv6', 'sub':
|
|
||||||
[{'state': None, 'name': 'tx-checksum-ipv4', 'sub': []}]}]
|
|
||||||
}]
|
|
||||||
|
|
||||||
node.interfaces[0].offloading_modes = old_modes
|
|
||||||
new_mode = {'state': True, 'name': 'new_offloading_mode', 'sub': []}
|
|
||||||
new_modes = [
|
|
||||||
{'state': True, 'name': 'tx-checksumming', 'sub': [
|
|
||||||
{'state': True, 'name': 'tx-checksum-sctp', 'sub': []},
|
|
||||||
{'state': True, 'name': 'tx-checksum-ipv6', 'sub':
|
|
||||||
[{'state': False, 'name': 'tx-checksum-ipv4', 'sub': []}]}]
|
|
||||||
},
|
|
||||||
new_mode]
|
|
||||||
|
|
||||||
objects.NIC.update_offloading_modes(node.interfaces[0], new_modes,
|
|
||||||
keep_states=True)
|
|
||||||
old_modes.append(new_mode)
|
|
||||||
# States for old offloading modes should be preserved
|
|
||||||
self.assertListEqual(node.interfaces[0].offloading_modes,
|
|
||||||
old_modes)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIPAddrObject(BaseTestCase):
|
class TestIPAddrObject(BaseTestCase):
|
||||||
|
|
||||||
|
|
|
@ -32,19 +32,6 @@ INTERFACES = {
|
||||||
"name": {"type": "string"},
|
"name": {"type": "string"},
|
||||||
"driver": base_types.NULLABLE_STRING,
|
"driver": base_types.NULLABLE_STRING,
|
||||||
"bus_info": base_types.NULLABLE_STRING,
|
"bus_info": base_types.NULLABLE_STRING,
|
||||||
"offloading_modes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {"type": "string"},
|
|
||||||
"state": base_types.NULLABLE_BOOL,
|
|
||||||
"sub": {
|
|
||||||
"$ref": "#/items/properties/offloading_modes"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pxe": {"type": "boolean"},
|
"pxe": {"type": "boolean"},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"type": "object",
|
"type": "object",
|
|
@ -18,7 +18,6 @@ from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nailgun.api.v1.validators.base import BasicValidator
|
from nailgun.api.v1.validators.base import BasicValidator
|
||||||
from nailgun.api.v1.validators.json_schema import iface_schema
|
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun.db import db
|
from nailgun.db import db
|
||||||
from nailgun.db.sqlalchemy.models import Cluster
|
from nailgun.db.sqlalchemy.models import Cluster
|
||||||
|
@ -27,6 +26,8 @@ from nailgun.db.sqlalchemy.models import NetworkGroup
|
||||||
from nailgun.db.sqlalchemy.models import Node
|
from nailgun.db.sqlalchemy.models import Node
|
||||||
from nailgun.db.sqlalchemy.models import NodeGroup
|
from nailgun.db.sqlalchemy.models import NodeGroup
|
||||||
from nailgun import errors
|
from nailgun import errors
|
||||||
|
from nailgun.extensions.network_manager.validators.json_schema import \
|
||||||
|
interface
|
||||||
from nailgun.extensions.network_manager.validators.json_schema import \
|
from nailgun.extensions.network_manager.validators.json_schema import \
|
||||||
network_template
|
network_template
|
||||||
from nailgun.extensions.network_manager.validators.json_schema import networks
|
from nailgun.extensions.network_manager.validators.json_schema import networks
|
||||||
|
@ -320,7 +321,7 @@ class NeutronNetworkConfigurationValidator(NetworkConfigurationValidator):
|
||||||
|
|
||||||
|
|
||||||
class NetAssignmentValidator(BasicValidator):
|
class NetAssignmentValidator(BasicValidator):
|
||||||
single_schema = iface_schema.INTERFACES
|
single_schema = interface.INTERFACES
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, node):
|
def validate(cls, node):
|
||||||
|
|
|
@ -344,7 +344,6 @@ class Node(NailgunObject):
|
||||||
# Add interfaces for node from 'meta'.
|
# Add interfaces for node from 'meta'.
|
||||||
if new_node.meta and new_node.meta.get('interfaces'):
|
if new_node.meta and new_node.meta.get('interfaces'):
|
||||||
cls.update_interfaces(new_node)
|
cls.update_interfaces(new_node)
|
||||||
cls.update_interfaces_offloading_modes(new_node)
|
|
||||||
|
|
||||||
# role cannot be assigned if cluster_id is not set
|
# role cannot be assigned if cluster_id is not set
|
||||||
if new_node_cluster_id:
|
if new_node_cluster_id:
|
||||||
|
@ -637,9 +636,6 @@ class Node(NailgunObject):
|
||||||
instance.mac = data.pop("mac", None) or instance.mac
|
instance.mac = data.pop("mac", None) or instance.mac
|
||||||
db().flush()
|
db().flush()
|
||||||
cls.update_interfaces(instance)
|
cls.update_interfaces(instance)
|
||||||
cls.update_interfaces_offloading_modes(
|
|
||||||
instance,
|
|
||||||
is_agent)
|
|
||||||
|
|
||||||
cluster_changed = False
|
cluster_changed = False
|
||||||
add_to_cluster = False
|
add_to_cluster = False
|
||||||
|
@ -831,27 +827,6 @@ class Node(NailgunObject):
|
||||||
data.pop('status', None)
|
data.pop('status', None)
|
||||||
return cls.update(instance, data)
|
return cls.update(instance, data)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_interfaces_offloading_modes(cls, instance, keep_states=False):
|
|
||||||
"""Update information about offloading modes for node interfaces.
|
|
||||||
|
|
||||||
:param instance: Node object
|
|
||||||
:param keep_states: If True, information about available modes will be
|
|
||||||
updated, but states configured by user will not be overwritten.
|
|
||||||
"""
|
|
||||||
for interface in instance.meta["interfaces"]:
|
|
||||||
new_offloading_modes = interface.get('offloading_modes')
|
|
||||||
if new_offloading_modes:
|
|
||||||
NIC.update_offloading_modes(
|
|
||||||
cls.get_interface_by_mac_or_name(
|
|
||||||
instance,
|
|
||||||
interface['mac'],
|
|
||||||
interface['name']
|
|
||||||
),
|
|
||||||
new_offloading_modes,
|
|
||||||
keep_states
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_roles(cls, instance, new_roles):
|
def update_roles(cls, instance, new_roles):
|
||||||
"""Update roles for Node instance.
|
"""Update roles for Node instance.
|
||||||
|
|
|
@ -26,7 +26,6 @@ from oslo_serialization import jsonutils
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun.db import db
|
from nailgun.db import db
|
||||||
from nailgun.db.sqlalchemy import models
|
from nailgun.db.sqlalchemy import models
|
||||||
from nailgun.extensions.network_manager.manager import NetworkManager
|
|
||||||
from nailgun import objects
|
from nailgun import objects
|
||||||
from nailgun.orchestrator import stages
|
from nailgun.orchestrator import stages
|
||||||
from nailgun.test import base
|
from nailgun.test import base
|
||||||
|
@ -462,28 +461,6 @@ class TestDeploymentAttributesSerialization70(
|
||||||
|
|
||||||
self.assertEqual(roles, dict(expected_roles))
|
self.assertEqual(roles, dict(expected_roles))
|
||||||
|
|
||||||
def test_offloading_modes_serialize(self):
|
|
||||||
meta = self.env.default_metadata()
|
|
||||||
changed_offloading_modes = {}
|
|
||||||
for interface in meta['interfaces']:
|
|
||||||
changed_offloading_modes[interface['name']] = \
|
|
||||||
NetworkManager._get_modified_offloading_modes(
|
|
||||||
interface.get('offloading_modes'))
|
|
||||||
|
|
||||||
for node in self.serialized_for_astute:
|
|
||||||
interfaces = node['network_scheme']['interfaces']
|
|
||||||
for iface_name in interfaces:
|
|
||||||
ethtool_blk = interfaces[iface_name].get('ethtool', None)
|
|
||||||
self.assertIsNotNone(
|
|
||||||
ethtool_blk,
|
|
||||||
"There is no 'ethtool' block in deployment data")
|
|
||||||
offload_blk = ethtool_blk.get('offload', None)
|
|
||||||
self.assertIsNotNone(
|
|
||||||
offload_blk,
|
|
||||||
"There is no 'offload' block in deployment data")
|
|
||||||
self.assertDictEqual(offload_blk,
|
|
||||||
changed_offloading_modes[iface_name])
|
|
||||||
|
|
||||||
def test_network_metadata(self):
|
def test_network_metadata(self):
|
||||||
neutron_serializer = self.serializer.get_net_provider_serializer(
|
neutron_serializer = self.serializer.get_net_provider_serializer(
|
||||||
self.cluster_db)
|
self.cluster_db)
|
||||||
|
|
|
@ -74,7 +74,7 @@ class TestNodeInterfacesDbModels(BaseTestCase):
|
||||||
'ip_addr': '10.20.0.2',
|
'ip_addr': '10.20.0.2',
|
||||||
'netmask': '255.255.255.0',
|
'netmask': '255.255.255.0',
|
||||||
'state': 'test_state',
|
'state': 'test_state',
|
||||||
'interface_properties': {'test_property': 'test_value'},
|
'attributes': {'test_property': 'test_value'},
|
||||||
'parent_id': 1,
|
'parent_id': 1,
|
||||||
'driver': 'test_driver',
|
'driver': 'test_driver',
|
||||||
'bus_info': 'some_test_info'
|
'bus_info': 'some_test_info'
|
||||||
|
@ -105,143 +105,18 @@ class TestNodeInterfacesDbModels(BaseTestCase):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
changed_modes = [
|
def test_interface_attributes_str_type_failure(self):
|
||||||
{
|
|
||||||
'name': 'mode_1',
|
|
||||||
'state': True,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'sub_mode_1',
|
|
||||||
'state': None,
|
|
||||||
'sub': []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'mode_2',
|
|
||||||
'state': None,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'sub_mode_2',
|
|
||||||
'state': False,
|
|
||||||
'sub': []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
expected_result = {
|
|
||||||
'mode_1': True,
|
|
||||||
'sub_mode_1': None,
|
|
||||||
'mode_2': None,
|
|
||||||
'sub_mode_2': False
|
|
||||||
}
|
|
||||||
|
|
||||||
deep_structure = [
|
|
||||||
{
|
|
||||||
'name': 'level_1',
|
|
||||||
'state': True,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'level_2',
|
|
||||||
'state': None,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'level_3',
|
|
||||||
'state': None,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'level_4',
|
|
||||||
'state': False,
|
|
||||||
'sub': []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
expected_result_deep = {
|
|
||||||
'level_1': True,
|
|
||||||
'level_2': None,
|
|
||||||
'level_3': None,
|
|
||||||
'level_4': False
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_offloading_modes_as_flat_dict(self):
|
|
||||||
self.assertDictEqual(
|
|
||||||
self.expected_result,
|
|
||||||
NodeNICInterface.offloading_modes_as_flat_dict(
|
|
||||||
self.changed_modes))
|
|
||||||
self.assertDictEqual(
|
|
||||||
self.expected_result_deep,
|
|
||||||
NodeNICInterface.offloading_modes_as_flat_dict(
|
|
||||||
self.deep_structure))
|
|
||||||
|
|
||||||
def test_update_offloading_modes_for_bond_interface(self):
|
|
||||||
different_modes = [
|
|
||||||
[{
|
|
||||||
'name': 'mode_for_nic1',
|
|
||||||
'state': None,
|
|
||||||
'sub': [
|
|
||||||
{
|
|
||||||
'name': 'sub_mode_for_nic1',
|
|
||||||
'state': None,
|
|
||||||
'sub': []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}],
|
|
||||||
[{
|
|
||||||
'name': 'mode_for_nic2',
|
|
||||||
'state': None,
|
|
||||||
'sub': []
|
|
||||||
}],
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
nics = []
|
|
||||||
for i in range(2):
|
|
||||||
nic_data = copy.deepcopy(self.sample_nic_interface_data)
|
|
||||||
nic_data['offloading_modes'] = \
|
|
||||||
self.unchanged_modes + different_modes[i]
|
|
||||||
nics.append(NodeNICInterface(**nic_data))
|
|
||||||
|
|
||||||
sample_bond_data = {
|
|
||||||
'node_id': 1,
|
|
||||||
'name': 'test_bond_interface',
|
|
||||||
'mode': 'active-backup',
|
|
||||||
'bond_properties': {'test_property': 'test_value'}
|
|
||||||
}
|
|
||||||
|
|
||||||
bond = NodeBondInterface(**sample_bond_data)
|
|
||||||
bond.slaves = nics
|
|
||||||
|
|
||||||
bond_modes = bond.offloading_modes
|
|
||||||
self.assertListEqual(self.unchanged_modes, bond_modes)
|
|
||||||
|
|
||||||
bond.offloading_modes = self.changed_modes
|
|
||||||
|
|
||||||
bond_modes = bond.offloading_modes
|
|
||||||
self.assertListEqual(self.changed_modes, bond_modes)
|
|
||||||
|
|
||||||
for i in range(2):
|
|
||||||
self.assertListEqual(self.changed_modes + different_modes[i],
|
|
||||||
nics[i].offloading_modes)
|
|
||||||
|
|
||||||
def test_interface_properties_str_type_failure(self):
|
|
||||||
nic_data = copy.deepcopy(self.sample_nic_interface_data)
|
nic_data = copy.deepcopy(self.sample_nic_interface_data)
|
||||||
nic_data['interface_properties'] = jsonutils.dumps(
|
nic_data['attributes'] = jsonutils.dumps(
|
||||||
nic_data['interface_properties']) # str type cause ValueError
|
nic_data['attributes']) # str type cause ValueError
|
||||||
self.assertRaises(ValueError, NodeNICInterface, **nic_data)
|
self.assertRaises(ValueError, NodeNICInterface, **nic_data)
|
||||||
|
|
||||||
def test_bond_properties_str_type_failure(self):
|
def test_bond_attributes_str_type_failure(self):
|
||||||
sample_bond_data = {
|
sample_bond_data = {
|
||||||
'node_id': 1,
|
'node_id': 1,
|
||||||
'name': 'test_bond_interface',
|
'name': 'test_bond_interface',
|
||||||
'mode': 'active-backup',
|
'mode': 'active-backup',
|
||||||
'bond_properties': jsonutils.dumps(
|
'attributes': jsonutils.dumps(
|
||||||
{'test_property': 'test_value'}) # str type cause ValueError
|
{'test_property': 'test_value'}) # str type cause ValueError
|
||||||
}
|
}
|
||||||
self.assertRaises(ValueError, NodeBondInterface, **sample_bond_data)
|
self.assertRaises(ValueError, NodeBondInterface, **sample_bond_data)
|
||||||
|
|
Loading…
Reference in New Issue