From e2eb6659a426bae05a54b93ebf31ff3247125258 Mon Sep 17 00:00:00 2001 From: Artom Lifshitz Date: Wed, 6 Jan 2016 02:31:45 +0000 Subject: [PATCH] Compute manager device tagging support This patch allows the network manager(s) to set virtual device tags if they are present during allocation. Implements: blueprint virt-device-role-tagging Co-authored-by: Vladik Romanovsky Co-authored-by: Dan Smith Change-Id: I8367f740d6d4ebaeb81bc74c6a82a8faf5cd8308 --- doc/notification_samples/service-update.json | 2 +- nova/compute/manager.py | 21 +++++ nova/network/manager.py | 27 ++++-- nova/network/neutronv2/api.py | 20 ++++- nova/objects/network_request.py | 11 ++- nova/objects/service.py | 4 +- nova/tests/unit/compute/test_compute_mgr.py | 82 +++++++++++++++++++ nova/tests/unit/network/test_manager.py | 16 +++- nova/tests/unit/network/test_neutronv2.py | 66 ++++++++++++--- .../unit/objects/test_network_request.py | 6 ++ nova/tests/unit/objects/test_objects.py | 2 +- 11 files changed, 229 insertions(+), 28 deletions(-) diff --git a/doc/notification_samples/service-update.json b/doc/notification_samples/service-update.json index be297591ca38..ede9a6068b70 100644 --- a/doc/notification_samples/service-update.json +++ b/doc/notification_samples/service-update.json @@ -13,7 +13,7 @@ "disabled_reason": null, "report_count": 1, "forced_down": false, - "version": 13 + "version": 14 } }, "event_type": "service.update", diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 7533757f26d2..219560c61d96 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1704,6 +1704,24 @@ class ComputeManager(manager.Manager): requested_networks, security_groups, block_device_mapping, node, limits) + def _check_device_tagging(self, requested_networks, block_device_mapping): + tagging_requested = False + if requested_networks: + for net in requested_networks: + if 'tag' in net and net.tag is not None: + tagging_requested = True + break + if block_device_mapping and not tagging_requested: + for bdm in block_device_mapping: + if 'tag' in bdm and bdm.tag is not None: + tagging_requested = True + break + if (tagging_requested and + not self.driver.capabilities.get('supports_device_tagging')): + raise exception.BuildAbortException('Attempt to boot guest with ' + 'tagged devices on host that ' + 'does not support tagging.') + @hooks.add_hook('build_instance') @wrap_exception() @reverts_task_state @@ -1856,6 +1874,9 @@ class ComputeManager(manager.Manager): image_name = image.get('name') self._notify_about_instance_usage(context, instance, 'create.start', extra_usage_info={'image_name': image_name}) + + self._check_device_tagging(requested_networks, block_device_mapping) + try: rt = self._get_resource_tracker(node) with rt.instance_claim(context, instance, limits): diff --git a/nova/network/manager.py b/nova/network/manager.py index 1db3cc65a0c8..580a61fe4fac 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -407,7 +407,7 @@ class NetworkManager(manager.Manager): try: self._allocate_mac_addresses(admin_context, instance_uuid, - networks, macs) + networks, macs, requested_networks) except Exception: with excutils.save_and_reraise_exception(): # If we fail to allocate any one mac address, clean up all @@ -663,26 +663,38 @@ class NetworkManager(manager.Manager): return subnets - def _allocate_mac_addresses(self, context, instance_uuid, networks, macs): + def _allocate_mac_addresses(self, context, instance_uuid, networks, macs, + requested_networks): """Generates mac addresses and creates vif rows in db for them.""" # make a copy we can mutate if macs is not None: available_macs = set(macs) + if requested_networks: + tags_by_network = { + network.network_id: network.tag if 'tag' in network else None + for network in requested_networks} + else: + tags_by_network = {} + for network in networks: if macs is None: - self._add_virtual_interface(context, instance_uuid, - network['id']) + self._add_virtual_interface( + context, instance_uuid, + network['id'], + tag=tags_by_network.get(network['uuid'])) else: try: mac = available_macs.pop() except KeyError: raise exception.VirtualInterfaceCreateException() - self._add_virtual_interface(context, instance_uuid, - network['id'], mac) + self._add_virtual_interface( + context, instance_uuid, + network['id'], mac, + tag=tags_by_network.get(network['uuid'])) def _add_virtual_interface(self, context, instance_uuid, network_id, - mac=None): + mac=None, tag=None): attempts = 1 if mac else CONF.create_unique_mac_address_attempts for i in range(attempts): try: @@ -691,6 +703,7 @@ class NetworkManager(manager.Manager): vif.instance_uuid = instance_uuid vif.network_id = network_id vif.uuid = str(uuid.uuid4()) + vif.tag = tag vif.create() return vif except exception.VirtualInterfaceCreateException: diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 1ad41570fe41..43b07e61cab4 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -192,7 +192,7 @@ class API(base_api.NetworkAPI): :param fixed_ip: Optional fixed IP to use from the given network. :param security_group_ids: Optional list of security group IDs to apply to the port. - :returns: ID of the created port. + :returns: The created port. :raises PortLimitExceeded: If neutron fails with an OverQuota error. :raises NoMoreFixedIps: If neutron fails with IpAddressGenerationFailure error. @@ -220,7 +220,7 @@ class API(base_api.NetworkAPI): LOG.debug('Successfully created port: %s', port_id, instance=instance) - return port_id + return port except neutron_client_exc.InvalidIpForNetworkClient: LOG.warning(_LW('Neutron error: %(ip)s is not a valid IP address ' 'for network %(network_id)s.'), @@ -629,6 +629,10 @@ class API(base_api.NetworkAPI): ports_in_requested_order = [] nets_in_requested_order = [] for request in ordered_networks: + vifobj = objects.VirtualInterface(context) + vifobj.instance_uuid = instance.uuid + vifobj.tag = request.tag if 'tag' in request else None + # Network lookup for available network_id network = None for net in nets: @@ -665,23 +669,31 @@ class API(base_api.NetworkAPI): bind_host_id=bind_host_id) self._populate_pci_mac_address(instance, request.pci_request_id, port_req_body) - self._populate_mac_address( + updated_mac = self._populate_mac_address( instance, port_req_body, available_macs) if dhcp_opts is not None: port_req_body['port']['extra_dhcp_opts'] = dhcp_opts if not request.port_id: - created_port_id = self._create_port( + created_port = self._create_port( port_client, instance, request.network_id, port_req_body, request.address, security_group_ids) + created_port_id = created_port['id'] created_port_ids.append(created_port_id) ports_in_requested_order.append(created_port_id) + vifobj.uuid = created_port_id + vifobj.address = created_port['mac_address'] else: self._update_port( port_client, instance, request.port_id, port_req_body) preexisting_port_ids.append(request.port_id) ports_in_requested_order.append(request.port_id) + vifobj.uuid = request.port_id + vifobj.address = (updated_mac or + ports[request.port_id]['mac_address']) + + vifobj.create() self._update_port_dns_name(context, instance, network, ports_in_requested_order[-1], diff --git a/nova/objects/network_request.py b/nova/objects/network_request.py index 91d8fbb4a587..6730a129f2c5 100644 --- a/nova/objects/network_request.py +++ b/nova/objects/network_request.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import versionutils + from nova.objects import base as obj_base from nova.objects import fields from nova import utils @@ -31,14 +33,21 @@ class NetworkRequest(obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added pci_request_id - VERSION = '1.1' + # Version 1.2: Added tag field + VERSION = '1.2' fields = { 'network_id': fields.StringField(nullable=True), 'address': fields.IPAddressField(nullable=True), 'port_id': fields.UUIDField(nullable=True), 'pci_request_id': fields.UUIDField(nullable=True), + 'tag': fields.StringField(nullable=True), } + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 2) and 'tag' in primitive: + del primitive['tag'] + def obj_load_attr(self, attr): setattr(self, attr, None) diff --git a/nova/objects/service.py b/nova/objects/service.py index 0b0b4bd9a76d..59030c92e455 100644 --- a/nova/objects/service.py +++ b/nova/objects/service.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) # NOTE(danms): This is the global service version counter -SERVICE_VERSION = 13 +SERVICE_VERSION = 14 # NOTE(danms): This is our SERVICE_VERSION history. The idea is that any @@ -81,6 +81,8 @@ SERVICE_VERSION_HISTORY = ( {'compute_rpc': '4.12'}, # Version 13: Compute RPC version 4.13 {'compute_rpc': '4.13'}, + # Version 14: The compute manager supports setting device tags. + {'compute_rpc': '4.13'}, ) diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index c6a4ada646ec..1a8162dbf008 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -45,6 +45,7 @@ from nova import objects from nova.objects import block_device as block_device_obj from nova.objects import instance as instance_obj from nova.objects import migrate_data as migrate_data_obj +from nova.objects import network_request as net_req_obj from nova import test from nova.tests import fixtures from nova.tests.unit.api.openstack import fakes @@ -266,6 +267,87 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): instance.info_cache = None self.compute._delete_instance(self.context, instance, [], quotas) + def test_check_device_tagging_no_tagging(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + instance_uuid=uuids.instance)]) + net_req = net_req_obj.NetworkRequest(tag=None) + net_req_list = net_req_obj.NetworkRequestList(objects=[net_req]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=False): + self.compute._check_device_tagging(net_req_list, bdms) + + def test_check_device_tagging_no_networks(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + instance_uuid=uuids.instance)]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=False): + self.compute._check_device_tagging(None, bdms) + + def test_check_device_tagging_tagged_net_req_no_virt_support(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + instance_uuid=uuids.instance)]) + net_req = net_req_obj.NetworkRequest(port_id='bar', tag='foo') + net_req_list = net_req_obj.NetworkRequestList(objects=[net_req]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=False): + self.assertRaises(exception.BuildAbortException, + self.compute._check_device_tagging, + net_req_list, bdms) + + def test_check_device_tagging_tagged_bdm_no_driver_support(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + tag='foo', + instance_uuid=uuids.instance)]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=False): + self.assertRaises(exception.BuildAbortException, + self.compute._check_device_tagging, + None, bdms) + + def test_check_device_tagging_tagged_bdm_no_driver_support_declared(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + tag='foo', + instance_uuid=uuids.instance)]) + with mock.patch.dict(self.compute.driver.capabilities): + self.compute.driver.capabilities.pop('supports_device_tagging', + None) + self.assertRaises(exception.BuildAbortException, + self.compute._check_device_tagging, + None, bdms) + + def test_check_device_tagging_tagged_bdm_with_driver_support(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + tag='foo', + instance_uuid=uuids.instance)]) + net_req = net_req_obj.NetworkRequest(network_id='bar') + net_req_list = net_req_obj.NetworkRequestList(objects=[net_req]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=True): + self.compute._check_device_tagging(net_req_list, bdms) + + def test_check_device_tagging_tagged_net_req_with_driver_support(self): + bdms = objects.BlockDeviceMappingList(objects=[ + objects.BlockDeviceMapping(source_type='volume', + destination_type='volume', + instance_uuid=uuids.instance)]) + net_req = net_req_obj.NetworkRequest(network_id='bar', tag='foo') + net_req_list = net_req_obj.NetworkRequestList(objects=[net_req]) + with mock.patch.dict(self.compute.driver.capabilities, + supports_device_tagging=True): + self.compute._check_device_tagging(net_req_list, bdms) + @mock.patch.object(network_api.API, 'allocate_for_instance') @mock.patch.object(objects.Instance, 'save') @mock.patch.object(time, 'sleep') diff --git a/nova/tests/unit/network/test_manager.py b/nova/tests/unit/network/test_manager.py index b98f55409c4f..3783cddfb840 100644 --- a/nova/tests/unit/network/test_manager.py +++ b/nova/tests/unit/network/test_manager.py @@ -2717,15 +2717,25 @@ class AllocateTestCase(test.TestCase): inst.uuid = FAKEUUID inst.create() networks = db.network_get_all(self.context) + reqnets = objects.NetworkRequestList(objects=[]) + index = 0 + project_id = self.user_context.project_id for network in networks: db.network_update(self.context, network['id'], - {'host': HOST}) - project_id = self.user_context.project_id + {'host': HOST, + 'project_id': project_id}) + if index == 0: + reqnets.objects.append(objects.NetworkRequest( + network_id=network['uuid'], + tag='mynic')) + index += 1 nw_info = self.network.allocate_for_instance(self.user_context, instance_id=inst['id'], instance_uuid=inst['uuid'], host=inst['host'], vpn=None, rxtx_factor=3, - project_id=project_id, macs=None) + project_id=project_id, macs=None, requested_networks=reqnets) self.assertEqual(1, len(nw_info)) + vifs = objects.VirtualInterfaceList.get_all(self.context) + self.assertEqual(['mynic'], [vif.tag for vif in vifs]) fixed_ip = nw_info.fixed_ips()[0]['address'] self.assertTrue(netutils.is_valid_ipv4(fixed_ip)) self.network.deallocate_for_instance(self.context, diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index a201b1f079c7..b7203dda513e 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -520,7 +520,9 @@ class TestNeutronv2Base(test.TestCase): preexisting_port_ids = [] ports_in_requested_net_order = [] nets_in_requested_net_order = [] + index = 0 for request in ordered_networks: + index += 1 port_req_body = { 'port': { 'device_id': self.instance.uuid, @@ -581,7 +583,8 @@ class TestNeutronv2Base(test.TestCase): if has_portbinding: port_req_body['port']['binding:host_id'] = ( self.instance.get('host')) - res_port = {'port': {'id': 'fake'}} + res_port = {'port': {'id': 'fake', + 'mac_address': 'fakemac%i' % index}} if kwargs.get('_break') == 'mac' + request.network_id: self.mox.ReplayAll() return api @@ -692,7 +695,17 @@ class TestNeutronv2Base(test.TestCase): def _allocate_for_instance(self, net_idx=1, **kwargs): api = self._stub_allocate_for_instance(net_idx, **kwargs) - return api.allocate_for_instance(self.context, self.instance, **kwargs) + self._vifs_created = [] + + def _new_vif(*args): + m = mock.MagicMock() + self._vifs_created.append(m) + return m + + with mock.patch('nova.objects.VirtualInterface') as mock_vif: + mock_vif.side_effect = _new_vif + return api.allocate_for_instance(self.context, self.instance, + **kwargs) class TestNeutronv2(TestNeutronv2Base): @@ -1018,14 +1031,23 @@ class TestNeutronv2(TestNeutronv2Base): self._allocate_for_instance(net_idx=1, requested_networks=requested_networks, macs=set(['ab:cd:ef:01:23:45'])) + self.assertEqual('ab:cd:ef:01:23:45', self._vifs_created[0].address) def test_allocate_for_instance_accepts_only_portid(self): # Make sure allocate_for_instance works when only a portid is provided self._returned_nw_info = self.port_data1 result = self._allocate_for_instance( requested_networks=objects.NetworkRequestList( - objects=[objects.NetworkRequest(port_id=uuids.portid_1)])) + objects=[objects.NetworkRequest(port_id=uuids.portid_1, + tag='test')])) self.assertEqual(self.port_data1, result) + self.assertEqual(1, len(self._vifs_created)) + self.assertEqual('test', self._vifs_created[0].tag) + self.assertEqual(self.instance.uuid, + self._vifs_created[0].instance_uuid) + self.assertEqual(uuids.portid_1, self._vifs_created[0].uuid) + self.assertEqual(self.port_data1[0]['mac_address'], + self._vifs_created[0].address) @mock.patch('nova.network.neutronv2.api.API._unbind_ports') def test_allocate_for_instance_not_enough_macs_via_ports(self, @@ -1096,8 +1118,18 @@ class TestNeutronv2(TestNeutronv2Base): requested_networks = objects.NetworkRequestList( objects=[objects.NetworkRequest(network_id=net['id']) for net in (self.nets3[0], self.nets3[2], self.nets3[1])]) + requested_networks[0].tag = 'foo' self._allocate_for_instance(net_idx=2, requested_networks=requested_networks) + self.assertEqual(3, len(self._vifs_created)) + # NOTE(danms) nets3[2] is chosen above as one that won't validate, + # so we never actually run create() on the VIF. + vifs_really_created = [vif for vif in self._vifs_created + if vif.create.called] + self.assertEqual(2, len(vifs_really_created)) + self.assertEqual([('foo', 'fakemac1'), (None, 'fakemac3')], + [(vif.tag, vif.address) + for vif in vifs_really_created]) def test_allocate_for_instance_with_requested_networks(self): # specify only first and last network @@ -1200,7 +1232,8 @@ class TestNeutronv2(TestNeutronv2Base): }, } port_req_body['port'].update(binding_port_req_body['port']) - port = {'id': 'portid_' + network['id']} + port = {'id': 'portid_' + network['id'], + 'mac_address': 'foo'} api._populate_neutron_extension_values(self.context, self.instance, None, binding_port_req_body, network=network, @@ -3642,7 +3675,9 @@ class TestNeutronv2WithMock(test.TestCase): '_populate_neutron_extension_values') @mock.patch('nova.network.neutronv2.api.API._get_available_networks') @mock.patch('nova.network.neutronv2.api.get_client') - def test_allocate_for_instance_unbind(self, mock_ntrn, + @mock.patch('nova.objects.VirtualInterface') + def test_allocate_for_instance_unbind(self, mock_vif, + mock_ntrn, mock_avail_nets, mock_ext_vals, mock_has_pbe, @@ -3656,6 +3691,7 @@ class TestNeutronv2WithMock(test.TestCase): def show_port(port_id): return {'port': {'network_id': 'net-1', 'id': port_id, + 'mac_address': 'fakemac', 'tenant_id': 'proj-1'}} mock_nc.show_port = show_port @@ -4491,8 +4527,9 @@ class TestNeutronPortSecurity(test.NoDBTestCase): @mock.patch.object(neutronapi.API, '_process_requested_networks') @mock.patch.object(neutronapi.API, '_has_port_binding_extension') @mock.patch.object(neutronapi, 'get_client') + @mock.patch('nova.objects.VirtualInterface') def test_no_security_groups_requested( - self, mock_get_client, mock_has_port_binding_extension, + self, mock_vif, mock_get_client, mock_has_port_binding_extension, mock_process_requested_networks, mock_get_available_networks, mock_process_security_groups, mock_check_external_network_attach, mock_populate_neutron_extension_values, mock_create_port, @@ -4520,6 +4557,7 @@ class TestNeutronPortSecurity(test.NoDBTestCase): mock_process_security_groups.return_value = [] api = neutronapi.API() + mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'} api.allocate_for_instance( 'context', instance, requested_networks=onets, security_groups=secgroups) @@ -4550,8 +4588,9 @@ class TestNeutronPortSecurity(test.NoDBTestCase): @mock.patch.object(neutronapi.API, '_process_requested_networks') @mock.patch.object(neutronapi.API, '_has_port_binding_extension') @mock.patch.object(neutronapi, 'get_client') + @mock.patch('nova.objects.VirtualInterface') def test_security_groups_requested( - self, mock_get_client, mock_has_port_binding_extension, + self, mock_vif, mock_get_client, mock_has_port_binding_extension, mock_process_requested_networks, mock_get_available_networks, mock_process_security_groups, mock_check_external_network_attach, mock_populate_neutron_extension_values, mock_create_port, @@ -4581,6 +4620,7 @@ class TestNeutronPortSecurity(test.NoDBTestCase): 'secgrp-uuid2'] api = neutronapi.API() + mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'} api.allocate_for_instance( 'context', instance, requested_networks=onets, security_groups=secgroups) @@ -4611,8 +4651,9 @@ class TestNeutronPortSecurity(test.NoDBTestCase): @mock.patch.object(neutronapi.API, '_process_requested_networks') @mock.patch.object(neutronapi.API, '_has_port_binding_extension') @mock.patch.object(neutronapi, 'get_client') + @mock.patch('nova.objects.VirtualInterface') def test_port_security_disabled_no_security_groups_requested( - self, mock_get_client, mock_has_port_binding_extension, + self, mock_vif, mock_get_client, mock_has_port_binding_extension, mock_process_requested_networks, mock_get_available_networks, mock_process_security_groups, mock_check_external_network_attach, mock_populate_neutron_extension_values, mock_create_port, @@ -4640,6 +4681,7 @@ class TestNeutronPortSecurity(test.NoDBTestCase): mock_process_security_groups.return_value = [] api = neutronapi.API() + mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'} api.allocate_for_instance( 'context', instance, requested_networks=onets, security_groups=secgroups) @@ -4670,8 +4712,9 @@ class TestNeutronPortSecurity(test.NoDBTestCase): @mock.patch.object(neutronapi.API, '_process_requested_networks') @mock.patch.object(neutronapi.API, '_has_port_binding_extension') @mock.patch.object(neutronapi, 'get_client') + @mock.patch('nova.objects.VirtualInterface') def test_port_security_disabled_and_security_groups_requested( - self, mock_get_client, mock_has_port_binding_extension, + self, mock_vif, mock_get_client, mock_has_port_binding_extension, mock_process_requested_networks, mock_get_available_networks, mock_process_security_groups, mock_check_external_network_attach, mock_populate_neutron_extension_values, mock_create_port, @@ -4918,11 +4961,14 @@ class TestNeutronv2AutoAllocateNetwork(test.NoDBTestCase): @mock.patch.object(self.api, '_populate_neutron_extension_values') @mock.patch.object(self.api, '_populate_mac_address') @mock.patch.object(self.api, '_create_port', spec=True, - return_value=uuids.port_id) + return_value={'id': uuids.port_id, + 'mac_address': 'foo'}) @mock.patch.object(self.api, '_update_port_dns_name') @mock.patch.object(self.api, 'get_instance_nw_info', fake_get_instance_nw_info) + @mock.patch('nova.objects.VirtualInterface') def do_test(self, + mock_vif, update_port_dsn_name_mock, create_port_mock, populate_mac_addr_mock, diff --git a/nova/tests/unit/objects/test_network_request.py b/nova/tests/unit/objects/test_network_request.py index 3408683057f0..80c63c8dbe93 100644 --- a/nova/tests/unit/objects/test_network_request.py +++ b/nova/tests/unit/objects/test_network_request.py @@ -130,6 +130,12 @@ class _TestNetworkRequestObject(object): network_id=network_request.NETWORK_ID_NONE)]) self.assertTrue(requests.no_allocate) + def test_obj_make_compatible_pre_1_2(self): + net_req = objects.NetworkRequest() + net_req.tag = 'foo' + primitive = net_req.obj_to_primitive(target_version='1.1') + self.assertNotIn('tag', primitive) + class TestNetworkRequestObject(test_objects._LocalTest, _TestNetworkRequestObject): diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index ea20d972262f..93f9704e1844 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1168,7 +1168,7 @@ object_data = { 'Network': '1.2-a977ab383aa462a479b2fae8211a5dde', 'NetworkInterfaceMetadata': '1.0-99a9574d086feb5ad45cd04a34855647', 'NetworkList': '1.2-69eca910d8fa035dfecd8ba10877ee59', - 'NetworkRequest': '1.1-7a3e4ca2ce1e7b62d8400488f2f2b756', + 'NetworkRequest': '1.2-af1ff2d986999fbb79377712794d82aa', 'NetworkRequestList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e', 'PciDevice': '1.5-0d5abe5c91645b8469eb2a93fc53f932', 'PCIDeviceBus': '1.0-2b891cb77e42961044689f3dc2718995',