From 8548f110ac7072d2afb498aee51877e340cd59c2 Mon Sep 17 00:00:00 2001
From: Adit Sarfaty <asarfaty@vmware.com>
Date: Mon, 24 Apr 2017 12:06:23 +0300
Subject: [PATCH] Stop using CommonDbMixin apis

Commit I5b804e09e630d88d551271d9731cc1f65c065259 changed/removed some of the
methods in CommonDbMixin.
As a result the way the plugins register the different extend-dict methods has changed,
and now uses a decorator.
Also those extend-dict callbacks are static methods which do not receive 'self', and
this caused some additional changes.

Change-Id: If99da0ea1e37792bd531ef92b0bbb880d2b05b8a
Depends-on: I5b804e09e630d88d551271d9731cc1f65c065259
---
 vmware_nsx/common/sync.py                     |  7 +-
 vmware_nsx/db/extended_security_group.py      | 44 +++++-----
 vmware_nsx/db/extended_security_group_rule.py | 10 +--
 vmware_nsx/db/maclearning.py                  | 15 ++--
 vmware_nsx/db/networkgw_db.py                 | 30 ++++---
 vmware_nsx/db/nsxrouter.py                    | 16 ++--
 vmware_nsx/db/qos_db.py                       | 60 +++++++------
 vmware_nsx/db/vnic_index_db.py                | 10 +--
 vmware_nsx/plugin.py                          |  5 ++
 vmware_nsx/plugins/dvs/plugin.py              | 27 +++---
 vmware_nsx/plugins/nsx_mh/plugin.py           | 14 +--
 vmware_nsx/plugins/nsx_v/plugin.py            | 87 +++++++++++--------
 vmware_nsx/plugins/nsx_v3/plugin.py           | 72 +++++++++------
 .../plugins/nsxv/resources/securitygroups.py  |  4 +-
 .../test_provider_security_groups.py          | 10 ++-
 vmware_nsx/tests/unit/nsx_v/test_plugin.py    |  2 +-
 .../tests/unit/shell/test_admin_utils.py      | 40 +++++----
 17 files changed, 250 insertions(+), 203 deletions(-)

diff --git a/vmware_nsx/common/sync.py b/vmware_nsx/common/sync.py
index f96b55a9ab..249d63cb56 100644
--- a/vmware_nsx/common/sync.py
+++ b/vmware_nsx/common/sync.py
@@ -25,6 +25,7 @@ from oslo_service import loopingcall
 from oslo_utils import timeutils
 import six
 
+from neutron.db import _model_query as model_query
 from neutron.db import api as db_api
 from neutron.db.models import external_net as external_net_db
 from neutron.db.models import l3 as l3_db
@@ -336,7 +337,7 @@ class NsxSynchronizer(object):
         if not scan_missing:
             filters['id'] = neutron_net_ids
 
-        networks = self._plugin._get_collection(
+        networks = model_query.get_collection(
             ctx, models_v2.Network, self._plugin._make_network_dict,
             filters=filters)
 
@@ -416,7 +417,7 @@ class NsxSynchronizer(object):
         # Fetch neutron routers from database
         filters = ({} if scan_missing else
                    {'id': neutron_router_mappings.keys()})
-        routers = self._plugin._get_collection(
+        routers = model_query.get_collection(
             ctx, l3_db.Router, self._plugin._make_router_dict,
             filters=filters)
         for router in routers:
@@ -518,7 +519,7 @@ class NsxSynchronizer(object):
                 external_net_db.ExternalNetwork,
                 (models_v2.Network.id ==
                  external_net_db.ExternalNetwork.network_id))]
-        ports = self._plugin._get_collection(
+        ports = model_query.get_collection(
             ctx, models_v2.Port, self._plugin._make_port_dict,
             filters=filters)
         for port in ports:
diff --git a/vmware_nsx/db/extended_security_group.py b/vmware_nsx/db/extended_security_group.py
index 30534a43f0..7e6a49c635 100644
--- a/vmware_nsx/db/extended_security_group.py
+++ b/vmware_nsx/db/extended_security_group.py
@@ -22,8 +22,8 @@ from sqlalchemy import sql
 
 from neutron.api.v2 import attributes
 from neutron.common import utils as n_utils
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import api as db_api
-from neutron.db import db_base_plugin_v2
 from neutron.db.models import securitygroup as securitygroups_db
 from neutron.extensions import securitygroup as ext_sg
 from neutron_lib.api import validators
@@ -59,6 +59,7 @@ class NsxExtendedSecurityGroupProperties(model_base.BASEV2):
                             uselist=False, cascade='delete'))
 
 
+@resource_extend.has_resource_extenders
 class ExtendedSecurityGroupPropertiesMixin(object):
 
     # NOTE(arosen): here we add a relationship so that from the ports model
@@ -345,39 +346,34 @@ class ExtendedSecurityGroupPropertiesMixin(object):
                                                                    sg_id):
             raise sg_policy.PolicySecurityGroupDeleteNotAdmin(id=sg_id)
 
-    def _extend_security_group_with_properties(self, sg_res, sg_db):
+    @staticmethod
+    @resource_extend.extends([ext_sg.SECURITYGROUPS])
+    def _extend_security_group_with_properties(sg_res, sg_db):
         if sg_db.ext_properties:
             sg_res[sg_logging.LOGGING] = sg_db.ext_properties.logging
             sg_res[provider_sg.PROVIDER] = sg_db.ext_properties.provider
             sg_res[sg_policy.POLICY] = sg_db.ext_properties.policy
 
-    def _extend_port_dict_provider_security_group(self, port_res, port_db):
-        # NOTE(arosen): this method overrides the one in the base
-        # security group db class. The reason this is needed is because
-        # we are storing provider security groups in the same security
-        # groups db model. We need to do this here to remove the provider
-        # security groups and put those on the port resource as their
-        # own attribute.
-
-        # Security group bindings will be retrieved from the SQLAlchemy
-        # model. As they're loaded eagerly with ports because of the
-        # joined load they will not cause an extra query.
-
+    @staticmethod
+    @resource_extend.extends([attributes.PORTS])
+    def _extend_port_dict_provider_security_group(port_res, port_db):
+        # Add the provider sg list to the port.
+        # later we will remove those from the regular sg list
         provider_groups = []
-        not_provider_groups = []
         for sec_group_mapping in port_db.security_groups:
             if sec_group_mapping.extended_grp.provider is True:
                 provider_groups.append(sec_group_mapping['security_group_id'])
