diff --git a/vmware_nsx/common/utils.py b/vmware_nsx/common/utils.py index 0d386ece45..078e47dc16 100644 --- a/vmware_nsx/common/utils.py +++ b/vmware_nsx/common/utils.py @@ -152,6 +152,24 @@ def add_v3_tag(tags, resource_type, tag): return tags +def update_v3_tags(tags, resources): + port_tags = dict((t['scope'], t['tag']) for t in tags) + resources = resources or [] + # Update tags + for resource in resources: + tag = resource['tag'][:MAX_TAG_LEN] + resource_type = resource['resource_type'] + if resource_type in port_tags: + if tag: + port_tags[resource_type] = tag + else: + port_tags.pop(resource_type, None) + else: + port_tags[resource_type] = tag + # Create the new set of tags + return [{'scope': k, 'tag': v} for k, v in port_tags.items()] + + def retry_upon_exception_nsxv3(exc, delay=500, max_delay=2000, max_attempts=cfg.CONF.nsx_v3.retries): return retrying.retry(retry_on_exception=lambda e: isinstance(e, exc), diff --git a/vmware_nsx/nsxlib/v3/resources.py b/vmware_nsx/nsxlib/v3/resources.py index f1aa4ad859..73ad063d07 100644 --- a/vmware_nsx/nsxlib/v3/resources.py +++ b/vmware_nsx/nsxlib/v3/resources.py @@ -258,9 +258,11 @@ class LogicalPort(AbstractRESTResource): max_attempts=cfg.CONF.nsx_v3.retries) def update(self, lport_id, name=None, admin_state=None, address_bindings=None, switch_profile_ids=None, - tags=None): + resources=None): lport = self.get(lport_id) - + tags = lport.get('tags', []) + if resources: + tags = utils.update_v3_tags(tags, resources) lport.update(self._build_body_attrs( display_name=name, admin_state=admin_state, tags=tags, diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 6540705975..9e403785f9 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -591,8 +591,10 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, tags = utils.build_v3_tags_payload( port_data, resource_type=resource_type, project_name=context.tenant_name) - if device_id: - tags = utils.add_v3_tag(tags, 'os-instance-uuid', device_id) + resource_type = self._get_resource_type_for_device_id( + device_owner, device_id) + if resource_type: + tags = utils.add_v3_tag(tags, resource_type, device_id) parent_name, tag = self._get_data_from_binding_profile( context, port_data) @@ -626,7 +628,7 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, if device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF and device_id: router = self._get_router(context, device_id) name = utils.get_name_and_uuid( - router['name'] or 'router', port_data['id'], tag='_port_') + router['name'] or 'router', port_data['id'], tag='port') elif device_owner == const.DEVICE_OWNER_DHCP: network = self.get_network(context, port_data['network_id']) name = utils.get_name_and_uuid('%s-%s' % ( @@ -819,15 +821,43 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, return updated_port + def _get_resource_type_for_device_id(self, device_owner, device_id): + if device_owner in const.ROUTER_INTERFACE_OWNERS: + return 'os-router-uuid' + elif device_owner.startswith(const.DEVICE_OWNER_COMPUTE_PREFIX): + return 'os-instance-uuid' + def _update_port_on_backend(self, context, lport_id, original_port, updated_port, address_bindings, switch_profile_ids): + original_device_owner = original_port.get('device_owner') + original_device_id = original_port.get('device_id') + updated_device_owner = updated_port.get('device_owner') + updated_device_id = updated_port.get('device_id') + resources = [] + if original_device_id != updated_device_id: + # Determine if we need to update or drop the tag. If the + # updated_device_id exists then the tag will be updated. This + # is done using the updated port. If the updated_device_id does + # not exist then we need to get the original resource type + # from original_device_owner. This enables us to drop the tag. + if updated_device_id: + resource_type = self._get_resource_type_for_device_id( + updated_device_owner, updated_device_id) + else: + resource_type = self._get_resource_type_for_device_id( + original_device_owner, updated_device_id) + if resource_type: + resources = [{'resource_type': resource_type, + 'tag': updated_device_id}] + self._port_client.update( lport_id, name=updated_port.get('name'), admin_state=updated_port.get('admin_state_up'), address_bindings=address_bindings, - switch_profile_ids=switch_profile_ids) + switch_profile_ids=switch_profile_ids, + resources=resources) security.update_lport_with_security_groups( context, lport_id, diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 512600a693..0d70bba89b 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -484,3 +484,52 @@ class TestNsxV3Utils(NsxV3PluginTestCaseMixin): [], 'fake-scope-name-is-far-too-long', 'fake-tag') + + def test_update_v3_tags_addition(self): + tags = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40}, + {'scope': 'os-project-id', 'tag': 'Y' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}] + resources = [{'resource_type': 'os-instance-uuid', + 'tag': 'A' * 40}] + tags = utils.update_v3_tags(tags, resources) + expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40}, + {'scope': 'os-project-id', 'tag': 'Y' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}, + {'scope': 'os-instance-uuid', + 'tag': 'A' * 40}] + self.assertEqual(sorted(expected), sorted(tags)) + + def test_update_v3_tags_removal(self): + tags = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40}, + {'scope': 'os-project-id', 'tag': 'Y' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}] + resources = [{'resource_type': 'os-neutron-net-id', + 'tag': ''}] + tags = utils.update_v3_tags(tags, resources) + expected = [{'scope': 'os-project-id', 'tag': 'Y' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}] + self.assertEqual(sorted(expected), sorted(tags)) + + def test_update_v3_tags_update(self): + tags = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40}, + {'scope': 'os-project-id', 'tag': 'Y' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}] + resources = [{'resource_type': 'os-project-id', + 'tag': 'A' * 40}] + tags = utils.update_v3_tags(tags, resources) + expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40}, + {'scope': 'os-project-id', 'tag': 'A' * 40}, + {'scope': 'os-project-name', 'tag': 'Z' * 40}, + {'scope': 'os-api-version', + 'tag': version.version_info.release_string()}] + self.assertEqual(sorted(expected), sorted(tags))