Remove offloading modes from data model

Change-Id: I4869f3a08bb1fd60cceb324d2b66a31553e90bd4
Implements: blueprint nics-and-nodes-attributes-via-plugin
This commit is contained in:
Andriy Popovych 2016-08-23 11:32:17 +03:00
parent 4ed6b4570a
commit 95321932af
14 changed files with 122 additions and 395 deletions

View File

@ -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

View File

@ -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()

View File

@ -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.

View File

@ -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

View File

@ -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(

View File

@ -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 = (

View File

@ -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 = (

View File

@ -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({

View File

@ -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):

View File

@ -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",

View File

@ -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):

View File

@ -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.

View File

@ -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)

View File

@ -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)