Merge "Improve packet-filter test coverage in NEC Plugin"

This commit is contained in:
Jenkins 2013-07-17 09:06:19 +00:00 committed by Gerrit Code Review
commit 9104b4edad
8 changed files with 754 additions and 352 deletions

View File

@ -33,7 +33,3 @@ class OFCConsistencyBroken(qexc.NeutronException):
class PortInfoNotFound(qexc.NotFound): class PortInfoNotFound(qexc.NotFound):
message = _("PortInfo %(id)s could not be found") message = _("PortInfo %(id)s could not be found")
class PacketFilterNotFound(qexc.NotFound):
message = _("PacketFilter %(id)s could not be found")

View File

@ -80,26 +80,3 @@ class PortInfo(model_base.BASEV2, models_v2.HasId):
port_no = sa.Column(sa.Integer, nullable=False) port_no = sa.Column(sa.Integer, nullable=False)
vlan_id = sa.Column(sa.Integer, nullable=False) vlan_id = sa.Column(sa.Integer, nullable=False)
mac = sa.Column(sa.String(32), 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)

View File

@ -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)

View 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)

View File

@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 NEC Corporation. # Copyright 2012-2013 NEC Corporation.
# All rights reserved. # All rights reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 import extensions
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.api.v2 import base from neutron.api.v2 import base
from neutron.common import exceptions
from neutron.manager import NeutronManager from neutron.manager import NeutronManager
from neutron import quota from neutron import quota
@ -33,20 +34,36 @@ quota_packet_filter_opts = [
help=_("Number of packet_filters allowed per tenant, " help=_("Number of packet_filters allowed per tenant, "
"-1 for unlimited")) "-1 for unlimited"))
] ]
# Register the configuration options
cfg.CONF.register_opts(quota_packet_filter_opts, 'QUOTAS') cfg.CONF.register_opts(quota_packet_filter_opts, 'QUOTAS')
PACKET_FILTER_ACTION_REGEX = "(?i)^(allow|accept|drop|deny)$" def convert_to_int(data):
PACKET_FILTER_NUMBER_REGEX = "(?i)^(0x[0-9a-fA-F]+|[0-9]+)$" try:
PACKET_FILTER_PROTOCOL_REGEX = "(?i)^(icmp|tcp|udp|arp|0x[0-9a-fA-F]+|[0-9]+)$" return int(data, 0)
PACKET_FILTER_ATTR_MAP = { 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, 'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'validate': {'type:uuid': None},
'is_visible': True}, 'is_visible': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '', 'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None},
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': False, 'network_id': {'allow_post': True, 'allow_put': False,
@ -62,9 +79,9 @@ PACKET_FILTER_ATTR_MAP = {
'validate': {'type:regex': PACKET_FILTER_ACTION_REGEX}, 'validate': {'type:regex': PACKET_FILTER_ACTION_REGEX},
'is_visible': True}, 'is_visible': True},
'priority': {'allow_post': True, 'allow_put': True, 'priority': {'allow_post': True, 'allow_put': True,
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX}, 'convert_to': convert_to_int,
'is_visible': True}, 'is_visible': True},
'in_port': {'allow_post': True, 'allow_put': True, 'in_port': {'allow_post': True, 'allow_put': False,
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'validate': {'type:uuid': None}, 'validate': {'type:uuid': None},
'is_visible': True}, 'is_visible': True},
@ -78,7 +95,7 @@ PACKET_FILTER_ATTR_MAP = {
'is_visible': True}, 'is_visible': True},
'eth_type': {'allow_post': True, 'allow_put': True, 'eth_type': {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX}, 'convert_to': convert_to_int,
'is_visible': True}, 'is_visible': True},
'src_cidr': {'allow_post': True, 'allow_put': True, 'src_cidr': {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
@ -94,40 +111,58 @@ PACKET_FILTER_ATTR_MAP = {
'is_visible': True}, 'is_visible': True},
'src_port': {'allow_post': True, 'allow_put': True, 'src_port': {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX}, 'convert_to': convert_to_int,
'is_visible': True}, 'is_visible': True},
'dst_port': {'allow_post': True, 'allow_put': True, 'dst_port': {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX}, 'convert_to': convert_to_int,
'is_visible': True}, 'is_visible': True},
} }
PACKET_FILTER_ATTR_MAP = {COLLECTION: PACKET_FILTER_ATTR_PARAMS}
class Packetfilter(extensions.ExtensionDescriptor): class Packetfilter(extensions.ExtensionDescriptor):
def get_name(self): @classmethod
return "PacketFilters" def get_name(cls):
return ALIAS
def get_alias(self): @classmethod
return "PacketFilters" def get_alias(cls):
return ALIAS
def get_description(self): @classmethod
return "PacketFilters" 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" return "http://www.nec.co.jp/api/ext/packet_filter/v2.0"
def get_updated(self): @classmethod
return "2012-07-24T00:00:00+09:00" def get_updated(cls):
return "2013-07-16T00:00:00+09:00"
def get_resources(self): @classmethod
resource = base.create_resource('packet_filters', 'packet_filter', def get_resources(cls):
NeutronManager.get_plugin(), qresource = quota.CountableResource(RESOURCE,
PACKET_FILTER_ATTR_MAP)
qresource = quota.CountableResource('packet_filter',
quota._count_resource, quota._count_resource,
'quota_packet_filter') 'quota_%s' % RESOURCE)
quota.QUOTAS.register_resource(qresource) quota.QUOTAS.register_resource(qresource)
return [extensions.ResourceExtension('packet_filters',
resource, resource = base.create_resource(COLLECTION, RESOURCE,
attr_map=PACKET_FILTER_ATTR_MAP)] 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 {}

View File

@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # 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 # 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 # 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 config
from neutron.plugins.nec.common import exceptions as nexc from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import api as ndb 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 ofc_manager
from neutron.plugins.nec import packet_filter
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -60,12 +61,13 @@ class OperationalStatus:
ERROR = "ERROR" ERROR = "ERROR"
class NECPluginV2(nec_plugin_base.NECPluginV2Base, class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
extraroute_db.ExtraRoute_db_mixin, extraroute_db.ExtraRoute_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin, l3_gwmode_db.L3_NAT_db_mixin,
sg_db_rpc.SecurityGroupServerRpcMixin, sg_db_rpc.SecurityGroupServerRpcMixin,
agentschedulers_db.L3AgentSchedulerDbMixin, agentschedulers_db.L3AgentSchedulerDbMixin,
agentschedulers_db.DhcpAgentSchedulerDbMixin): agentschedulers_db.DhcpAgentSchedulerDbMixin,
packet_filter.PacketFilterMixin):
"""NECPluginV2 controls an OpenFlow Controller. """NECPluginV2 controls an OpenFlow Controller.
The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks
@ -82,13 +84,15 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
"binding", "security-group", "binding", "security-group",
"extraroute", "agent", "extraroute", "agent",
"l3_agent_scheduler", "l3_agent_scheduler",
"dhcp_agent_scheduler"] "dhcp_agent_scheduler",
"packet-filter"]
@property @property
def supported_extension_aliases(self): def supported_extension_aliases(self):
if not hasattr(self, '_aliases'): if not hasattr(self, '_aliases'):
aliases = self._supported_extension_aliases[:] aliases = self._supported_extension_aliases[:]
sg_rpc.disable_security_group_extension_if_noop_driver(aliases) sg_rpc.disable_security_group_extension_if_noop_driver(aliases)
self.remove_packet_filter_extension_if_disabled(aliases)
self._aliases = aliases self._aliases = aliases
return self._aliases return self._aliases
@ -96,11 +100,6 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
ndb.initialize() ndb.initialize()
self.ofc = ofc_manager.OFCManager() 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 # Set the plugin default extension path
# if no api_extensions_path is specified. # if no api_extensions_path is specified.
if not config.CONF.api_extensions_path: if not config.CONF.api_extensions_path:
@ -172,14 +171,11 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
if self.packet_filter_enabled: if self.packet_filter_enabled:
if port_status is OperationalStatus.ACTIVE: if port_status is OperationalStatus.ACTIVE:
filters = dict(in_port=[port['id']], filters = dict(in_port=[port['id']],
status=[OperationalStatus.DOWN], status=[pf_db.PF_STATUS_DOWN],
admin_state_up=[True]) admin_state_up=[True])
pfs = (super(NECPluginV2, self). pfs = self.get_packet_filters(context, filters=filters)
get_packet_filters(context, filters=filters))
for pf in pfs: for pf in pfs:
self._activate_packet_filter_if_ready(context, pf, self.activate_packet_filter_if_ready(context, pf)
network=network,
in_port=port)
if port_status in [OperationalStatus.ACTIVE]: if port_status in [OperationalStatus.ACTIVE]:
if self.ofc.exists_ofc_port(context, port['id']): 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. # deactivate packet_filters after the port has deleted from OFC.
if self.packet_filter_enabled: if self.packet_filter_enabled:
filters = dict(in_port=[port['id']], filters = dict(in_port=[port['id']],
status=[OperationalStatus.ACTIVE]) status=[pf_db.PF_STATUS_ACTIVE])
pfs = super(NECPluginV2, self).get_packet_filters(context, pfs = self.get_packet_filters(context, filters=filters)
filters=filters)
for pf in pfs: for pf in pfs:
self._deactivate_packet_filter(context, pf) self.deactivate_packet_filter(context, pf)
# Quantm Plugin Basic methods # Quantm Plugin Basic methods
@ -280,32 +275,22 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
if changed and not new_net['admin_state_up']: if changed and not new_net['admin_state_up']:
self._update_resource_status(context, "network", id, self._update_resource_status(context, "network", id,
OperationalStatus.DOWN) 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]) filters = dict(network_id=[id], status=[OperationalStatus.ACTIVE])
ports = super(NECPluginV2, self).get_ports(context, ports = super(NECPluginV2, self).get_ports(context,
filters=filters) filters=filters)
for port in ports: for port in ports:
self.deactivate_port(context, port) 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']: elif changed and new_net['admin_state_up']:
self._update_resource_status(context, "network", id, self._update_resource_status(context, "network", id,
OperationalStatus.ACTIVE) OperationalStatus.ACTIVE)
# enable ports and packet_filters of the network # enable ports of the network
filters = dict(network_id=[id], status=[OperationalStatus.DOWN], filters = dict(network_id=[id], status=[OperationalStatus.DOWN],
admin_state_up=[True]) admin_state_up=[True])
ports = super(NECPluginV2, self).get_ports(context, ports = super(NECPluginV2, self).get_ports(context,
filters=filters) filters=filters)
for port in ports: for port in ports:
self.activate_port_if_ready(context, port, new_net) 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 return new_net
@ -329,11 +314,12 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
' from OFC: %s'), port) ' from OFC: %s'), port)
self.deactivate_port(context, 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: if self.packet_filter_enabled:
filters = dict(network_id=[id]) filters = dict(network_id=[id])
pfs = (super(NECPluginV2, self). pfs = self.get_packet_filters(context, filters=filters)
get_packet_filters(context, filters=filters)) for pf in pfs:
self.delete_packet_filter(context, pf['id'])
super(NECPluginV2, self).delete_network(context, id) super(NECPluginV2, self).delete_network(context, id)
try: try:
@ -346,11 +332,6 @@ class NECPluginV2(nec_plugin_base.NECPluginV2Base,
# resources, so this plugin just warns. # resources, so this plugin just warns.
LOG.warn(reason) 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 # delete unnessary ofc_tenant
filters = dict(tenant_id=[tenant_id]) filters = dict(tenant_id=[tenant_id])
nets = super(NECPluginV2, self).get_networks(context, filters=filters) 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 # delete all packet_filters of the port
if self.packet_filter_enabled: if self.packet_filter_enabled:
filters = dict(port_id=[id]) filters = dict(port_id=[id])
pfs = (super(NECPluginV2, self). pfs = self.get_packet_filters(context, filters=filters)
get_packet_filters(context, filters=filters))
for packet_filter in pfs: for packet_filter in pfs:
self.delete_packet_filter(context, packet_filter['id']) 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) self._extend_port_dict_binding(context, port)
return [self._fields(port, fields) for port in ports] 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, class NECPluginV2AgentNotifierApi(proxy.RpcProxy,
sg_rpc.SecurityGroupAgentRpcApiMixin): sg_rpc.SecurityGroupAgentRpcApiMixin):