-            else:
-                not_provider_groups.append(
-                    sec_group_mapping['security_group_id'])
-
-        port_res[ext_sg.SECURITYGROUPS] = not_provider_groups
         port_res[provider_sg.PROVIDER_SECURITYGROUPS] = provider_groups
         return port_res
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attributes.PORTS, ['_extend_port_dict_provider_security_group'])
+    @staticmethod
+    def _remove_provider_security_groups_from_list(port_res):
+        # Remove provider security groups from the list of regular security
+        # groups of the result port
+        if (ext_sg.SECURITYGROUPS not in port_res or
+            provider_sg.PROVIDER_SECURITYGROUPS not in port_res):
+            return
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        ext_sg.SECURITYGROUPS, ['_extend_security_group_with_properties'])
+        port_res[ext_sg.SECURITYGROUPS] = list(
+            set(port_res[ext_sg.SECURITYGROUPS]) -
+            set(port_res[provider_sg.PROVIDER_SECURITYGROUPS]))
diff --git a/vmware_nsx/db/extended_security_group_rule.py b/vmware_nsx/db/extended_security_group_rule.py
index 99eeb4092c..fc39dd209e 100644
--- a/vmware_nsx/db/extended_security_group_rule.py
+++ b/vmware_nsx/db/extended_security_group_rule.py
@@ -17,8 +17,8 @@ from neutron_lib.db import model_base
 import sqlalchemy as sa
 from sqlalchemy import orm
 
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import api as db_api
-from neutron.db import db_base_plugin_v2
 from neutron.db.models import securitygroup
 from neutron.extensions import securitygroup as ext_sg
 from neutron_lib.api import validators
@@ -52,6 +52,7 @@ class NsxExtendedSecurityGroupRuleProperties(model_base.BASEV2):
                             uselist=False, cascade='delete'))
 
 
+@resource_extend.has_resource_extenders
 class ExtendedSecurityGroupRuleMixin(object):
 
     def _check_local_ip_prefix(self, context, rule):
@@ -81,10 +82,9 @@ class ExtendedSecurityGroupRuleMixin(object):
         rule_res[ext_local_ip.LOCAL_IP_PREFIX] = (
             rule_req[ext_local_ip.LOCAL_IP_PREFIX])
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        ext_sg.SECURITYGROUPRULES, ['_extend_security_group_rule_with_params'])
-
-    def _extend_security_group_rule_with_params(self, sg_rule_res, sg_rule_db):
+    @staticmethod
+    @resource_extend.extends([ext_sg.SECURITYGROUPRULES])
+    def _extend_security_group_rule_with_params(sg_rule_res, sg_rule_db):
         if sg_rule_db.ext_properties:
             sg_rule_res[ext_local_ip.LOCAL_IP_PREFIX] = (
                 sg_rule_db.ext_properties.local_ip_prefix)
diff --git a/vmware_nsx/db/maclearning.py b/vmware_nsx/db/maclearning.py
index e2bb659045..95ef7b0a4b 100644
--- a/vmware_nsx/db/maclearning.py
+++ b/vmware_nsx/db/maclearning.py
@@ -16,9 +16,10 @@
 from sqlalchemy.orm import exc
 
 from neutron.api.v2 import attributes
+from neutron.db import _model_query as model_query
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import api as db_api
-from neutron.db import db_base_plugin_v2
 
 from oslo_log import log as logging
 
@@ -28,6 +29,7 @@ from vmware_nsx.extensions import maclearning as mac
 LOG = logging.getLogger(__name__)
 
 
+@resource_extend.has_resource_extenders
 class MacLearningDbMixin(object):
     """Mixin class for mac learning."""
 
@@ -36,18 +38,17 @@ class MacLearningDbMixin(object):
                mac.MAC_LEARNING: port[mac.MAC_LEARNING]}
         return db_utils.resource_fields(res, fields)
 
-    def _extend_port_mac_learning_state(self, port_res, port_db):
+    @staticmethod
+    @resource_extend.extends([attributes.PORTS])
+    def _extend_port_mac_learning_state(port_res, port_db):
         state = port_db.mac_learning_state
         if state and state.mac_learning_enabled:
             port_res[mac.MAC_LEARNING] = state.mac_learning_enabled
 
-    # Register dict extend functions for ports
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attributes.PORTS, ['_extend_port_mac_learning_state'])
-
     def _update_mac_learning_state(self, context, port_id, enabled):
         try:
-            query = self._model_query(context, nsx_models.MacLearningState)
+            query = model_query.query_with_hooks(
+                context, nsx_models.MacLearningState)
             state = query.filter(
                 nsx_models.MacLearningState.port_id == port_id).one()
             state.update({mac.MAC_LEARNING: enabled})
diff --git a/vmware_nsx/db/networkgw_db.py b/vmware_nsx/db/networkgw_db.py
index bdbc8a3d2d..eae45318cd 100644
--- a/vmware_nsx/db/networkgw_db.py
+++ b/vmware_nsx/db/networkgw_db.py
@@ -14,6 +14,7 @@
 
 from sqlalchemy.orm import exc as sa_orm_exc
 
+from neutron.db import _model_query as model_query
 from neutron.db import _utils as db_utils
 from neutron.db import api as db_api
 from neutron.plugins.common import utils
@@ -97,7 +98,8 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 
     def _get_network_gateway(self, context, gw_id):
         try:
-            gw = self._get_by_id(context, nsx_models.NetworkGateway, gw_id)
+            gw = model_query.get_by_id(context, nsx_models.NetworkGateway,
+                                       gw_id)
         except sa_orm_exc.NoResultFound:
             raise GatewayNotFound(gateway_id=gw_id)
         return gw
@@ -172,9 +174,9 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
         for k, v in six.iteritems(mapping_info):
             if v and k != NETWORK_ID:
                 filters[k] = [v]
-        query = self._get_collection_query(context,
-                                           nsx_models.NetworkConnection,
-                                           filters)
+        query = model_query.get_collection_query(context,
+                                                 nsx_models.NetworkConnection,
+                                                 filters)
         return query.one() if only_one else query.all()
 
     def _unset_default_network_gateways(self, context):
@@ -265,12 +267,12 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
                              page_reverse=False):
         marker_obj = self._get_marker_obj(
             context, 'network_gateway', limit, marker)
