]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Improve packet-filter test coverage in NEC Plugin
authorRyota MIBU <r-mibu@cq.jp.nec.com>
Tue, 9 Jul 2013 08:18:10 +0000 (17:18 +0900)
committerRyota MIBU <r-mibu@cq.jp.nec.com>
Wed, 17 Jul 2013 00:13:00 +0000 (09:13 +0900)
blueprint nec-plugin-test-coverage

This commit adds unit tests for packet-filter in NEC Plugin.

This commit refactors packet-filter in NEC Plugin.
- Put packet-filter classes and methods into nec/packet_filter.py (a) and
  nec/db/packetfilter.py (b), NEC Plugin specific codes are in (a)
- Change stateless methods to class methods in extenstions/packetfilter.py
- Add 'convert_to' option to the attribute map of packet-filter to convert
  some string parameter to int at the api layer

Also, this commit includes the following changes in packet-filter.
- Fix attribute map of packet-filter; set in_port to allow_put=False
- Add new methods to update attribute map properly
- Make packet-filters ignore status of associated resource (network)

Change-Id: I7c0b76afb603f1f078b28610181b16ce66225d37

neutron/plugins/nec/common/exceptions.py
neutron/plugins/nec/db/models.py
neutron/plugins/nec/db/nec_plugin_base.py [deleted file]
neutron/plugins/nec/db/packetfilter.py [new file with mode: 0644]
neutron/plugins/nec/extensions/packetfilter.py
neutron/plugins/nec/nec_plugin.py
neutron/plugins/nec/packet_filter.py [new file with mode: 0644]
neutron/tests/unit/nec/test_packet_filter.py

index a5f09a4cab80bc444323ac951251b889d77f9f04..226ce6e996d358c3b594dae17a69a7937b0105eb 100644 (file)
@@ -33,7 +33,3 @@ class OFCConsistencyBroken(qexc.NeutronException):
 
 class PortInfoNotFound(qexc.NotFound):
     message = _("PortInfo %(id)s could not be found")
-
-
-class PacketFilterNotFound(qexc.NotFound):
-    message = _("PacketFilter %(id)s could not be found")
index 22b7b28feb8cf0c6d7b0f5af5248507861d83234..a04edf5287fd8264489a3e7b7f3632cac7190cec 100644 (file)
@@ -80,26 +80,3 @@ class PortInfo(model_base.BASEV2, models_v2.HasId):
     port_no = sa.Column(sa.Integer, nullable=False)
     vlan_id = sa.Column(sa.Integer, nullable=False)
     mac = sa.Column(sa.String(32), nullable=False)
