diff --git a/nailgun/nailgun/extensions/network_manager/objects/bond.py b/nailgun/nailgun/extensions/network_manager/objects/bond.py index 9f96a55cb0..c1c6d420d8 100644 --- a/nailgun/nailgun/extensions/network_manager/objects/bond.py +++ b/nailgun/nailgun/extensions/network_manager/objects/bond.py @@ -22,6 +22,7 @@ from nailgun.extensions.network_manager.objects.interface import DPDKMixin from nailgun.extensions.network_manager.objects.interface import NIC from nailgun.objects import NailgunCollection from nailgun.objects import NailgunObject +from nailgun.objects import Release from nailgun.objects.serializers.base import BasicSerializer from nailgun.plugins.manager import PluginManager from nailgun import utils @@ -72,7 +73,7 @@ class Bond(DPDKMixin, NailgunObject): @classmethod def dpdk_available(cls, instance, dpdk_drivers): - return all(NIC.get_dpdk_driver(iface, dpdk_drivers) + return all(NIC.dpdk_available(iface, dpdk_drivers) for iface in instance.slaves) @classmethod @@ -105,6 +106,76 @@ class Bond(DPDKMixin, NailgunObject): return attributes + @classmethod + def get_meta(cls, instance): + """Get immutable attributes for bond. + + :param instance: NodeBondInterface instance + :type instance: NodeBondInterface model + :returns: dict -- Object of bond attributes + """ + meta = {} + dpdk_drivers = Release.get_supported_dpdk_drivers( + instance.node.cluster.release) + meta['dpdk'] = { + 'available': cls.dpdk_available(instance, dpdk_drivers)} + meta['offloading'] = { + 'modes': Bond.get_available_offloading_modes(instance) + } + return meta + + @classmethod + def get_available_offloading_modes(cls, instance): + """Get available offloading modes for bond. + + This method collects information about available offloading modes + for each slave interface and returns their intersection. + + :param instance: NodeBondInterface instance + :type instance: NodeBondInterface model + :returns: list -- Available offloading modes + """ + structure = None + intersection_dict = {} + for interface in instance.slaves: + modes = interface.meta['offloading_modes'] + if structure is None: + structure = copy.deepcopy(modes) + intersection_dict = \ + NIC.offloading_modes_as_flat_dict(structure) + continue + intersection_dict = cls._intersect_offloading_dicts( + intersection_dict, + NIC.offloading_modes_as_flat_dict(modes) + ) + return cls._apply_intersection(structure, intersection_dict) + + @staticmethod + def _intersect_offloading_dicts(dict1, dict2): + result = dict() + for mode in dict1: + if mode in dict2: + if dict1[mode] is None or dict2[mode] is None: + result[mode] = None + else: + result[mode] = dict1[mode] and dict2[mode] + return result + + @classmethod + def _apply_intersection(cls, modes, intersection_dict): + result = list() + if modes is None: + return result + for mode in modes: + if mode["name"] not in intersection_dict: + continue + mode["state"] = intersection_dict[mode["name"]] + if mode["sub"]: + mode["sub"] = \ + cls._apply_intersection(mode["sub"], intersection_dict) + result.append(mode) + return result + @classmethod def get_default_attributes(cls, cluster): """Get native and plugin default attributes for bond. diff --git a/nailgun/nailgun/extensions/network_manager/objects/serializers/nic.py b/nailgun/nailgun/extensions/network_manager/objects/serializers/nic.py index 820ace7200..41b0da54fc 100644 --- a/nailgun/nailgun/extensions/network_manager/objects/serializers/nic.py +++ b/nailgun/nailgun/extensions/network_manager/objects/serializers/nic.py @@ -104,6 +104,7 @@ class NodeInterfacesSerializer(BasicSerializer): data_dict = BasicSerializer.serialize(instance, fields=fields) data_dict['slaves'] = [{'name': s.name} for s in instance.slaves] data_dict['attributes'] = Bond.get_attributes(instance) + data_dict['meta'] = Bond.get_meta(instance) return data_dict diff --git a/nailgun/nailgun/extensions/network_manager/tests/test_objects.py b/nailgun/nailgun/extensions/network_manager/tests/test_objects.py index 00b02d90b8..115c692e00 100644 --- a/nailgun/nailgun/extensions/network_manager/tests/test_objects.py +++ b/nailgun/nailgun/extensions/network_manager/tests/test_objects.py @@ -121,6 +121,74 @@ class TestBondObject(BaseTestCase): self.env.clusters[0]) self.assertEqual(len(bond_interfaces), 1) + def check_offloading_modes_intersection(self, modes_1, modes_2, + expected_result): + + data = { + 'name': 'bond0', + 'slaves': self.node.nic_interfaces, + 'node': self.node + } + + bond = objects.Bond.create(data) + + self.node.nic_interfaces[0].meta['offloading_modes'] = modes_1 + self.node.nic_interfaces[1].meta['offloading_modes'] = modes_2 + self.assertEquals( + objects.Bond.get_available_offloading_modes(bond), + expected_result) + + def test_get_available_offloading_modes(self): + different_modes = [ + [{ + 'name': 'mode_for_nic1', + 'state': None, + 'sub': [ + { + 'name': 'sub_mode_for_nic1', + 'state': False, + 'sub': [] + } + ] + }], + [{ + 'name': 'mode_for_nic2', + 'state': True, + 'sub': [] + }], + + ] + self.check_offloading_modes_intersection(different_modes[0], + different_modes[1], + []) + common_mode = {'name': 'common_mode', 'state': True, 'sub': []} + for modes in different_modes: + modes.append(common_mode) + + self.check_offloading_modes_intersection(different_modes[0], + different_modes[1], + [common_mode]) + + common_mode_2 = { + 'name': 'common_mode_2', + 'state': True, 'sub': [ + { + 'name': 'common_sub', + 'state': False, + 'sub': [] + } + ]} + for i, modes in enumerate(different_modes): + mode = copy.deepcopy(common_mode_2) + mode['sub'].append({'name': 'uncommon_sub_{}'.format(i), + 'state': None, + 'sub': []}) + modes.append(mode) + + self.check_offloading_modes_intersection(different_modes[0], + different_modes[1], + [common_mode, common_mode_2]) + class TestNICObject(BaseTestCase):