-        return self._get_collection(context, nsx_models.NetworkGateway,
-                                    self._make_network_gateway_dict,
-                                    filters=filters, fields=fields,
-                                    sorts=sorts, limit=limit,
-                                    marker_obj=marker_obj,
-                                    page_reverse=page_reverse)
+        return model_query.get_collection(context, nsx_models.NetworkGateway,
+                                          self._make_network_gateway_dict,
+                                          filters=filters, fields=fields,
+                                          sorts=sorts, limit=limit,
+                                          marker_obj=marker_obj,
+                                          page_reverse=page_reverse)
 
     def connect_network(self, context, network_gateway_id,
                         network_mapping_info):
@@ -396,14 +398,14 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 
     def _get_gateway_device(self, context, device_id):
         try:
-            return self._get_by_id(context,
-                                   nsx_models.NetworkGatewayDevice,
-                                   device_id)
+            return model_query.get_by_id(context,
+                                         nsx_models.NetworkGatewayDevice,
+                                         device_id)
         except sa_orm_exc.NoResultFound:
             raise GatewayDeviceNotFound(device_id=device_id)
 
     def _is_device_in_use(self, context, device_id):
-        query = self._get_collection_query(
+        query = model_query.get_collection_query(
             context, nsx_models.NetworkGatewayDeviceReference,
             {'id': [device_id]})
         return query.first()
diff --git a/vmware_nsx/db/nsxrouter.py b/vmware_nsx/db/nsxrouter.py
index 5a954e1c54..d832790d42 100644
--- a/vmware_nsx/db/nsxrouter.py
+++ b/vmware_nsx/db/nsxrouter.py
@@ -13,25 +13,25 @@
 #    under the License.
 #
 
-from neutron.db import db_base_plugin_v2
-from neutron.extensions import l3
 from oslo_log import log as logging
 
+from neutron.db import _resource_extend as resource_extend
+
 from vmware_nsx.db import nsxv_models
 
 LOG = logging.getLogger(__name__)
 
 
+@resource_extend.has_resource_extenders
 class NsxRouterMixin(object):
     """Mixin class to enable nsx router support."""
 
     nsx_attributes = []
 
-    def _extend_nsx_router_dict(self, router_res, router_db):
+    @staticmethod
+    def _extend_nsx_router_dict(router_res, router_db, nsx_attributes):
         nsx_attrs = router_db['nsx_attributes']
-        # Return False if nsx attributes are not definied for this
-        # neutron router
-        for attr in self.nsx_attributes:
+        for attr in nsx_attributes:
             name = attr['name']
             default = attr['default']
             router_res[name] = (
@@ -61,7 +61,3 @@ class NsxRouterMixin(object):
                     name, default)
         LOG.debug("Nsx router extension successfully processed "
                   "for router:%s", router_db['id'])
-
-    # Register dict extend functions for ports
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        l3.ROUTERS, ['_extend_nsx_router_dict'])
diff --git a/vmware_nsx/db/qos_db.py b/vmware_nsx/db/qos_db.py
index c69e4aa3e1..7d12e5a34d 100644
--- a/vmware_nsx/db/qos_db.py
+++ b/vmware_nsx/db/qos_db.py
@@ -16,9 +16,10 @@
 from sqlalchemy.orm import exc
 
 from neutron.api.v2 import attributes as attr
+from neutron.db import _model_query as model_query
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import api as db_api
-from neutron.db import db_base_plugin_v2
 from neutron.db import models_v2
 
 from oslo_log import log
@@ -30,6 +31,7 @@ from vmware_nsx.extensions import qos_queue as qos
 LOG = log.getLogger(__name__)
 
 
+@resource_extend.has_resource_extenders
 class QoSDbMixin(qos.QueuePluginBase):
     """Mixin class to add queues."""
 
@@ -54,19 +56,20 @@ class QoSDbMixin(qos.QueuePluginBase):
 
     def _get_qos_queue(self, context, queue_id):
         try:
-            return self._get_by_id(context, nsx_models.QoSQueue, queue_id)
+            return model_query.get_by_id(context, nsx_models.QoSQueue,
+                                         queue_id)
         except exc.NoResultFound:
             raise qos.QueueNotFound(id=queue_id)
 
     def get_qos_queues(self, context, filters=None, fields=None, sorts=None,
                        limit=None, marker=None, page_reverse=False):
         marker_obj = self._get_marker_obj(context, 'qos_queue', limit, marker)
-        return self._get_collection(context, nsx_models.QoSQueue,
-                                    self._make_qos_queue_dict,
-                                    filters=filters, fields=fields,
-                                    sorts=sorts, limit=limit,
-                                    marker_obj=marker_obj,
-                                    page_reverse=page_reverse)
+        return model_query.get_collection(context, nsx_models.QoSQueue,
+                                          self._make_qos_queue_dict,
+                                          filters=filters, fields=fields,
+                                          sorts=sorts, limit=limit,
+                                          marker_obj=marker_obj,
+                                          page_reverse=page_reverse)
 
     def delete_qos_queue(self, context, queue_id):
         with db_api.context_manager.writer.using(context):
@@ -83,12 +86,14 @@ class QoSDbMixin(qos.QueuePluginBase):
                                 queue_id=queue_id))
 
     def _get_port_queue_bindings(self, context, filters=None, fields=None):
-        return self._get_collection(context, nsx_models.PortQueueMapping,
-                                    self._make_port_queue_binding_dict,
-                                    filters=filters, fields=fields)
+        return model_query.get_collection(context,
+                                          nsx_models.PortQueueMapping,
+                                          self._make_port_queue_binding_dict,
+                                          filters=filters, fields=fields)
 
     def _delete_port_queue_mapping(self, context, port_id):
-        query = self._model_query(context, nsx_models.PortQueueMapping)
+        query = model_query.query_with_hooks(context,
+                                             nsx_models.PortQueueMapping)
         try:
             binding = query.filter(
                 nsx_models.PortQueueMapping.port_id == port_id).one()
@@ -110,9 +115,11 @@ class QoSDbMixin(qos.QueuePluginBase):
                                                queue_id=queue_id))
 
     def _get_network_queue_bindings(self, context, filters=None, fields=None):
-        return self._get_collection(context, nsx_models.NetworkQueueMapping,
-                                    self._make_network_queue_binding_dict,
-                                    filters=filters, fields=fields)
+        return model_query.get_collection(
+            context,
+            nsx_models.NetworkQueueMapping,
+            self._make_network_queue_binding_dict,
+            filters=filters, fields=fields)
 
     def _delete_network_queue_mapping(self, context, network_id):
         query = self._model_query(context, nsx_models.NetworkQueueMapping)