-
-
-class PacketFilter(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
-    """Represents a packet filter."""
-    name = sa.Column(sa.String(255))
-    network_id = sa.Column(sa.String(36),
-                           sa.ForeignKey('networks.id', ondelete="CASCADE"),
-                           nullable=False)
-    priority = sa.Column(sa.Integer, nullable=False)
-    action = sa.Column(sa.String(16), nullable=False)
-    # condition
-    in_port = sa.Column(sa.String(36), nullable=False)
-    src_mac = sa.Column(sa.String(32), nullable=False)
-    dst_mac = sa.Column(sa.String(32), nullable=False)
-    eth_type = sa.Column(sa.Integer, nullable=False)
-    src_cidr = sa.Column(sa.String(64), nullable=False)
-    dst_cidr = sa.Column(sa.String(64), nullable=False)
-    protocol = sa.Column(sa.String(16), nullable=False)
-    src_port = sa.Column(sa.Integer, nullable=False)
-    dst_port = sa.Column(sa.Integer, nullable=False)
-    # status
-    admin_state_up = sa.Column(sa.Boolean(), nullable=False)
-    status = sa.Column(sa.String(16), nullable=False)
diff --git a/neutron/plugins/nec/db/nec_plugin_base.py b/neutron/plugins/nec/db/nec_plugin_base.py
deleted file mode 100644 (file)
index 7693783..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 NEC Corporation.  All rights reserved.
-#
-#    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.
-# @author: Ryota MIBU
-
-from sqlalchemy.orm import exc
-
-from neutron.api.v2 import attributes
-from neutron.db import db_base_plugin_v2
-from neutron.openstack.common import log as logging
-from neutron.openstack.common import uuidutils
-from neutron.plugins.nec.common import exceptions as q_exc
-from neutron.plugins.nec.db import models as nmodels
-
-
-LOG = logging.getLogger(__name__)
-
-
-class NECPluginV2Base(db_base_plugin_v2.NeutronDbPluginV2):
-
-    """Base class of plugins that handle packet filters."""
-
-    def _make_packet_filter_dict(self, packet_filter, fields=None):
-        res = {'id': packet_filter['id'],
-               'name': packet_filter['name'],
-               'tenant_id': packet_filter['tenant_id'],
-               'network_id': packet_filter['network_id'],
-               'action': packet_filter['action'],
-               'priority': packet_filter['priority'],
-               'in_port': packet_filter['in_port'],
-               'src_mac': packet_filter['src_mac'],
-               'dst_mac': packet_filter['dst_mac'],
-               'eth_type': packet_filter['eth_type'],
-               'src_cidr': packet_filter['src_cidr'],
-               'dst_cidr': packet_filter['dst_cidr'],
-               'protocol': packet_filter['protocol'],
-               'src_port': packet_filter['src_port'],
-               'dst_port': packet_filter['dst_port'],
-               'admin_state_up': packet_filter['admin_state_up'],
-               'status': packet_filter['status']}
-        return self._fields(res, fields)
-
-    def _get_packet_filter(self, context, id):
-        try:
-            packet_filter = self._get_by_id(context, nmodels.PacketFilter, id)
-        except exc.NoResultFound:
-            raise q_exc.PacketFilterNotFound(id=id)
-        return packet_filter
-
-    def get_packet_filter(self, context, id, fields=None):
-        packet_filter = self._get_packet_filter(context, id)
-        return self._make_packet_filter_dict(packet_filter, fields)
-
-    def get_packet_filters(self, context, filters=None, fields=None):
-        return self._get_collection(context,
-                                    nmodels.PacketFilter,
-                                    self._make_packet_filter_dict,
-                                    filters=filters,
-                                    fields=fields)
-
-    def create_packet_filter(self, context, packet_filter):
-        pf = packet_filter['packet_filter']
-        tenant_id = self._get_tenant_id_for_create(context, pf)
-
-        # validate network ownership
-        super(NECPluginV2Base, self).get_network(context, pf['network_id'])
-        if pf.get('in_port') is not attributes.ATTR_NOT_SPECIFIED:
-            # validate port ownership
-            super(NECPluginV2Base, self).get_port(context, pf['in_port'])
-
-        params = {'tenant_id': tenant_id,
-                  'id': pf.get('id') or uuidutils.generate_uuid(),
-                  'name': pf['name'],
-                  'network_id': pf['network_id'],
-                  'priority': pf['priority'],
-                  'action': pf['action'],
-                  'admin_state_up': pf.get('admin_state_up', True),
-                  'status': "ACTIVE"}
-        conditions = {'in_port': '',
-                      'src_mac': '',
-                      'dst_mac': '',
-                      'eth_type': 0,
-                      'src_cidr': '',
-                      'dst_cidr': '',
-                      'src_port': 0,
-                      'dst_port': 0,
-                      'protocol': ''}
-        for key, default in conditions.items():
-            if pf.get(key) is attributes.ATTR_NOT_SPECIFIED:
-                params.update({key: default})
-            else:
-                params.update({key: pf.get(key)})
-
-        with context.session.begin(subtransactions=True):
-            pf_entry = nmodels.PacketFilter(**params)
-            context.session.add(pf_entry)
-        return self._make_packet_filter_dict(pf_entry)
-
-    def update_packet_filter(self, context, id, packet_filter):
-        pf = packet_filter['packet_filter']
-        with context.session.begin(subtransactions=True):
-            pf_entry = self._get_packet_filter(context, id)
-            pf_entry.update(pf)
-        return self._make_packet_filter_dict(pf_entry)
-
-    def delete_packet_filter(self, context, id):
-        with context.session.begin(subtransactions=True):
-            packet_filter = self._get_packet_filter(context, id)
-            context.session.delete(packet_filter)
diff --git a/neutron/plugins/nec/db/packetfilter.py b/neutron/plugins/nec/db/packetfilter.py
new file mode 100644 (file)
index 0000000..a45bb26
--- /dev/null
@@ -0,0 +1,148 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012-2013 NEC Corporation.  All rights reserved.
+#
+#    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.
+# @author: Ryota MIBU
+
+import sqlalchemy as sa
+from sqlalchemy.orm import exc as sa_exc
+
+from neutron.api.v2 import attributes
+from neutron.common import exceptions
+from neutron.db import model_base
+from neutron.db import models_v2
+from neutron.openstack.common import uuidutils
+
+
+PF_STATUS_ACTIVE = 'ACTIVE'
+PF_STATUS_DOWN = 'DOWN'
+PF_STATUS_ERROR = 'ERROR'
+
+
+class PacketFilterNotFound(exceptions.NotFound):
+    message = _("PacketFilter %(id)s could not be found")
+
+
+class PacketFilter(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
+    """Represents a packet filter."""
+    name = sa.Column(sa.String(255))
+    network_id = sa.Column(sa.String(36),
+                           sa.ForeignKey('networks.id', ondelete="CASCADE"),
+                           nullable=False)
+    priority = sa.Column(sa.Integer, nullable=False)
+    action = sa.Column(sa.String(16), nullable=False)
+    # condition
+    in_port = sa.Column(sa.String(36), nullable=False)
+    src_mac = sa.Column(sa.String(32), nullable=False)
+    dst_mac = sa.Column(sa.String(32), nullable=False)
+    eth_type = sa.Column(sa.Integer, nullable=False)
+    src_cidr = sa.Column(sa.String(64), nullable=False)
+    dst_cidr = sa.Column(sa.String(64), nullable=False)
+    protocol = sa.Column(sa.String(16), nullable=False)
+    src_port = sa.Column(sa.Integer, nullable=False)
+    dst_port = sa.Column(sa.Integer, nullable=False)
+    # status
+    admin_state_up = sa.Column(sa.Boolean(), nullable=False)
+    status = sa.Column(sa.String(16), nullable=False)
+
+
+class PacketFilterDbMixin(object):
+
+    def _make_packet_filter_dict(self, pf_entry, fields=None):
+        res = {'id': pf_entry['id'],
+               'name': pf_entry['name'],
+               'tenant_id': pf_entry['tenant_id'],
+               'network_id': pf_entry['network_id'],
+               'action': pf_entry['action'],
+               'priority': pf_entry['priority'],
+               'in_port': pf_entry['in_port'],
+               'src_mac': pf_entry['src_mac'],
+               'dst_mac': pf_entry['dst_mac'],
+               'eth_type': pf_entry['eth_type'],
+               'src_cidr': pf_entry['src_cidr'],
+               'dst_cidr': pf_entry['dst_cidr'],
+               'protocol': pf_entry['protocol'],
+               'src_port': pf_entry['src_port'],
+               'dst_port': pf_entry['dst_port'],
+               'admin_state_up': pf_entry['admin_state_up'],
+               'status': pf_entry['status']}
+        return self._fields(res, fields)
+
+    def _get_packet_filter(self, context, id):
+        try:
+            pf_entry = self._get_by_id(context, PacketFilter, id)
+        except sa_exc.NoResultFound:
+            raise PacketFilterNotFound(id=id)
+        return pf_entry
+
+    def get_packet_filter(self, context, id, fields=None):
+        pf_entry = self._get_packet_filter(context, id)
+        return self._make_packet_filter_dict(pf_entry, fields)
+
+    def get_packet_filters(self, context, filters=None, fields=None):
+        return self._get_collection(context,
+                                    PacketFilter,
+                                    self._make_packet_filter_dict,
+                                    filters=filters,
+                                    fields=fields)
+
+    def create_packet_filter(self, context, packet_filter):
+        pf_dict = packet_filter['packet_filter']
+        tenant_id = self._get_tenant_id_for_create(context, pf_dict)
+
+        if pf_dict['in_port'] == attributes.ATTR_NOT_SPECIFIED:
+            # validate network ownership
+            self.get_network(context, pf_dict['network_id'])
+        else:
+            # validate port ownership
+            self.get_port(context, pf_dict['in_port'])
+
+        params = {'tenant_id': tenant_id,
+                  'id': pf_dict.get('id') or uuidutils.generate_uuid(),
+                  'name': pf_dict['name'],
+                  'network_id': pf_dict['network_id'],
+                  'priority': pf_dict['priority'],
+                  'action': pf_dict['action'],
+                  'admin_state_up': pf_dict.get('admin_state_up', True),
+                  'status': PF_STATUS_DOWN,
+                  'in_port': pf_dict['in_port'],
+                  'src_mac': pf_dict['src_mac'],
+                  'dst_mac': pf_dict['dst_mac'],
+                  'eth_type': pf_dict['eth_type'],
+                  'src_cidr': pf_dict['src_cidr'],
+                  'dst_cidr': pf_dict['dst_cidr'],
+                  'src_port': pf_dict['src_port'],
+                  'dst_port': pf_dict['dst_port'],
+                  'protocol': pf_dict['protocol']}
+        for key, default in params.items():
+            if params[key] == attributes.ATTR_NOT_SPECIFIED:
+                params.update({key: ''})
+
+        with context.session.begin(subtransactions=True):
+            pf_entry = PacketFilter(**params)
+            context.session.add(pf_entry)
+
+        return self._make_packet_filter_dict(pf_entry)
+
+    def update_packet_filter(self, context, id, packet_filter):
+        pf = packet_filter['packet_filter']
+        with context.session.begin(subtransactions=True):
+            pf_entry = self._get_packet_filter(context, id)
+            pf_entry.update(pf)
+        return self._make_packet_filter_dict(pf_entry)
+
+    def delete_packet_filter(self, context, id):
+        with context.session.begin(subtransactions=True):
+            pf_entry = self._get_packet_filter(context, id)
+            context.session.delete(pf_entry)
index ce485eb16ff0008cc7234e3a7425320e454d60b3..52591a64b1f17d9aa1949149d1e3e6d14da08a1b 100644 (file)
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2012 NEC Corporation.
+# Copyright 2012-2013 NEC Corporation.
 # All rights reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -23,6 +23,7 @@ from oslo.config import cfg
 from neutron.api import extensions
 from neutron.api.v2 import attributes
 from neutron.api.v2 import base
+from neutron.common import exceptions
 from neutron.manager import NeutronManager
 from neutron import quota
 
@@ -33,20 +34,36 @@ quota_packet_filter_opts = [
                help=_("Number of packet_filters allowed per tenant, "
                       "-1 for unlimited"))
 ]
-# Register the configuration options
 cfg.CONF.register_opts(quota_packet_filter_opts, 'QUOTAS')
 
 
-PACKET_FILTER_ACTION_REGEX = "(?i)^(allow|accept|drop|deny)$"
-PACKET_FILTER_NUMBER_REGEX = "(?i)^(0x[0-9a-fA-F]+|[0-9]+)$"
-PACKET_FILTER_PROTOCOL_REGEX = "(?i)^(icmp|tcp|udp|arp|0x[0-9a-fA-F]+|[0-9]+)$"
-PACKET_FILTER_ATTR_MAP = {
+def convert_to_int(data):
+    try:
+        return int(data, 0)
+    except (ValueError, TypeError):
+        pass
+    try:
+        return int(data)
+    except (ValueError, TypeError):
+        msg = _("'%s' is not a integer") % data
+        raise exceptions.InvalidInput(error_message=msg)
+
+
+ALIAS = 'packet-filter'
+RESOURCE = 'packet_filter'
+COLLECTION = 'packet_filters'
+PACKET_FILTER_ACTION_REGEX = '(?i)^(allow|accept|drop|deny)$'
+PACKET_FILTER_PROTOCOL_REGEX = (
+    '(?i)^(icmp|tcp|udp|arp|0x[0-9a-fA-F]+|[0-9]+|)$')
+PACKET_FILTER_ATTR_PARAMS = {
     'id': {'allow_post': False, 'allow_put': False,
            'validate': {'type:uuid': None},
            'is_visible': True},
     'name': {'allow_post': True, 'allow_put': True, 'default': '',
+             'validate': {'type:string': None},
              'is_visible': True},
     'tenant_id': {'allow_post': True, 'allow_put': False,
+                  'validate': {'type:string': None},
                   'required_by_policy': True,
                   'is_visible': True},
     'network_id': {'allow_post': True, 'allow_put': False,
@@ -62,9 +79,9 @@ PACKET_FILTER_ATTR_MAP = {
                'validate': {'type:regex': PACKET_FILTER_ACTION_REGEX},
                'is_visible': True},
     'priority': {'allow_post': True, 'allow_put': True,
-                 'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
+                 'convert_to': convert_to_int,
                  'is_visible': True},
-    'in_port': {'allow_post': True, 'allow_put': True,
+    'in_port': {'allow_post': True, 'allow_put': False,
                 'default': attributes.ATTR_NOT_SPECIFIED,
                 'validate': {'type:uuid': None},
                 'is_visible': True},
@@ -78,7 +95,7 @@ PACKET_FILTER_ATTR_MAP = {
                 'is_visible': True},
     'eth_type': {'allow_post': True, 'allow_put': True,
                  'default': attributes.ATTR_NOT_SPECIFIED,
-                 'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
+                 'convert_to': convert_to_int,
                  'is_visible': True},
     'src_cidr': {'allow_post': True, 'allow_put': True,
                  'default': attributes.ATTR_NOT_SPECIFIED,
@@ -94,40 +111,58 @@ PACKET_FILTER_ATTR_MAP = {
                  'is_visible': True},
     'src_port': {'allow_post': True, 'allow_put': True,
                  'default': attributes.ATTR_NOT_SPECIFIED,
-                 'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
+                 'convert_to': convert_to_int,
                  'is_visible': True},
     'dst_port': {'allow_post': True, 'allow_put': True,
                  'default': attributes.ATTR_NOT_SPECIFIED,
-                 'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
+                 'convert_to': convert_to_int,
                  'is_visible': True},
 }
+PACKET_FILTER_ATTR_MAP = {COLLECTION: PACKET_FILTER_ATTR_PARAMS}
 
 
 class Packetfilter(extensions.ExtensionDescriptor):
 
-    def get_name(self):
-        return "PacketFilters"
+    @classmethod
+    def get_name(cls):
+        return ALIAS
 
-    def get_alias(self):
-        return "PacketFilters"
+    @classmethod
+    def get_alias(cls):
+        return ALIAS
 
-    def get_description(self):
-        return "PacketFilters"
+    @classmethod
+    def get_description(cls):
+        return "PacketFilters on OFC"
 
-    def get_namespace(self):
+    @classmethod
+    def get_namespace(cls):
         return "http://www.nec.co.jp/api/ext/packet_filter/v2.0"
 
-    def get_updated(self):
-        return "2012-07-24T00:00:00+09:00"
+    @classmethod
+    def get_updated(cls):
+        return "2013-07-16T00:00:00+09:00"
 
-    def get_resources(self):
-        resource = base.create_resource('packet_filters', 'packet_filter',
-                                        NeutronManager.get_plugin(),
-                                        PACKET_FILTER_ATTR_MAP)
-        qresource = quota.CountableResource('packet_filter',
+    @classmethod
+    def get_resources(cls):
+        qresource = quota.CountableResource(RESOURCE,
                                             quota._count_resource,
-                                            'quota_packet_filter')
+                                            'quota_%s' % RESOURCE)
         quota.QUOTAS.register_resource(qresource)
-        return [extensions.ResourceExtension('packet_filters',
-                                             resource,
-                                             attr_map=PACKET_FILTER_ATTR_MAP)]
+
+        resource = base.create_resource(COLLECTION, RESOURCE,
+                                        NeutronManager.get_plugin(),
+                                        PACKET_FILTER_ATTR_PARAMS)
+        pf_ext = extensions.ResourceExtension(
+            COLLECTION, resource, attr_map=PACKET_FILTER_ATTR_PARAMS)
+        return [pf_ext]
+
+    def update_attributes_map(self, attributes):
+        super(Packetfilter, self).update_attributes_map(
+            attributes, extension_attrs_map=PACKET_FILTER_ATTR_MAP)
+
+    def get_extended_resources(self, version):
+        if version == "2.0":
+            return PACKET_FILTER_ATTR_MAP
+        else:
+            return {}
index 3f3752455c2f7b12b86f0eebf861c7c60599faa5..08b74403e6d93f2d2666b7cb60b8c75c39fecfa3 100644 (file)
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 #
-# Copyright 2012 NEC Corporation.  All rights reserved.
+# Copyright 2012-2013 NEC Corporation.  All rights reserved.
 #
 #    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
@@ -39,8 +39,9 @@ from neutron.openstack.common.rpc import proxy
 from neutron.plugins.nec.common import config
 from neutron.plugins.nec.common import exceptions as nexc
 from neutron.plugins.nec.db import api as ndb
-from neutron.plugins.nec.db import nec_plugin_base
+from neutron.plugins.nec.db import packetfilter as pf_db
 from neutron.plugins.nec import ofc_manager
+from neutron.plugins.nec import packet_filter
 
 LOG = logging.getLogger(__name__)
 
@@ -60,12 +61,13 @@ class OperationalStatus:
     ERROR = "ERROR"
 
 
-class NECPluginV2(nec_plugin_base.NECPluginV2Base,
+class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                   extraroute_db.ExtraRoute_db_mixin,
                   l3_gwmode_db.L3_NAT_db_mixin,
                   sg_db_rpc.SecurityGroupServerRpcMixin,
                   agentschedulers_db.L3AgentSchedulerDbMixin,
-                  agentschedulers_db.DhcpAgentSchedulerDbMixin):
+                  agentschedulers_db.DhcpAgentSchedulerDbMixin,
+                  packet_filter.PacketFilterMixin):
     """NECPluginV2 controls an OpenFlow Controller.
 
     The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks
@@ -82,13 +84,15 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
                                     "binding", "security-group",
                                     "extraroute", "agent",
                                     "l3_agent_scheduler",
-                                    "dhcp_agent_scheduler"]
+                                    "dhcp_agent_scheduler",
+                                    "packet-filter"]
 
     @property
     def supported_extension_aliases(self):
         if not hasattr(self, '_aliases'):
             aliases = self._supported_extension_aliases[:]
             sg_rpc.disable_security_group_extension_if_noop_driver(aliases)
+            self.remove_packet_filter_extension_if_disabled(aliases)
             self._aliases = aliases
         return self._aliases
 
@@ -96,11 +100,6 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
         ndb.initialize()
         self.ofc = ofc_manager.OFCManager()
 
-        self.packet_filter_enabled = (config.OFC.enable_packet_filter and
-                                      self.ofc.driver.filter_supported())
-        if self.packet_filter_enabled:
-            self.supported_extension_aliases.append("PacketFilters")
-
         # Set the plugin default extension path
         # if no api_extensions_path is specified.
         if not config.CONF.api_extensions_path:
@@ -172,14 +171,11 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
         if self.packet_filter_enabled:
             if port_status is OperationalStatus.ACTIVE:
                 filters = dict(in_port=[port['id']],
-                               status=[OperationalStatus.DOWN],
+                               status=[pf_db.PF_STATUS_DOWN],
                                admin_state_up=[True])
-                pfs = (super(NECPluginV2, self).
-                       get_packet_filters(context, filters=filters))
+                pfs = self.get_packet_filters(context, filters=filters)
                 for pf in pfs:
-                    self._activate_packet_filter_if_ready(context, pf,
-                                                          network=network,
-                                                          in_port=port)
+                    self.activate_packet_filter_if_ready(context, pf)
 
         if port_status in [OperationalStatus.ACTIVE]:
             if self.ofc.exists_ofc_port(context, port['id']):
@@ -221,11 +217,10 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
         # deactivate packet_filters after the port has deleted from OFC.
         if self.packet_filter_enabled:
             filters = dict(in_port=[port['id']],
-                           status=[OperationalStatus.ACTIVE])
-            pfs = super(NECPluginV2, self).get_packet_filters(context,
-                                                              filters=filters)
+                           status=[pf_db.PF_STATUS_ACTIVE])
+            pfs = self.get_packet_filters(context, filters=filters)
             for pf in pfs:
-                self._deactivate_packet_filter(context, pf)
+                self.deactivate_packet_filter(context, pf)
 
     # Quantm Plugin Basic methods
 
@@ -280,32 +275,22 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
         if changed and not new_net['admin_state_up']:
             self._update_resource_status(context, "network", id,
                                          OperationalStatus.DOWN)
-            # disable all active ports and packet_filters of the network
+            # disable all active ports of the network
             filters = dict(network_id=[id], status=[OperationalStatus.ACTIVE])
             ports = super(NECPluginV2, self).get_ports(context,
                                                        filters=filters)
             for port in ports:
                 self.deactivate_port(context, port)
-            if self.packet_filter_enabled:
-                pfs = (super(NECPluginV2, self).
-                       get_packet_filters(context, filters=filters))
-                for pf in pfs:
-                    self._deactivate_packet_filter(context, pf)
         elif changed and new_net['admin_state_up']:
             self._update_resource_status(context, "network", id,
                                          OperationalStatus.ACTIVE)
-            # enable ports and packet_filters of the network
+            # enable ports of the network
             filters = dict(network_id=[id], status=[OperationalStatus.DOWN],
                            admin_state_up=[True])
             ports = super(NECPluginV2, self).get_ports(context,
                                                        filters=filters)
             for port in ports:
                 self.activate_port_if_ready(context, port, new_net)
-            if self.packet_filter_enabled:
-                pfs = (super(NECPluginV2, self).
-                       get_packet_filters(context, filters=filters))
-                for pf in pfs:
-                    self._activate_packet_filter_if_ready(context, pf, new_net)
 
         return new_net
 
@@ -329,11 +314,12 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
                         ' from OFC: %s'), port)
             self.deactivate_port(context, port)
 
-        # get packet_filters associated with the network
+        # delete all packet_filters of the network
         if self.packet_filter_enabled:
             filters = dict(network_id=[id])
-            pfs = (super(NECPluginV2, self).
-                   get_packet_filters(context, filters=filters))
+            pfs = self.get_packet_filters(context, filters=filters)
+            for pf in pfs:
+                self.delete_packet_filter(context, pf['id'])
 
         super(NECPluginV2, self).delete_network(context, id)
         try:
@@ -346,11 +332,6 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
             #       resources, so this plugin just warns.
             LOG.warn(reason)
 
-        # delete all packet_filters of the network
-        if self.packet_filter_enabled:
-            for pf in pfs:
-                self.delete_packet_filter(context, pf['id'])
-
         # delete unnessary ofc_tenant
         filters = dict(tenant_id=[tenant_id])
         nets = super(NECPluginV2, self).get_networks(context, filters=filters)
@@ -426,8 +407,7 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
         # delete all packet_filters of the port
         if self.packet_filter_enabled:
             filters = dict(port_id=[id])
-            pfs = (super(NECPluginV2, self).
-                   get_packet_filters(context, filters=filters))
+            pfs = self.get_packet_filters(context, filters=filters)
             for packet_filter in pfs:
                 self.delete_packet_filter(context, packet_filter['id'])
 
@@ -456,129 +436,6 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
                 self._extend_port_dict_binding(context, port)
         return [self._fields(port, fields) for port in ports]
 
-    # For PacketFilter Extension
-
-    def _activate_packet_filter_if_ready(self, context, packet_filter,
-                                         network=None, in_port=None):
-        """Activate packet_filter by creating filter on OFC if ready.
-
-        Conditions to create packet_filter on OFC are:
-            * packet_filter admin_state is UP
-            * network admin_state is UP
-            * (if 'in_port' is specified) portinfo is available
-        """
-        net_id = packet_filter['network_id']
-        if not network:
-            network = super(NECPluginV2, self).get_network(context, net_id)
-        in_port_id = packet_filter.get("in_port")
-        if in_port_id and not in_port:
-            in_port = super(NECPluginV2, self).get_port(context, in_port_id)
-
-        pf_status = OperationalStatus.ACTIVE
-        if not packet_filter['admin_state_up']:
-            LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
-                        "packet_filter.admin_state_up is False."))
-            pf_status = OperationalStatus.DOWN
-        elif not network['admin_state_up']:
-            LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
-                        "network.admin_state_up is False."))
-            pf_status = OperationalStatus.DOWN
-        elif in_port_id and in_port_id is in_port.get('id'):
-            LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
-                        "invalid in_port_id."))
-            pf_status = OperationalStatus.DOWN
-        elif in_port_id and not ndb.get_portinfo(context.session, in_port_id):
-            LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
-                        "no portinfo for in_port."))
-            pf_status = OperationalStatus.DOWN
-
-        if pf_status in [OperationalStatus.ACTIVE]:
-            if self.ofc.exists_ofc_packet_filter(context, packet_filter['id']):
-                LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
-                            "ofc_packet_filter already exists."))
-            else:
-                try:
-                    (self.ofc.
-                     create_ofc_packet_filter(context,
-                                              packet_filter['id'],
-                                              packet_filter))
-                except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
-                    reason = _("create_ofc_packet_filter() failed due to "
-                               "%s") % exc
-                    LOG.error(reason)
-                    pf_status = OperationalStatus.ERROR
-
-        if pf_status is not packet_filter['status']:
-            self._update_resource_status(context, "packet_filter",
-                                         packet_filter['id'], pf_status)
-
-    def _deactivate_packet_filter(self, context, packet_filter):
-        """Deactivate packet_filter by deleting filter from OFC if exixts."""
-        pf_status = OperationalStatus.DOWN
-        if not self.ofc.exists_ofc_packet_filter(context, packet_filter['id']):
-            LOG.debug(_("_deactivate_packet_filter(): skip, "
-                        "ofc_packet_filter does not exist."))
-        else:
-            try:
-                self.ofc.delete_ofc_packet_filter(context, packet_filter['id'])
-            except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
-                reason = _("delete_ofc_packet_filter() failed due to "
-                           "%s") % exc
-                LOG.error(reason)
-                pf_status = OperationalStatus.ERROR
-
-        if pf_status is not packet_filter['status']:
-            self._update_resource_status(context, "packet_filter",
-                                         packet_filter['id'], pf_status)
-
-    def create_packet_filter(self, context, packet_filter):
-        """Create a new packet_filter entry on DB, then try to activate it."""
-        LOG.debug(_("NECPluginV2.create_packet_filter() called, "
-                    "packet_filter=%s ."), packet_filter)
-        new_pf = super(NECPluginV2, self).create_packet_filter(context,
-                                                               packet_filter)
-        self._update_resource_status(context, "packet_filter", new_pf['id'],
-                                     OperationalStatus.BUILD)
-
-        self._activate_packet_filter_if_ready(context, new_pf)
-
-        return new_pf
-
-    def update_packet_filter(self, context, id, packet_filter):
-        """Update packet_filter entry on DB, and recreate it if changed.
-
-        If any rule of the packet_filter was changed, recreate it on OFC.
-        """
-        LOG.debug(_("NECPluginV2.update_packet_filter() called, "
-                    "id=%(id)s packet_filter=%(packet_filter)s ."),
-                  {'id': id, 'packet_filter': packet_filter})
-        with context.session.begin(subtransactions=True):
-            old_pf = super(NECPluginV2, self).get_packet_filter(context, id)
-            new_pf = super(NECPluginV2, self).update_packet_filter(
-                context, id, packet_filter)
-
-        changed = False
-        exclude_items = ["id", "name", "tenant_id", "network_id", "status"]
-        for key in new_pf['packet_filter'].keys():
-            if key not in exclude_items:
-                if old_pf[key] is not new_pf[key]:
-                    changed = True
-                    break
-
-        if changed:
-            self._deactivate_packet_filter(context, old_pf)
-            self._activate_packet_filter_if_ready(context, new_pf)
-
-        return new_pf
-
-    def delete_packet_filter(self, context, id):
-        """Deactivate and delete packet_filter."""
-        LOG.debug(_("NECPluginV2.delete_packet_filter() called, id=%s ."), id)
-        pf = super(NECPluginV2, self).get_packet_filter(context, id)
-        self._deactivate_packet_filter(context, pf)
-
-        super(NECPluginV2, self).delete_packet_filter(context, id)
-
 
 class NECPluginV2AgentNotifierApi(proxy.RpcProxy,
                                   sg_rpc.SecurityGroupAgentRpcApiMixin):
diff --git a/neutron/plugins/nec/packet_filter.py b/neutron/plugins/nec/packet_filter.py
new file mode 100644 (file)
index 0000000..43f1cf8
--- /dev/null
@@ -0,0 +1,170 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012-2013 NEC Corporation.  All rights reserved.
+#
+#    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.
+# @author: Ryota MIBU
+
+from neutron.openstack.common import log as logging
+from neutron.plugins.nec.common import config
+from neutron.plugins.nec.common import exceptions as nexc
+from neutron.plugins.nec.db import api as ndb
+from neutron.plugins.nec.db import packetfilter as pf_db
+
+
+LOG = logging.getLogger(__name__)
+
+
+class PacketFilterMixin(pf_db.PacketFilterDbMixin):
+    """Mixin class to add packet filter to NECPluginV2."""
+
+    @property
+    def packet_filter_enabled(self):
+        if not hasattr(self, '_packet_filter_enabled'):
+            self._packet_filter_enabled = (
+                config.OFC.enable_packet_filter and
+                self.ofc.driver.filter_supported())
+        return self._packet_filter_enabled
+
+    def remove_packet_filter_extension_if_disabled(self, aliases):
+        if not self.packet_filter_enabled:
+            LOG.debug(_('Disabled packet-filter extension.'))
+            aliases.remove('packet-filter')
+
+    def create_packet_filter(self, context, packet_filter):
+        """Create a new packet_filter entry on DB, then try to activate it."""
+        LOG.debug(_("create_packet_filter() called, packet_filter=%s ."),
+                  packet_filter)
+
+        pf = super(PacketFilterMixin, self).create_packet_filter(
+            context, packet_filter)
+
+        return self.activate_packet_filter_if_ready(context, pf)
+
+    def update_packet_filter(self, context, id, packet_filter):
+        """Update packet_filter entry on DB, and recreate it if changed.
+
+        If any rule of the packet_filter was changed, recreate it on OFC.
+        """
+        LOG.debug(_("update_packet_filter() called, "
+                    "id=%(id)s packet_filter=%(packet_filter)s ."),
+                  {'id': id, 'packet_filter': packet_filter})
+
+        # validate ownership
+        pf_old = self.get_packet_filter(context, id)
+
+        pf = super(PacketFilterMixin, self).update_packet_filter(
+            context, id, packet_filter)
+
+        def _packet_filter_changed(old_pf, new_pf):
+            for key in new_pf:
+                if key not in ('id', 'name', 'tenant_id', 'network_id',
+                               'in_port', 'status'):
+                    if old_pf[key] != new_pf[key]:
+                        return True
+            return False
+
+        if _packet_filter_changed(pf_old, pf):
+            pf = self.deactivate_packet_filter(context, pf)
+            pf = self.activate_packet_filter_if_ready(context, pf)
+
+        return pf
+
+    def delete_packet_filter(self, context, id):
+        """Deactivate and delete packet_filter."""
+        LOG.debug(_("delete_packet_filter() called, id=%s ."), id)
+
+        # validate ownership
+        pf = self.get_packet_filter(context, id)
+
+        pf = self.deactivate_packet_filter(context, pf)
+        if pf['status'] == pf_db.PF_STATUS_ERROR:
+            msg = _("failed to delete packet_filter id=%s which remains in "
+                    "error status.") % id
+            LOG.error(msg)
+            raise nexc.OFCException(reason=msg)
+
+        super(PacketFilterMixin, self).delete_packet_filter(context, id)
+
+    def activate_packet_filter_if_ready(self, context, packet_filter):
+        """Activate packet_filter by creating filter on OFC if ready.
+
+        Conditions to create packet_filter on OFC are:
+            * packet_filter admin_state is UP
+            * (if 'in_port' is specified) portinfo is available
+        """
+        LOG.debug(_("activate_packet_filter_if_ready() called, "
+                    "packet_filter=%s."), packet_filter)
+
+        pf_id = packet_filter['id']
+        in_port_id = packet_filter.get('in_port')
+        current = packet_filter['status']
+
+        pf_status = current
+        if not packet_filter['admin_state_up']:
+            LOG.debug(_("activate_packet_filter_if_ready(): skip pf_id=%s, "
+                        "packet_filter.admin_state_up is False."), pf_id)
+        elif in_port_id and not ndb.get_portinfo(context.session, in_port_id):
+            LOG.debug(_("activate_packet_filter_if_ready(): skip "
+                        "pf_id=%s, no portinfo for the in_port."), pf_id)
+        elif self.ofc.exists_ofc_packet_filter(context, packet_filter['id']):
+            LOG.debug(_("_activate_packet_filter_if_ready(): skip, "
+                        "ofc_packet_filter already exists."))
+        else:
+            LOG.debug(_("activate_packet_filter_if_ready(): create "
+                        "packet_filter id=%s on OFC."), pf_id)
+            try:
+                self.ofc.create_ofc_packet_filter(context, pf_id,
+                                                  packet_filter)
+                pf_status = pf_db.PF_STATUS_ACTIVE
+            except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
+                LOG.error(_("failed to create packet_filter id=%(id)s on "
+                            "OFC: %(exc)s"), {'id': pf_id, 'exc': str(exc)})
+                pf_status = pf_db.PF_STATUS_ERROR
+
+        if pf_status != current:
+            self._update_resource_status(context, "packet_filter", pf_id,
+                                         pf_status)
+            packet_filter.update({'status': pf_status})
+
+        return packet_filter
+
+    def deactivate_packet_filter(self, context, packet_filter):
+        """Deactivate packet_filter by deleting filter from OFC if exixts."""
+        LOG.debug(_("deactivate_packet_filter_if_ready() called, "
+                    "packet_filter=%s."), packet_filter)
+        pf_id = packet_filter['id']
+        current = packet_filter['status']
+
+        pf_status = current
+        if self.ofc.exists_ofc_packet_filter(context, pf_id):
+            LOG.debug(_("deactivate_packet_filter(): "
+                        "deleting packet_filter id=%s from OFC."), pf_id)
+            try:
+                self.ofc.delete_ofc_packet_filter(context, pf_id)
+                pf_status = pf_db.PF_STATUS_DOWN
+            except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
+                LOG.error(_("failed to delete packet_filter id=%(id)s from "
+                            "OFC: %(exc)s"), {'id': pf_id, 'exc': str(exc)})
+                pf_status = pf_db.PF_STATUS_ERROR
+        else:
+            LOG.debug(_("deactivate_packet_filter(): skip, "
+                        "Not found OFC Mapping for packet_filter id=%s."),
+                      pf_id)
+
+        if pf_status != current:
+            self._update_resource_status(context, "packet_filter", pf_id,
+                                         pf_status)
+            packet_filter.update({'status': pf_status})
+
+        return packet_filter
index 37924b1517fd54a4f5c4183e4286398e4fccae7a..92c1fa12b2f685d34e7eecd87801d7105c6037ff 100644 (file)
@@ -21,35 +21,41 @@ import webob.exc
 from neutron.api.v2 import attributes
 from neutron.common.test_lib import test_config
 from neutron import context
+from neutron.plugins.nec.common import exceptions as nexc
 from neutron.plugins.nec.extensions import packetfilter
+from neutron.tests.unit.nec import test_nec_plugin
 from neutron.tests.unit import test_db_plugin as test_plugin
 
 
-PLUGIN_NAME = 'neutron.plugins.nec.nec_plugin.NECPluginV2'
-OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager'
+NEC_PLUGIN_PF_INI = """
+[DEFAULT]
+api_extensions_path = neutron/plugins/nec/extensions
+[OFC]
+driver = neutron.tests.unit.nec.stub_ofc_driver.StubOFCDriver
+enable_packet_filter = True
+"""
 
 
 class PacketfilterExtensionManager(packetfilter.Packetfilter):
 
-    def get_resources(self):
+    @classmethod
+    def get_resources(cls):
         # Add the resources to the global attribute map
         # This is done here as the setup process won't
         # initialize the main API router which extends
         # the global attribute map
         attributes.RESOURCE_ATTRIBUTE_MAP.update(
             {'packet_filters': packetfilter.PACKET_FILTER_ATTR_MAP})
-        return super(PacketfilterExtensionManager, self).get_resources()
+        return super(PacketfilterExtensionManager, cls).get_resources()
 
 
-class TestNecPluginPacketFilter(test_plugin.NeutronDbPluginV2TestCase):
+class TestNecPluginPacketFilter(test_nec_plugin.NecPluginV2TestCase):
+
+    _nec_ini = NEC_PLUGIN_PF_INI
 
     def setUp(self):
-        self.addCleanup(mock.patch.stopall)
-        ofc_manager_cls = mock.patch(OFC_MANAGER).start()
-        ofc_driver = ofc_manager_cls.return_value.driver
-        ofc_driver.filter_supported.return_value = True
         test_config['extension_manager'] = PacketfilterExtensionManager()
-        super(TestNecPluginPacketFilter, self).setUp(PLUGIN_NAME)
+        super(TestNecPluginPacketFilter, self).setUp()
 
     def _create_packet_filter(self, fmt, net_id, expected_res_status=None,
                               arg_list=None, **kwargs):
@@ -98,6 +104,124 @@ class TestNecPluginPacketFilter(test_plugin.NeutronDbPluginV2TestCase):
                 if do_delete:
                     self._delete('packet_filters', pf['packet_filter']['id'])
 
+    @contextlib.contextmanager
+    def packet_filter_on_port(self, port=None, fmt=None, do_delete=True,
+                              set_portinfo=True, **kwargs):
+        with test_plugin.optional_ctx(port, self.port) as port_to_use:
+            net_id = port_to_use['port']['network_id']
+            port_id = port_to_use['port']['id']
+
+            if set_portinfo:
+                portinfo = {'id': port_id,
+                            'port_no': kwargs.get('port_no', 123)}
+                kw = {'added': [portinfo]}
+                if 'datapath_id' in kwargs:
+                    kw['datapath_id'] = kwargs['datapath_id']
+                self.rpcapi_update_ports(**kw)
+
+            kwargs['in_port'] = port_id
+            pf = self._make_packet_filter(fmt or self.fmt, net_id, **kwargs)
+            self.assertEqual(port_id, pf['packet_filter']['in_port'])
+            try:
+                yield pf
+            finally:
+                if do_delete:
+                    self._delete('packet_filters', pf['packet_filter']['id'])
+
+    def test_list_packet_filters(self):
+        self._list('packet_filters')
+
+    def test_create_pf_on_network_no_ofc_creation(self):
+        with self.packet_filter_on_network(admin_state_up=False) as pf:
+            self.assertEqual(pf['packet_filter']['status'], 'DOWN')
+
+        self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+        self.assertFalse(self.ofc.delete_ofc_packet_filter.called)
+
+    def test_create_pf_on_port_no_ofc_creation(self):
+        with self.packet_filter_on_port(admin_state_up=False,
+                                        set_portinfo=False) as pf:
+            self.assertEqual(pf['packet_filter']['status'], 'DOWN')
+
+        self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+        self.assertFalse(self.ofc.delete_ofc_packet_filter.called)
+
+    def test_create_pf_on_network_with_ofc_creation(self):
+        with self.packet_filter_on_network() as pf:
+            pf_id = pf['packet_filter']['id']
+            self.assertEqual(pf['packet_filter']['status'], 'ACTIVE')
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 1)
+
+    def test_create_pf_on_port_with_ofc_creation(self):
+        with self.packet_filter_on_port() as pf:
+            pf_id = pf['packet_filter']['id']
+            self.assertEqual(pf['packet_filter']['status'], 'ACTIVE')
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 1)
+
+    def test_create_pf_with_invalid_priority(self):
+        with self.network() as net:
+            net_id = net['network']['id']
+            kwargs = {'priority': 'high'}
+            self._create_packet_filter(self.fmt, net_id,
+                                       webob.exc.HTTPBadRequest.code,
+                                       **kwargs)
+
+        self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+
+    def test_create_pf_with_ofc_creation_failure(self):
+        self.ofc.set_raise_exc('create_ofc_packet_filter',
+                               nexc.OFCException(reason='hoge'))
+
+        with self.packet_filter_on_network() as pf:
+            pf_id = pf['packet_filter']['id']
+            pf_ref = self._show('packet_filters', pf_id)
+            self.assertEqual(pf_ref['packet_filter']['status'], 'ERROR')
+
+            self.ofc.set_raise_exc('create_ofc_packet_filter', None)
+
+            # Retry deactivate packet_filter.
+            data = {'packet_filter': {'priority': 1000}}
+            self._update('packet_filters', pf_id, data)
+
+            pf_ref = self._show('packet_filters', pf_id)
+            self.assertEqual(pf_ref['packet_filter']['status'], 'ACTIVE')
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 2)
+
     def test_show_pf_on_network(self):
         kwargs = {
             'name': 'test-pf-net',
@@ -125,3 +249,219 @@ class TestNecPluginPacketFilter(test_plugin.NeutronDbPluginV2TestCase):
             self.assertEqual(pf_id, pf_ref['packet_filter']['id'])
             for key in kwargs:
                 self.assertEqual(kwargs[key], pf_ref['packet_filter'][key])
+
+    def test_show_pf_on_port(self):
+        kwargs = {
+            'name': 'test-pf-port',
+            'admin_state_up': False,
+            'action': 'DENY',
+            'priority': '0o147',
+            'src_mac': '00:11:22:33:44:55',
+            'dst_mac': '66:77:88:99:aa:bb',
+            'eth_type': 2048,
+            'src_cidr': '192.168.1.0/24',
+            'dst_cidr': '192.168.2.0/24',
+            'protocol': 'TCP',
+            'dst_port': '0x50'
+        }
+
+        with self.packet_filter_on_port(**kwargs) as pf:
+            pf_id = pf['packet_filter']['id']
+            pf_ref = self._show('packet_filters', pf_id)
+
+            # convert string to int.
+            kwargs.update({'priority': 103, 'eth_type': 2048,
+                           'src_port': u'', 'dst_port': 80})
+
+            self.assertEqual(pf_id, pf_ref['packet_filter']['id'])
+            for key in kwargs:
+                self.assertEqual(kwargs[key], pf_ref['packet_filter'][key])
+
+    def test_show_pf_not_found(self):
+        pf_id = '00000000-ffff-ffff-ffff-000000000000'
+
+        self._show('packet_filters', pf_id,
+                   expected_code=webob.exc.HTTPNotFound.code)
+
+    def test_update_pf_on_network(self):
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        with self.packet_filter_on_network(admin_state_up=False) as pf:
+            pf_id = pf['packet_filter']['id']
+
+            self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+            data = {'packet_filter': {'admin_state_up': True}}
+            self._update('packet_filters', pf_id, data)
+            self.ofc.create_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id, pf_dict)
+
+            self.assertFalse(self.ofc.delete_ofc_packet_filter.called)
+            data = {'packet_filter': {'admin_state_up': False}}
+            self._update('packet_filters', pf_id, data)
+            self.ofc.delete_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id)
+
+    def test_update_pf_on_port(self):
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        with self.packet_filter_on_port(admin_state_up=False) as pf:
+            pf_id = pf['packet_filter']['id']
+
+            self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+            data = {'packet_filter': {'admin_state_up': True}}
+            self._update('packet_filters', pf_id, data)
+            self.ofc.create_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id, pf_dict)
+
+            self.assertFalse(self.ofc.delete_ofc_packet_filter.called)
+            data = {'packet_filter': {'admin_state_up': False}}
+            self._update('packet_filters', pf_id, data)
+            self.ofc.delete_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id)
+
+    def test_activate_pf_on_port_triggered_by_update_port(self):
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        with self.packet_filter_on_port(set_portinfo=False) as pf:
+            pf_id = pf['packet_filter']['id']
+            in_port_id = pf['packet_filter']['in_port']
+
+            self.assertFalse(self.ofc.create_ofc_packet_filter.called)
+            portinfo = {'id': in_port_id, 'port_no': 123}
+            kw = {'added': [portinfo]}
+            self.rpcapi_update_ports(**kw)
+            self.ofc.create_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id, pf_dict)
+
+            self.assertFalse(self.ofc.delete_ofc_packet_filter.called)
+            kw = {'removed': [in_port_id]}
+            self.rpcapi_update_ports(**kw)
+            self.ofc.delete_ofc_packet_filter.assert_called_once_with(
+                ctx, pf_id)
+
+        # Ensure pf was created before in_port has activated.
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        port_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+            mock.call.exists_ofc_port(ctx, in_port_id),
+            mock.call.create_ofc_port(ctx, in_port_id, port_dict),
+
+            mock.call.exists_ofc_port(ctx, in_port_id),
+            mock.call.delete_ofc_port(ctx, in_port_id, port_dict),
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 1)
+
+    def test_activate_pf_while_exists_on_ofc(self):
+        ctx = mock.ANY
+        with self.packet_filter_on_network() as pf:
+            pf_id = pf['packet_filter']['id']
+
+            self.ofc.set_raise_exc('delete_ofc_packet_filter',
+                                   nexc.OFCException(reason='hoge'))
+
+            # This update request will make plugin reactivate pf.
+            data = {'packet_filter': {'priority': 1000}}
+            self._update('packet_filters', pf_id, data)
+
+            self.ofc.set_raise_exc('delete_ofc_packet_filter', None)
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 2)
+
+    def test_deactivate_pf_with_ofc_deletion_failure(self):
+        ctx = mock.ANY
+        with self.packet_filter_on_network() as pf:
+            pf_id = pf['packet_filter']['id']
+
+            self.ofc.set_raise_exc('delete_ofc_packet_filter',
+                                   nexc.OFCException(reason='hoge'))
+
+            data = {'packet_filter': {'admin_state_up': False}}
+            self._update('packet_filters', pf_id, data)
+
+            pf_ref = self._show('packet_filters', pf_id)
+            self.assertEqual(pf_ref['packet_filter']['status'], 'ERROR')
+
+            self.ofc.set_raise_exc('delete_ofc_packet_filter', None)
+
+            data = {'packet_filter': {'priority': 1000}}
+            self._update('packet_filters', pf_id, data)
+
+            pf_ref = self._show('packet_filters', pf_id)
+            self.assertEqual(pf_ref['packet_filter']['status'], 'DOWN')
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 2)
+
+    def test_delete_pf_with_ofc_deletion_failure(self):
+        self.ofc.set_raise_exc('delete_ofc_packet_filter',
+                               nexc.OFCException(reason='hoge'))
+
+        with self.packet_filter_on_network() as pf:
+            pf_id = pf['packet_filter']['id']
+
+            self._delete('packet_filters', pf_id,
+                         expected_code=webob.exc.HTTPInternalServerError.code)
+
+            pf_ref = self._show('packet_filters', pf_id)
+            self.assertEqual(pf_ref['packet_filter']['status'], 'ERROR')
+
+            self.ofc.set_raise_exc('delete_ofc_packet_filter', None)
+            # Then, self._delete('packet_filters', pf_id) will success.
+
+        ctx = mock.ANY
+        pf_dict = mock.ANY
+        expected = [
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.create_ofc_packet_filter(ctx, pf_id, pf_dict),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+
+            mock.call.exists_ofc_packet_filter(ctx, pf_id),
+            mock.call.delete_ofc_packet_filter(ctx, pf_id),
+        ]
+        self.ofc.assert_has_calls(expected)
+        self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 2)
+
+    def test_auto_delete_pf_in_network_deletion(self):
+        with self.packet_filter_on_network(admin_state_up=False,
+                                           do_delete=False) as pf:
+            pf_id = pf['packet_filter']['id']
+
+        self._show('packet_filters', pf_id,
+                   expected_code=webob.exc.HTTPNotFound.code)