Improve packet-filter test coverage in NEC Plugin
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
This commit is contained in:
parent
3ba25fa060
commit
0043044f2e
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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)
|
148
neutron/plugins/nec/db/packetfilter.py
Normal file
148
neutron/plugins/nec/db/packetfilter.py
Normal file
@ -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)
|
@ -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 {}
|
||||
|
@ -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):
|
||||
|
170
neutron/plugins/nec/packet_filter.py
Normal file
170
neutron/plugins/nec/packet_filter.py
Normal file
@ -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
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user