@@ -121,24 +128,15 @@ class QoSDbMixin(qos.QueuePluginBase):
             if binding:
                 context.session.delete(binding)
 
-    def _extend_dict_qos_queue(self, obj_res, obj_db):
+    @staticmethod
+    @resource_extend.extends([attr.NETWORKS])
+    @resource_extend.extends([attr.PORTS])
+    def _extend_dict_qos_queue(obj_res, obj_db):
         queue_mapping = obj_db['qos_queue']
         if queue_mapping:
             obj_res[qos.QUEUE] = queue_mapping.get('queue_id')
         return obj_res
 
-    def _extend_port_dict_qos_queue(self, port_res, port_db):
-        self._extend_dict_qos_queue(port_res, port_db)
-
-    def _extend_network_dict_qos_queue(self, network_res, network_db):
-        self._extend_dict_qos_queue(network_res, network_db)
-
-    # Register dict extend functions for networks and ports
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.NETWORKS, ['_extend_network_dict_qos_queue'])
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.PORTS, ['_extend_port_dict_qos_queue'])
-
     def _make_qos_queue_dict(self, queue, fields=None):
         res = {'id': queue['id'],
                'name': queue.get('name'),
@@ -203,9 +201,9 @@ class QoSDbMixin(qos.QueuePluginBase):
             filters = {'device_id': [port.get('device_id')],
                        'network_id': [network['network_id'] for
                                       network in networks_with_same_queue]}
-            query = self._model_query(context, models_v2.Port.id)
-            query = self._apply_filters_to_query(query, models_v2.Port,
-                                                 filters)
+            query = model_query.query_with_hooks(context, models_v2.Port.id)
+            model_query.apply_filters(query, models_v2.Port,
+                                      filters, context)
             ports_ids = [p[0] for p in query]
             if ports_ids:
                 # shared queue already exists find the queue id
diff --git a/vmware_nsx/db/vnic_index_db.py b/vmware_nsx/db/vnic_index_db.py
index b860932673..4a9450798d 100644
--- a/vmware_nsx/db/vnic_index_db.py
+++ b/vmware_nsx/db/vnic_index_db.py
@@ -16,7 +16,7 @@
 from sqlalchemy.orm import exc
 
 from neutron.api.v2 import attributes as attr
-from neutron.db import db_base_plugin_v2
+from neutron.db import _resource_extend as resource_extend
 
 from oslo_db import exception as db_exc
 from oslo_log import log as logging
@@ -27,15 +27,15 @@ from vmware_nsx.extensions import vnicindex as vnicidx
 LOG = logging.getLogger(__name__)
 
 
+@resource_extend.has_resource_extenders
 class VnicIndexDbMixin(object):
 
-    def _extend_port_vnic_index_binding(self, port_res, port_db):
+    @staticmethod
+    @resource_extend.extends([attr.PORTS])
+    def _extend_port_vnic_index_binding(port_res, port_db):
         state = port_db.vnic_index
         port_res[vnicidx.VNIC_INDEX] = state.index if state else None
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.PORTS, ['_extend_port_vnic_index_binding'])
-
     def _get_port_vnic_index(self, context, port_id):
         """Returns the vnic index for the given port.
         If the port is not associated with any vnic then return None
diff --git a/vmware_nsx/plugin.py b/vmware_nsx/plugin.py
index 0c71981dbf..022e99378f 100644
--- a/vmware_nsx/plugin.py
+++ b/vmware_nsx/plugin.py
@@ -15,6 +15,11 @@
 #    under the License.
 #
 
+# Note: this import should be here in order to appear before NeutronDbPluginV2
+#  in each of the plugins. If not: security-group/-rule will not have all the
+# relevant extend dict registries.
+from neutron.db.models import securitygroup  # noqa
+
 from vmware_nsx.plugins.dvs import plugin as dvs
 from vmware_nsx.plugins.nsx_mh import plugin as nsx_mh
 from vmware_nsx.plugins.nsx_v import plugin as nsx_v
diff --git a/vmware_nsx/plugins/dvs/plugin.py b/vmware_nsx/plugins/dvs/plugin.py
index e30ca7b703..045f2b7b94 100644
--- a/vmware_nsx/plugins/dvs/plugin.py
+++ b/vmware_nsx/plugins/dvs/plugin.py
@@ -15,12 +15,12 @@
 
 import uuid
 
-from neutron_lib import context as n_context
 from oslo_log import log as logging
 from oslo_utils import excutils
 
 from neutron.api import extensions as neutron_extensions
 from neutron.api.v2 import attributes as attr
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import agentschedulers_db
 from neutron.db import allowedaddresspairs_db as addr_pair_db
@@ -62,6 +62,7 @@ from vmware_nsx.dvs import dvs_utils
 LOG = logging.getLogger(__name__)
 
 
+@resource_extend.has_resource_extenders
 class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
                agentschedulers_db.DhcpAgentSchedulerDbMixin,
                db_base_plugin_v2.NeutronDbPluginV2,
@@ -104,11 +105,9 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
         self._dvs = dvs.SingleDvsManager()
         self.setup_dhcpmeta_access()
 
-    # Register extend dict methods for port resources.
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.PORTS, ['_ext_extend_port_dict'])
-
-    def _extend_port_dict_binding(self, portdb, result):
+    @staticmethod
+    @resource_extend.extends([attr.PORTS])
+    def _extend_port_dict_binding(result, portdb):
         result[pbin.VIF_TYPE] = nsx_constants.VIF_TYPE_DVS
         port_attr = portdb.get('nsx_port_attributes')
         if port_attr:
@@ -117,14 +116,8 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
             result[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
         result[pbin.VIF_DETAILS] = {
             # TODO(rkukura): Replace with new VIF security details
-            pbin.CAP_PORT_FILTER:
-            'security-group' in self.supported_extension_aliases}
-
-    def _ext_extend_port_dict(self, result, portdb):
-        ctx = n_context.get_admin_context()
-        with db_api.context_manager.writer.using(ctx):
-            self._extend_port_dict_binding(portdb,
-                                           result)
+            # security-groups extension supported by this plugin
+            pbin.CAP_PORT_FILTER: True}
 
     def _extend_network_dict_provider(self, context, network,
                                       multiprovider=None, bindings=None):
@@ -202,7 +195,7 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
                 net_db = self._get_network(context, new_net['id'])
                 net_db['vlan_transparent'] = trunk_mode
                 net_data['vlan_transparent'] = trunk_mode
-                self._apply_dict_extend_functions('networks', net_data, net_db)
+                resource_extend.apply_funcs('networks', net_data, net_db)
 
                 nsx_db.add_network_binding(
                     context.session, new_net['id'],
@@ -223,7 +216,7 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         net_model = self._get_network(context, net_data['id'])
-        self._apply_dict_extend_functions('networks', new_net, net_model)
+        resource_extend.apply_funcs('networks', new_net, net_model)
 
         self.handle_network_dhcp_access(context, new_net,
                                         action='create_network')
@@ -397,7 +390,7 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         port_model = self._get_port(context, port_data['id'])
-        self._apply_dict_extend_functions('ports', port_data, port_model)
+        resource_extend.apply_funcs('ports', port_data, port_model)
 
         self.handle_port_dhcp_access(context, port_data, action='create_port')
         return port_data
diff --git a/vmware_nsx/plugins/nsx_mh/plugin.py b/vmware_nsx/plugins/nsx_mh/plugin.py
index 8f47b78d44..ada25c1af2 100644
--- a/vmware_nsx/plugins/nsx_mh/plugin.py
+++ b/vmware_nsx/plugins/nsx_mh/plugin.py
@@ -32,6 +32,8 @@ import webob.exc
 from neutron.api import extensions as neutron_extensions
 from neutron.api.v2 import attributes as attr
 from neutron.api.v2 import base
+from neutron.db import _model_query as model_query
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import agentschedulers_db
 from neutron.db import allowedaddresspairs_db as addr_pair_db
@@ -1005,7 +1007,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         net_model = self._get_network(context, new_net['id'])
-        self._apply_dict_extend_functions('networks', new_net, net_model)
+        resource_extend.apply_funcs('networks', new_net, net_model)
         self.handle_network_dhcp_access(context, new_net,
                                         action='create_network')
         return new_net
@@ -1197,7 +1199,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         port_model = self._get_port(context, neutron_port_id)
-        self._apply_dict_extend_functions('ports', port_data, port_model)
+        resource_extend.apply_funcs('ports', port_data, port_model)
         self.handle_port_dhcp_access(context, port_data, action='create_port')
         return port_data
 
@@ -2155,7 +2157,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         LOG.error("Rolling back database changes for gateway device %s "
                   "because of an error in the NSX backend", device_id)
         with db_api.context_manager.writer.using(context):
-            query = self._model_query(
+            query = model_query.query_with_hooks(
                 context, nsx_models.NetworkGatewayDevice).filter(
                     nsx_models.NetworkGatewayDevice.id == device_id)
             if is_create:
@@ -2191,7 +2193,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 
             # set NSX GW device in neutron database and update status
             with db_api.context_manager.writer.using(context):
-                query = self._model_query(
+                query = model_query.query_with_hooks(
                     context, nsx_models.NetworkGatewayDevice).filter(
                         nsx_models.NetworkGatewayDevice.id == neutron_id)
                 query.update({'status': device_status,
@@ -2230,7 +2232,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
                                                             nsx_id)
             # update status
             with db_api.context_manager.writer.using(context):
-                query = self._model_query(
+                query = model_query.query_with_hooks(
                     context, nsx_models.NetworkGatewayDevice).filter(
                         nsx_models.NetworkGatewayDevice.id == neutron_id)
                 query.update({'status': device_status},
@@ -2265,7 +2267,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # TODO(salv-orlando): Asynchronous sync for gateway device status
         # Update status in database
         with db_api.context_manager.writer.using(context):
-            query = self._model_query(
+            query = model_query.query_with_hooks(
                 context, nsx_models.NetworkGatewayDevice).filter(
                     nsx_models.NetworkGatewayDevice.id == device_id)
             query.update({'status': device_status},
diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py
index 325ee1b438..0d866f06d5 100644
--- a/vmware_nsx/plugins/nsx_v/plugin.py
+++ b/vmware_nsx/plugins/nsx_v/plugin.py
@@ -38,6 +38,7 @@ from neutron.common import ipv6_utils
 from neutron.common import rpc as n_rpc
 from neutron.common import topics
 from neutron.common import utils as n_utils
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import address_scope_db
 from neutron.db import agents_db
@@ -136,6 +137,7 @@ ROUTER_SIZE = routersize.ROUTER_SIZE
 VALID_EDGE_SIZES = routersize.VALID_EDGE_SIZES
 
 
+@resource_extend.has_resource_extenders
 class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
                    agents_db.AgentDbMixin,
                    db_base_plugin_v2.NeutronDbPluginV2,
@@ -289,16 +291,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # Bind QoS notifications
         qos_driver.register(self)
 
-    # Register extend dict methods for network and port resources.
-    # Each extension driver that supports extend attribute for the resources
-    # can add those attribute to the result.
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.NETWORKS, ['_ext_extend_network_dict'])
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.PORTS, ['_ext_extend_port_dict'])
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.SUBNETS, ['_ext_extend_subnet_dict'])
-
     def init_complete(self, resource, event, trigger, **kwargs):
         has_metadata_cfg = (
             cfg.CONF.nsxv.nova_metadata_ips
@@ -378,24 +370,34 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # Bind FWaaS callbacks to the driver
         self.fwaas_callbacks = fwaas_callbacks.NsxvFwaasCallbacks()
 
-    def _ext_extend_network_dict(self, result, netdb):
+    @staticmethod
+    @resource_extend.extends([attr.NETWORKS])
+    def _ext_extend_network_dict(result, netdb):
         ctx = n_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_network_dict(
+            plugin._extension_manager.extend_network_dict(
                 ctx.session, netdb, result)
 
-    def _ext_extend_port_dict(self, result, portdb):
+    @staticmethod
+    @resource_extend.extends([attr.PORTS])
+    def _ext_extend_port_dict(result, portdb):
         ctx = n_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_port_dict(
+            plugin._extension_manager.extend_port_dict(
                 ctx.session, portdb, result)
-            self._extend_port_dict_binding(portdb,
-                                           result)
 
-    def _ext_extend_subnet_dict(self, result, subnetdb):
+    @staticmethod
+    @resource_extend.extends([attr.SUBNETS])
+    def _ext_extend_subnet_dict(result, subnetdb):
         ctx = n_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_subnet_dict(
+            plugin._extension_manager.extend_subnet_dict(
                 ctx.session, subnetdb, result)
 
     def _create_security_group_container(self):
@@ -448,6 +450,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
             r["distributed"] = False
             r["router_type"] = router_type
 
+    @staticmethod
+    @resource_extend.extends([l3.ROUTERS])
+    def _extend_nsx_router_dict(router_res, router_db):
+        router_type_obj = rt_rtr.RouterType_mixin()
+        router_type_obj._extend_nsx_router_dict(
+            router_res, router_db, router_type_obj.nsx_attributes)
+
     def _create_cluster_default_fw_section(self):
         section_name = 'OS Cluster Security Group section'
 
@@ -1229,7 +1238,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         net_model = self._get_network(context, new_net['id'])
-        self._apply_dict_extend_functions('networks', new_net, net_model)
+        resource_extend.apply_funcs('networks', new_net, net_model)
         return new_net
 
     def _update_qos_on_created_network(self, context, net_data):
@@ -1741,7 +1750,16 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         port_model = self._get_port(context, port_data['id'])
-        self._apply_dict_extend_functions('ports', port_data, port_model)
+        resource_extend.apply_funcs('ports', port_data, port_model)
+        self._remove_provider_security_groups_from_list(port_data)
+        return port_data
+
+    def _make_port_dict(self, port, fields=None,
+                        process_extensions=True):
+        port_data = super(NsxVPluginV2, self)._make_port_dict(
+            port, fields=fields,
+            process_extensions=process_extensions)
+        self._remove_provider_security_groups_from_list(port_data)
         return port_data
 
     def _get_port_subnet_mask(self, context, port):
@@ -2190,7 +2208,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 
         self._delete_dhcp_static_binding(context, neutron_db_port)
 
-    def _extend_port_dict_binding(self, portdb, result):
+    @staticmethod
+    @resource_extend.extends([attr.PORTS])
+    def _extend_nsx_port_dict_binding(result, portdb):
         result[pbin.VIF_TYPE] = nsx_constants.VIF_TYPE_DVS
         port_attr = portdb.get('nsx_port_attributes')
         if port_attr:
@@ -2199,8 +2219,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
             result[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
         result[pbin.VIF_DETAILS] = {
             # TODO(rkukura): Replace with new VIF security details
-            pbin.CAP_PORT_FILTER:
-            'security-group' in self.supported_extension_aliases}
+            # security-groups extension supported by this plugin
+            pbin.CAP_PORT_FILTER: True}
 
     def delete_subnet(self, context, id):
         subnet = self._get_subnet(context, id)
@@ -2460,10 +2480,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
                 self._update_subnet_dhcp_status(subnet, context)
         return subnet
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.SUBNETS, ['_extend_subnet_dict_extended_attributes'])
-
-    def _extend_subnet_dict_extended_attributes(self, subnet_res, subnet_db):
+    @staticmethod
+    @resource_extend.extends([attr.SUBNETS])
+    def _extend_subnet_dict_extended_attributes(subnet_res, subnet_db):
         subnet_attr = subnet_db.get('nsxv_subnet_attributes')
         if subnet_attr:
             subnet_res['dns_search_domain'] = subnet_attr.dns_search_domain
@@ -3027,10 +3046,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         super(NsxVPluginV2, self).delete_router(context, id)
         router_driver.delete_router(context, id)
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attr.NETWORKS, ['_extend_availability_zone_hints'])
-
-    def _extend_availability_zone_hints(self, net_res, net_db):
+    @staticmethod
+    @resource_extend.extends([attr.NETWORKS])
+    def _extend_availability_zone_hints(net_res, net_db):
         net_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list(
             net_db[az_ext.AZ_HINTS])
 
@@ -3078,12 +3096,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
         if validators.is_attr_set(r.get('flavor_id')):
             router_db.flavor_id = r['flavor_id']
 
-    def add_flavor_id(plugin, router_res, router_db):
+    @staticmethod
+    @resource_extend.extends([l3.ROUTERS])
+    def add_flavor_id(router_res, router_db):
         router_res['flavor_id'] = router_db['flavor_id']
 
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        l3.ROUTERS, [add_flavor_id])
-
     def get_router(self, context, id, fields=None):
         router = super(NsxVPluginV2, self).get_router(context, id, fields)
         if router.get("distributed") and 'router_type' in router:
diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py
index a8faa95fd3..496ee97b5c 100644
--- a/vmware_nsx/plugins/nsx_v3/plugin.py
+++ b/vmware_nsx/plugins/nsx_v3/plugin.py
@@ -22,6 +22,7 @@ from neutron.api.rpc.handlers import metadata_rpc
 from neutron.api.v2 import attributes
 from neutron.common import rpc as n_rpc
 from neutron.common import topics
+from neutron.db import _resource_extend as resource_extend
 from neutron.db import _utils as db_utils
 from neutron.db import address_scope_db
 from neutron.db import agents_db
@@ -64,6 +65,7 @@ from neutron_lib.callbacks import resources
 from neutron_lib import constants as const
 from neutron_lib import context as q_context
 from neutron_lib import exceptions as n_exc
+from neutron_lib.plugins import directory
 from neutron_lib.utils import helpers
 from oslo_config import cfg
 from oslo_db import exception as db_exc
@@ -119,6 +121,7 @@ NSX_V3_EXCLUDED_PORT_NSGROUP_NAME = 'neutron_excluded_port_nsgroup'
 # this needs to be above securitygroups_db.SecurityGroupDbMixin.
 # FIXME(arosen): we can solve this inheritance order issue by just mixining in
 # the classes into a new class to handle the order correctly.
+@resource_extend.has_resource_extenders
 class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
                   extended_security_group.ExtendedSecurityGroupPropertiesMixin,
                   addr_pair_db.AllowedAddressPairsMixin,
@@ -242,17 +245,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         # Register NSXv3 trunk driver to support trunk extensions
         self.trunk_driver = trunk_driver.NsxV3TrunkDriver.create(self)
 
-    # Register extend dict methods for network and port resources.
-    # Each extension driver that supports extend attribute for the resources
-    # can add those attribute to the result.
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attributes.NETWORKS, ['_ext_extend_network_dict',
-                              '_extend_availability_zone_hints'])
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attributes.PORTS, ['_ext_extend_port_dict'])
-    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
-        attributes.SUBNETS, ['_ext_extend_subnet_dict'])
-
     def init_availability_zones(self):
         # availability zones are supported only with native dhcp
         # if not - the default az will be loaded and used internally only
@@ -320,7 +312,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         for az in self.get_azs_list():
             az.translate_configured_names_to_uuids(self.nsxlib)
 
-    def _extend_port_dict_binding(self, context, port_data):
+    def _extend_nsx_port_dict_binding(self, context, port_data):
+        # Not using the register api for this because we need the context
         port_data[pbin.VIF_TYPE] = pbin.VIF_TYPE_OVS
         port_data[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
         if 'network_id' in port_data:
@@ -557,22 +550,34 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
 
         return self.conn.consume_in_threads()
 
-    def _ext_extend_network_dict(self, result, netdb):
+    @staticmethod
+    @resource_extend.extends([attributes.NETWORKS])
+    def _ext_extend_network_dict(result, netdb):
         ctx = q_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_network_dict(
+            plugin._extension_manager.extend_network_dict(
                 ctx.session, netdb, result)
 
-    def _ext_extend_port_dict(self, result, portdb):
+    @staticmethod
+    @resource_extend.extends([attributes.PORTS])
+    def _ext_extend_port_dict(result, portdb):
         ctx = q_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_port_dict(
+            plugin._extension_manager.extend_port_dict(
                 ctx.session, portdb, result)
 
-    def _ext_extend_subnet_dict(self, result, subnetdb):
+    @staticmethod
+    @resource_extend.extends([attributes.SUBNETS])
+    def _ext_extend_subnet_dict(result, subnetdb):
         ctx = q_context.get_admin_context()
+        # get the core plugin as this is a static method with no 'self'
+        plugin = directory.get_plugin()
         with db_api.context_manager.writer.using(ctx):
-            self._extension_manager.extend_subnet_dict(
+            plugin._extension_manager.extend_subnet_dict(
                 ctx.session, subnetdb, result)
 
     def _validate_provider_create(self, context, network_data, az):
@@ -829,7 +834,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         net_model = self._get_network(context, created_net['id'])
-        self._apply_dict_extend_functions('networks', created_net, net_model)
+        resource_extend.apply_funcs('networks', created_net, net_model)
 
         if qos_consts.QOS_POLICY_ID in net_data:
             # attach the policy to the network in neutron DB
@@ -2041,7 +2046,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
             # sgids is a set() so we need to | it in.
             if provider_groups:
                 sgids = list(set(sgids) | set(provider_groups))
-            self._extend_port_dict_binding(context, port_data)
+            self._extend_nsx_port_dict_binding(context, port_data)
             if validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)):
                 if is_psec_on:
                     msg = _('Mac learning requires that port security be '
@@ -2105,7 +2110,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         # this extra lookup is necessary to get the
         # latest db model for the extension functions
         port_model = self._get_port(context, port_data['id'])
-        self._apply_dict_extend_functions('ports', port_data, port_model)
+        resource_extend.apply_funcs('ports', port_data, port_model)
+        self._remove_provider_security_groups_from_list(port_data)
 
         # Add Mac/IP binding to native DHCP server and neutron DB.
         if cfg.CONF.nsx_v3.native_dhcp_metadata:
@@ -2436,7 +2442,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
                 context, updated_port)
             self._process_portbindings_create_and_update(
                 context, port['port'], updated_port)
-            self._extend_port_dict_binding(context, updated_port)
+            self._extend_nsx_port_dict_binding(context, updated_port)
             mac_learning_state = updated_port.get(mac_ext.MAC_LEARNING)
             if mac_learning_state is not None:
                 if port_security and mac_learning_state:
@@ -2505,8 +2511,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
 
         return updated_port
 
-    def _extend_get_port_dict_binding(self, context, port):
-        self._extend_port_dict_binding(context, port)
+    def _extend_get_port_dict_qos_and_binding(self, context, port):
+        # Not using the register api for this because we need the context
+        self._extend_nsx_port_dict_binding(context, port)
 
         # add the qos policy id from the DB
         if 'id' in port:
@@ -2515,8 +2522,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
 
     def get_port(self, context, id, fields=None):
         port = super(NsxV3Plugin, self).get_port(context, id, fields=None)
-        self._extend_get_port_dict_binding(context, port)
-
+        if 'id' in port:
+            port_model = self._get_port(context, port['id'])
+            resource_extend.apply_funcs('ports', port, port_model)
+        self._extend_get_port_dict_qos_and_binding(context, port)
+        self._remove_provider_security_groups_from_list(port)
         return db_utils.resource_fields(port, fields)
 
     def get_ports(self, context, filters=None, fields=None,
@@ -2530,7 +2540,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
                     limit, marker, page_reverse))
             # Add port extensions
             for port in ports:
-                self._extend_get_port_dict_binding(context, port)
+                if 'id' in port:
+                    port_model = self._get_port(context, port['id'])
+                    resource_extend.apply_funcs('ports', port, port_model)
+                self._extend_get_port_dict_qos_and_binding(context, port)
+                self._remove_provider_security_groups_from_list(port)
         return (ports if not fields else
                 [db_utils.resource_fields(port, fields) for port in ports])
 
@@ -3520,7 +3534,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         # Validate against the configured AZs
         return self.validate_obj_azs(availability_zones)
 
-    def _extend_availability_zone_hints(self, net_res, net_db):
+    @staticmethod
+    @resource_extend.extends([attributes.NETWORKS])
+    def _extend_availability_zone_hints(net_res, net_db):
         net_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list(
             net_db[az_ext.AZ_HINTS])
         if cfg.CONF.nsx_v3.native_dhcp_metadata:
diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py
index 075a971d43..5a803cb954 100644
--- a/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py
+++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py
@@ -27,6 +27,7 @@ from oslo_log import log as logging
 from vmware_nsx.common import utils as com_utils
 from vmware_nsx.db import db as nsx_db
 from vmware_nsx.db import extended_security_group as extended_secgroup
+from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
 from vmware_nsx.db import nsx_models
 from vmware_nsx.db import nsxv_db
 from vmware_nsx.db import nsxv_models
@@ -44,7 +45,8 @@ LOG = logging.getLogger(__name__)
 class NeutronSecurityGroupDB(
     utils.NeutronDbClient,
     securitygroups_db.SecurityGroupDbMixin,
-    extended_secgroup.ExtendedSecurityGroupPropertiesMixin):
+    extended_secgroup.ExtendedSecurityGroupPropertiesMixin,
+    extend_sg_rule.ExtendedSecurityGroupRuleMixin):
 
     def __init__(self):
         super(NeutronSecurityGroupDB, self)
diff --git a/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py b/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py
index a9620b3727..73a3ba6db8 100644
--- a/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py
+++ b/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py
@@ -37,7 +37,7 @@ PLUGIN_NAME = ('vmware_nsx.tests.unit.extensions.'
 class ProviderSecurityGroupTestPlugin(
     db_base_plugin_v2.NeutronDbPluginV2,
     extended_security_group.ExtendedSecurityGroupPropertiesMixin,
-        securitygroups_db.SecurityGroupDbMixin):
+    securitygroups_db.SecurityGroupDbMixin):
 
     supported_extension_aliases = ["security-group",
                                    "provider-security-group"]
@@ -97,6 +97,14 @@ class ProviderSecurityGroupTestPlugin(
                 context, port, original_port, updated_port)
             return self.get_port(context, id)
 
+    def _make_port_dict(self, port, fields=None, process_extensions=True):
+        port_data = super(
+            ProviderSecurityGroupTestPlugin, self)._make_port_dict(
+            port, fields=fields,
+            process_extensions=process_extensions)
+        self._remove_provider_security_groups_from_list(port_data)
+        return port_data
+
     def delete_security_group(self, context, id):
         self._prevent_non_admin_delete_provider_sg(context, id)
         super(ProviderSecurityGroupTestPlugin,
diff --git a/vmware_nsx/tests/unit/nsx_v/test_plugin.py b/vmware_nsx/tests/unit/nsx_v/test_plugin.py
index 23dfbc1553..97612bb9f4 100644
--- a/vmware_nsx/tests/unit/nsx_v/test_plugin.py
+++ b/vmware_nsx/tests/unit/nsx_v/test_plugin.py
@@ -5023,7 +5023,7 @@ class TestRouterFlavorTestCase(extension.ExtensionTestCase,
 
     FLAVOR_PLUGIN = 'neutron.services.flavors.flavors_plugin.FlavorsPlugin'
 
-    def _mock_add_flavor_id(self, resource_type, router_res, router_db):
+    def _mock_add_flavor_id(dummy, router_res, router_db):
         # this function is a registered callback so we can't mock it
         # in a regular way.
         # need to change behavior for this test suite only, since
diff --git a/vmware_nsx/tests/unit/shell/test_admin_utils.py b/vmware_nsx/tests/unit/shell/test_admin_utils.py
index 922668a6da..66abb72744 100644
--- a/vmware_nsx/tests/unit/shell/test_admin_utils.py
+++ b/vmware_nsx/tests/unit/shell/test_admin_utils.py
@@ -26,14 +26,14 @@ from neutron.common import config as neutron_config
 from neutron.db import servicetype_db  # noqa
 from neutron.quota import resource_registry
 from neutron.tests import base
-from neutron.tests.unit.api import test_extensions
 from neutron_lib.callbacks import registry
 
 from vmware_nsx._i18n import _
 from vmware_nsx.common import config  # noqa
 from vmware_nsx.db import nsxv_db
 from vmware_nsx.dvs import dvs_utils
-import vmware_nsx.shell.admin.plugins.nsxv.resources.utils as utils
+from vmware_nsx.shell.admin.plugins.nsxv.resources import utils as nsxv_utils
+from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils as nsxv3_utils
 from vmware_nsx.shell import resources
 from vmware_nsx.tests import unit as vmware
 from vmware_nsx.tests.unit.nsx_v import test_plugin as test_v_plugin
@@ -137,8 +137,20 @@ class TestNsxvAdminUtils(AbstractTestAdminUtils,
                    'NsxVPluginWrapper.count_spawn_jobs',
                    return_value=0).start()
 
+        self._plugin = nsxv_utils.NsxVPluginWrapper()
+        mock_nm_get_plugin = mock.patch(
+            "neutron_lib.plugins.directory.get_plugin")
+        self.mock_nm_get_plugin = mock_nm_get_plugin.start()
+        self.mock_nm_get_plugin.return_value = self._plugin
+
         # Create a router to make sure we have deployed an edge
-        self.create_router()
+        self.router = self.create_router()
+
+    def tearDown(self):
+        if self.router and self.router.get('id'):
+            edgeapi = nsxv_utils.NeutronDbClient()
+            self._plugin.delete_router(edgeapi.context, self.router['id'])
+        super(TestNsxvAdminUtils, self).tearDown()
 
     def test_nsxv_resources(self):
         self._test_resources(resources.nsxv_resources)
@@ -149,26 +161,18 @@ class TestNsxvAdminUtils(AbstractTestAdminUtils,
         self._test_resource('edges', 'nsx-update', **args)
 
     def create_router(self):
-        # Global configuration to support router creation without messing up
-        # the plugin wrapper
-        cfg.CONF.set_override('track_quota_usage', False,
-                              group='QUOTAS')
-        ext_mgr = test_v_plugin.TestL3ExtensionManager()
-        ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
-
         # Create an exclusive router (with an edge)
         tenant_id = uuidutils.generate_uuid()
         data = {'router': {'tenant_id': tenant_id}}
         data['router']['name'] = 'dummy'
         data['router']['admin_state_up'] = True
         data['router']['router_type'] = 'exclusive'
-        router_req = self.new_create_request('routers', data, self.fmt)
-        res = router_req.get_response(ext_api)
-        r = self.deserialize(self.fmt, res)
-        return r
+
+        edgeapi = nsxv_utils.NeutronDbClient()
+        return self._plugin.create_router(edgeapi.context, data)
 
     def get_edge_id(self):
-        edgeapi = utils.NeutronDbClient()
+        edgeapi = nsxv_utils.NeutronDbClient()
         bindings = nsxv_db.get_nsxv_router_bindings(edgeapi.context.session)
         for binding in bindings:
             if binding.edge_id:
@@ -251,6 +255,12 @@ class TestNsxv3AdminUtils(AbstractTestAdminUtils,
                            return_value=[{'id': uuidutils.generate_uuid()}])
         super(TestNsxv3AdminUtils, self)._init_mock_plugin()
 
+        self._plugin = nsxv3_utils.NsxV3PluginWrapper()
+        mock_nm_get_plugin = mock.patch(
+            "neutron_lib.plugins.directory.get_plugin")
+        self.mock_nm_get_plugin = mock_nm_get_plugin.start()
+        self.mock_nm_get_plugin.return_value = self._plugin
+
     def _get_plugin_name(self):
         return 'nsxv3'