diff --git a/nova/network/os_vif_util.py b/nova/network/os_vif_util.py new file mode 100644 index 000000000000..3ca431aef4f1 --- /dev/null +++ b/nova/network/os_vif_util.py @@ -0,0 +1,375 @@ +# Copyright 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +''' +This module contains code for converting from the original +nova.network.model data structure, to the new os-vif based +versioned object model os_vif.objects.* +''' + +import sys + +import os_vif +from os_vif import objects +from oslo_config import cfg +from oslo_log import log as logging + +from nova import exception +from nova.network import model + + +LOG = logging.getLogger(__name__) +CONF = cfg.CONF + +# Ensure os-vif objects are registered and plugins loaded +os_vif.initialize() + + +def _get_vif_name(vif): + """Get a VIF device name + + :param vif: the nova.nework.model.VIF instance + + Get a string suitable for use as a host OS network + device name + + :returns: a device name + """ + + if vif.get('devname', None) is not None: + return vif['devname'] + return ('nic' + vif['id'])[:model.NIC_NAME_LEN] + + +def _get_hybrid_bridge_name(vif): + """Get a bridge device name + + :param vif: the nova.nework.model.VIF instance + + Get a string suitable for use as a host OS bridge + device name + + :returns: a bridge name + """ + + return ('qbr' + vif['id'])[:model.NIC_NAME_LEN] + + +def _is_firewall_required(vif): + """Check if local firewall is required + + :param vif: the nova.nework.model.VIF instance + + :returns: True if local firewall is required + """ + + if vif.is_neutron_filtering_enabled(): + return False + if CONF.firewall_driver != "nova.virt.firewall.NoopFirewallDriver": + return True + return False + + +def nova_to_osvif_instance(instance): + """Convert a Nova instance object to an os-vif instance object + + :param vif: a nova.objects.Instance instance + + :returns: a os_vif.objects.instance_info.InstanceInfo + """ + + info = objects.instance_info.InstanceInfo( + uuid=instance.uuid, + name=instance.name) + + if (instance.obj_attr_is_set("project_id") and + instance.project_id is not None): + info.project_id = instance.project_id + + return info + + +def _nova_to_osvif_ip(ip): + """Convert Nova IP object into os_vif object + + :param route: nova.network.model.IP instance + + :returns: os_vif.objects.fixed_ip.FixedIP instance + """ + floating_ips = [fip['address'] for fip in ip.get('floating_ips', [])] + return objects.fixed_ip.FixedIP( + address=ip['address'], + floating_ips=floating_ips) + + +def _nova_to_osvif_ips(ips): + """Convert Nova IP list into os_vif object + + :param routes: list of nova.network.model.IP instances + + :returns: os_vif.objects.fixed_ip.FixedIPList instance + """ + + return objects.fixed_ip.FixedIPList( + objects=[_nova_to_osvif_ip(ip) for ip in ips]) + + +def _nova_to_osvif_route(route): + """Convert Nova route object into os_vif object + + :param route: nova.network.model.Route instance + + :returns: os_vif.objects.route.Route instance + """ + + obj = objects.route.Route( + cidr=route['cidr'], + interface=route['interface']) + + if (route['gateway'] is not None and + route['gateway']['address'] is not None): + obj.gateway = route['gateway']['address'] + + return obj + + +def _nova_to_osvif_routes(routes): + """Convert Nova route list into os_vif object + + :param routes: list of nova.network.model.Route instances + + :returns: os_vif.objects.route.RouteList instance + """ + + return objects.route.RouteList( + objects=[_nova_to_osvif_route(route) for route in routes]) + + +def _nova_to_osvif_subnet(subnet): + """Convert Nova subnet object into os_vif object + + :param subnet: nova.network.model.Subnet instance + + :returns: os_vif.objects.subnet.Subnet instance + """ + + dnsaddrs = [ip['address'] for ip in subnet['dns']] + + obj = objects.subnet.Subnet( + dns=dnsaddrs, + ips=_nova_to_osvif_ips(subnet['ips']), + routes=_nova_to_osvif_routes(subnet['routes'])) + if subnet['cidr'] is not None: + obj.cidr = subnet['cidr'] + if (subnet['gateway'] is not None and + subnet['gateway']['address'] is not None): + obj.gateway = subnet['gateway']['address'] + return obj + + +def _nova_to_osvif_subnets(subnets): + """Convert Nova subnet list into os_vif object + + :param subnets: list of nova.network.model.Subnet instances + + :returns: os_vif.objects.subnet.SubnetList instance + """ + + return objects.subnet.SubnetList( + objects=[_nova_to_osvif_subnet(subnet) for subnet in subnets]) + + +def _nova_to_osvif_network(network): + """Convert Nova network object into os_vif object + + :param network: nova.network.model.Network instance + + :returns: os_vif.objects.network.Network instance + """ + + netobj = objects.network.Network( + id=network['id'], + subnets=_nova_to_osvif_subnets(network['subnets'])) + + if network["bridge"] is not None: + netobj.bridge = network['bridge'] + if network['label'] is not None: + netobj.label = network['label'] + + if network.get_meta("multi_host") is not None: + netobj.multi_host = network.get_meta("multi_host") + if network.get_meta("should_provide_bridge") is not None: + netobj.should_provide_bridge = \ + network.get_meta("should_provide_bridge") + if network.get_meta("bridge_interface") is not None: + netobj.bridge_interface = network.get_meta("bridge_interface") + if network.get_meta("should_provide_vlan") is not None: + netobj.should_provide_vlan = network.get_meta("should_provide_vlan") + if network.get_meta("vlan") is not None: + netobj.vlan = network.get_meta("vlan") + + return netobj + + +def _get_vif_instance(vif, cls, **kwargs): + """Instantiate an os-vif VIF instance + + :param vif: the nova.nework.model.VIF instance + :param cls: class for a os_vif.objects.vif.VIFBase subclass + + :returns: a os_vif.objects.vif.VIFBase instance + """ + + return cls( + id=vif['id'], + address=vif['address'], + network=_nova_to_osvif_network(vif['network']), + has_traffic_filtering=vif.is_neutron_filtering_enabled(), + preserve_on_delete=vif['preserve_on_delete'], + active=vif['active'], + **kwargs) + + +# VIF_TYPE_BRIDGE = 'bridge' +def _nova_to_osvif_vif_bridge(vif): + obj = _get_vif_instance( + vif, + objects.vif.VIFBridge, + plugin="linux_bridge", + vif_name=_get_vif_name(vif)) + if vif["network"]["bridge"] is not None: + obj.bridge_name = vif["network"]["bridge"] + return obj + + +# VIF_TYPE_OVS = 'ovs' +def _nova_to_osvif_vif_ovs(vif): + profile = objects.vif.VIFPortProfileOpenVSwitch( + interface_id=vif.get('ovs_interfaceid') or vif['id']) + if _is_firewall_required(vif) or vif.is_hybrid_plug_enabled(): + obj = _get_vif_instance( + vif, + objects.vif.VIFBridge, + port_profile=profile, + plugin="ovs", + vif_name=_get_vif_name(vif), + bridge_name=_get_hybrid_bridge_name(vif)) + else: + obj = _get_vif_instance( + vif, + objects.vif.VIFOpenVSwitch, + port_profile=profile, + plugin="ovs", + vif_name=_get_vif_name(vif)) + if vif["network"]["bridge"] is not None: + obj.bridge_name = vif["network"]["bridge"] + return obj + + +# VIF_TYPE_IVS = 'ivs' +def _nova_to_osvif_vif_ivs(vif): + raise NotImplementedError() + + +# VIF_TYPE_DVS = 'dvs' +def _nova_to_osvif_vif_dvs(vif): + raise NotImplementedError() + + +# VIF_TYPE_IOVISOR = 'iovisor' +def _nova_to_osvif_vif_iovisor(vif): + raise NotImplementedError() + + +# VIF_TYPE_802_QBG = '802.1qbg' +def _nova_to_osvif_vif_802_1qbg(vif): + raise NotImplementedError() + + +# VIF_TYPE_802_QBH = '802.1qbh' +def _nova_to_osvif_vif_802_1qbh(vif): + raise NotImplementedError() + + +# VIF_TYPE_HW_VEB = 'hw_veb' +def _nova_to_osvif_vif_hw_veb(vif): + raise NotImplementedError() + + +# VIF_TYPE_IB_HOSTDEV = 'ib_hostdev' +def _nova_to_osvif_vif_ib_hostdev(vif): + raise NotImplementedError() + + +# VIF_TYPE_MIDONET = 'midonet' +def _nova_to_osvif_vif_midonet(vif): + raise NotImplementedError() + + +# VIF_TYPE_VHOSTUSER = 'vhostuser' +def _nova_to_osvif_vif_vhostuser(vif): + raise NotImplementedError() + + +# VIF_TYPE_VROUTER = 'vrouter' +def _nova_to_osvif_vif_vrouter(vif): + raise NotImplementedError() + + +# VIF_TYPE_TAP = 'tap' +def _nova_to_osvif_vif_tap(vif): + raise NotImplementedError() + + +# VIF_TYPE_MACVTAP = 'macvtap' +def _nova_to_osvif_vif_macvtap(vif): + raise NotImplementedError() + + +# VIF_TYPE_HOSTDEV = 'hostdev_physical' +def _nova_to_osvif_vif_hostdev_physical(vif): + raise NotImplementedError() + + +def nova_to_osvif_vif(vif): + """Convert a Nova VIF model to an os-vif object + + :param vif: a nova.network.model.VIF instance + + Attempt to convert a nova VIF instance into an os-vif + VIF object, pointing to a suitable plugin. This will + return None if there is no os-vif plugin available yet. + + :returns: a os_vif.objects.vif.VIFBase subclass, or None + if not supported with os-vif yet + """ + + LOG.debug("Converting VIF %s", vif) + + funcname = "_nova_to_osvif_vif_" + vif['type'].replace(".", "_") + func = getattr(sys.modules[__name__], funcname, None) + + if not func: + raise exception.NovaException( + "Unsupported VIF type %(type)s convert '%(func)s'" % + {'type': vif['type'], 'func': funcname}) + + try: + vifobj = func(vif) + LOG.debug("Converted object %s", vifobj) + return vifobj + except NotImplementedError: + LOG.debug("No conversion for VIF type %s yet", + vif['type']) + return None diff --git a/nova/tests/unit/network/test_os_vif_util.py b/nova/tests/unit/network/test_os_vif_util.py new file mode 100644 index 000000000000..944f87ff27b7 --- /dev/null +++ b/nova/tests/unit/network/test_os_vif_util.py @@ -0,0 +1,550 @@ +# Copyright 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from os_vif import objects as osv_objects + +from nova import exception +from nova.network import model +from nova.network import os_vif_util +from nova import objects +from nova import test + + +class OSVIFUtilTestCase(test.NoDBTestCase): + + def setUp(self): + super(OSVIFUtilTestCase, self).setUp() + + osv_objects.register_all() + + # Remove when all os-vif objects include the + # ComparableVersionedObject mix-in + def assertObjEqual(self, expect, actual): + actual.obj_reset_changes(recursive=True) + expect.obj_reset_changes(recursive=True) + self.assertEqual(expect.obj_to_primitive(), + actual.obj_to_primitive()) + + def _test_is_firewall_required(self, port_filter, driver, expect): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type=model.VIF_TYPE_BRIDGE, + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + details={ + model.VIF_DETAILS_PORT_FILTER: port_filter, + } + ) + self.flags(firewall_driver=driver) + + self.assertEqual(expect, os_vif_util._is_firewall_required(vif)) + + def test_is_firewall_required_via_vif(self): + self._test_is_firewall_required( + True, "nova.virt.libvirt.firewall.IptablesFirewallDriver", False) + + def test_is_firewall_required_via_driver(self): + self._test_is_firewall_required( + False, "nova.virt.libvirt.firewall.IptablesFirewallDriver", True) + + def test_is_firewall_required_not(self): + self._test_is_firewall_required( + False, "nova.virt.firewall.NoopFirewallDriver", False) + + def test_nova_to_osvif_instance(self): + inst = objects.Instance( + id="1242", + uuid="d5b1090c-9e00-4fa4-9504-4b1494857970", + project_id="2f37d7f6-e51a-4a1f-8b6e-b0917ffc8390") + + info = os_vif_util.nova_to_osvif_instance(inst) + + expect = osv_objects.instance_info.InstanceInfo( + uuid="d5b1090c-9e00-4fa4-9504-4b1494857970", + name="instance-000004da", + project_id="2f37d7f6-e51a-4a1f-8b6e-b0917ffc8390") + + self.assertObjEqual(info, expect) + + def test_nova_to_osvif_instance_minimal(self): + inst = objects.Instance( + id="1242", + uuid="d5b1090c-9e00-4fa4-9504-4b1494857970") + + actual = os_vif_util.nova_to_osvif_instance(inst) + + expect = osv_objects.instance_info.InstanceInfo( + uuid=inst.uuid, + name=inst.name) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_ips(self): + ips = [ + model.FixedIP( + address="192.168.122.24", + floating_ips=[ + model.IP(address="192.168.122.100", + type="floating"), + model.IP(address="192.168.122.101", + type="floating"), + model.IP(address="192.168.122.102", + type="floating"), + ], + version=4), + model.FixedIP( + address="2001::beef", + version=6), + ] + + actual = os_vif_util._nova_to_osvif_ips(ips) + + expect = osv_objects.fixed_ip.FixedIPList( + objects=[ + osv_objects.fixed_ip.FixedIP( + address="192.168.122.24", + floating_ips=[ + "192.168.122.100", + "192.168.122.101", + "192.168.122.102", + ]), + osv_objects.fixed_ip.FixedIP( + address="2001::beef", + floating_ips=[]), + ], + ) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_routes(self): + routes = [ + model.Route(cidr="192.168.1.0/24", + gateway=model.IP( + address="192.168.1.254", + type='gateway'), + interface="eth0"), + model.Route(cidr="10.0.0.0/8", + gateway=model.IP( + address="10.0.0.1", + type='gateway'), + interface="eth1"), + ] + + expect = osv_objects.route.RouteList( + objects=[ + osv_objects.route.Route( + cidr="192.168.1.0/24", + gateway="192.168.1.254", + interface="eth0"), + osv_objects.route.Route( + cidr="10.0.0.0/8", + gateway="10.0.0.1", + interface="eth1"), + ]) + + actual = os_vif_util._nova_to_osvif_routes(routes) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_subnets(self): + subnets = [ + model.Subnet(cidr="192.168.1.0/24", + dns=[ + model.IP( + address="192.168.1.1", + type="dns"), + model.IP( + address="192.168.1.2", + type="dns"), + ], + gateway=model.IP( + address="192.168.1.254", + type='gateway'), + ips=[ + model.FixedIP( + address="192.168.1.100", + ), + model.FixedIP( + address="192.168.1.101", + ), + ], + routes=[ + model.Route( + cidr="10.0.0.1/24", + gateway=model.IP( + address="192.168.1.254", + type="gateway"), + interface="eth0"), + ]), + model.Subnet(dns=[ + model.IP( + address="192.168.1.1", + type="dns"), + model.IP( + address="192.168.1.2", + type="dns"), + ], + ips=[ + model.FixedIP( + address="192.168.1.100", + ), + model.FixedIP( + address="192.168.1.101", + ), + ], + routes=[ + model.Route( + cidr="10.0.0.1/24", + gateway=model.IP( + address="192.168.1.254", + type="gateway"), + interface="eth0"), + ]), + model.Subnet(dns=[ + model.IP( + address="192.168.1.1", + type="dns"), + model.IP( + address="192.168.1.2", + type="dns"), + ], + gateway=model.IP( + type='gateway'), + ips=[ + model.FixedIP( + address="192.168.1.100", + ), + model.FixedIP( + address="192.168.1.101", + ), + ], + routes=[ + model.Route( + cidr="10.0.0.1/24", + gateway=model.IP( + address="192.168.1.254", + type="gateway"), + interface="eth0"), + ]), + ] + + expect = osv_objects.subnet.SubnetList( + objects=[ + osv_objects.subnet.Subnet( + cidr="192.168.1.0/24", + dns=["192.168.1.1", + "192.168.1.2"], + gateway="192.168.1.254", + ips=osv_objects.fixed_ip.FixedIPList( + objects=[ + osv_objects.fixed_ip.FixedIP( + address="192.168.1.100", + floating_ips=[]), + osv_objects.fixed_ip.FixedIP( + address="192.168.1.101", + floating_ips=[]), + ]), + routes=osv_objects.route.RouteList( + objects=[ + osv_objects.route.Route( + cidr="10.0.0.1/24", + gateway="192.168.1.254", + interface="eth0") + ]), + ), + osv_objects.subnet.Subnet( + dns=["192.168.1.1", + "192.168.1.2"], + ips=osv_objects.fixed_ip.FixedIPList( + objects=[ + osv_objects.fixed_ip.FixedIP( + address="192.168.1.100", + floating_ips=[]), + osv_objects.fixed_ip.FixedIP( + address="192.168.1.101", + floating_ips=[]), + ]), + routes=osv_objects.route.RouteList( + objects=[ + osv_objects.route.Route( + cidr="10.0.0.1/24", + gateway="192.168.1.254", + interface="eth0") + ]), + ), + osv_objects.subnet.Subnet( + dns=["192.168.1.1", + "192.168.1.2"], + ips=osv_objects.fixed_ip.FixedIPList( + objects=[ + osv_objects.fixed_ip.FixedIP( + address="192.168.1.100", + floating_ips=[]), + osv_objects.fixed_ip.FixedIP( + address="192.168.1.101", + floating_ips=[]), + ]), + routes=osv_objects.route.RouteList( + objects=[ + osv_objects.route.Route( + cidr="10.0.0.1/24", + gateway="192.168.1.254", + interface="eth0") + ]), + ), + ]) + + actual = os_vif_util._nova_to_osvif_subnets(subnets) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_network(self): + network = model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + bridge="br0", + subnets=[ + model.Subnet(cidr="192.168.1.0/24", + gateway=model.IP( + address="192.168.1.254", + type='gateway')), + ]) + + expect = osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + bridge="br0", + subnets=osv_objects.subnet.SubnetList( + objects=[ + osv_objects.subnet.Subnet( + cidr="192.168.1.0/24", + dns=[], + gateway="192.168.1.254", + ips=osv_objects.fixed_ip.FixedIPList( + objects=[]), + routes=osv_objects.route.RouteList( + objects=[]), + ) + ])) + + actual = os_vif_util._nova_to_osvif_network(network) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_network_extra(self): + network = model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + bridge="br0", + multi_host=True, + should_provide_bridge=True, + should_provide_vlan=True, + bridge_interface="eth0", + vlan=1729, + subnets=[ + model.Subnet(cidr="192.168.1.0/24", + gateway=model.IP( + address="192.168.1.254", + type='gateway')), + ]) + + expect = osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + bridge="br0", + multi_host=True, + should_provide_bridge=True, + should_provide_vlan=True, + bridge_interface="eth0", + vlan=1729, + subnets=osv_objects.subnet.SubnetList( + objects=[ + osv_objects.subnet.Subnet( + cidr="192.168.1.0/24", + dns=[], + gateway="192.168.1.254", + ips=osv_objects.fixed_ip.FixedIPList( + objects=[]), + routes=osv_objects.route.RouteList( + objects=[]), + ) + ])) + + actual = os_vif_util._nova_to_osvif_network(network) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_network_labeled_no_bridge(self): + network = model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[ + model.Subnet(cidr="192.168.1.0/24", + gateway=model.IP( + address="192.168.1.254", + type='gateway')), + ]) + + expect = osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=osv_objects.subnet.SubnetList( + objects=[ + osv_objects.subnet.Subnet( + cidr="192.168.1.0/24", + dns=[], + gateway="192.168.1.254", + ips=osv_objects.fixed_ip.FixedIPList( + objects=[]), + routes=osv_objects.route.RouteList( + objects=[]), + ) + ])) + + actual = os_vif_util._nova_to_osvif_network(network) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_vif_linux_bridge(self): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type=model.VIF_TYPE_BRIDGE, + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + details={ + model.VIF_DETAILS_PORT_FILTER: True, + } + ) + + actual = os_vif_util.nova_to_osvif_vif(vif) + + expect = osv_objects.vif.VIFBridge( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + active=False, + address="22:52:25:62:e2:aa", + has_traffic_filtering=True, + plugin="linux_bridge", + preserve_on_delete=False, + vif_name="nicdc065497-3c", + network=osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=osv_objects.subnet.SubnetList( + objects=[]))) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_vif_ovs_plain(self): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type=model.VIF_TYPE_OVS, + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + details={ + model.VIF_DETAILS_PORT_FILTER: True, + } + ) + + actual = os_vif_util.nova_to_osvif_vif(vif) + + expect = osv_objects.vif.VIFOpenVSwitch( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + active=False, + address="22:52:25:62:e2:aa", + has_traffic_filtering=True, + plugin="ovs", + port_profile=osv_objects.vif.VIFPortProfileOpenVSwitch( + interface_id="dc065497-3c8d-4f44-8fb4-e1d33c16a536"), + preserve_on_delete=False, + vif_name="nicdc065497-3c", + network=osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=osv_objects.subnet.SubnetList( + objects=[]))) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_vif_ovs_hybrid(self): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type=model.VIF_TYPE_OVS, + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + details={ + model.VIF_DETAILS_PORT_FILTER: False, + } + ) + + actual = os_vif_util.nova_to_osvif_vif(vif) + + expect = osv_objects.vif.VIFBridge( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + active=False, + address="22:52:25:62:e2:aa", + has_traffic_filtering=False, + plugin="ovs", + bridge_name="qbrdc065497-3c", + port_profile=osv_objects.vif.VIFPortProfileOpenVSwitch( + interface_id="dc065497-3c8d-4f44-8fb4-e1d33c16a536"), + preserve_on_delete=False, + vif_name="nicdc065497-3c", + network=osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=osv_objects.subnet.SubnetList( + objects=[]))) + + self.assertObjEqual(expect, actual) + + def test_nova_to_osvif_vif_ivs_plain(self): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type=model.VIF_TYPE_IVS, + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + details={ + model.VIF_DETAILS_PORT_FILTER: True, + } + ) + + actual = os_vif_util.nova_to_osvif_vif(vif) + + self.assertIsNone(actual) + + def test_nova_to_osvif_vif_unknown(self): + vif = model.VIF( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + type="wibble", + address="22:52:25:62:e2:aa", + network=model.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=[]), + ) + + self.assertRaises(exception.NovaException, + os_vif_util.nova_to_osvif_vif, + vif) diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index de7dc242a939..471183f5adf7 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -283,7 +283,7 @@ class TestNotificationObjectVersions(test.NoDBTestCase): def test_versions(self): checker = fixture.ObjectVersionChecker( - base.NovaObjectRegistry.obj_classes()) + test_objects.get_nova_objects()) notification_object_data.update(test_objects.object_data) expected, actual = checker.test_hashes(notification_object_data) self.assertEqual(expected, actual, diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index d8c76d204ada..db0dccb2f844 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1206,10 +1206,31 @@ object_data = { } +def get_nova_objects(): + """Get Nova versioned objects + + This returns a dict of versioned objects which are + in the Nova project namespace only. ie excludes + objects from os-vif and other 3rd party modules + + :return: a dict mapping class names to lists of versioned objects + """ + + all_classes = base.NovaObjectRegistry.obj_classes() + nova_classes = {} + for name in all_classes: + objclasses = all_classes[name] + if (objclasses[0].OBJ_PROJECT_NAMESPACE != + base.NovaObject.OBJ_PROJECT_NAMESPACE): + continue + nova_classes[name] = objclasses + return nova_classes + + class TestObjectVersions(test.NoDBTestCase): def test_versions(self): checker = fixture.ObjectVersionChecker( - base.NovaObjectRegistry.obj_classes()) + get_nova_objects()) fingerprints = checker.get_hashes() if os.getenv('GENERATE_HASHES'): diff --git a/requirements.txt b/requirements.txt index 116b05e96c3d..4724a42fcc5c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -54,6 +54,7 @@ oslo.middleware>=3.0.0 # Apache-2.0 psutil<2.0.0,>=1.1.1 # BSD oslo.versionedobjects>=1.13.0 # Apache-2.0 os-brick>=1.3.0 # Apache-2.0 +os-vif>=1.0.0 # Apache-2.0 os-win>=0.2.3 # Apache-2.0 castellan>=0.4.0 # Apache-2.0 microversion-parse>=0.1.2 # Apache-2.0