View 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

View File

@ -21,35 +21,41 @@ import webob.exc
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.common.test_lib import test_config from neutron.common.test_lib import test_config
from neutron import context from neutron import context
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.extensions import packetfilter 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 from neutron.tests.unit import test_db_plugin as test_plugin
PLUGIN_NAME = 'neutron.plugins.nec.nec_plugin.NECPluginV2' NEC_PLUGIN_PF_INI = """
OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager' [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): class PacketfilterExtensionManager(packetfilter.Packetfilter):
def get_resources(self): @classmethod
def get_resources(cls):
# Add the resources to the global attribute map # Add the resources to the global attribute map
# This is done here as the setup process won't # This is done here as the setup process won't
# initialize the main API router which extends # initialize the main API router which extends
# the global attribute map # the global attribute map
attributes.RESOURCE_ATTRIBUTE_MAP.update( attributes.RESOURCE_ATTRIBUTE_MAP.update(
{'packet_filters': packetfilter.PACKET_FILTER_ATTR_MAP}) {'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): 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() 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, def _create_packet_filter(self, fmt, net_id, expected_res_status=None,
arg_list=None, **kwargs): arg_list=None, **kwargs):
@ -98,6 +104,124 @@ class TestNecPluginPacketFilter(test_plugin.NeutronDbPluginV2TestCase):
if do_delete: if do_delete:
self._delete('packet_filters', pf['packet_filter']['id']) 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): def test_show_pf_on_network(self):
kwargs = { kwargs = {
'name': 'test-pf-net', 'name': 'test-pf-net',
@ -125,3 +249,219 @@ class TestNecPluginPacketFilter(test_plugin.NeutronDbPluginV2TestCase):
self.assertEqual(pf_id, pf_ref['packet_filter']['id']) self.assertEqual(pf_id, pf_ref['packet_filter']['id'])
for key in kwargs: for key in kwargs:
self.assertEqual(kwargs[key], pf_ref['packet_filter'][key]) 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)