NEC plugin code split

- Removes main codes and unit tests of NEC plugin
- Add plugin-specific requirements.txt to the plugin dir.
- Move config to the plugin top dir
- Remove references of networking_nec from config module
  because it may prevent config guide generator.
- agent command can be moved to neutron/cmd/eventlet
  if some consensus has been made.

Closes-Bug: #1419396
Related to blueprint core-vendor-decomposition

Change-Id: I946e0f829e8f6251a63572331c783d7a7b5f6c01
This commit is contained in:
Akihiro Motoki 2015-02-07 22:22:14 +09:00
parent ddb546443f
commit 1fdbcb1343
41 changed files with 48 additions and 8287 deletions

View File

@ -412,6 +412,8 @@ The following chart captures the following aspects:
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-mlnx_ | | | | | |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-nec_ | core | yes | no | [C] | Kilo |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| nuage-openstack-neutron_ | | | | | |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-odl_ | ml2,l3,lb,fw | yes | no | [C] | Kilo |
@ -468,6 +470,14 @@ MidoNet
.. _networking-mlnx:
.. _networking-nec:
NEC
---
* Git: https://github.com/stackforge/networking-nec
* Launchpad: https://launchpad.net/networking-nec
.. _nuage-openstack-neutron:
.. _networking-odl:

View File

@ -1,4 +1,5 @@
# Copyright 2014 NEC Corporation. All rights reserved.
# 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
@ -12,13 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
import sys
def cmp_dpid(dpid_a, dpid_b):
"""Compare two datapath IDs as hexadecimal int.
from networking_nec.plugins.openflow.agent import l2_agent
It returns True if equal, otherwise False.
"""
try:
return (int(dpid_a, 16) == int(dpid_b, 16))
except Exception:
return False
from neutron.common import config as common_config
from neutron.plugins.nec import config as nec_config
def main():
nec_config.register_agent_opts()
common_config.init(sys.argv[1:])
common_config.setup_logging()
l2_agent.run()

View File

@ -1,13 +1,11 @@
Quantum NEC OpenFlow Plugin
Neutron NEC OpenFlow Plugin
===========================
Neutron plugins for NEC OpenFlow networking products and
Trema Sliceable Switch (reference implementation).
# -- What's this?
* Main Page: https://wiki.openstack.org/wiki/Neutron/NEC_OpenFlow_Plugin
https://wiki.openstack.org/wiki/Neutron/NEC_OpenFlow_Plugin
# -- Installation
Use QuickStart Script for this plugin. This provides you auto installation and
configuration of Nova, Neutron and Trema.
https://github.com/nec-openstack/quantum-openflow-plugin/tree/folsom
* Repository:
* http://git.openstack.org/cgit/stackforge/networking-nec/
* https://github.com/stackforge/networking-nec

View File

@ -1,232 +0,0 @@
#!/usr/bin/env python
# Copyright 2012 NEC Corporation.
# Based on ryu/openvswitch agents.
#
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
# Copyright 2011 VMware, Inc.
# 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.
import socket
import sys
import time
import eventlet
eventlet.monkey_patch()
import oslo_messaging
from neutron.agent.linux import ovs_lib
from neutron.agent import rpc as agent_rpc
from neutron.agent import securitygroups_rpc as sg_rpc
from neutron.common import config as common_config
from neutron.common import constants as q_const
from neutron.common import topics
from neutron import context as q_context
from neutron.extensions import securitygroup as ext_sg
from neutron.i18n import _LE, _LI
from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
from neutron.plugins.nec.common import config
LOG = logging.getLogger(__name__)
class NECPluginApi(agent_rpc.PluginApi):
def update_ports(self, context, agent_id, datapath_id,
port_added, port_removed):
"""RPC to update information of ports on Neutron Server."""
LOG.info(_LI("Update ports: added=%(added)s, "
"removed=%(removed)s"),
{'added': port_added, 'removed': port_removed})
cctxt = self.client.prepare()
return cctxt.call(context, 'update_ports',
agent_id=agent_id,
datapath_id=datapath_id,
port_added=port_added,
port_removed=port_removed)
class NECAgentRpcCallback(object):
target = oslo_messaging.Target(version='1.0')
def __init__(self, context, agent, sg_agent):
super(NECAgentRpcCallback, self).__init__()
self.context = context
self.agent = agent
self.sg_agent = sg_agent
def port_update(self, context, **kwargs):
LOG.debug("port_update received: %s", kwargs)
port = kwargs.get('port')
# Validate that port is on OVS
vif_port = self.agent.int_br.get_vif_port_by_id(port['id'])
if not vif_port:
return
if ext_sg.SECURITYGROUPS in port:
self.sg_agent.refresh_firewall()
class SecurityGroupAgentRpcCallback(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
target = oslo_messaging.Target(version=sg_rpc.SG_RPC_VERSION)
def __init__(self, context, sg_agent):
super(SecurityGroupAgentRpcCallback, self).__init__()
self.context = context
self.sg_agent = sg_agent
class NECNeutronAgent(object):
def __init__(self, integ_br, polling_interval):
'''Constructor.
:param integ_br: name of the integration bridge.
:param polling_interval: interval (secs) to check the bridge.
'''
self.int_br = ovs_lib.OVSBridge(integ_br)
self.polling_interval = polling_interval
self.cur_ports = []
self.need_sync = True
self.datapath_id = "0x%s" % self.int_br.get_datapath_id()
self.agent_state = {
'binary': 'neutron-nec-agent',
'host': config.CONF.host,
'topic': q_const.L2_AGENT_TOPIC,
'configurations': {},
'agent_type': q_const.AGENT_TYPE_NEC,
'start_flag': True}
self.setup_rpc()
def setup_rpc(self):
self.host = socket.gethostname()
self.agent_id = 'nec-q-agent.%s' % self.host
LOG.info(_LI("RPC agent_id: %s"), self.agent_id)
self.topic = topics.AGENT
self.context = q_context.get_admin_context_without_session()
self.plugin_rpc = NECPluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN)
self.sg_agent = sg_rpc.SecurityGroupAgentRpc(self.context,
self.sg_plugin_rpc)
# RPC network init
# Handle updates from service
self.callback_nec = NECAgentRpcCallback(self.context,
self, self.sg_agent)
self.callback_sg = SecurityGroupAgentRpcCallback(self.context,
self.sg_agent)
self.endpoints = [self.callback_nec, self.callback_sg]
# Define the listening consumer for the agent
consumers = [[topics.PORT, topics.UPDATE],
[topics.SECURITY_GROUP, topics.UPDATE]]
self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic,
consumers)
report_interval = config.CONF.AGENT.report_interval
if report_interval:
heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
heartbeat.start(interval=report_interval)
def _report_state(self):
try:
# How many devices are likely used by a VM
num_devices = len(self.cur_ports)
self.agent_state['configurations']['devices'] = num_devices
self.state_rpc.report_state(self.context,
self.agent_state)
self.agent_state.pop('start_flag', None)
except Exception:
LOG.exception(_LE("Failed reporting state!"))
def _vif_port_to_port_info(self, vif_port):
return dict(id=vif_port.vif_id, port_no=vif_port.ofport,
mac=vif_port.vif_mac)
def _process_security_group(self, port_added, port_removed):
if port_added:
devices_added = [p['id'] for p in port_added]
self.sg_agent.prepare_devices_filter(devices_added)
if port_removed:
self.sg_agent.remove_devices_filter(port_removed)
def loop_handler(self):
try:
# self.cur_ports will be kept until loop_handler succeeds.
cur_ports = [] if self.need_sync else self.cur_ports
new_ports = []
port_added = []
for vif_port in self.int_br.get_vif_ports():
port_id = vif_port.vif_id
new_ports.append(port_id)
if port_id not in cur_ports:
port_info = self._vif_port_to_port_info(vif_port)
port_added.append(port_info)
port_removed = []
for port_id in cur_ports:
if port_id not in new_ports:
port_removed.append(port_id)
if port_added or port_removed:
self.plugin_rpc.update_ports(self.context,
self.agent_id, self.datapath_id,
port_added, port_removed)
self._process_security_group(port_added, port_removed)
else:
LOG.debug("No port changed.")
self.cur_ports = new_ports
self.need_sync = False
except Exception:
LOG.exception(_LE("Error in agent event loop"))
self.need_sync = True
def daemon_loop(self):
"""Main processing loop for NEC Plugin Agent."""
while True:
self.loop_handler()
time.sleep(self.polling_interval)
def main():
common_config.init(sys.argv[1:])
common_config.setup_logging()
# Determine which agent type to use.
integ_br = config.OVS.integration_bridge
polling_interval = config.AGENT.polling_interval
agent = NECNeutronAgent(integ_br, polling_interval)
# Start everything.
agent.daemon_loop()
if __name__ == "__main__":
main()

View File

@ -1,22 +0,0 @@
# Copyright 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.
ROUTER_PROVIDER_L3AGENT = 'l3-agent'
ROUTER_PROVIDER_OPENFLOW = 'openflow'
DEFAULT_ROUTER_PROVIDERS = [ROUTER_PROVIDER_L3AGENT, ROUTER_PROVIDER_OPENFLOW]
DEFAULT_ROUTER_PROVIDER = ROUTER_PROVIDER_L3AGENT
ROUTER_STATUS_ACTIVE = 'ACTIVE'
ROUTER_STATUS_ERROR = 'ERROR'

View File

@ -1,82 +0,0 @@
# 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.
from neutron.common import exceptions as qexc
class OFCException(qexc.NeutronException):
message = _("An OFC exception has occurred: %(reason)s")
def __init__(self, **kwargs):
super(OFCException, self).__init__(**kwargs)
self.status = kwargs.get('status')
self.err_msg = kwargs.get('err_msg')
self.err_code = kwargs.get('err_code')
class OFCResourceNotFound(qexc.NotFound):
message = _("The specified OFC resource (%(resource)s) is not found.")
class NECDBException(qexc.NeutronException):
message = _("An exception occurred in NECPluginV2 DB: %(reason)s")
class OFCMappingNotFound(qexc.NotFound):
message = _("Neutron-OFC resource mapping for "
"%(resource)s %(neutron_id)s is not found. "
"It may be deleted during processing.")
class OFCServiceUnavailable(OFCException):
message = _("OFC returns Server Unavailable (503) "
"(Retry-After=%(retry_after)s)")
def __init__(self, **kwargs):
super(OFCServiceUnavailable, self).__init__(**kwargs)
self.retry_after = kwargs.get('retry_after')
class PortInfoNotFound(qexc.NotFound):
message = _("PortInfo %(id)s could not be found")
class ProfilePortInfoInvalidDataPathId(qexc.InvalidInput):
message = _('Invalid input for operation: '
'datapath_id should be a hex string '
'with at most 8 bytes')
class ProfilePortInfoInvalidPortNo(qexc.InvalidInput):
message = _('Invalid input for operation: '
'port_no should be [0:65535]')
class RouterExternalGatewayNotSupported(qexc.BadRequest):
message = _("Router (provider=%(provider)s) does not support "
"an external network")
class ProviderNotFound(qexc.NotFound):
message = _("Provider %(provider)s could not be found")
class RouterOverLimit(qexc.Conflict):
message = _("Cannot create more routers with provider=%(provider)s")
class RouterProviderMismatch(qexc.Conflict):
message = _("Provider of Router %(router_id)s is %(provider)s. "
"This operation is supported only for router provider "
"%(expected_provider)s.")

View File

@ -1,156 +0,0 @@
# 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.
import time
from oslo_serialization import jsonutils
from oslo_utils import excutils
import requests
from neutron.i18n import _LI, _LW
from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import exceptions as nexc
LOG = logging.getLogger(__name__)
class OFCClient(object):
"""A HTTP/HTTPS client for OFC Drivers."""
def __init__(self, host="127.0.0.1", port=8888, use_ssl=False,
key_file=None, cert_file=None, insecure_ssl=False):
"""Creates a new client to some OFC.
:param host: The host where service resides
:param port: The port where service resides
:param use_ssl: True to use SSL, False to use HTTP
:param key_file: The SSL key file to use if use_ssl is true
:param cert_file: The SSL cert file to use if use_ssl is true
:param insecure_ssl: Don't verify SSL certificate
"""
self.host = host
self.port = port
self.use_ssl = use_ssl
self.key_file = key_file
self.cert_file = cert_file
self.insecure_ssl = insecure_ssl
self.connection = None
def _format_error_message(self, status, detail):
detail = ' ' + detail if detail else ''
return (_("Operation on OFC failed: %(status)s%(msg)s") %
{'status': status, 'msg': detail})
def _get_response(self, method, action, body=None):
headers = {"Content-Type": "application/json"}
protocol = "http"
certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
verify = True
if self.use_ssl:
protocol = "https"
if self.insecure_ssl:
verify = False
url = "%s://%s:%d%s" % (protocol, self.host, int(self.port),
action)
res = requests.request(method, url, data=body, headers=headers,
cert=certs, verify=verify)
return res
def do_single_request(self, method, action, body=None):
action = config.OFC.path_prefix + action
LOG.debug("Client request: %(host)s:%(port)s "
"%(method)s %(action)s [%(body)s]",
{'host': self.host, 'port': self.port,
'method': method, 'action': action, 'body': body})
if type(body) is dict:
body = jsonutils.dumps(body)
try:
res = self._get_response(method, action, body)
data = res.text
LOG.debug("OFC returns [%(status)s:%(data)s]",
{'status': res.status_code,
'data': data})
# Try to decode JSON data if possible.
try:
data = jsonutils.loads(data)
except (ValueError, TypeError):
pass
if res.status_code in (requests.codes.OK,
requests.codes.CREATED,
requests.codes.ACCEPTED,
requests.codes.NO_CONTENT):
return data
elif res.status_code == requests.codes.SERVICE_UNAVAILABLE:
retry_after = res.headers.get('retry-after')
LOG.warning(_LW("OFC returns ServiceUnavailable "
"(retry-after=%s)"), retry_after)
raise nexc.OFCServiceUnavailable(retry_after=retry_after)
elif res.status_code == requests.codes.NOT_FOUND:
LOG.info(_LI("Specified resource %s does not exist on OFC "),
action)
raise nexc.OFCResourceNotFound(resource=action)
else:
LOG.warning(_LW("Operation on OFC failed: "
"status=%(status)s, detail=%(detail)s"),
{'status': res.status_code, 'detail': data})
params = {'reason': _("Operation on OFC failed"),
'status': res.status_code}
if isinstance(data, dict):
params['err_code'] = data.get('err_code')
params['err_msg'] = data.get('err_msg')
else:
params['err_msg'] = data
raise nexc.OFCException(**params)
except requests.exceptions.RequestException as e:
reason = _("Failed to connect OFC : %s") % e
LOG.error(reason)
raise nexc.OFCException(reason=reason)
def do_request(self, method, action, body=None):
max_attempts = config.OFC.api_max_attempts
for i in range(max_attempts, 0, -1):
try:
return self.do_single_request(method, action, body)
except nexc.OFCServiceUnavailable as e:
with excutils.save_and_reraise_exception() as ctxt:
try:
wait_time = int(e.retry_after)
except (ValueError, TypeError):
wait_time = None
if i > 1 and wait_time:
LOG.info(_LI("Waiting for %s seconds due to "
"OFC Service_Unavailable."), wait_time)
time.sleep(wait_time)
ctxt.reraise = False
continue
def get(self, action):
return self.do_request("GET", action)
def post(self, action, body=None):
return self.do_request("POST", action, body=body)
def put(self, action, body=None):
return self.do_request("PUT", action, body=body)
def delete(self, action):
return self.do_request("DELETE", action)

View File

@ -15,7 +15,6 @@
from oslo_config import cfg
from neutron.agent.common import config
from neutron.plugins.nec.common import constants as nconst
ovs_opts = [
@ -60,23 +59,20 @@ ofc_opts = [
provider_opts = [
cfg.StrOpt('default_router_provider',
default=nconst.DEFAULT_ROUTER_PROVIDER,
default='l3-agent',
help=_('Default router provider to use.')),
cfg.ListOpt('router_providers',
default=nconst.DEFAULT_ROUTER_PROVIDERS,
default=['l3-agent', 'openflow'],
help=_('List of enabled router providers.'))
]
cfg.CONF.register_opts(ovs_opts, "OVS")
cfg.CONF.register_opts(agent_opts, "AGENT")
cfg.CONF.register_opts(ofc_opts, "OFC")
cfg.CONF.register_opts(provider_opts, "PROVIDER")
config.register_agent_state_opts_helper(cfg.CONF)
def register_plugin_opts():
cfg.CONF.register_opts(ofc_opts, "OFC")
cfg.CONF.register_opts(provider_opts, "PROVIDER")
# shortcuts
CONF = cfg.CONF
OVS = cfg.CONF.OVS
AGENT = cfg.CONF.AGENT
OFC = cfg.CONF.OFC
PROVIDER = cfg.CONF.PROVIDER
def register_agent_opts():
cfg.CONF.register_opts(agent_opts, "AGENT")
cfg.CONF.register_opts(ovs_opts, "OVS")
config.register_agent_state_opts_helper(cfg.CONF)

View File

@ -1,179 +0,0 @@
# 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.
import sqlalchemy as sa
from neutron.db import api as db
from neutron.db import models_v2
from neutron.db import securitygroups_db as sg_db
from neutron.extensions import securitygroup as ext_sg
from neutron.i18n import _LW
from neutron import manager
from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import config # noqa
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import models as nmodels
LOG = logging.getLogger(__name__)
OFP_VLAN_NONE = 0xffff
resource_map = {'ofc_tenant': nmodels.OFCTenantMapping,
'ofc_network': nmodels.OFCNetworkMapping,
'ofc_port': nmodels.OFCPortMapping,
'ofc_router': nmodels.OFCRouterMapping,
'ofc_packet_filter': nmodels.OFCFilterMapping}
# utitlity methods
def _get_resource_model(resource):
return resource_map[resource]
def get_ofc_item(session, resource, neutron_id):
model = _get_resource_model(resource)
if not model:
return
try:
return session.query(model).filter_by(neutron_id=neutron_id).one()
except sa.orm.exc.NoResultFound:
return
def get_ofc_id(session, resource, neutron_id):
ofc_item = get_ofc_item(session, resource, neutron_id)
if ofc_item:
return ofc_item.ofc_id
else:
raise nexc.OFCMappingNotFound(resource=resource,
neutron_id=neutron_id)
def exists_ofc_item(session, resource, neutron_id):
if get_ofc_item(session, resource, neutron_id):
return True
else:
return False
def find_ofc_item(session, resource, ofc_id):
try:
model = _get_resource_model(resource)
params = dict(ofc_id=ofc_id)
return (session.query(model).filter_by(**params).one())
except sa.orm.exc.NoResultFound:
return None
def add_ofc_item(session, resource, neutron_id, ofc_id):
try:
model = _get_resource_model(resource)
params = dict(neutron_id=neutron_id, ofc_id=ofc_id)
item = model(**params)
with session.begin(subtransactions=True):
session.add(item)
session.flush()
except Exception as exc:
LOG.exception(exc)
raise nexc.NECDBException(reason=exc.message)
return item
def del_ofc_item(session, resource, neutron_id):
try:
model = _get_resource_model(resource)
with session.begin(subtransactions=True):
item = session.query(model).filter_by(neutron_id=neutron_id).one()
session.delete(item)
return True
except sa.orm.exc.NoResultFound:
LOG.warning(_LW("del_ofc_item(): NotFound item "
"(resource=%(resource)s, id=%(id)s) "),
{'resource': resource, 'id': neutron_id})
return False
def get_portinfo(session, id):
try:
return (session.query(nmodels.PortInfo).
filter_by(id=id).
one())
except sa.orm.exc.NoResultFound:
return None
def add_portinfo(session, id, datapath_id='', port_no=0,
vlan_id=OFP_VLAN_NONE, mac=''):
try:
portinfo = nmodels.PortInfo(id=id, datapath_id=datapath_id,
port_no=port_no, vlan_id=vlan_id, mac=mac)
with session.begin(subtransactions=True):
session.add(portinfo)
except Exception as exc:
LOG.exception(exc)
raise nexc.NECDBException(reason=exc.message)
return portinfo
def del_portinfo(session, id):
try:
with session.begin(subtransactions=True):
portinfo = session.query(nmodels.PortInfo).filter_by(id=id).one()
session.delete(portinfo)
except sa.orm.exc.NoResultFound:
LOG.warning(_LW("del_portinfo(): NotFound portinfo for "
"port_id: %s"), id)
def get_active_ports_on_ofc(context, network_id, port_id=None):
"""Retrieve ports on OFC on a given network.
It returns a list of tuple (neutron port_id, OFC id).
"""
query = context.session.query(nmodels.OFCPortMapping)
query = query.join(models_v2.Port,
nmodels.OFCPortMapping.neutron_id == models_v2.Port.id)
query = query.filter(models_v2.Port.network_id == network_id)
if port_id:
query = query.filter(nmodels.OFCPortMapping.neutron_id == port_id)
return [(p['neutron_id'], p['ofc_id']) for p in query]
def get_port_from_device(port_id):
"""Get port from database."""
LOG.debug("get_port_with_securitygroups() called:port_id=%s", port_id)
session = db.get_session()
sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
query = session.query(models_v2.Port,
sg_db.SecurityGroupPortBinding.security_group_id)
query = query.outerjoin(sg_db.SecurityGroupPortBinding,
models_v2.Port.id == sg_binding_port)
query = query.filter(models_v2.Port.id == port_id)
port_and_sgs = query.all()
if not port_and_sgs:
return None
port = port_and_sgs[0][0]
plugin = manager.NeutronManager.get_plugin()
port_dict = plugin._make_port_dict(port)
port_dict[ext_sg.SECURITYGROUPS] = [
sg_id for port_, sg_id in port_and_sgs if sg_id]
port_dict['security_group_rules'] = []
port_dict['security_group_source_groups'] = []
port_dict['fixed_ips'] = [ip['ip_address']
for ip in port['fixed_ips']]
return port_dict

View File

@ -1,181 +0,0 @@
# 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.
from sqlalchemy.orm import exc as sa_exc
from sqlalchemy import sql
from neutron.api.v2 import attributes
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.db import models as nmodels
from neutron.plugins.nec.extensions import packetfilter as ext_pf
PF_STATUS_ACTIVE = 'ACTIVE'
PF_STATUS_DOWN = 'DOWN'
PF_STATUS_ERROR = 'ERROR'
INT_FIELDS = ('eth_type', 'src_port', 'dst_port')
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'],
# "or None" ensure the filed is None if empty
'src_mac': pf_entry['src_mac'] or None,
'dst_mac': pf_entry['dst_mac'] or None,
'eth_type': pf_entry['eth_type'] or None,
'src_cidr': pf_entry['src_cidr'] or None,
'dst_cidr': pf_entry['dst_cidr'] or None,
'protocol': pf_entry['protocol'] or None,
'src_port': pf_entry['src_port'] or None,
'dst_port': pf_entry['dst_port'] or None,
'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, nmodels.PacketFilter, id)
except sa_exc.NoResultFound:
raise ext_pf.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,
nmodels.PacketFilter,
self._make_packet_filter_dict,
filters=filters,
fields=fields)
def _replace_unspecified_field(self, params, key):
if not attributes.is_attr_set(params[key]):
if key == 'in_port':
params[key] = None
elif key in INT_FIELDS:
# Integer field
params[key] = 0
else:
params[key] = ''
def _get_eth_type_for_protocol(self, protocol):
if protocol.upper() in ("ICMP", "TCP", "UDP"):
return 0x800
elif protocol.upper() == "ARP":
return 0x806
def _set_eth_type_from_protocol(self, filter_dict):
if filter_dict.get('protocol'):
eth_type = self._get_eth_type_for_protocol(filter_dict['protocol'])
if eth_type:
filter_dict['eth_type'] = eth_type
def _check_eth_type_and_protocol(self, new_filter, current_filter):
if 'protocol' in new_filter or 'eth_type' not in new_filter:
return
eth_type = self._get_eth_type_for_protocol(current_filter['protocol'])
if not eth_type:
return
if eth_type != new_filter['eth_type']:
raise ext_pf.PacketFilterEtherTypeProtocolMismatch(
eth_type=hex(new_filter['eth_type']),
protocol=current_filter['protocol'])
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 in params:
self._replace_unspecified_field(params, key)
self._set_eth_type_from_protocol(params)
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):
params = packet_filter['packet_filter']
for key in params:
self._replace_unspecified_field(params, key)
self._set_eth_type_from_protocol(params)
with context.session.begin(subtransactions=True):
pf_entry = self._get_packet_filter(context, id)
self._check_eth_type_and_protocol(params, pf_entry)
pf_entry.update(params)
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)
def get_packet_filters_for_port(self, context, port):
"""Retrieve packet filters on OFC on a given port.
It returns a list of tuple (neutron filter_id, OFC id).
"""
query = (context.session.query(nmodels.OFCFilterMapping)
.join(nmodels.PacketFilter,
nmodels.OFCFilterMapping.neutron_id
== nmodels.PacketFilter.id)
.filter(nmodels.PacketFilter.admin_state_up == sql.true()))
network_id = port['network_id']
net_pf_query = (query.filter(nmodels.PacketFilter.network_id
== network_id)
.filter(nmodels.PacketFilter.in_port == sql.null()))
net_filters = [(pf['neutron_id'], pf['ofc_id']) for pf in net_pf_query]
port_pf_query = query.filter(nmodels.PacketFilter.in_port
== port['id'])
port_filters = [(pf['neutron_id'], pf['ofc_id'])
for pf in port_pf_query]
return net_filters + port_filters

View File

@ -1,75 +0,0 @@
# Copyright 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.
from sqlalchemy.orm import exc as sa_exc
from neutron.db import l3_db
from neutron.openstack.common import log as logging
from neutron.plugins.nec.db import models as nmodels
LOG = logging.getLogger(__name__)
def _get_router_providers_query(query, provider=None, router_ids=None):
if provider:
query = query.filter_by(provider=provider)
if router_ids:
column = nmodels.RouterProvider.router_id
query = query.filter(column.in_(router_ids))
return query
def get_router_providers(session, provider=None, router_ids=None):
"""Retrieve a list of a pair of router ID and its provider."""
query = session.query(nmodels.RouterProvider)
query = _get_router_providers_query(query, provider, router_ids)
return [{'provider': router.provider, 'router_id': router.router_id}
for router in query]
def get_routers_by_provider(session, provider, router_ids=None):
"""Retrieve a list of router IDs with the given provider."""
query = session.query(nmodels.RouterProvider.router_id)
query = _get_router_providers_query(query, provider, router_ids)
return [router[0] for router in query]
def get_router_count_by_provider(session, provider, tenant_id=None):
"""Return the number of routers with the given provider."""
query = session.query(nmodels.RouterProvider).filter_by(provider=provider)
if tenant_id:
query = (query.join('router').
filter(l3_db.Router.tenant_id == tenant_id))
return query.count()
def get_provider_by_router(session, router_id):
"""Retrieve a provider of the given router."""
try:
binding = (session.query(nmodels.RouterProvider).
filter_by(router_id=router_id).
one())
except sa_exc.NoResultFound:
return None
return binding.provider
def add_router_provider_binding(session, provider, router_id):
"""Add a router provider association."""
LOG.debug("Add provider binding "
"(router=%(router_id)s, provider=%(provider)s)",
{'router_id': router_id, 'provider': provider})
binding = nmodels.RouterProvider(provider=provider, router_id=router_id)
session.add(binding)
return binding

View File

@ -1,39 +0,0 @@
# 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.
from oslo_utils import importutils
from neutron.i18n import _LI
from neutron.openstack.common import log as logging
LOG = logging.getLogger(__name__)
DRIVER_PATH = "neutron.plugins.nec.drivers.%s"
DRIVER_LIST = {
'trema': DRIVER_PATH % "trema.TremaPortBaseDriver",
'trema_port': DRIVER_PATH % "trema.TremaPortBaseDriver",
'trema_portmac': DRIVER_PATH % "trema.TremaPortMACBaseDriver",
'trema_mac': DRIVER_PATH % "trema.TremaMACBaseDriver",
'pfc': DRIVER_PATH % "pfc.PFCV51Driver",
'pfc_v3': DRIVER_PATH % "pfc.PFCV3Driver",
'pfc_v4': DRIVER_PATH % "pfc.PFCV4Driver",
'pfc_v5': DRIVER_PATH % "pfc.PFCV5Driver",
'pfc_v51': DRIVER_PATH % "pfc.PFCV51Driver",
}
def get_driver(driver_name):
LOG.info(_LI("Loading OFC driver: %s"), driver_name)
driver_klass = DRIVER_LIST.get(driver_name) or driver_name
return importutils.import_class(driver_klass)

View File

@ -1,399 +0,0 @@
# 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.
import re
import uuid
import netaddr
from neutron.api.v2 import attributes
from neutron.common import constants
from neutron.common import exceptions as qexc
from neutron.common import log as call_log
from neutron import manager
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import ofc_client
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec.extensions import packetfilter as ext_pf
from neutron.plugins.nec import ofc_driver_base
class InvalidOFCIdFormat(qexc.NeutronException):
message = _("OFC %(resource)s ID has an invalid format: %(ofc_id)s")
class PFCDriverBase(ofc_driver_base.OFCDriverBase):
"""Base Class for PDC Drivers.
PFCDriverBase provides methods to handle PFC resources through REST API.
This uses ofc resource path instead of ofc resource ID.
The class implements the API for PFC V4.0 or later.
"""
router_supported = False
match_ofc_network_id = re.compile(
"^/tenants/(?P<tenant_id>[^/]+)/networks/(?P<network_id>[^/]+)$")
match_ofc_port_id = re.compile(
"^/tenants/(?P<tenant_id>[^/]+)/networks/(?P<network_id>[^/]+)"
"/ports/(?P<port_id>[^/]+)$")
def __init__(self, conf_ofc):
self.client = ofc_client.OFCClient(host=conf_ofc.host,
port=conf_ofc.port,
use_ssl=conf_ofc.use_ssl,
key_file=conf_ofc.key_file,
cert_file=conf_ofc.cert_file,
insecure_ssl=conf_ofc.insecure_ssl)
@classmethod
def filter_supported(cls):
return False
def _generate_pfc_str(self, raw_str):
"""Generate PFC acceptable String."""
return re.sub(r'[^0-9a-zA-Z]', '_', raw_str)
def _generate_pfc_id(self, id_str):
"""Generate ID on PFC.
Currently, PFC ID must be less than 32.
Shorten UUID string length from 36 to 31 by follows:
* delete UUID Version and hyphen (see RFC4122)
* ensure str length
"""
try:
# openstack.common.uuidutils.is_uuid_like() returns
# False for KeyStone tenant_id, so uuid.UUID is used
# directly here to accept tenant_id as UUID string
uuid_str = str(uuid.UUID(id_str)).replace('-', '')
uuid_no_version = uuid_str[:12] + uuid_str[13:]
return uuid_no_version[:31]
except Exception:
return self._generate_pfc_str(id_str)[:31]
def _generate_pfc_description(self, desc):
"""Generate Description on PFC.
Currently, PFC Description must be less than 128.
"""
return self._generate_pfc_str(desc)[:127]
def _extract_ofc_network_id(self, ofc_network_id):
match = self.match_ofc_network_id.match(ofc_network_id)
if match:
return match.group('network_id')
raise InvalidOFCIdFormat(resource='network', ofc_id=ofc_network_id)
def _extract_ofc_port_id(self, ofc_port_id):
match = self.match_ofc_port_id.match(ofc_port_id)
if match:
return {'tenant': match.group('tenant_id'),
'network': match.group('network_id'),
'port': match.group('port_id')}
raise InvalidOFCIdFormat(resource='port', ofc_id=ofc_port_id)
def create_tenant(self, description, tenant_id=None):
ofc_tenant_id = self._generate_pfc_id(tenant_id)
body = {'id': ofc_tenant_id}
self.client.post('/tenants', body=body)
return '/tenants/' + ofc_tenant_id
def delete_tenant(self, ofc_tenant_id):
return self.client.delete(ofc_tenant_id)
def create_network(self, ofc_tenant_id, description, network_id=None):
path = "%s/networks" % ofc_tenant_id
pfc_desc = self._generate_pfc_description(description)
body = {'description': pfc_desc}
res = self.client.post(path, body=body)
ofc_network_id = res['id']
return path + '/' + ofc_network_id
def delete_network(self, ofc_network_id):
return self.client.delete(ofc_network_id)
def create_port(self, ofc_network_id, portinfo,
port_id=None, filters=None):
path = "%s/ports" % ofc_network_id
body = {'datapath_id': portinfo.datapath_id,
'port': str(portinfo.port_no),
'vid': str(portinfo.vlan_id)}
if self.filter_supported() and filters:
body['filters'] = [self._extract_ofc_filter_id(pf[1])
for pf in filters]
res = self.client.post(path, body=body)
ofc_port_id = res['id']
return path + '/' + ofc_port_id
def delete_port(self, ofc_port_id):
return self.client.delete(ofc_port_id)
class PFCFilterDriverMixin(object):
"""PFC PacketFilter Driver Mixin."""
filters_path = "/filters"
filter_path = "/filters/%s"
# PFC specific constants
MIN_PRIORITY = 1
MAX_PRIORITY = 32766
CREATE_ONLY_FIELDS = ['action', 'priority']
PFC_ALLOW_ACTION = "pass"
PFC_DROP_ACTION = "drop"
match_ofc_filter_id = re.compile("^/filters/(?P<filter_id>[^/]+)$")
@classmethod
def filter_supported(cls):
return True
def _set_param(self, filter_dict, body, key, create, convert_to=None):
if key in filter_dict:
if filter_dict[key]:
if convert_to:
body[key] = convert_to(filter_dict[key])
else:
body[key] = filter_dict[key]
elif not create:
body[key] = ""
def _extract_ofc_filter_port_id(self, ofc_port_id):
"""Return ofc port id description for packet filter.
It returns either of the following format:
{'tenant': xxxx, 'network': xxxx, 'port': xxxx} or
{'tenant': xxxx, 'router': xxxx, 'interface': xxxx}
If no matching ofc id is found, InvalidOFCIdFormat is raised.
"""
if config.OFC.support_packet_filter_on_ofc_router:
try:
return self._extract_ofc_router_inf_id(ofc_port_id)
except InvalidOFCIdFormat:
pass
return self._extract_ofc_port_id(ofc_port_id)
def _generate_body(self, filter_dict, apply_ports=None, create=True):
body = {}
if create:
# action : pass, drop (mandatory)
if filter_dict['action'].lower() in ext_pf.ALLOW_ACTIONS:
body['action'] = self.PFC_ALLOW_ACTION
else:
body['action'] = self.PFC_DROP_ACTION
# priority : mandatory
body['priority'] = filter_dict['priority']
for key in ['src_mac', 'dst_mac', 'src_port', 'dst_port']:
self._set_param(filter_dict, body, key, create)
for key in ['src_cidr', 'dst_cidr']:
# CIDR must contain netmask even if it is an address.
convert_to = lambda x: str(netaddr.IPNetwork(x))
self._set_param(filter_dict, body, key, create, convert_to)
# protocol : decimal (0-255)
if 'protocol' in filter_dict:
if (not filter_dict['protocol'] or
# In the case of ARP, ip_proto should be set to wildcard.
# eth_type is set during adding an entry to DB layer.
filter_dict['protocol'].lower() == ext_pf.PROTO_NAME_ARP):
if not create:
body['protocol'] = ""
elif filter_dict['protocol'].lower() == constants.PROTO_NAME_ICMP:
body['protocol'] = constants.PROTO_NUM_ICMP
elif filter_dict['protocol'].lower() == constants.PROTO_NAME_TCP:
body['protocol'] = constants.PROTO_NUM_TCP
elif filter_dict['protocol'].lower() == constants.PROTO_NAME_UDP:
body['protocol'] = constants.PROTO_NUM_UDP
else:
body['protocol'] = int(filter_dict['protocol'], 0)
# eth_type : hex (0x0-0xFFFF)
self._set_param(filter_dict, body, 'eth_type', create, hex)
# apply_ports
if apply_ports:
# each element of apply_ports is a tuple of (neutron_id, ofc_id),
body['apply_ports'] = []
for p in apply_ports:
try:
_ofc_id = self._extract_ofc_filter_port_id(p[1])
body['apply_ports'].append(_ofc_id)
except InvalidOFCIdFormat:
pass
return body
def _validate_filter_common(self, filter_dict):
# Currently PFC support only IPv4 CIDR.
for field in ['src_cidr', 'dst_cidr']:
if (not filter_dict.get(field) or
filter_dict[field] == attributes.ATTR_NOT_SPECIFIED):
continue
net = netaddr.IPNetwork(filter_dict[field])
if net.version != 4:
raise ext_pf.PacketFilterIpVersionNonSupported(
version=net.version, field=field, value=filter_dict[field])
if ('priority' in filter_dict and
not (self.MIN_PRIORITY <= filter_dict['priority']
<= self.MAX_PRIORITY)):
raise ext_pf.PacketFilterInvalidPriority(
min=self.MIN_PRIORITY, max=self.MAX_PRIORITY)
def _validate_duplicate_priority(self, context, filter_dict):
plugin = manager.NeutronManager.get_plugin()
filters = {'network_id': [filter_dict['network_id']],
'priority': [filter_dict['priority']]}
ret = plugin.get_packet_filters(context, filters=filters,
fields=['id'])
if ret:
raise ext_pf.PacketFilterDuplicatedPriority(
priority=filter_dict['priority'])
def validate_filter_create(self, context, filter_dict):
self._validate_filter_common(filter_dict)
self._validate_duplicate_priority(context, filter_dict)
def validate_filter_update(self, context, filter_dict):
for field in self.CREATE_ONLY_FIELDS:
if field in filter_dict:
raise ext_pf.PacketFilterUpdateNotSupported(field=field)
self._validate_filter_common(filter_dict)
@call_log.log
def create_filter(self, context, filter_dict, filter_id=None):
in_port_id = filter_dict.get('in_port')
apply_ports = ndb.get_active_ports_on_ofc(
context, filter_dict['network_id'], in_port_id)
body = self._generate_body(filter_dict, apply_ports, create=True)
res = self.client.post(self.filters_path, body=body)
# filter_id passed from a caller is not used.
# ofc_filter_id is generated by PFC because the prefix of
# filter_id has special meaning and it is internally used.
ofc_filter_id = res['id']
return self.filter_path % ofc_filter_id
@call_log.log
def update_filter(self, ofc_filter_id, filter_dict):
body = self._generate_body(filter_dict, create=False)
self.client.put(ofc_filter_id, body)
@call_log.log
def delete_filter(self, ofc_filter_id):
return self.client.delete(ofc_filter_id)
def _extract_ofc_filter_id(self, ofc_filter_id):
match = self.match_ofc_filter_id.match(ofc_filter_id)
if match:
return match.group('filter_id')
raise InvalidOFCIdFormat(resource='filter', ofc_id=ofc_filter_id)
class PFCRouterDriverMixin(object):
router_supported = True
router_nat_supported = False
match_ofc_router_inf_id = re.compile(
"^/tenants/(?P<tenant_id>[^/]+)/routers/(?P<router_id>[^/]+)"
"/interfaces/(?P<router_inf_id>[^/]+)$")
def _extract_ofc_router_inf_id(self, ofc_router_inf_id):
match = self.match_ofc_router_inf_id.match(ofc_router_inf_id)
if match:
return {'tenant': match.group('tenant_id'),
'router': match.group('router_id'),
'interface': match.group('router_inf_id')}
raise InvalidOFCIdFormat(resource='router-interface',
ofc_id=ofc_router_inf_id)
def create_router(self, ofc_tenant_id, router_id, description):
path = '%s/routers' % ofc_tenant_id
res = self.client.post(path, body=None)
ofc_router_id = res['id']
return path + '/' + ofc_router_id
def delete_router(self, ofc_router_id):
return self.client.delete(ofc_router_id)
def add_router_interface(self, ofc_router_id, ofc_net_id,
ip_address=None, mac_address=None):
# ip_address : <ip_address>/<netmask> (e.g., 10.0.0.0/24)
path = '%s/interfaces' % ofc_router_id
body = {'net_id': self._extract_ofc_network_id(ofc_net_id)}
if ip_address:
body['ip_address'] = ip_address
if mac_address:
body['mac_address'] = mac_address
res = self.client.post(path, body=body)
return path + '/' + res['id']
def update_router_interface(self, ofc_router_inf_id,
ip_address=None, mac_address=None):
# ip_address : <ip_address>/<netmask> (e.g., 10.0.0.0/24)
if not ip_address and not mac_address:
return
body = {}
if ip_address:
body['ip_address'] = ip_address
if mac_address:
body['mac_address'] = mac_address
return self.client.put(ofc_router_inf_id, body=body)
def delete_router_interface(self, ofc_router_inf_id):
return self.client.delete(ofc_router_inf_id)
def list_router_routes(self, ofc_router_id):
path = '%s/routes' % ofc_router_id
ret = self.client.get(path)
# Prepend ofc_router_id to route_id
for r in ret['routes']:
r['id'] = ofc_router_id + '/routes/' + r['id']
return ret['routes']
def add_router_route(self, ofc_router_id, destination, nexthop):
path = '%s/routes' % ofc_router_id
body = {'destination': destination,
'nexthop': nexthop}
ret = self.client.post(path, body=body)
return path + '/' + ret['id']
def delete_router_route(self, ofc_router_route_id):
return self.client.delete(ofc_router_route_id)
class PFCV3Driver(PFCDriverBase):
def create_tenant(self, description, tenant_id):
ofc_tenant_id = self._generate_pfc_id(tenant_id)
return "/tenants/" + ofc_tenant_id
def delete_tenant(self, ofc_tenant_id):
pass
class PFCV4Driver(PFCDriverBase):
pass
class PFCV5Driver(PFCRouterDriverMixin, PFCDriverBase):
pass
class PFCV51Driver(PFCFilterDriverMixin, PFCV5Driver):
pass

View File

@ -1,259 +0,0 @@
# 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.
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.common import ofc_client
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec import ofc_driver_base
class TremaDriverBase(ofc_driver_base.OFCDriverBase):
"""Common class for Trema (Sliceable Switch) Drivers."""
networks_path = "/networks"
network_path = "/networks/%s"
router_supported = False
def __init__(self, conf_ofc):
# Trema sliceable REST API does not support HTTPS
self.client = ofc_client.OFCClient(host=conf_ofc.host,
port=conf_ofc.port)
def _get_network_id(self, ofc_network_id):
# ofc_network_id : /networks/<network-id>
return ofc_network_id.split('/')[2]
def _get_tenant_id(self, tenant_id):
# Trema does not use tenant_id, but it returns
# /tenants/<tenant_id> format to keep consistency with PFC driver.
return '/tenants/' + tenant_id
def create_tenant(self, description, tenant_id=None):
return self._get_tenant_id(tenant_id or uuidutils.generate_uuid())
def update_tenant(self, ofc_tenant_id, description):
pass
def delete_tenant(self, ofc_tenant_id):
pass
def create_network(self, ofc_tenant_id, description, network_id=None):
ofc_network_id = network_id or uuidutils.generate_uuid()
body = {'id': ofc_network_id, 'description': description}
self.client.post(self.networks_path, body=body)
return self.network_path % ofc_network_id
def delete_network(self, ofc_network_id):
return self.client.delete(ofc_network_id)
class TremaFilterDriverMixin(object):
"""Trema (Sliceable Switch) PacketFilter Driver Mixin."""
filters_path = "/filters"
filter_path = "/filters/%s"
@classmethod
def filter_supported(cls):
return True
def create_filter(self, context, filter_dict, filter_id=None):
ofc_network_id = ndb.get_ofc_id(context.session, "ofc_network",
filter_dict['network_id'])
# Prepare portinfo
in_port_id = filter_dict.get('in_port')
if in_port_id:
portinfo = ndb.get_portinfo(context.session, in_port_id)
if not portinfo:
raise nexc.PortInfoNotFound(id=in_port_id)
else:
portinfo = None
# Prepare filter body
if filter_dict['action'].upper() in ["ACCEPT", "ALLOW"]:
ofc_action = "ALLOW"
elif filter_dict['action'].upper() in ["DROP", "DENY"]:
ofc_action = "DENY"
body = {'priority': filter_dict['priority'],
'slice': self._get_network_id(ofc_network_id),
'action': ofc_action}
ofp_wildcards = ["dl_vlan", "dl_vlan_pcp", "nw_tos"]
if portinfo:
body['in_datapath_id'] = portinfo.datapath_id
body['in_port'] = portinfo.port_no
else:
body['wildcards'] = "in_datapath_id"
ofp_wildcards.append("in_port")
if filter_dict['src_mac']:
body['dl_src'] = filter_dict['src_mac']
else:
ofp_wildcards.append("dl_src")
if filter_dict['dst_mac']:
body['dl_dst'] = filter_dict['dst_mac']
else:
ofp_wildcards.append("dl_dst")
if filter_dict['src_cidr']:
body['nw_src'] = filter_dict['src_cidr']
else:
ofp_wildcards.append("nw_src:32")
if filter_dict['dst_cidr']:
body['nw_dst'] = filter_dict['dst_cidr']
else:
ofp_wildcards.append("nw_dst:32")
if filter_dict['protocol']:
if filter_dict['protocol'].upper() == "ICMP":
body['dl_type'] = "0x800"
body['nw_proto'] = hex(1)
elif filter_dict['protocol'].upper() == "TCP":
body['dl_type'] = "0x800"
body['nw_proto'] = hex(6)
elif filter_dict['protocol'].upper() == "UDP":
body['dl_type'] = "0x800"
body['nw_proto'] = hex(17)
elif filter_dict['protocol'].upper() == "ARP":
body['dl_type'] = "0x806"
ofp_wildcards.append("nw_proto")
else:
body['nw_proto'] = filter_dict['protocol']
else:
ofp_wildcards.append("nw_proto")
if 'dl_type' in body:
pass
elif filter_dict['eth_type']:
body['dl_type'] = filter_dict['eth_type']
else:
ofp_wildcards.append("dl_type")
if filter_dict['src_port']:
body['tp_src'] = hex(filter_dict['src_port'])
else:
ofp_wildcards.append("tp_src")
if filter_dict['dst_port']:
body['tp_dst'] = hex(filter_dict['dst_port'])
else:
ofp_wildcards.append("tp_dst")
ofc_filter_id = filter_id or uuidutils.generate_uuid()
body['id'] = ofc_filter_id
body['ofp_wildcards'] = ','.join(ofp_wildcards)
self.client.post(self.filters_path, body=body)
return self.filter_path % ofc_filter_id
def delete_filter(self, ofc_filter_id):
return self.client.delete(ofc_filter_id)
class TremaPortBaseDriver(TremaDriverBase, TremaFilterDriverMixin):
"""Trema (Sliceable Switch) Driver for port base binding.
TremaPortBaseDriver uses port base binding.
Ports are identified by datapath_id, port_no and vlan_id.
"""
ports_path = "%(network)s/ports"
port_path = "%(network)s/ports/%(port)s"
def create_port(self, ofc_network_id, portinfo,
port_id=None, filters=None):
ofc_port_id = port_id or uuidutils.generate_uuid()
path = self.ports_path % {'network': ofc_network_id}
body = {'id': ofc_port_id,
'datapath_id': portinfo.datapath_id,
'port': str(portinfo.port_no),
'vid': str(portinfo.vlan_id)}
self.client.post(path, body=body)
return self.port_path % {'network': ofc_network_id,
'port': ofc_port_id}
def delete_port(self, ofc_port_id):
return self.client.delete(ofc_port_id)
class TremaPortMACBaseDriver(TremaDriverBase, TremaFilterDriverMixin):
"""Trema (Sliceable Switch) Driver for port-mac base binding.
TremaPortBaseDriver uses port-mac base binding.
Ports are identified by datapath_id, port_no, vlan_id and mac.
"""
ports_path = "%(network)s/ports"
port_path = "%(network)s/ports/%(port)s"
attachments_path = "%(network)s/ports/%(port)s/attachments"
attachment_path = "%(network)s/ports/%(port)s/attachments/%(attachment)s"
def create_port(self, ofc_network_id, portinfo, port_id=None,
filters=None):
#NOTE: This Driver create slices with Port-MAC Based bindings on Trema
# Sliceable. It's REST API requires Port Based binding before you
# define Port-MAC Based binding.
ofc_port_id = port_id or uuidutils.generate_uuid()
dummy_port_id = "dummy-%s" % ofc_port_id
path = self.ports_path % {'network': ofc_network_id}
body = {'id': dummy_port_id,
'datapath_id': portinfo.datapath_id,
'port': str(portinfo.port_no),
'vid': str(portinfo.vlan_id)}
self.client.post(path, body=body)
path = self.attachments_path % {'network': ofc_network_id,
'port': dummy_port_id}
body = {'id': ofc_port_id, 'mac': portinfo.mac}
self.client.post(path, body=body)
path = self.port_path % {'network': ofc_network_id,
'port': dummy_port_id}
self.client.delete(path)
return self.attachment_path % {'network': ofc_network_id,
'port': dummy_port_id,
'attachment': ofc_port_id}
def delete_port(self, ofc_port_id):
return self.client.delete(ofc_port_id)
class TremaMACBaseDriver(TremaDriverBase):
"""Trema (Sliceable Switch) Driver for mac base binding.
TremaPortBaseDriver uses mac base binding.
Ports are identified by mac.
"""
attachments_path = "%(network)s/attachments"
attachment_path = "%(network)s/attachments/%(attachment)s"
@classmethod
def filter_supported(cls):
return False
def create_port(self, ofc_network_id, portinfo, port_id=None,
filters=None):
ofc_port_id = port_id or uuidutils.generate_uuid()
path = self.attachments_path % {'network': ofc_network_id}
body = {'id': ofc_port_id, 'mac': portinfo.mac}
self.client.post(path, body=body)
return self.attachment_path % {'network': ofc_network_id,
'attachment': ofc_port_id}
def delete_port(self, ofc_port_id):
return self.client.delete(ofc_port_id)

View File

@ -12,84 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import oslo_messaging
from oslo_utils import excutils
from oslo_utils import importutils
from neutron.agent import securitygroups_rpc as sg_rpc
from neutron.api import extensions as neutron_extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.rpc.handlers import dhcp_rpc
from neutron.api.rpc.handlers import l3_rpc
from neutron.api.rpc.handlers import metadata_rpc
from neutron.api.rpc.handlers import securitygroups_rpc
from neutron.api.v2 import attributes as attrs
from neutron.common import constants as const
from neutron.common import exceptions as n_exc
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron.db import agents_db
from neutron.db import agentschedulers_db
from neutron.db import allowedaddresspairs_db as addr_pair_db
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import portbindings_base
from neutron.db import portbindings_db
from neutron.db import quota_db # noqa
from neutron.db import securitygroups_rpc_base as sg_db_rpc
from neutron.extensions import allowedaddresspairs as addr_pair
from neutron.extensions import portbindings
from neutron.i18n import _LE, _LI, _LW
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants as svc_constants
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.common import utils as necutils
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec.db import router as rdb
from neutron.plugins.nec import extensions
from neutron.plugins.nec import nec_router
from neutron.plugins.nec import ofc_manager
from neutron.plugins.nec import packet_filter
from networking_nec.plugins.openflow import plugin
LOG = logging.getLogger(__name__)
from neutron.plugins.nec import config as nec_config
class SecurityGroupServerRpcMixin(sg_db_rpc.SecurityGroupServerRpcMixin):
class NECPluginV2(plugin.NECPluginV2Impl):
@staticmethod
def get_port_from_device(device):
port = ndb.get_port_from_device(device)
if port:
port['device'] = device
LOG.debug("NECPluginV2.get_port_from_device() called, "
"device=%(device)s => %(ret)s.",
{'device': device, 'ret': port})
return port
class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin,
nec_router.RouterMixin,
SecurityGroupServerRpcMixin,
agentschedulers_db.DhcpAgentSchedulerDbMixin,
nec_router.L3AgentSchedulerDbMixin,
packet_filter.PacketFilterMixin,
portbindings_db.PortBindingMixin,
addr_pair_db.AllowedAddressPairsMixin):
"""NECPluginV2 controls an OpenFlow Controller.
The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks
on an OpenFlow enabled network. An OpenFlow Controller (OFC) provides
L2 network isolation without VLAN and this plugin controls the OFC.
NOTE: This is for Neutron API V2. Codes for V1.0 and V1.1 are available
at https://github.com/nec-openstack/neutron-openflow-plugin .
The port binding extension enables an external application relay
information to and from the plugin.
"""
_supported_extension_aliases = ["agent",
"allowed-address-pairs",
"binding",
@ -109,653 +39,10 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
def supported_extension_aliases(self):
if not hasattr(self, '_aliases'):
aliases = self._supported_extension_aliases[:]
sg_rpc.disable_security_group_extension_by_config(aliases)
self.remove_packet_filter_extension_if_disabled(aliases)
self.setup_extension_aliases(aliases)
self._aliases = aliases
return self._aliases
def __init__(self):
nec_config.register_plugin_opts()
super(NECPluginV2, self).__init__()
self.ofc = ofc_manager.OFCManager(self.safe_reference)
self.base_binding_dict = self._get_base_binding_dict()
portbindings_base.register_port_dict_function()
neutron_extensions.append_api_extensions_path(extensions.__path__)
self.setup_rpc()
self.l3_rpc_notifier = nec_router.L3AgentNotifyAPI()
self.network_scheduler = importutils.import_object(
config.CONF.network_scheduler_driver
)
self.router_scheduler = importutils.import_object(
config.CONF.router_scheduler_driver
)
nec_router.load_driver(self.safe_reference, self.ofc)
self.port_handlers = {
'create': {
const.DEVICE_OWNER_ROUTER_GW: self.create_router_port,
const.DEVICE_OWNER_ROUTER_INTF: self.create_router_port,
'default': self.activate_port_if_ready,
},
'delete': {
const.DEVICE_OWNER_ROUTER_GW: self.delete_router_port,
const.DEVICE_OWNER_ROUTER_INTF: self.delete_router_port,
'default': self.deactivate_port,
}
}
self.start_periodic_dhcp_agent_status_check()
def setup_rpc(self):
self.service_topics = {svc_constants.CORE: topics.PLUGIN,
svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
self.conn = n_rpc.create_connection(new=True)
self.notifier = NECPluginV2AgentNotifierApi(topics.AGENT)
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
)
self.agent_notifiers[const.AGENT_TYPE_L3] = (
nec_router.L3AgentNotifyAPI()
)
# NOTE: callback_sg is referred to from the sg unit test.
self.callback_sg = securitygroups_rpc.SecurityGroupServerRpcCallback()
self.endpoints = [
NECPluginV2RPCCallbacks(self.safe_reference),
dhcp_rpc.DhcpRpcCallback(),
l3_rpc.L3RpcCallback(),
self.callback_sg,
agents_db.AgentExtRpcCallback(),
metadata_rpc.MetadataRpcCallback()]
for svc_topic in self.service_topics.values():
self.conn.create_consumer(svc_topic, self.endpoints, fanout=False)
# Consume from all consumers in threads
self.conn.consume_in_threads()
def _update_resource_status(self, context, resource, id, status):
"""Update status of specified resource."""
request = {'status': status}
obj_getter = getattr(self, '_get_%s' % resource)
with context.session.begin(subtransactions=True):
obj_db = obj_getter(context, id)
obj_db.update(request)
def _update_resource_status_if_changed(self, context, resource_type,
resource_dict, new_status):
if resource_dict['status'] != new_status:
self._update_resource_status(context, resource_type,
resource_dict['id'],
new_status)
resource_dict['status'] = new_status
def _check_ofc_tenant_in_use(self, context, tenant_id):
"""Check if the specified tenant is used."""
# All networks are created on OFC
filters = {'tenant_id': [tenant_id]}
if self.get_networks_count(context, filters=filters):
return True
if rdb.get_router_count_by_provider(context.session,
nec_router.PROVIDER_OPENFLOW,
tenant_id):
return True
return False
def _cleanup_ofc_tenant(self, context, tenant_id):
if not self._check_ofc_tenant_in_use(context, tenant_id):
try:
if self.ofc.exists_ofc_tenant(context, tenant_id):
self.ofc.delete_ofc_tenant(context, tenant_id)
else:
LOG.debug('_cleanup_ofc_tenant: No OFC tenant for %s',
tenant_id)
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
LOG.warn(_LW("delete_ofc_tenant() failed due to %s"), exc)
def activate_port_if_ready(self, context, port, network=None):
"""Activate port by creating port on OFC if ready.
Conditions to activate port on OFC are:
* port admin_state is UP
* network admin_state is UP
* portinfo are available (to identify port on OFC)
"""
if not network:
network = super(NECPluginV2, self).get_network(context,
port['network_id'])
if not port['admin_state_up']:
LOG.debug("activate_port_if_ready(): skip, "
"port.admin_state_up is False.")
return port
elif not network['admin_state_up']:
LOG.debug("activate_port_if_ready(): skip, "
"network.admin_state_up is False.")
return port
elif not ndb.get_portinfo(context.session, port['id']):
LOG.debug("activate_port_if_ready(): skip, "
"no portinfo for this port.")
return port
elif self.ofc.exists_ofc_port(context, port['id']):
LOG.debug("activate_port_if_ready(): skip, "
"ofc_port already exists.")
return port
try:
self.ofc.create_ofc_port(context, port['id'], port)
port_status = const.PORT_STATUS_ACTIVE
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
LOG.error(_LE("create_ofc_port() failed due to %s"), exc)
port_status = const.PORT_STATUS_ERROR
if port_status != port['status']:
self._update_resource_status(context, "port", port['id'],
port_status)
port['status'] = port_status
return port
def deactivate_port(self, context, port, raise_exc=True):
"""Deactivate port by deleting port from OFC if exists."""
if not self.ofc.exists_ofc_port(context, port['id']):
LOG.debug("deactivate_port(): skip, ofc_port for port=%s "
"does not exist.", port['id'])
return port
try:
self.ofc.delete_ofc_port(context, port['id'], port)
self._update_resource_status_if_changed(
context, "port", port, const.PORT_STATUS_DOWN)
return port
except (nexc.OFCResourceNotFound, nexc.OFCMappingNotFound):
# There is a case where multiple delete_port operation are
# running concurrently. For example, delete_port from
# release_dhcp_port and deletion of network owned ports in
# delete_network. In such cases delete_ofc_port may receive
# 404 error from OFC.
# Also there is a case where neutron port is deleted
# between exists_ofc_port and get_ofc_id in delete_ofc_port.
# In this case OFCMappingNotFound is raised.
# These two cases are valid situations.
LOG.info(_LI("deactivate_port(): OFC port for port=%s is "
"already removed."), port['id'])
# The port is already removed, so there is no need
# to update status in the database.
port['status'] = const.PORT_STATUS_DOWN
return port
except nexc.OFCException as exc:
with excutils.save_and_reraise_exception() as ctxt:
LOG.error(_LE("Failed to delete port=%(port)s from OFC: "
"%(exc)s"), {'port': port['id'], 'exc': exc})
self._update_resource_status_if_changed(
context, "port", port, const.PORT_STATUS_ERROR)
if not raise_exc:
ctxt.reraise = False
return port
def _net_status(self, network):
# NOTE: NEC Plugin accept admin_state_up. When it's False, this plugin
# deactivate all ports on the network to drop all packet and show
# status='DOWN' to users. But the network is kept defined on OFC.
if network['network']['admin_state_up']:
return const.NET_STATUS_ACTIVE
else:
return const.NET_STATUS_DOWN
def create_network(self, context, network):
"""Create a new network entry on DB, and create it on OFC."""
LOG.debug("NECPluginV2.create_network() called, "
"network=%s .", network)
tenant_id = self._get_tenant_id_for_create(context, network['network'])
net_name = network['network']['name']
net_id = uuidutils.generate_uuid()
#set up default security groups
self._ensure_default_security_group(context, tenant_id)
network['network']['id'] = net_id
network['network']['status'] = self._net_status(network)
try:
if not self.ofc.exists_ofc_tenant(context, tenant_id):
self.ofc.create_ofc_tenant(context, tenant_id)
self.ofc.create_ofc_network(context, tenant_id, net_id, net_name)
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
LOG.error(_LE("Failed to create network id=%(id)s on "
"OFC: %(exc)s"), {'id': net_id, 'exc': exc})
network['network']['status'] = const.NET_STATUS_ERROR
with context.session.begin(subtransactions=True):
new_net = super(NECPluginV2, self).create_network(context, network)
self._process_l3_create(context, new_net, network['network'])
return new_net
def update_network(self, context, id, network):
"""Update network and handle resources associated with the network.
Update network entry on DB. If 'admin_state_up' was changed, activate
or deactivate ports and packetfilters associated with the network.
"""
LOG.debug("NECPluginV2.update_network() called, "
"id=%(id)s network=%(network)s .",
{'id': id, 'network': network})
if 'admin_state_up' in network['network']:
network['network']['status'] = self._net_status(network)
session = context.session
with session.begin(subtransactions=True):
old_net = super(NECPluginV2, self).get_network(context, id)
new_net = super(NECPluginV2, self).update_network(context, id,
network)
self._process_l3_update(context, new_net, network['network'])
changed = (old_net['admin_state_up'] != new_net['admin_state_up'])
if changed and not new_net['admin_state_up']:
# disable all active ports of the network
filters = dict(network_id=[id], status=[const.PORT_STATUS_ACTIVE])
ports = super(NECPluginV2, self).get_ports(context,
filters=filters)
for port in ports:
# If some error occurs, status of errored port is set to ERROR.
# This is avoids too many rollback.
# TODO(amotoki): Raise an exception after all port operations
# are finished to inform the caller of API of the failure.
self.deactivate_port(context, port, raise_exc=False)
elif changed and new_net['admin_state_up']:
# enable ports of the network
filters = dict(network_id=[id], status=[const.PORT_STATUS_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)
return new_net
def delete_network(self, context, id):
"""Delete network and packet_filters associated with the network.
Delete network entry from DB and OFC. Then delete packet_filters
associated with the network. If the network is the last resource
of the tenant, delete unnessary ofc_tenant.
"""
LOG.debug("NECPluginV2.delete_network() called, id=%s .", id)
net_db = self._get_network(context, id)
tenant_id = net_db['tenant_id']
ports = self.get_ports(context, filters={'network_id': [id]})
# check if there are any tenant owned ports in-use;
# consider ports owned by floating ips as auto_delete as if there are
# no other tenant owned ports, those floating ips are disassociated
# and will be auto deleted with self._process_l3_delete()
only_auto_del = all(p['device_owner'] in
db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS or
p['device_owner'] == const.DEVICE_OWNER_FLOATINGIP
for p in ports)
if not only_auto_del:
raise n_exc.NetworkInUse(net_id=id)
self._process_l3_delete(context, id)
# Make sure auto-delete ports on OFC are deleted.
# If an error occurs during port deletion,
# delete_network will be aborted.
for port in [p for p in ports if p['device_owner']
in db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS]:
port = self.deactivate_port(context, port)
# delete all packet_filters of the network from the controller
for pf in net_db.packetfilters:
self.delete_packet_filter(context, pf['id'])
if self.ofc.exists_ofc_network(context, id):
try:
self.ofc.delete_ofc_network(context, id, net_db)
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("delete_network() failed due to %s"), exc)
self._update_resource_status(
context, "network", net_db['id'],
const.NET_STATUS_ERROR)
super(NECPluginV2, self).delete_network(context, id)
self._cleanup_ofc_tenant(context, tenant_id)
def _get_base_binding_dict(self):
sg_enabled = sg_rpc.is_firewall_enabled()
vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled,
portbindings.OVS_HYBRID_PLUG: sg_enabled}
binding = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
portbindings.VIF_DETAILS: vif_details}
return binding
def _extend_port_dict_binding_portinfo(self, port_res, portinfo):
if portinfo:
port_res[portbindings.PROFILE] = {
'datapath_id': portinfo['datapath_id'],
'port_no': portinfo['port_no'],
}
elif portbindings.PROFILE in port_res:
del port_res[portbindings.PROFILE]
def _validate_portinfo(self, profile):
key_specs = {
'datapath_id': {'type:string': None, 'required': True},
'port_no': {'type:non_negative': None, 'required': True,
'convert_to': attrs.convert_to_int}
}
msg = attrs._validate_dict_or_empty(profile, key_specs=key_specs)
if msg:
raise n_exc.InvalidInput(error_message=msg)
datapath_id = profile.get('datapath_id')
port_no = profile.get('port_no')
try:
dpid = int(datapath_id, 16)
except ValueError:
raise nexc.ProfilePortInfoInvalidDataPathId()
if dpid > 0xffffffffffffffffL:
raise nexc.ProfilePortInfoInvalidDataPathId()
# Make sure dpid is a hex string beginning with 0x.
dpid = hex(dpid)
if int(port_no) > 65535:
raise nexc.ProfilePortInfoInvalidPortNo()
return {'datapath_id': dpid, 'port_no': port_no}
def _process_portbindings_portinfo_create(self, context, port_data, port):
"""Add portinfo according to bindings:profile in create_port().
:param context: neutron api request context
:param port_data: port attributes passed in PUT request
:param port: port attributes to be returned
"""
profile = port_data.get(portbindings.PROFILE)
# If portbindings.PROFILE is None, unspecified or an empty dict
# it is regarded that portbinding.PROFILE is not set.
profile_set = attrs.is_attr_set(profile) and profile
if profile_set:
portinfo = self._validate_portinfo(profile)
portinfo['mac'] = port['mac_address']
ndb.add_portinfo(context.session, port['id'], **portinfo)
else:
portinfo = None
self._extend_port_dict_binding_portinfo(port, portinfo)
def _process_portbindings_portinfo_update(self, context, port_data, port):
"""Update portinfo according to bindings:profile in update_port().
:param context: neutron api request context
:param port_data: port attributes passed in PUT request
:param port: port attributes to be returned
:returns: 'ADD', 'MOD', 'DEL' or None
"""
if portbindings.PROFILE not in port_data:
return
profile = port_data.get(portbindings.PROFILE)
# If binding:profile is None or an empty dict,
# it means binding:.profile needs to be cleared.
# TODO(amotoki): Allow Make None in binding:profile in
# the API layer. See LP bug #1220011.
profile_set = attrs.is_attr_set(profile) and profile
cur_portinfo = ndb.get_portinfo(context.session, port['id'])
if profile_set:
portinfo = self._validate_portinfo(profile)
portinfo_changed = 'ADD'
if cur_portinfo:
if (necutils.cmp_dpid(portinfo['datapath_id'],
cur_portinfo.datapath_id) and
portinfo['port_no'] == cur_portinfo.port_no):
return
ndb.del_portinfo(context.session, port['id'])
portinfo_changed = 'MOD'
portinfo['mac'] = port['mac_address']
ndb.add_portinfo(context.session, port['id'], **portinfo)
elif cur_portinfo:
portinfo_changed = 'DEL'
portinfo = None
ndb.del_portinfo(context.session, port['id'])
else:
portinfo = None
portinfo_changed = None
self._extend_port_dict_binding_portinfo(port, portinfo)
return portinfo_changed
def extend_port_dict_binding(self, port_res, port_db):
super(NECPluginV2, self).extend_port_dict_binding(port_res, port_db)
self._extend_port_dict_binding_portinfo(port_res, port_db.portinfo)
def _process_portbindings_create(self, context, port_data, port):
super(NECPluginV2, self)._process_portbindings_create_and_update(
context, port_data, port)
self._process_portbindings_portinfo_create(context, port_data, port)
def _process_portbindings_update(self, context, port_data, port):
super(NECPluginV2, self)._process_portbindings_create_and_update(
context, port_data, port)
portinfo_changed = self._process_portbindings_portinfo_update(
context, port_data, port)
return portinfo_changed
def _get_port_handler(self, operation, device_owner):
handlers = self.port_handlers[operation]
handler = handlers.get(device_owner)
if handler:
return handler
else:
return handlers['default']
def create_port(self, context, port):
"""Create a new port entry on DB, then try to activate it."""
LOG.debug("NECPluginV2.create_port() called, port=%s .", port)
port['port']['status'] = const.PORT_STATUS_DOWN
port_data = port['port']
with context.session.begin(subtransactions=True):
self._ensure_default_security_group_on_port(context, port)
sgids = self._get_security_groups_on_port(context, port)
port = super(NECPluginV2, self).create_port(context, port)
self._process_portbindings_create(context, port_data, port)
self._process_port_create_security_group(
context, port, sgids)
port[addr_pair.ADDRESS_PAIRS] = (
self._process_create_allowed_address_pairs(
context, port,
port_data.get(addr_pair.ADDRESS_PAIRS)))
self.notify_security_groups_member_updated(context, port)
handler = self._get_port_handler('create', port['device_owner'])
return handler(context, port)
def _update_ofc_port_if_required(self, context, old_port, new_port,
portinfo_changed):
def get_ofport_exist(port):
return (port['admin_state_up'] and
bool(port.get(portbindings.PROFILE)))
# Determine it is required to update OFC port
need_add = False
need_del = False
need_packet_filter_update = False
old_ofport_exist = get_ofport_exist(old_port)
new_ofport_exist = get_ofport_exist(new_port)
if old_port['admin_state_up'] != new_port['admin_state_up']:
if new_port['admin_state_up']:
need_add |= new_ofport_exist
else:
need_del |= old_ofport_exist
if portinfo_changed:
if portinfo_changed in ['DEL', 'MOD']:
need_del |= old_ofport_exist
if portinfo_changed in ['ADD', 'MOD']:
need_add |= new_ofport_exist
need_packet_filter_update |= True
# Update OFC port if required
if need_del:
self.deactivate_port(context, new_port)
if need_packet_filter_update:
self.deactivate_packet_filters_by_port(context, id)
if need_add:
if need_packet_filter_update:
self.activate_packet_filters_by_port(context, id)
self.activate_port_if_ready(context, new_port)
def update_port(self, context, id, port):
"""Update port, and handle packetfilters associated with the port.
Update network entry on DB. If admin_state_up was changed, activate
or deactivate the port and packetfilters associated with it.
"""
LOG.debug("NECPluginV2.update_port() called, "
"id=%(id)s port=%(port)s .",
{'id': id, 'port': port})
need_port_update_notify = False
with context.session.begin(subtransactions=True):
old_port = super(NECPluginV2, self).get_port(context, id)
new_port = super(NECPluginV2, self).update_port(context, id, port)
portinfo_changed = self._process_portbindings_update(
context, port['port'], new_port)
if addr_pair.ADDRESS_PAIRS in port['port']:
need_port_update_notify |= (
self.update_address_pairs_on_port(context, id, port,
old_port,
new_port))
need_port_update_notify |= self.update_security_group_on_port(
context, id, port, old_port, new_port)
need_port_update_notify |= self.is_security_group_member_updated(
context, old_port, new_port)
if need_port_update_notify:
self.notifier.port_update(context, new_port)
self._update_ofc_port_if_required(context, old_port, new_port,
portinfo_changed)
return new_port
def delete_port(self, context, id, l3_port_check=True):
"""Delete port and packet_filters associated with the port."""
LOG.debug("NECPluginV2.delete_port() called, id=%s .", id)
# ext_sg.SECURITYGROUPS attribute for the port is required
# since notifier.security_groups_member_updated() need the attribute.
# Thus we need to call self.get_port() instead of super().get_port()
port_db = self._get_port(context, id)
port = self._make_port_dict(port_db)
handler = self._get_port_handler('delete', port['device_owner'])
# handler() raises an exception if an error occurs during processing.
port = handler(context, port)
# delete all packet_filters of the port from the controller
for pf in port_db.packetfilters:
self.delete_packet_filter(context, pf['id'])
# if needed, check to see if this is a port owned by
# and l3-router. If so, we should prevent deletion.
if l3_port_check:
self.prevent_l3_port_deletion(context, id)
with context.session.begin(subtransactions=True):
router_ids = self.disassociate_floatingips(
context, id, do_notify=False)
super(NECPluginV2, self).delete_port(context, id)
# now that we've left db transaction, we are safe to notify
self.notify_routers_updated(context, router_ids)
self.notify_security_groups_member_updated(context, port)
class NECPluginV2AgentNotifierApi(sg_rpc.SecurityGroupAgentRpcApiMixin):
'''RPC API for NEC plugin agent.'''
def __init__(self, topic):
self.topic = topic
self.topic_port_update = topics.get_topic_name(
topic, topics.PORT, topics.UPDATE)
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def port_update(self, context, port):
cctxt = self.client.prepare(topic=self.topic_port_update, fanout=True)
cctxt.cast(context, 'port_update', port=port)
class NECPluginV2RPCCallbacks(object):
target = oslo_messaging.Target(version='1.0')
def __init__(self, plugin):
super(NECPluginV2RPCCallbacks, self).__init__()
self.plugin = plugin
def update_ports(self, rpc_context, **kwargs):
"""Update ports' information and activate/deavtivate them.
Expected input format is:
{'topic': 'q-agent-notifier',
'agent_id': 'nec-q-agent.' + <hostname>,
'datapath_id': <datapath_id of br-int on remote host>,
'port_added': [<new PortInfo>,...],
'port_removed': [<removed Port ID>,...]}
"""
LOG.debug("NECPluginV2RPCCallbacks.update_ports() called, "
"kwargs=%s .", kwargs)
datapath_id = kwargs['datapath_id']
session = rpc_context.session
for p in kwargs.get('port_added', []):
id = p['id']
portinfo = ndb.get_portinfo(session, id)
if portinfo:
if (necutils.cmp_dpid(portinfo.datapath_id, datapath_id) and
portinfo.port_no == p['port_no']):
LOG.debug("update_ports(): ignore unchanged portinfo in "
"port_added message (port_id=%s).", id)
continue
ndb.del_portinfo(session, id)
port = self._get_port(rpc_context, id)
if port:
ndb.add_portinfo(session, id, datapath_id, p['port_no'],
mac=p.get('mac', ''))
# NOTE: Make sure that packet filters on this port exist while
# the port is active to avoid unexpected packet transfer.
if portinfo:
self.plugin.deactivate_port(rpc_context, port,
raise_exc=False)
self.plugin.deactivate_packet_filters_by_port(
rpc_context, id, raise_exc=False)
self.plugin.activate_packet_filters_by_port(rpc_context, id)
self.plugin.activate_port_if_ready(rpc_context, port)
for id in kwargs.get('port_removed', []):
portinfo = ndb.get_portinfo(session, id)
if not portinfo:
LOG.debug("update_ports(): ignore port_removed message "
"due to portinfo for port_id=%s was not "
"registered", id)
continue
if not necutils.cmp_dpid(portinfo.datapath_id, datapath_id):
LOG.debug("update_ports(): ignore port_removed message "
"received from different host "
"(registered_datapath_id=%(registered)s, "
"received_datapath_id=%(received)s).",
{'registered': portinfo.datapath_id,
'received': datapath_id})
continue
ndb.del_portinfo(session, id)
port = self._get_port(rpc_context, id)
if port:
self.plugin.deactivate_port(rpc_context, port, raise_exc=False)
self.plugin.deactivate_packet_filters_by_port(
rpc_context, id, raise_exc=False)
def _get_port(self, context, port_id):
try:
return self.plugin.get_port(context, port_id)
except n_exc.PortNotFound:
return None

View File

@ -1,358 +0,0 @@
# Copyright 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.
from oslo_utils import excutils
from oslo_utils import importutils
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
from neutron.api.v2 import attributes as attr
from neutron.common import exceptions as n_exc
from neutron.db import db_base_plugin_v2
from neutron.db import extraroute_db
from neutron.db import l3_agentschedulers_db
from neutron.db import l3_db
from neutron.db import l3_gwmode_db
from neutron.db import models_v2
from neutron.extensions import l3
from neutron.i18n import _LE, _LI, _LW
from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import constants as nconst
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import router as rdb
from neutron.plugins.nec.extensions import router_provider as ext_provider
LOG = logging.getLogger(__name__)
PROVIDER_L3AGENT = nconst.ROUTER_PROVIDER_L3AGENT
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
ROUTER_DRIVER_PATH = 'neutron.plugins.nec.router_drivers.'
ROUTER_DRIVER_MAP = {
PROVIDER_L3AGENT: ROUTER_DRIVER_PATH + 'RouterL3AgentDriver',
PROVIDER_OPENFLOW: ROUTER_DRIVER_PATH + 'RouterOpenFlowDriver'
}
ROUTER_DRIVERS = {}
STATUS_ACTIVE = nconst.ROUTER_STATUS_ACTIVE
STATUS_ERROR = nconst.ROUTER_STATUS_ERROR
class RouterMixin(extraroute_db.ExtraRoute_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin):
def create_router(self, context, router):
"""Create a new router entry on DB, and create it on OFC."""
LOG.debug("RouterMixin.create_router() called, "
"router=%s .", router)
tenant_id = self._get_tenant_id_for_create(context, router['router'])
provider = get_provider_with_default(
router['router'].get(ext_provider.ROUTER_PROVIDER))
driver = get_driver_by_provider(provider)
with context.session.begin(subtransactions=True):
new_router = super(RouterMixin, self).create_router(context,
router)
new_router['gw_port'] = self._get_gw_port_detail(
context, driver, new_router['gw_port_id'])
rdb.add_router_provider_binding(context.session,
provider, str(new_router['id']))
self._extend_router_dict_provider(new_router, provider)
# create router on the network controller
try:
return driver.create_router(context, tenant_id, new_router)
except nexc.RouterOverLimit:
with excutils.save_and_reraise_exception():
super(RouterMixin, self).delete_router(context,
new_router['id'])
def update_router(self, context, router_id, router):
LOG.debug("RouterMixin.update_router() called, "
"id=%(id)s, router=%(router)s .",
{'id': router_id, 'router': router})
with context.session.begin(subtransactions=True):
old_rtr = super(RouterMixin, self).get_router(context, router_id)
provider = old_rtr[ext_provider.ROUTER_PROVIDER]
driver = get_driver_by_provider(provider)
old_rtr['gw_port'] = self._get_gw_port_detail(
context, driver, old_rtr['gw_port_id'])
new_rtr = super(RouterMixin, self).update_router(
context, router_id, router)
new_rtr['gw_port'] = self._get_gw_port_detail(
context, driver, new_rtr['gw_port_id'])
driver.update_router(context, router_id, old_rtr, new_rtr)
return new_rtr
def delete_router(self, context, router_id):
LOG.debug("RouterMixin.delete_router() called, id=%s.", router_id)
router = super(RouterMixin, self).get_router(context, router_id)
tenant_id = router['tenant_id']
# Since l3_db.delete_router() has no interaction with the plugin layer,
# we need to check if the router can be deleted first.
self._check_router_in_use(context, router_id)
driver = self._get_router_driver_by_id(context, router_id)
# If gw_port exists, remove it.
gw_port = self._get_gw_port(context, router_id)
if gw_port:
driver.delete_interface(context, router_id, gw_port)
driver.delete_router(context, router_id, router)
super(RouterMixin, self).delete_router(context, router_id)
self._cleanup_ofc_tenant(context, tenant_id)
def add_router_interface(self, context, router_id, interface_info):
LOG.debug("RouterMixin.add_router_interface() called, "
"id=%(id)s, interface=%(interface)s.",
{'id': router_id, 'interface': interface_info})
return super(RouterMixin, self).add_router_interface(
context, router_id, interface_info)
def remove_router_interface(self, context, router_id, interface_info):
LOG.debug("RouterMixin.remove_router_interface() called, "
"id=%(id)s, interface=%(interface)s.",
{'id': router_id, 'interface': interface_info})
return super(RouterMixin, self).remove_router_interface(
context, router_id, interface_info)
def create_router_port(self, context, port):
# This method is called from plugin.create_port()
router_id = port['device_id']
driver = self._get_router_driver_by_id(context, router_id)
port = driver.add_interface(context, router_id, port)
return port
def delete_router_port(self, context, port):
# This method is called from plugin.delete_port()
router_id = port['device_id']
driver = self._get_router_driver_by_id(context, router_id)
return driver.delete_interface(context, router_id, port)
def _get_gw_port_detail(self, context, driver, gw_port_id):
if not gw_port_id or not driver.need_gw_info:
return
ctx_elevated = context.elevated()
gw_port = self._get_port(ctx_elevated, gw_port_id)
# At this moment gw_port has been created, so it is guaranteed
# that fixed_ip is assigned for the gw_port.
ext_subnet_id = gw_port['fixed_ips'][0]['subnet_id']
ext_subnet = self._get_subnet(ctx_elevated, ext_subnet_id)
gw_info = {'network_id': gw_port['network_id'],
'ip_address': gw_port['fixed_ips'][0]['ip_address'],
'mac_address': gw_port['mac_address'],
'cidr': ext_subnet['cidr'],
'gateway_ip': ext_subnet['gateway_ip']}
return gw_info
def _get_gw_port(self, context, router_id):
device_filter = {'device_id': [router_id],
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_GW]}
ports = self.get_ports(context.elevated(), filters=device_filter)
if ports:
return ports[0]
def _check_router_in_use(self, context, router_id):
with context.session.begin(subtransactions=True):
# Ensure that the router is not used
router_filter = {'router_id': [router_id]}
fips = self.get_floatingips_count(context.elevated(),
filters=router_filter)
if fips:
raise l3.RouterInUse(router_id=router_id)
device_filter = {'device_id': [router_id],
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
ports = self.get_ports_count(context.elevated(),
filters=device_filter)
if ports:
raise l3.RouterInUse(router_id=router_id)
def _get_router_for_floatingip(self, context, internal_port,
internal_subnet_id,
external_network_id):
"""Get a router for a requested floating IP.
OpenFlow vrouter does not support NAT, so we need to exclude them
from candidate routers for floating IP association.
This method is called in l3_db.get_assoc_data().
"""
subnet_db = self._get_subnet(context, internal_subnet_id)
if not subnet_db['gateway_ip']:
msg = (_('Cannot add floating IP to port on subnet %s '
'which has no gateway_ip') % internal_subnet_id)
raise n_exc.BadRequest(resource='floatingip', msg=msg)
# find router interface ports on this network
router_intf_qry = context.session.query(models_v2.Port)
router_intf_ports = router_intf_qry.filter_by(
network_id=internal_port['network_id'],
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF)
for intf_p in router_intf_ports:
if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
router_id = intf_p['device_id']
router_gw_qry = context.session.query(models_v2.Port)
has_gw_port = router_gw_qry.filter_by(
network_id=external_network_id,
device_id=router_id,
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).count()
driver = self._get_router_driver_by_id(context, router_id)
if (has_gw_port and driver.floating_ip_support()):
return router_id
raise l3.ExternalGatewayForFloatingIPNotFound(
subnet_id=internal_subnet_id,
external_network_id=external_network_id,
port_id=internal_port['id'])
def _get_sync_routers(self, context, router_ids=None, active=None):
"""Query routers and their gw ports for l3 agent.
The difference from the superclass in l3_db is that this method
only lists routers hosted on l3-agents.
"""
router_list = super(RouterMixin, self)._get_sync_routers(
context, router_ids, active)
if router_list:
_router_ids = [r['id'] for r in router_list]
agent_routers = rdb.get_routers_by_provider(
context.session, 'l3-agent',
router_ids=_router_ids)
router_list = [r for r in router_list
if r['id'] in agent_routers]
return router_list
def _get_router_driver_by_id(self, context, router_id):
provider = self._get_provider_by_router_id(context, router_id)
return get_driver_by_provider(provider)
def _get_provider_by_router_id(self, context, router_id):
return rdb.get_provider_by_router(context.session, router_id)
def _extend_router_dict_provider(self, router_res, provider):
router_res[ext_provider.ROUTER_PROVIDER] = provider
def extend_router_dict_provider(self, router_res, router_db):
# NOTE: router_db.provider is None just after creating a router,
# so we need to skip setting router_provider here.
if not router_db.provider:
return
self._extend_router_dict_provider(router_res,
router_db.provider['provider'])
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
l3.ROUTERS, [extend_router_dict_provider])
class L3AgentSchedulerDbMixin(l3_agentschedulers_db.L3AgentSchedulerDbMixin):
def auto_schedule_routers(self, context, host, router_ids):
router_ids = rdb.get_routers_by_provider(
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
# If no l3-agent hosted router, there is no need to schedule.
if not router_ids:
return
return super(L3AgentSchedulerDbMixin, self).auto_schedule_routers(
context, host, router_ids)
def schedule_router(self, context, router, candidates=None):
if (self._get_provider_by_router_id(context, router) ==
nconst.ROUTER_PROVIDER_L3AGENT):
return super(L3AgentSchedulerDbMixin, self).schedule_router(
context, router, candidates=candidates)
def add_router_to_l3_agent(self, context, id, router_id):
provider = self._get_provider_by_router_id(context, router_id)
if provider != nconst.ROUTER_PROVIDER_L3AGENT:
raise nexc.RouterProviderMismatch(
router_id=router_id, provider=provider,
expected_provider=nconst.ROUTER_PROVIDER_L3AGENT)
return super(L3AgentSchedulerDbMixin, self).add_router_to_l3_agent(
context, id, router_id)
class L3AgentNotifyAPI(l3_rpc_agent_api.L3AgentNotifyAPI):
def _notification(self, context, method, router_ids, operation,
shuffle_agents):
"""Notify all the agents that are hosting the routers.
_notification() is called in L3 db plugin for all routers regardless
the routers are hosted on l3 agents or not. When the routers are
not hosted on l3 agents, there is no need to notify.
This method filters routers not hosted by l3 agents.
"""
router_ids = rdb.get_routers_by_provider(
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
super(L3AgentNotifyAPI, self)._notification(
context, method, router_ids, operation, shuffle_agents)
def load_driver(plugin, ofc_manager):
if (PROVIDER_OPENFLOW in ROUTER_DRIVER_MAP and
not ofc_manager.driver.router_supported):
LOG.warning(
_LW('OFC does not support router with provider=%(provider)s, '
'so removed it from supported provider '
'(new router driver map=%(driver_map)s)'),
{'provider': PROVIDER_OPENFLOW,
'driver_map': ROUTER_DRIVER_MAP})
del ROUTER_DRIVER_MAP[PROVIDER_OPENFLOW]
if config.PROVIDER.default_router_provider not in ROUTER_DRIVER_MAP:
LOG.error(_LE('default_router_provider %(default)s is supported! '
'Please specify one of %(supported)s'),
{'default': config.PROVIDER.default_router_provider,
'supported': ROUTER_DRIVER_MAP.keys()})
raise SystemExit(1)
enabled_providers = (set(config.PROVIDER.router_providers +
[config.PROVIDER.default_router_provider]) &
set(ROUTER_DRIVER_MAP.keys()))
for driver in enabled_providers:
driver_klass = importutils.import_class(ROUTER_DRIVER_MAP[driver])
ROUTER_DRIVERS[driver] = driver_klass(plugin, ofc_manager)
LOG.info(_LI('Enabled router drivers: %s'), ROUTER_DRIVERS.keys())
if not ROUTER_DRIVERS:
LOG.error(_LE('No router provider is enabled. neutron-server '
'terminated! (supported=%(supported)s, '
'configured=%(config)s)'),
{'supported': ROUTER_DRIVER_MAP.keys(),
'config': config.PROVIDER.router_providers})
raise SystemExit(1)
def get_provider_with_default(provider):
if not attr.is_attr_set(provider):
provider = config.PROVIDER.default_router_provider
elif provider not in ROUTER_DRIVERS:
raise nexc.ProviderNotFound(provider=provider)
return provider
def get_driver_by_provider(provider):
if provider is None:
provider = config.PROVIDER.default_router_provider
elif provider not in ROUTER_DRIVERS:
raise nexc.ProviderNotFound(provider=provider)
return ROUTER_DRIVERS[provider]

View File

@ -1,101 +0,0 @@
# 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.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class OFCDriverBase(object):
"""OpenFlow Controller (OFC) Driver Specification.
OFCDriverBase defines the minimum set of methods required by this plugin.
It would be better that other methods like update_* are implemented.
"""
@abc.abstractmethod
def create_tenant(self, description, tenant_id=None):
"""Create a new tenant at OpenFlow Controller.
:param description: A description of this tenant.
:param tenant_id: A hint of OFC tenant ID.
A driver could use this id as a OFC id or ignore it.
:returns: ID of the tenant created at OpenFlow Controller.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass
@abc.abstractmethod
def delete_tenant(self, ofc_tenant_id):
"""Delete a tenant at OpenFlow Controller.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass
@abc.abstractmethod
def create_network(self, ofc_tenant_id, description, network_id=None):
"""Create a new network on specified OFC tenant at OpenFlow Controller.
:param ofc_tenant_id: a OFC tenant ID in which a new network belongs.
:param description: A description of this network.
:param network_id: A hint of an ID of OFC network.
:returns: ID of the network created at OpenFlow Controller.
ID returned must be unique in the OpenFlow Controller.
If a network is identified in conjunction with other information
such as a tenant ID, such information should be included in the ID.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass
@abc.abstractmethod
def delete_network(self, ofc_network_id):
"""Delete a netwrok at OpenFlow Controller.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass
@abc.abstractmethod
def create_port(self, ofc_network_id, portinfo,
port_id=None, filters=None):
"""Create a new port on specified network at OFC.
:param ofc_network_id: a OFC tenant ID in which a new port belongs.
:param portinfo: An OpenFlow information of this port.
{'datapath_id': Switch ID that a port connected.
'port_no': Port Number that a port connected on a Swtich.
'vlan_id': VLAN ID that a port tagging.
'mac': Mac address.
}
:param port_id: A hint of an ID of OFC port.
ID returned must be unique in the OpenFlow Controller.
If a port is identified in combination with a network or
a tenant, such information should be included in the ID.
:param filters: A list of packet filter associated with the port.
Each element is a tuple (neutron ID, OFC ID)
:returns: ID of the port created at OpenFlow Controller.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass
@abc.abstractmethod
def delete_port(self, ofc_port_id):
"""Delete a port at OpenFlow Controller.
:raises: neutron.plugin.nec.common.exceptions.OFCException
"""
pass

View File

@ -1,182 +0,0 @@
# 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.
import netaddr
from neutron.common import utils
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 import drivers
LOG = logging.getLogger(__name__)
class OFCManager(object):
"""This class manages an OpenFlow Controller and map resources.
This class manage an OpenFlow Controller (OFC) with a driver specified in
a configuration of this plugin. This keeps mappings between IDs on Neutron
and OFC for various entities such as Tenant, Network and Filter. A Port on
OFC is identified by a switch ID 'datapath_id' and a port number 'port_no'
of the switch. An ID named as 'ofc_*' is used to identify resource on OFC.
"""
def __init__(self, plugin):
self.driver = drivers.get_driver(config.OFC.driver)(config.OFC)
self.plugin = plugin
def _get_ofc_id(self, context, resource, neutron_id):
return ndb.get_ofc_id(context.session, resource, neutron_id)
def _exists_ofc_item(self, context, resource, neutron_id):
return ndb.exists_ofc_item(context.session, resource, neutron_id)
def _add_ofc_item(self, context, resource, neutron_id, ofc_id):
# Ensure a new item is added to the new mapping table
ndb.add_ofc_item(context.session, resource, neutron_id, ofc_id)
def _del_ofc_item(self, context, resource, neutron_id):
ndb.del_ofc_item(context.session, resource, neutron_id)
def ensure_ofc_tenant(self, context, tenant_id):
if not self.exists_ofc_tenant(context, tenant_id):
self.create_ofc_tenant(context, tenant_id)
def create_ofc_tenant(self, context, tenant_id):
desc = "ID=%s at OpenStack." % tenant_id
ofc_tenant_id = self.driver.create_tenant(desc, tenant_id)
self._add_ofc_item(context, "ofc_tenant", tenant_id, ofc_tenant_id)
def exists_ofc_tenant(self, context, tenant_id):
return self._exists_ofc_item(context, "ofc_tenant", tenant_id)
def delete_ofc_tenant(self, context, tenant_id):
ofc_tenant_id = self._get_ofc_id(context, "ofc_tenant", tenant_id)
self.driver.delete_tenant(ofc_tenant_id)
self._del_ofc_item(context, "ofc_tenant", tenant_id)
def create_ofc_network(self, context, tenant_id, network_id,
network_name=None):
ofc_tenant_id = self._get_ofc_id(context, "ofc_tenant", tenant_id)
desc = "ID=%s Name=%s at Neutron." % (network_id, network_name)
ofc_net_id = self.driver.create_network(ofc_tenant_id, desc,
network_id)
self._add_ofc_item(context, "ofc_network", network_id, ofc_net_id)
def exists_ofc_network(self, context, network_id):
return self._exists_ofc_item(context, "ofc_network", network_id)
def delete_ofc_network(self, context, network_id, network):
ofc_net_id = self._get_ofc_id(context, "ofc_network", network_id)
self.driver.delete_network(ofc_net_id)
self._del_ofc_item(context, "ofc_network", network_id)
def create_ofc_port(self, context, port_id, port):
ofc_net_id = self._get_ofc_id(context, "ofc_network",
port['network_id'])
portinfo = ndb.get_portinfo(context.session, port_id)
if not portinfo:
raise nexc.PortInfoNotFound(id=port_id)
# Associate packet filters
filters = self.plugin.get_packet_filters_for_port(context, port)
if filters is not None:
params = {'filters': filters}
else:
params = {}
ofc_port_id = self.driver.create_port(ofc_net_id, portinfo, port_id,
**params)
self._add_ofc_item(context, "ofc_port", port_id, ofc_port_id)
def exists_ofc_port(self, context, port_id):
return self._exists_ofc_item(context, "ofc_port", port_id)
def delete_ofc_port(self, context, port_id, port):
ofc_port_id = self._get_ofc_id(context, "ofc_port", port_id)
self.driver.delete_port(ofc_port_id)
self._del_ofc_item(context, "ofc_port", port_id)
def create_ofc_packet_filter(self, context, filter_id, filter_dict):
ofc_pf_id = self.driver.create_filter(context, filter_dict, filter_id)
self._add_ofc_item(context, "ofc_packet_filter", filter_id, ofc_pf_id)
def update_ofc_packet_filter(self, context, filter_id, filter_dict):
ofc_pf_id = self._get_ofc_id(context, "ofc_packet_filter", filter_id)
self.driver.update_filter(ofc_pf_id, filter_dict)
def exists_ofc_packet_filter(self, context, filter_id):
return self._exists_ofc_item(context, "ofc_packet_filter", filter_id)
def delete_ofc_packet_filter(self, context, filter_id):
ofc_pf_id = self._get_ofc_id(context, "ofc_packet_filter", filter_id)
self.driver.delete_filter(ofc_pf_id)
self._del_ofc_item(context, "ofc_packet_filter", filter_id)
def create_ofc_router(self, context, tenant_id, router_id, name=None):
ofc_tenant_id = self._get_ofc_id(context, "ofc_tenant", tenant_id)
desc = "ID=%s Name=%s at Neutron." % (router_id, name)
ofc_router_id = self.driver.create_router(ofc_tenant_id, router_id,
desc)
self._add_ofc_item(context, "ofc_router", router_id, ofc_router_id)
def exists_ofc_router(self, context, router_id):
return self._exists_ofc_item(context, "ofc_router", router_id)
def delete_ofc_router(self, context, router_id, router):
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
self.driver.delete_router(ofc_router_id)
self._del_ofc_item(context, "ofc_router", router_id)
def add_ofc_router_interface(self, context, router_id, port_id, port):
# port must have the following fields:
# network_id, cidr, ip_address, mac_address
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
ofc_net_id = self._get_ofc_id(context, "ofc_network",
port['network_id'])
ip_address = '%s/%s' % (port['ip_address'],
netaddr.IPNetwork(port['cidr']).prefixlen)
mac_address = port['mac_address']
ofc_inf_id = self.driver.add_router_interface(
ofc_router_id, ofc_net_id, ip_address, mac_address)
# Use port mapping table to maintain an interface of OFC router
self._add_ofc_item(context, "ofc_port", port_id, ofc_inf_id)
def delete_ofc_router_interface(self, context, router_id, port_id):
# Use port mapping table to maintain an interface of OFC router
ofc_inf_id = self._get_ofc_id(context, "ofc_port", port_id)
self.driver.delete_router_interface(ofc_inf_id)
self._del_ofc_item(context, "ofc_port", port_id)
def update_ofc_router_route(self, context, router_id, new_routes):
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
ofc_routes = self.driver.list_router_routes(ofc_router_id)
route_dict = {}
cur_routes = []
for r in ofc_routes:
key = ','.join((r['destination'], r['nexthop']))
route_dict[key] = r['id']
del r['id']
cur_routes.append(r)
added, removed = utils.diff_list_of_dict(cur_routes, new_routes)
for r in removed:
key = ','.join((r['destination'], r['nexthop']))
route_id = route_dict[key]
self.driver.delete_router_route(route_id)
for r in added:
self.driver.add_router_route(ofc_router_id, r['destination'],
r['nexthop'])

View File

@ -1,255 +0,0 @@
# 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.
from oslo_utils import excutils
from neutron.i18n import _LE
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 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)
if hasattr(self.ofc.driver, 'validate_filter_create'):
pf = packet_filter['packet_filter']
self.ofc.driver.validate_filter_create(context, pf)
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})
pf_data = packet_filter['packet_filter']
if hasattr(self.ofc.driver, 'validate_filter_update'):
self.ofc.driver.validate_filter_update(context, pf_data)
# 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):
LOG.debug('old_pf=%(old_pf)s, new_pf=%(new_pf)s',
{'old_pf': old_pf, 'new_pf': new_pf})
# When the status is ERROR, force sync to OFC.
if old_pf['status'] == pf_db.PF_STATUS_ERROR:
LOG.debug('update_packet_filter: Force filter update '
'because the previous status is ERROR.')
return True
for key in new_pf:
if key in ('id', 'name', 'tenant_id', 'network_id',
'in_port', 'status'):
continue
if old_pf[key] != new_pf[key]:
return True
return False
if _packet_filter_changed(pf_old, pf):
if hasattr(self.ofc.driver, 'update_filter'):
# admin_state is changed
if pf_old['admin_state_up'] != pf['admin_state_up']:
LOG.debug('update_packet_filter: admin_state '
'is changed to %s', pf['admin_state_up'])
if pf['admin_state_up']:
self.activate_packet_filter_if_ready(context, pf)
else:
self.deactivate_packet_filter(context, pf)
elif pf['admin_state_up']:
LOG.debug('update_packet_filter: admin_state is '
'unchanged (True)')
if self.ofc.exists_ofc_packet_filter(context, id):
pf = self._update_packet_filter(context, pf, pf_data)
else:
pf = self.activate_packet_filter_if_ready(context, pf)
else:
LOG.debug('update_packet_filter: admin_state is unchanged '
'(False). No need to update OFC filter.')
else:
pf = self.deactivate_packet_filter(context, pf)
pf = self.activate_packet_filter_if_ready(context, pf)
return pf
def _update_packet_filter(self, context, new_pf, pf_data):
pf_id = new_pf['id']
prev_status = new_pf['status']
try:
# If previous status is ERROR, try to sync all attributes.
pf = new_pf if prev_status == pf_db.PF_STATUS_ERROR else pf_data
self.ofc.update_ofc_packet_filter(context, pf_id, pf)
new_status = pf_db.PF_STATUS_ACTIVE
if new_status != prev_status:
self._update_resource_status(context, "packet_filter",
pf_id, new_status)
new_pf['status'] = new_status
return new_pf
except Exception as exc:
with excutils.save_and_reraise_exception():
if (isinstance(exc, nexc.OFCException) or
isinstance(exc, nexc.OFCConsistencyBroken)):
LOG.error(_LE("Failed to create packet_filter id=%(id)s "
"on OFC: %(exc)s"),
{'id': pf_id, 'exc': exc})
new_status = pf_db.PF_STATUS_ERROR
if new_status != prev_status:
self._update_resource_status(context, "packet_filter",
pf_id, new_status)
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)
# deactivate_packet_filter() raises an exception
# if an error occurs during processing.
pf = self.deactivate_packet_filter(context, pf)
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']
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 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.PortInfoNotFound:
LOG.debug("Skipped to create a packet filter pf_id=%s "
"on OFC, no portinfo for the in_port.", pf_id)
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
LOG.error(_LE("Failed to create packet_filter id=%(id)s on "
"OFC: %(exc)s"), {'id': pf_id, 'exc': 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']
if not self.ofc.exists_ofc_packet_filter(context, pf_id):
LOG.debug("deactivate_packet_filter(): skip, "
"Not found OFC Mapping for packet_filter id=%s.",
pf_id)
return packet_filter
LOG.debug("deactivate_packet_filter(): "
"deleting packet_filter id=%s from OFC.", pf_id)
try:
self.ofc.delete_ofc_packet_filter(context, pf_id)
self._update_resource_status_if_changed(
context, "packet_filter", packet_filter, pf_db.PF_STATUS_DOWN)
return packet_filter
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Failed to delete packet_filter id=%(id)s "
"from OFC: %(exc)s"),
{'id': pf_id, 'exc': exc})
self._update_resource_status_if_changed(
context, "packet_filter", packet_filter,
pf_db.PF_STATUS_ERROR)
def activate_packet_filters_by_port(self, context, port_id):
if not self.packet_filter_enabled:
return
filters = {'in_port': [port_id], 'admin_state_up': [True],
'status': [pf_db.PF_STATUS_DOWN]}
pfs = self.get_packet_filters(context, filters=filters)
for pf in pfs:
self.activate_packet_filter_if_ready(context, pf)
def deactivate_packet_filters_by_port(self, context, port_id,
raise_exc=True):
if not self.packet_filter_enabled:
return
filters = {'in_port': [port_id], 'status': [pf_db.PF_STATUS_ACTIVE]}
pfs = self.get_packet_filters(context, filters=filters)
error = False
for pf in pfs:
try:
self.deactivate_packet_filter(context, pf)
except (nexc.OFCException, nexc.OFCMappingNotFound):
error = True
if raise_exc and error:
raise nexc.OFCException(_('Error occurred while disabling packet '
'filter(s) for port %s'), port_id)
def get_packet_filters_for_port(self, context, port):
if self.packet_filter_enabled:
return super(PacketFilterMixin,
self).get_packet_filters_for_port(context, port)

View File

@ -0,0 +1 @@
networking-nec>=2015.1,<2015.2

View File

@ -1,221 +0,0 @@
# Copyright 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.
import abc
import httplib
from oslo_utils import excutils
import six
from neutron.common import constants
from neutron.common import log as call_log
from neutron.common import utils
from neutron.i18n import _LE, _LW
from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import constants as nconst
from neutron.plugins.nec.common import exceptions as nexc
LOG = logging.getLogger(__name__)
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
@six.add_metaclass(abc.ABCMeta)
class RouterDriverBase(object):
def __init__(self, plugin, ofc_manager):
self.plugin = plugin
self.ofc = ofc_manager
def floating_ip_support(self):
return True
@abc.abstractmethod
def create_router(self, context, tenant_id, router):
pass
@abc.abstractmethod
def update_router(self, context, router_id, old_router, new_router):
pass
@abc.abstractmethod
def delete_router(self, context, router_id, router):
pass
@abc.abstractmethod
def add_interface(self, context, router_id, port):
pass
@abc.abstractmethod
def delete_interface(self, context, router_id, port):
pass
class RouterL3AgentDriver(RouterDriverBase):
need_gw_info = False
@call_log.log
def create_router(self, context, tenant_id, router):
return router
@call_log.log
def update_router(self, context, router_id, old_router, new_router):
return new_router
@call_log.log
def delete_router(self, context, router_id, router):
pass
@call_log.log
def add_interface(self, context, router_id, port):
return self.plugin.activate_port_if_ready(context, port)
@call_log.log
def delete_interface(self, context, router_id, port):
return self.plugin.deactivate_port(context, port)
class RouterOpenFlowDriver(RouterDriverBase):
need_gw_info = True
def floating_ip_support(self):
return self.ofc.driver.router_nat_supported
def _process_gw_port(self, gw_info, routes):
if gw_info and gw_info['gateway_ip']:
routes.append({'destination': constants.IPv4_ANY,
'nexthop': gw_info['gateway_ip']})
@call_log.log
def create_router(self, context, tenant_id, router):
try:
router_id = router['id']
added_routes = []
self.ofc.ensure_ofc_tenant(context, tenant_id)
self.ofc.create_ofc_router(context, tenant_id, router_id,
router['name'])
self._process_gw_port(router['gw_port'], added_routes)
if added_routes:
self.ofc.update_ofc_router_route(context, router_id,
added_routes, [])
new_status = nconst.ROUTER_STATUS_ACTIVE
self.plugin._update_resource_status(context, "router",
router['id'],
new_status)
router['status'] = new_status
return router
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
if (isinstance(exc, nexc.OFCException) and
exc.status == httplib.CONFLICT):
raise nexc.RouterOverLimit(provider=PROVIDER_OPENFLOW)
LOG.error(_LE("create_router() failed due to %s"), exc)
new_status = nconst.ROUTER_STATUS_ERROR
self._update_resource_status(context, "router",
router['id'],
new_status)
@call_log.log
def update_router(self, context, router_id, old_router, new_router):
old_routes = old_router['routes'][:]
new_routes = new_router['routes'][:]
self._process_gw_port(old_router['gw_port'], old_routes)
self._process_gw_port(new_router['gw_port'], new_routes)
added, removed = utils.diff_list_of_dict(old_routes, new_routes)
if added or removed:
try:
# NOTE(amotoki): PFC supports one-by-one route update at now.
# It means there may be a case where some route is updated but
# some not. To allow the next call of failures to sync routes
# with Neutron side, we pass the whole new routes here.
# PFC should support atomic route update in the future.
self.ofc.update_ofc_router_route(context, router_id,
new_routes)
new_status = nconst.ROUTER_STATUS_ACTIVE
self.plugin._update_resource_status(
context, "router", router_id, new_status)
new_router['status'] = new_status
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("_update_ofc_routes() failed due to %s"),
exc)
new_status = nconst.ROUTER_STATUS_ERROR
self.plugin._update_resource_status(
context, "router", router_id, new_status)
return new_router
@call_log.log
def delete_router(self, context, router_id, router):
if not self.ofc.exists_ofc_router(context, router_id):
return
try:
self.ofc.delete_ofc_router(context, router_id, router)
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("delete_router() failed due to %s"), exc)
self.plugin._update_resource_status(
context, "router", router_id, nconst.ROUTER_STATUS_ERROR)
@call_log.log
def add_interface(self, context, router_id, port):
port_id = port['id']
# port['fixed_ips'] may be empty if ext_net has no subnet.
# Such port is invalid for a router port and we don't create a port
# on OFC. The port is removed in l3_db._create_router_gw_port.
if not port['fixed_ips']:
LOG.warning(_LW('RouterOpenFlowDriver.add_interface(): the '
'requested port '
'has no subnet. add_interface() is skipped. '
'router_id=%(id)s, port=%(port)s)'),
{'id': router_id, 'port': port})
return port
fixed_ip = port['fixed_ips'][0]
subnet = self.plugin._get_subnet(context, fixed_ip['subnet_id'])
port_info = {'network_id': port['network_id'],
'ip_address': fixed_ip['ip_address'],
'cidr': subnet['cidr'],
'mac_address': port['mac_address']}
try:
self.ofc.add_ofc_router_interface(context, router_id,
port_id, port_info)
new_status = nconst.ROUTER_STATUS_ACTIVE
self.plugin._update_resource_status(
context, "port", port_id, new_status)
return port
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("add_router_interface() failed due to %s"), exc)
new_status = nconst.ROUTER_STATUS_ERROR
self.plugin._update_resource_status(
context, "port", port_id, new_status)
@call_log.log
def delete_interface(self, context, router_id, port):
port_id = port['id']
try:
self.ofc.delete_ofc_router_interface(context, router_id, port_id)
new_status = nconst.ROUTER_STATUS_ACTIVE
self.plugin._update_resource_status(context, "port", port_id,
new_status)
port['status'] = new_status
return port
except (nexc.OFCException, nexc.OFCMappingNotFound) as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE("delete_router_interface() failed due to %s"),
exc)
new_status = nconst.ROUTER_STATUS_ERROR
self.plugin._update_resource_status(context, "port", port_id,
new_status)

View File

@ -1,106 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation.
#
# 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.
import mock
OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager'
def patch_ofc_manager():
m = mock.patch(OFC_MANAGER).start()
f = FakeOFCManager()
m.create_ofc_tenant.side_effect = f.create_ofc_tenant
m.delete_ofc_tenant.side_effect = f.delete_ofc_tenant
m.exists_ofc_tenant.side_effect = f.exists_ofc_tenant
m.create_ofc_network.side_effect = f.create_ofc_net
m.delete_ofc_network.side_effect = f.delete_ofc_net
m.exists_ofc_network.side_effect = f.exists_ofc_net
m.create_ofc_port.side_effect = f.create_ofc_port
m.delete_ofc_port.side_effect = f.delete_ofc_port
m.exists_ofc_port.side_effect = f.exists_ofc_port
m.create_ofc_packet_filter.side_effect = f.create_ofc_pf
m.delete_ofc_packet_filter.side_effect = f.delete_ofc_pf
m.exists_ofc_packet_filter.side_effect = f.exists_ofc_pf
m.set_raise_exc = f.set_raise_exc
return m
class FakeOFCManager(object):
def __init__(self):
self.ofc_tenants = {}
self.ofc_nets = {}
self.ofc_ports = {}
self.ofc_pfs = {}
self.raise_exc_map = {}
def set_raise_exc(self, func, raise_exc):
self.raise_exc_map.update({func: raise_exc})
def _raise_exc(self, func):
exc = self.raise_exc_map.get(func)
if exc:
raise exc
def create_ofc_tenant(self, context, tenant_id):
self._raise_exc('create_ofc_tenant')
self.ofc_tenants.update({tenant_id: True})
def exists_ofc_tenant(self, context, tenant_id):
self._raise_exc('exists_ofc_tenant')
return self.ofc_tenants.get(tenant_id, False)
def delete_ofc_tenant(self, context, tenant_id):
self._raise_exc('delete_ofc_tenant')
del self.ofc_tenants[tenant_id]
def create_ofc_net(self, context, tenant_id, net_id, net_name=None):
self._raise_exc('create_ofc_network')
self.ofc_nets.update({net_id: True})
def exists_ofc_net(self, context, net_id):
self._raise_exc('exists_ofc_network')
return self.ofc_nets.get(net_id, False)
def delete_ofc_net(self, context, net_id, net):
self._raise_exc('delete_ofc_network')
del self.ofc_nets[net_id]
def create_ofc_port(self, context, port_id, port):
self._raise_exc('create_ofc_port')
self.ofc_ports.update({port_id: True})
def exists_ofc_port(self, context, port_id):
self._raise_exc('exists_ofc_port')
return self.ofc_ports.get(port_id, False)
def delete_ofc_port(self, context, port_id, port):
self._raise_exc('delete_ofc_port')
del self.ofc_ports[port_id]
def create_ofc_pf(self, context, pf_id, pf_dict):
self._raise_exc('create_ofc_packet_filter')
self.ofc_pfs.update({pf_id: True})
def exists_ofc_pf(self, context, pf_id):
self._raise_exc('exists_ofc_packet_filter')
return self.ofc_pfs.get(pf_id, False)
def delete_ofc_pf(self, context, pf_id):
self._raise_exc('delete_ofc_packet_filter')
del self.ofc_pfs[pf_id]

View File

@ -1,289 +0,0 @@
# 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.
import netaddr
from neutron.common import log as call_log
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec import ofc_driver_base
LOG = logging.getLogger(__name__)
MAX_NUM_OPENFLOW_ROUTER = 2
class StubOFCDriver(ofc_driver_base.OFCDriverBase):
"""Stub OFC driver for testing.
This driver can be used not only for unit tests but also for real testing
as a logging driver. It stores the created resources on OFC and returns
them in get methods().
If autocheck is enabled, it checks whether the specified resource exists
in OFC and raises an exception if it is different from expected status.
"""
def __init__(self, conf):
self.autocheck = False
self.reset_all()
def reset_all(self):
self.ofc_tenant_dict = {}
self.ofc_network_dict = {}
self.ofc_port_dict = {}
self.ofc_filter_dict = {}
self.ofc_router_dict = {}
self.ofc_router_inf_dict = {}
self.ofc_router_route_dict = {}
def enable_autocheck(self):
self.autocheck = True
def disable_autocheck(self):
self.autocheck = False
@call_log.log
def create_tenant(self, description, tenant_id=None):
ofc_id = "ofc-" + tenant_id[:-4]
if self.autocheck:
if ofc_id in self.ofc_tenant_dict:
raise Exception(_('(create_tenant) OFC tenant %s '
'already exists') % ofc_id)
self.ofc_tenant_dict[ofc_id] = {'tenant_id': tenant_id,
'description': description}
return ofc_id
@call_log.log
def delete_tenant(self, ofc_tenant_id):
if ofc_tenant_id in self.ofc_tenant_dict:
del self.ofc_tenant_dict[ofc_tenant_id]
else:
if self.autocheck:
raise Exception(_('(delete_tenant) OFC tenant %s not found')
% ofc_tenant_id)
LOG.debug('delete_tenant: SUCCEED')
@call_log.log
def create_network(self, ofc_tenant_id, description, network_id=None):
ofc_id = "ofc-" + network_id[:-4]
if self.autocheck:
if ofc_tenant_id not in self.ofc_tenant_dict:
raise Exception(_('(create_network) OFC tenant %s not found')
% ofc_tenant_id)
if ofc_id in self.ofc_network_dict:
raise Exception(_('(create_network) OFC network %s '
'already exists') % ofc_id)
self.ofc_network_dict[ofc_id] = {'tenant_id': ofc_tenant_id,
'network_id': network_id,
'description': description}
return ofc_id
@call_log.log
def update_network(self, ofc_network_id, description):
if self.autocheck:
if ofc_network_id not in self.ofc_network_dict:
raise Exception(_('(update_network) OFC network %s not found')
% ofc_network_id)
data = {'description': description}
self.ofc_network_dict[ofc_network_id].update(data)
LOG.debug('update_network: SUCCEED')
@call_log.log
def delete_network(self, ofc_network_id):
if ofc_network_id in self.ofc_network_dict:
del self.ofc_network_dict[ofc_network_id]
else:
if self.autocheck:
raise Exception(_('(delete_network) OFC network %s not found')
% ofc_network_id)
LOG.debug('delete_network: SUCCEED')
@call_log.log
def create_port(self, ofc_network_id, info, port_id=None, filters=None):
ofc_id = "ofc-" + port_id[:-4]
if self.autocheck:
if ofc_network_id not in self.ofc_network_dict:
raise Exception(_('(create_port) OFC network %s not found')
% ofc_network_id)
if ofc_id in self.ofc_port_dict:
raise Exception(_('(create_port) OFC port %s already exists')
% ofc_id)
self.ofc_port_dict[ofc_id] = {'network_id': ofc_network_id,
'port_id': port_id}
if filters:
self.ofc_port_dict[ofc_id]['filters'] = filters
return ofc_id
@call_log.log
def delete_port(self, ofc_port_id):
if ofc_port_id in self.ofc_port_dict:
del self.ofc_port_dict[ofc_port_id]
else:
if self.autocheck:
raise Exception(_('(delete_port) OFC port %s not found')
% ofc_port_id)
LOG.debug('delete_port: SUCCEED')
@classmethod
def filter_supported(cls):
return True
def create_filter(self, context, filter_dict, filter_id=None):
return "ofc-" + filter_id[:-4]
def delete_filter(self, ofc_filter_id):
pass
def convert_ofc_tenant_id(self, context, ofc_tenant_id):
return ofc_tenant_id
def convert_ofc_network_id(self, context, ofc_network_id, tenant_id):
return ofc_network_id
def convert_ofc_port_id(self, context, ofc_port_id, tenant_id, network_id):
return ofc_port_id
def convert_ofc_filter_id(self, context, ofc_filter_id):
return ofc_filter_id
router_supported = True
router_nat_supported = True
@call_log.log
def create_router(self, ofc_tenant_id, router_id, description):
ofc_id = "ofc-" + router_id[:-4]
if self.autocheck:
if ofc_tenant_id not in self.ofc_tenant_dict:
raise Exception(_('(create_router) OFC tenant %s not found')
% ofc_tenant_id)
if ofc_id in self.ofc_router_dict:
raise Exception(_('(create_router) OFC router %s '
'already exists') % ofc_id)
if len(self.ofc_router_dict) >= MAX_NUM_OPENFLOW_ROUTER:
params = {'reason': _("Operation on OFC is failed"),
'status': 409}
raise nexc.OFCException(**params)
self.ofc_router_dict[ofc_id] = {'tenant_id': ofc_tenant_id,
'router_id': router_id,
'description': description}
return ofc_id
@call_log.log
def delete_router(self, ofc_router_id):
if ofc_router_id in self.ofc_router_dict:
del self.ofc_router_dict[ofc_router_id]
else:
if self.autocheck:
raise Exception(_('(delete_router) OFC router %s not found')
% ofc_router_id)
LOG.debug('delete_router: SUCCEED')
@call_log.log
def add_router_interface(self, ofc_router_id, ofc_net_id,
ip_address=None, mac_address=None):
if_id = "ofc-" + uuidutils.generate_uuid()[:-4]
# IP address should have a format of a.b.c.d/N
if ip_address != str(netaddr.IPNetwork(ip_address)):
raise Exception(_('(add_router_interface) '
'ip_address %s is not a valid format (a.b.c.d/N).')
% ip_address)
if self.autocheck:
if ofc_router_id not in self.ofc_router_dict:
raise Exception(_('(add_router_interface) '
'OFC router %s not found') % ofc_router_id)
if ofc_net_id not in self.ofc_network_dict:
raise Exception(_('(add_router_interface) '
'OFC network %s not found') % ofc_net_id)
# Check duplicate destination
self.ofc_router_inf_dict[if_id] = {'router_id': ofc_router_id,
'network_id': ofc_net_id,
'ip_address': ip_address,
'mac_address': mac_address}
LOG.debug('add_router_interface: SUCCEED (if_id=%s)', if_id)
return if_id
@call_log.log
def update_router_interface(self, ofc_router_inf_id,
ip_address=None, mac_address=None):
if ofc_router_inf_id not in self.ofc_router_inf_dict:
if self.autocheck:
raise Exception(_('(delete_router_interface) '
'OFC router interface %s not found')
% ofc_router_inf_id)
self.ofc_router_inf_dict[ofc_router_inf_id] = {}
inf = self.ofc_router_inf_dict[ofc_router_inf_id]
if ip_address:
inf.update({'ip_address': ip_address})
if mac_address:
inf.update({'mac_address': mac_address})
LOG.debug('update_router_route: SUCCEED')
@call_log.log
def delete_router_interface(self, ofc_router_inf_id):
if ofc_router_inf_id in self.ofc_router_inf_dict:
del self.ofc_router_inf_dict[ofc_router_inf_id]
else:
if self.autocheck:
raise Exception(_('(delete_router_interface) '
'OFC router interface %s not found')
% ofc_router_inf_id)
LOG.debug('delete_router_interface: SUCCEED')
@call_log.log
def add_router_route(self, ofc_router_id, destination, nexthop):
route_id = "ofc-" + uuidutils.generate_uuid()[:-4]
# IP address format check
netaddr.IPNetwork(destination)
netaddr.IPAddress(nexthop)
if self.autocheck:
if ofc_router_id not in self.ofc_router_dict:
raise Exception(_('(add_router_route) OFC router %s not found')
% ofc_router_id)
# Check duplicate destination
if destination in [route['destination'] for route in
self.ofc_router_route_dict.values()]:
raise Exception(_('(add_router_route) '
'route to "%s" already exists') % destination)
self.ofc_router_route_dict[route_id] = {'router_id': ofc_router_id,
'destination': destination,
'nexthop': nexthop}
LOG.debug('add_router_route: SUCCEED (route_id=%s)', route_id)
return route_id
@call_log.log
def delete_router_route(self, ofc_router_route_id):
if ofc_router_route_id in self.ofc_router_route_dict:
del self.ofc_router_route_dict[ofc_router_route_id]
else:
if self.autocheck:
raise Exception(_('(delete_router_route) OFC router route %s '
'not found') % ofc_router_route_id)
LOG.debug('delete_router_route: SUCCEED')
@call_log.log
def list_router_routes(self, ofc_router_id):
if self.autocheck:
if ofc_router_id not in self.ofc_router_dict:
raise Exception(_('(delete_router) OFC router %s not found')
% ofc_router_id)
routes = [{'id': k,
'destination': v['destination'],
'nexthop': v['nexthop']}
for k, v in self.ofc_router_route_dict.items()
if v['router_id'] == ofc_router_id]
LOG.debug('list_router_routes: routes=%s', routes)
return routes

View File

@ -1,118 +0,0 @@
# Copyright 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.
import contextlib
from neutron.api.rpc.handlers import l3_rpc
from neutron.common import constants
from neutron.tests.unit.nec import test_nec_plugin
from neutron.tests.unit.openvswitch import test_agent_scheduler
L3_HOSTA = test_agent_scheduler.L3_HOSTA
L3_HOSTB = test_agent_scheduler.L3_HOSTB
class NecAgentSchedulerTestCase(
test_agent_scheduler.OvsAgentSchedulerTestCase,
test_nec_plugin.NecPluginV2TestCaseBase):
plugin_str = test_nec_plugin.PLUGIN_NAME
l3_plugin = None
def setUp(self):
self.setup_nec_plugin_base()
super(NecAgentSchedulerTestCase, self).setUp()
class NecDhcpAgentNotifierTestCase(
test_agent_scheduler.OvsDhcpAgentNotifierTestCase,
test_nec_plugin.NecPluginV2TestCaseBase):
plugin_str = test_nec_plugin.PLUGIN_NAME
l3_plugin = None
def setUp(self):
self.setup_nec_plugin_base()
super(NecDhcpAgentNotifierTestCase, self).setUp()
class NecL3AgentNotifierTestCase(
test_agent_scheduler.OvsL3AgentNotifierTestCase,
test_nec_plugin.NecPluginV2TestCaseBase):
plugin_str = test_nec_plugin.PLUGIN_NAME
l3_plugin = None
def setUp(self):
self.setup_nec_plugin_base()
super(NecL3AgentNotifierTestCase, self).setUp()
class NecL3AgentSchedulerWithOpenFlowRouter(
test_agent_scheduler.OvsAgentSchedulerTestCaseBase,
test_nec_plugin.NecPluginV2TestCaseBase):
plugin_str = test_nec_plugin.PLUGIN_NAME
l3_plugin = None
def setUp(self):
self.setup_nec_plugin_base()
super(NecL3AgentSchedulerWithOpenFlowRouter, self).setUp()
def test_router_auto_schedule_with_l3agent_and_openflow(self):
with contextlib.nested(
self.router(),
self.router(arg_list=('provider',),
provider='openflow'
)) as (r1, r2):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
ret_b = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTB)
l3_agents = self._list_l3_agents_hosting_router(
r1['router']['id'])
self.assertEqual(1, len(ret_a))
self.assertFalse(len(ret_b))
self.assertIn(r1['router']['id'], [r['id'] for r in ret_a])
self.assertNotIn(r2['router']['id'], [r['id'] for r in ret_a])
self.assertEqual(1, len(l3_agents['agents']))
self.assertEqual(L3_HOSTA, l3_agents['agents'][0]['host'])
def test_router_auto_schedule_only_with_openflow_router(self):
with contextlib.nested(
self.router(arg_list=('provider',), provider='openflow'),
self.router(arg_list=('provider',), provider='openflow')
) as (r1, r2):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
l3_agents_1 = self._list_l3_agents_hosting_router(
r1['router']['id'])
l3_agents_2 = self._list_l3_agents_hosting_router(
r2['router']['id'])
self.assertFalse(len(ret_a))
self.assertNotIn(r1['router']['id'], [r['id'] for r in ret_a])
self.assertNotIn(r2['router']['id'], [r['id'] for r in ret_a])
self.assertFalse(len(l3_agents_1['agents']))
self.assertFalse(len(l3_agents_2['agents']))
def test_add_router_to_l3_agent_for_openflow_router(self):
with self.router(arg_list=('provider',), provider='openflow') as r1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
self._add_router_to_l3_agent(hosta_id,
r1['router']['id'],
expected_code=409)

View File

@ -1,41 +0,0 @@
# 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.
from neutron.plugins.nec.common import config
from neutron.tests import base
class ConfigurationTest(base.BaseTestCase):
def test_defaults(self):
self.assertEqual('br-int', config.CONF.OVS.integration_bridge)
self.assertEqual(2, config.CONF.AGENT.polling_interval)
self.assertEqual('127.0.0.1', config.CONF.OFC.host)
self.assertEqual('8888', config.CONF.OFC.port)
# Check path_prefix is an empty string explicitly.
self.assertEqual('', config.CONF.OFC.path_prefix)
self.assertEqual('trema', config.CONF.OFC.driver)
self.assertTrue(config.CONF.OFC.enable_packet_filter)
self.assertTrue(config.CONF.OFC.support_packet_filter_on_ofc_router)
self.assertFalse(config.CONF.OFC.use_ssl)
self.assertIsNone(config.CONF.OFC.key_file)
self.assertIsNone(config.CONF.OFC.cert_file)
def test_shortcuts(self):
self.assertEqual(config.CONF.OVS.integration_bridge,
config.OVS.integration_bridge)
self.assertEqual(config.CONF.AGENT.polling_interval,
config.AGENT.polling_interval)
self.assertEqual(config.CONF.OFC.host, config.OFC.host)

View File

@ -1,172 +0,0 @@
# 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.
import contextlib
import random
from neutron.common import constants as q_const
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import api as ndb
from neutron.tests.unit.nec import test_nec_plugin
class NECPluginV2DBTestBase(test_nec_plugin.NecPluginV2TestCase):
"""Class conisting of NECPluginV2 DB unit tests."""
def setUp(self):
"""Setup for tests."""
super(NECPluginV2DBTestBase, self).setUp()
self.session = self.context.session
def get_ofc_item_random_params(self):
"""create random parameters for ofc_item test."""
ofc_id = uuidutils.generate_uuid()
neutron_id = uuidutils.generate_uuid()
none = uuidutils.generate_uuid()
return ofc_id, neutron_id, none
@contextlib.contextmanager
def portinfo_random_params(self):
with self.port() as port:
params = {'port_id': port['port']['id'],
'datapath_id': hex(random.randint(0, 0xffffffff)),
'port_no': random.randint(1, 100),
'vlan_id': random.randint(q_const.MIN_VLAN_TAG,
q_const.MAX_VLAN_TAG),
'mac': ':'.join(["%02x" % random.randint(0, 0xff)
for x in range(6)])
}
yield params
class NECPluginV2DBOfcMappingTest(NECPluginV2DBTestBase):
def test_add_ofc_item(self):
"""test add OFC item."""
o, q, n = self.get_ofc_item_random_params()
tenant = ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
self.assertEqual(tenant.ofc_id, o)
self.assertEqual(tenant.neutron_id, q)
def test_add_ofc_item_duplicate_entry(self):
o, q, n = self.get_ofc_item_random_params()
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
self.assertRaises(nexc.NECDBException,
ndb.add_ofc_item,
self.session, 'ofc_tenant', q, o)
def test_get_ofc_item(self):
o, q, n = self.get_ofc_item_random_params()
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
tenant = ndb.get_ofc_item(self.session, 'ofc_tenant', q)
self.assertEqual(tenant.ofc_id, o)
self.assertEqual(tenant.neutron_id, q)
def test_get_ofc_item_for_nonexisting_entry(self):
self.assertIsNone(
ndb.get_ofc_item(self.session, 'ofc_tenant', 'non-exist-id'))
def test_get_ofc_id(self):
o, q, n = self.get_ofc_item_random_params()
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
tenant_id = ndb.get_ofc_id(self.session, 'ofc_tenant', q)
self.assertEqual(tenant_id, o)
def test_get_ofc_id_for_nonexisting_entry(self):
self.assertRaises(nexc.OFCMappingNotFound,
ndb.get_ofc_id,
self.session, 'ofc_tenant', 'non-exist-id')
def test_exists_ofc_item(self):
o, q, n = self.get_ofc_item_random_params()
self.assertFalse(ndb.exists_ofc_item(self.session, 'ofc_tenant', q))
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
self.assertTrue(ndb.exists_ofc_item(self.session, 'ofc_tenant', q))
ndb.del_ofc_item(self.session, 'ofc_tenant', q)
self.assertFalse(ndb.exists_ofc_item(self.session, 'ofc_tenant', q))
def test_find_ofc_item(self):
o, q, n = self.get_ofc_item_random_params()
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
tenant = ndb.find_ofc_item(self.session, 'ofc_tenant', o)
self.assertEqual(tenant.ofc_id, o)
self.assertEqual(tenant.neutron_id, q)
def test_find_ofc_item_for_nonexisting_entry(self):
self.assertIsNone(
ndb.find_ofc_item(self.session, 'ofc_tenant', 'non-existi-id'))
def test_del_ofc_item(self):
o, q, n = self.get_ofc_item_random_params()
ndb.add_ofc_item(self.session, 'ofc_tenant', q, o)
self.assertTrue(ndb.del_ofc_item(self.session, 'ofc_tenant', q))
self.assertIsNone(ndb.get_ofc_item(self.session, 'ofc_tenant', q))
self.assertIsNone(ndb.find_ofc_item(self.session, 'ofc_tenant', o))
def test_del_ofc_item_for_nonexisting_entry(self):
self.assertFalse(
ndb.del_ofc_item(self.session, 'ofc_tenant', 'non-existi-id'))
class NECPluginV2DBPortInfoTest(NECPluginV2DBTestBase):
def _compare_portinfo(self, portinfo, expected):
self.assertEqual(portinfo.id, expected['port_id'])
self.assertEqual(portinfo.datapath_id, expected['datapath_id'])
self.assertEqual(portinfo.port_no, expected['port_no'])
self.assertEqual(portinfo.vlan_id, expected['vlan_id'])
self.assertEqual(portinfo.mac, expected['mac'])
def _add_portinfo(self, session, params):
return ndb.add_portinfo(session, params['port_id'],
params['datapath_id'], params['port_no'],
params['vlan_id'], params['mac'])
def testd_add_portinfo(self):
"""test add portinfo."""
with self.portinfo_random_params() as params:
portinfo = self._add_portinfo(self.session, params)
self._compare_portinfo(portinfo, params)
exception_raised = False
try:
self._add_portinfo(self.session, params)
except nexc.NECDBException:
exception_raised = True
self.assertTrue(exception_raised)
def teste_get_portinfo(self):
"""test get portinfo."""
with self.portinfo_random_params() as params:
self._add_portinfo(self.session, params)
portinfo = ndb.get_portinfo(self.session, params['port_id'])
self._compare_portinfo(portinfo, params)
nonexist_id = uuidutils.generate_uuid()
portinfo_none = ndb.get_portinfo(self.session, nonexist_id)
self.assertIsNone(portinfo_none)
def testf_del_portinfo(self):
"""test delete portinfo."""
with self.portinfo_random_params() as params:
self._add_portinfo(self.session, params)
portinfo = ndb.get_portinfo(self.session, params['port_id'])
self.assertEqual(portinfo.id, params['port_id'])
ndb.del_portinfo(self.session, params['port_id'])
portinfo_none = ndb.get_portinfo(self.session, params['port_id'])
self.assertIsNone(portinfo_none)

View File

@ -1,345 +0,0 @@
# Copyright 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.
import contextlib
import copy
import itertools
import time
import mock
from oslo_config import cfg
from six import moves
import testtools
from neutron.agent.linux import ovs_lib
from neutron.extensions import securitygroup as ext_sg
from neutron.plugins.nec.agent import nec_neutron_agent
from neutron.tests import base
DAEMON_LOOP_COUNT = 10
OVS_DPID = '00000629355b6943'
OVS_DPID_0X = '0x' + OVS_DPID
class TestNecAgentBase(base.BaseTestCase):
def setUp(self):
super(TestNecAgentBase, self).setUp()
cfg.CONF.set_default('firewall_driver',
'neutron.agent.firewall.NoopFirewallDriver',
group='SECURITYGROUP')
cfg.CONF.set_override('host', 'dummy-host')
with contextlib.nested(
mock.patch.object(ovs_lib.OVSBridge, 'get_datapath_id',
return_value=OVS_DPID),
mock.patch('socket.gethostname', return_value='dummy-host'),
mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall'),
mock.patch('neutron.agent.rpc.PluginReportStateAPI')
) as (get_datapath_id, gethostname,
loopingcall, state_rpc_api):
kwargs = {'integ_br': 'integ_br', 'polling_interval': 1}
self.agent = nec_neutron_agent.NECNeutronAgent(**kwargs)
self.loopingcall = loopingcall
self.state_rpc_api = state_rpc_api
class TestNecAgent(TestNecAgentBase):
def _setup_mock(self):
vif_ports = [ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
self.agent.int_br),
ovs_lib.VifPort('port2', '2', 'id-2', 'mac-2',
self.agent.int_br)]
self.get_vif_ports = mock.patch.object(
ovs_lib.OVSBridge, 'get_vif_ports',
return_value=vif_ports).start()
self.update_ports = mock.patch.object(
nec_neutron_agent.NECPluginApi, 'update_ports').start()
self.prepare_devices_filter = mock.patch.object(
self.agent.sg_agent, 'prepare_devices_filter').start()
self.remove_devices_filter = mock.patch.object(
self.agent.sg_agent, 'remove_devices_filter').start()
def _test_single_loop(self, with_exc=False, need_sync=False):
self.agent.cur_ports = ['id-0', 'id-1']
self.agent.need_sync = need_sync
self.agent.loop_handler()
if with_exc:
self.assertEqual(self.agent.cur_ports, ['id-0', 'id-1'])
self.assertTrue(self.agent.need_sync)
else:
self.assertEqual(self.agent.cur_ports, ['id-1', 'id-2'])
self.assertFalse(self.agent.need_sync)
def test_single_loop_normal(self):
self._setup_mock()
self._test_single_loop()
agent_id = 'nec-q-agent.dummy-host'
self.update_ports.assert_called_once_with(
mock.ANY, agent_id, OVS_DPID_0X,
[{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
['id-0'])
self.prepare_devices_filter.assert_called_once_with(['id-2'])
self.remove_devices_filter.assert_called_once_with(['id-0'])
def test_single_loop_need_sync(self):
self._setup_mock()
self._test_single_loop(need_sync=True)
agent_id = 'nec-q-agent.dummy-host'
self.update_ports.assert_called_once_with(
mock.ANY, agent_id, OVS_DPID_0X,
[{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'},
{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
[])
self.prepare_devices_filter.assert_called_once_with(['id-1', 'id-2'])
self.assertFalse(self.remove_devices_filter.call_count)
def test_single_loop_with_sg_exception_remove(self):
self._setup_mock()
self.update_ports.side_effect = Exception()
self._test_single_loop(with_exc=True)
def test_single_loop_with_sg_exception_prepare(self):
self._setup_mock()
self.prepare_devices_filter.side_effect = Exception()
self._test_single_loop(with_exc=True)
def test_single_loop_with_update_ports_exception(self):
self._setup_mock()
self.remove_devices_filter.side_effect = Exception()
self._test_single_loop(with_exc=True)
def test_daemon_loop(self):
def state_check(index):
self.assertEqual(len(self.vif_ports_scenario[index]),
len(self.agent.cur_ports))
# Fake time.sleep to stop the infinite loop in daemon_loop()
self.sleep_count = 0
def sleep_mock(*args, **kwargs):
state_check(self.sleep_count)
self.sleep_count += 1
if self.sleep_count >= DAEMON_LOOP_COUNT:
raise RuntimeError()
vif_ports = [ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
self.agent.int_br),
ovs_lib.VifPort('port2', '2', 'id-2', 'mac-2',
self.agent.int_br)]
self.vif_ports_scenario = [[], [], vif_ports[0:1], vif_ports[0:2],
vif_ports[1:2], []]
# Ensure vif_ports_scenario is longer than DAEMON_LOOP_COUNT
if len(self.vif_ports_scenario) < DAEMON_LOOP_COUNT:
self.vif_ports_scenario.extend(
[] for _i in moves.xrange(DAEMON_LOOP_COUNT -
len(self.vif_ports_scenario)))
with contextlib.nested(
mock.patch.object(time, 'sleep', side_effect=sleep_mock),
mock.patch.object(ovs_lib.OVSBridge, 'get_vif_ports'),
mock.patch.object(nec_neutron_agent.NECPluginApi, 'update_ports'),
mock.patch.object(self.agent.sg_agent, 'prepare_devices_filter'),
mock.patch.object(self.agent.sg_agent, 'remove_devices_filter')
) as (sleep, get_vif_potrs, update_ports,
prepare_devices_filter, remove_devices_filter):
get_vif_potrs.side_effect = self.vif_ports_scenario
with testtools.ExpectedException(RuntimeError):
self.agent.daemon_loop()
self.assertEqual(update_ports.call_count, 4)
self.assertEqual(sleep.call_count, DAEMON_LOOP_COUNT)
agent_id = 'nec-q-agent.dummy-host'
expected = [
mock.call(mock.ANY, agent_id, OVS_DPID_0X,
[{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'}],
[]),
mock.call(mock.ANY, agent_id, OVS_DPID_0X,
[{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
[]),
mock.call(mock.ANY, agent_id, OVS_DPID_0X,
[], ['id-1']),
mock.call(mock.ANY, agent_id, OVS_DPID_0X,
[], ['id-2'])
]
update_ports.assert_has_calls(expected)
expected = [mock.call(['id-1']),
mock.call(['id-2'])]
self.assertEqual(prepare_devices_filter.call_count, 2)
prepare_devices_filter.assert_has_calls(expected)
self.assertEqual(remove_devices_filter.call_count, 2)
remove_devices_filter.assert_has_calls(expected)
sleep.assert_called_with(self.agent.polling_interval)
def test_report_state_installed(self):
self.loopingcall.assert_called_once_with(self.agent._report_state)
instance = self.loopingcall.return_value
self.assertTrue(instance.start.called)
def _check_report_state(self, cur_ports, num_ports, fail_mode,
first=False):
self.assertEqual(first or fail_mode,
'start_flag' in self.agent.agent_state)
self.agent.cur_ports = cur_ports
self.agent._report_state()
self.assertEqual(fail_mode,
'start_flag' in self.agent.agent_state)
self.assertEqual(self.agent.
agent_state['configurations']['devices'],
num_ports)
self.num_ports_hist.append(num_ports)
def _test_report_state(self, fail_mode):
log_mocked = mock.patch.object(nec_neutron_agent, 'LOG')
log_patched = log_mocked.start()
def record_state(*args, **kwargs):
self.record_calls.append(copy.deepcopy(args))
if fail_mode:
raise Exception()
self.record_calls = []
self.num_ports_hist = []
state_rpc = self.state_rpc_api.return_value
state_rpc.report_state.side_effect = record_state
dummy_vif = ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1', None)
self.state_rpc_api.assert_called_once_with('q-plugin')
self.assertIn('start_flag', self.agent.agent_state)
self._check_report_state([], 0, fail_mode, first=True)
self._check_report_state([dummy_vif] * 2, 2, fail_mode)
self._check_report_state([dummy_vif] * 5, 5, fail_mode)
self._check_report_state([], 0, fail_mode)
# Since loopingcall start is mocked, call_count is same as
# the call count of check_report_state.
self.assertEqual(state_rpc.report_state.call_count, 4)
self.assertEqual(len(self.record_calls), 4)
for i, x in enumerate(itertools.izip(self.record_calls,
self.num_ports_hist)):
rec, num_ports = x
expected_state = {
'binary': 'neutron-nec-agent',
'host': 'dummy-host',
'topic': 'N/A',
'configurations': {'devices': 0},
'agent_type': 'NEC plugin agent'}
expected_state['configurations']['devices'] = num_ports
if i == 0 or fail_mode:
expected_state['start_flag'] = True
self.assertEqual(expected_state, rec[1])
self.assertEqual(fail_mode, log_patched.exception.called)
def test_report_state(self):
self._test_report_state(fail_mode=False)
def test_report_state_fail(self):
self._test_report_state(fail_mode=True)
class TestNecAgentCallback(TestNecAgentBase):
def test_port_update(self):
with contextlib.nested(
mock.patch.object(ovs_lib.OVSBridge, 'get_vif_port_by_id'),
mock.patch.object(self.agent.sg_agent, 'refresh_firewall')
) as (get_vif_port_by_id, refresh_firewall):
context = mock.Mock()
vifport = ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
self.agent.int_br)
# The OVS port does not exist.
get_vif_port_by_id.return_value = None
port = {'id': 'update-port-1'}
self.agent.callback_nec.port_update(context, port=port)
self.assertEqual(get_vif_port_by_id.call_count, 1)
self.assertFalse(refresh_firewall.call_count)
# The OVS port exists but no security group is associated.
get_vif_port_by_id.return_value = vifport
port = {'id': 'update-port-1'}
self.agent.callback_nec.port_update(context, port=port)
self.assertEqual(get_vif_port_by_id.call_count, 2)
self.assertFalse(refresh_firewall.call_count)
# The OVS port exists but a security group is associated.
get_vif_port_by_id.return_value = vifport
port = {'id': 'update-port-1',
ext_sg.SECURITYGROUPS: ['default']}
self.agent.callback_nec.port_update(context, port=port)
self.assertEqual(get_vif_port_by_id.call_count, 3)
self.assertEqual(refresh_firewall.call_count, 1)
get_vif_port_by_id.return_value = None
port = {'id': 'update-port-1',
ext_sg.SECURITYGROUPS: ['default']}
self.agent.callback_nec.port_update(context, port=port)
self.assertEqual(get_vif_port_by_id.call_count, 4)
self.assertEqual(refresh_firewall.call_count, 1)
class TestNecAgentPluginApi(TestNecAgentBase):
def test_plugin_api(self):
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc.client, 'prepare'),
mock.patch.object(self.agent.plugin_rpc.client, 'call'),
) as (mock_prepare, mock_call):
mock_prepare.return_value = self.agent.plugin_rpc.client
agent_id = 'nec-q-agent.dummy-host'
port_added = [{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'},
{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}]
port_removed = ['id-3', 'id-4', 'id-5']
self.agent.plugin_rpc.update_ports(
mock.sentinel.ctx, agent_id, OVS_DPID_0X,
port_added, port_removed)
mock_call.assert_called_once_with(
mock.sentinel.ctx, 'update_ports',
agent_id=agent_id, datapath_id=OVS_DPID_0X,
port_added=port_added, port_removed=port_removed)
class TestNecAgentMain(base.BaseTestCase):
def test_main(self):
with contextlib.nested(
mock.patch.object(nec_neutron_agent, 'NECNeutronAgent'),
mock.patch.object(nec_neutron_agent, 'common_config'),
mock.patch.object(nec_neutron_agent, 'config')
) as (agent, common_config, cfg):
cfg.OVS.integration_bridge = 'br-int-x'
cfg.AGENT.polling_interval = 10
nec_neutron_agent.main()
self.assertTrue(common_config.setup_logging.called)
agent.assert_has_calls([
mock.call('br-int-x', 10),
mock.call().daemon_loop()
])

View File

@ -1,941 +0,0 @@
# Copyright (c) 2012 OpenStack Foundation.
#
# 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.
import mock
import webob.exc
from neutron.common import constants
from neutron.common import test_lib
from neutron.common import topics
from neutron import context
from neutron.db import db_base_plugin_v2
from neutron import manager
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec import nec_plugin
from neutron.tests.unit.nec import fake_ofc_manager
from neutron.tests.unit import test_db_plugin as test_plugin
from neutron.tests.unit import test_extension_allowedaddresspairs as test_pair
PLUGIN_NAME = 'neutron.plugins.nec.nec_plugin.NECPluginV2'
OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager'
NOTIFIER = 'neutron.plugins.nec.nec_plugin.NECPluginV2AgentNotifierApi'
NEC_PLUGIN_INI = """
[DEFAULT]
api_extensions_path = neutron/plugins/nec/extensions
[OFC]
driver = neutron.tests.unit.nec.stub_ofc_driver.StubOFCDriver
enable_packet_filter = False
"""
class NecPluginV2TestCaseBase(object):
_nec_ini = NEC_PLUGIN_INI
def _set_nec_ini(self):
self.nec_ini_file = self.get_temp_file_path('nec.ini')
with open(self.nec_ini_file, 'w') as f:
f.write(self._nec_ini)
if 'config_files' in test_lib.test_config.keys():
for c in test_lib.test_config['config_files']:
if c.rfind("/nec.ini") > -1:
test_lib.test_config['config_files'].remove(c)
test_lib.test_config['config_files'].append(self.nec_ini_file)
else:
test_lib.test_config['config_files'] = [self.nec_ini_file]
self.addCleanup(self._clean_nec_ini)
def _clean_nec_ini(self):
test_lib.test_config['config_files'].remove(self.nec_ini_file)
self.nec_ini_file = None
def patch_remote_calls(self):
self.plugin_notifier_p = mock.patch(NOTIFIER)
self.ofc_manager_p = mock.patch(OFC_MANAGER)
self.plugin_notifier_p.start()
self.ofc_manager_p.start()
def setup_nec_plugin_base(self):
self._set_nec_ini()
self.patch_remote_calls()
class NecPluginV2TestCase(NecPluginV2TestCaseBase,
test_plugin.NeutronDbPluginV2TestCase):
_plugin_name = PLUGIN_NAME
def rpcapi_update_ports(self, agent_id='nec-q-agent.fake',
datapath_id="0xabc", added=[], removed=[]):
kwargs = {'topic': topics.AGENT,
'agent_id': agent_id,
'datapath_id': datapath_id,
'port_added': added, 'port_removed': removed}
self.callback_nec.update_ports(self.context, **kwargs)
def setUp(self, plugin=None, ext_mgr=None):
self._set_nec_ini()
plugin = plugin or self._plugin_name
super(NecPluginV2TestCase, self).setUp(plugin, ext_mgr=ext_mgr)
self.plugin = manager.NeutronManager.get_plugin()
self.plugin.ofc = fake_ofc_manager.patch_ofc_manager()
self.ofc = self.plugin.ofc
self.callback_nec = nec_plugin.NECPluginV2RPCCallbacks(self.plugin)
self.context = context.get_admin_context()
self.net_create_status = 'ACTIVE'
self.port_create_status = 'DOWN'
class TestNecBasicGet(test_plugin.TestBasicGet, NecPluginV2TestCase):
pass
class TestNecV2HTTPResponse(test_plugin.TestV2HTTPResponse,
NecPluginV2TestCase):
pass
class TestNecNetworksV2(test_plugin.TestNetworksV2, NecPluginV2TestCase):
pass
class TestNecPortsV2Callback(NecPluginV2TestCase):
def _get_portinfo(self, port_id):
return ndb.get_portinfo(self.context.session, port_id)
def test_portinfo_create(self):
with self.port() as port:
port_id = port['port']['id']
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'DOWN')
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
self.assertIsNone(self._get_portinfo(port_id))
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'ACTIVE')
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertIsNotNone(self._get_portinfo(port_id))
expected = [
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY),
]
self.ofc.assert_has_calls(expected)
def test_portinfo_delete_before_port_deletion(self):
self._test_portinfo_delete()
def test_portinfo_delete_after_port_deletion(self):
self._test_portinfo_delete(portinfo_delete_first=False)
def _test_portinfo_delete(self, portinfo_delete_first=True):
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 456}
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
self.assertIsNone(self._get_portinfo(port_id))
self.rpcapi_update_ports(added=[portinfo])
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
self.assertIsNotNone(self._get_portinfo(port_id))
# Before port-deletion, switch port removed message is sent.
if portinfo_delete_first:
self.rpcapi_update_ports(removed=[port_id])
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self.assertIsNone(self._get_portinfo(port_id))
self._delete('ports', port['port']['id'])
# The port and portinfo is expected to delete when exiting with-clause.
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self.assertIsNone(self._get_portinfo(port_id))
if not portinfo_delete_first:
self.rpcapi_update_ports(removed=[port_id])
# Ensure port deletion is called once.
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self.assertIsNone(self._get_portinfo(port_id))
expected = [
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY),
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.delete_ofc_port(mock.ANY, port_id, mock.ANY),
]
self.ofc.assert_has_calls(expected)
def test_portinfo_added_unknown_port(self):
portinfo = {'id': 'dummy-p1', 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.assertIsNone(ndb.get_portinfo(self.context.session,
'dummy-p1'))
self.assertEqual(self.ofc.exists_ofc_port.call_count, 0)
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
def _test_portinfo_change(self, portinfo_change_first=True):
with self.port() as port:
port_id = port['port']['id']
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
self.assertEqual(ndb.get_portinfo(self.context.session,
port_id).port_no, 123)
if portinfo_change_first:
portinfo = {'id': port_id, 'port_no': 456}
self.rpcapi_update_ports(added=[portinfo])
# OFC port is recreated.
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self.assertEqual(ndb.get_portinfo(self.context.session,
port_id).port_no, 456)
self._delete('ports', port['port']['id'])
if not portinfo_change_first:
# The port is expected to delete when exiting with-clause.
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
portinfo = {'id': port_id, 'port_no': 456}
self.rpcapi_update_ports(added=[portinfo])
# No OFC operations are expected.
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self.assertIsNone(ndb.get_portinfo(self.context.session, port_id))
def test_portinfo_change(self):
self._test_portinfo_change()
def test_portinfo_change_for_nonexisting_port(self):
self._test_portinfo_change(portinfo_change_first=False)
def test_port_migration(self):
agent_id_a, datapath_id_a, port_no_a = 'nec-q-agent.aa', '0xaaa', 10
agent_id_b, datapath_id_b, port_no_b = 'nec-q-agent.bb', '0xbbb', 11
with self.port() as port:
port_id = port['port']['id']
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'DOWN')
portinfo_a = {'id': port_id, 'port_no': port_no_a}
self.rpcapi_update_ports(agent_id=agent_id_a,
datapath_id=datapath_id_a,
added=[portinfo_a])
portinfo_b = {'id': port_id, 'port_no': port_no_b}
self.rpcapi_update_ports(agent_id=agent_id_b,
datapath_id=datapath_id_b,
added=[portinfo_b])
self.rpcapi_update_ports(agent_id=agent_id_a,
datapath_id=datapath_id_a,
removed=[port_id])
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'ACTIVE')
self.assertTrue(self.ofc.ofc_ports[port_id])
expected = [
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY),
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.delete_ofc_port(mock.ANY, port_id, mock.ANY),
mock.call.exists_ofc_port(mock.ANY, port_id),
mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(2, self.ofc.create_ofc_port.call_count)
self.assertEqual(1, self.ofc.delete_ofc_port.call_count)
def test_portinfo_readd(self):
with self.port() as port:
port_id = port['port']['id']
self.plugin.get_port(self.context, port_id)
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'ACTIVE')
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
self.assertIsNotNone(self._get_portinfo(port_id))
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
sport = self.plugin.get_port(self.context, port_id)
self.assertEqual(sport['status'], 'ACTIVE')
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
self.assertIsNotNone(self._get_portinfo(port_id))
class TestNecPluginDbTest(NecPluginV2TestCase):
def test_update_resource(self):
with self.network() as network:
self.assertEqual("ACTIVE", network['network']['status'])
net_id = network['network']['id']
for status in ["DOWN", "BUILD", "ERROR", "ACTIVE"]:
self.plugin._update_resource_status(
self.context, 'network', net_id,
getattr(constants, 'NET_STATUS_%s' % status))
n = self.plugin._get_network(self.context, net_id)
self.assertEqual(status, n.status)
class TestNecPluginOfcManager(NecPluginV2TestCase):
def setUp(self):
super(TestNecPluginOfcManager, self).setUp()
self.ofc = self.plugin.ofc
def _create_resource(self, resource, data):
collection = resource + 's'
data = {resource: data}
req = self.new_create_request(collection, data)
res = self.deserialize(self.fmt, req.get_response(self.api))
return res[resource]
def _update_resource(self, resource, id, data):
collection = resource + 's'
data = {resource: data}
req = self.new_update_request(collection, data, id)
res = self.deserialize(self.fmt, req.get_response(self.api))
return res[resource]
def _show_resource(self, resource, id):
collection = resource + 's'
req = self.new_show_request(collection, id)
res = self.deserialize(self.fmt, req.get_response(self.api))
return res[resource]
def _list_resource(self, resource):
collection = resource + 's'
req = self.new_list_request(collection)
res = req.get_response(self.api)
return res[collection]
def _delete_resource(self, resource, id):
collection = resource + 's'
req = self.new_delete_request(collection, id)
res = req.get_response(self.api)
return res.status_int
def test_create_network(self):
net = None
ctx = mock.ANY
with self.network() as network:
net = network['network']
self.assertEqual(network['network']['status'], 'ACTIVE')
self._delete('networks', network['network']['id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_create_network_with_admin_state_down(self):
net = None
ctx = mock.ANY
with self.network(admin_state_up=False) as network:
net = network['network']
self.assertEqual(network['network']['status'], 'DOWN')
self._delete('networks', network['network']['id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_create_two_network(self):
nets = []
ctx = mock.ANY
with self.network() as net1:
nets.append(net1['network'])
self.assertEqual(net1['network']['status'], 'ACTIVE')
with self.network() as net2:
nets.append(net2['network'])
self.assertEqual(net2['network']['status'], 'ACTIVE')
self._delete('networks', net2['network']['id'])
self._delete('networks', net1['network']['id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, nets[0]['id'],
nets[0]['name']),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, nets[1]['id'],
nets[1]['name']),
mock.call.exists_ofc_network(ctx, nets[1]['id']),
mock.call.delete_ofc_network(ctx, nets[1]['id'], mock.ANY),
mock.call.exists_ofc_network(ctx, nets[0]['id']),
mock.call.delete_ofc_network(ctx, nets[0]['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_create_network_fail(self):
self.ofc.create_ofc_network.side_effect = nexc.OFCException(
reason='hoge')
net = None
ctx = mock.ANY
# NOTE: We don't delete network through api, but db will be cleaned in
# tearDown(). When OFCManager has failed to create a network on OFC,
# it does not keeps ofc_network entry and will fail to delete this
# network from OFC. Deletion of network is not the scope of this test.
with self.network() as network:
net = network['network']
self.assertEqual(net['status'], 'ERROR')
net_ref = self._show('networks', net['id'])
self.assertEqual(net_ref['network']['status'], 'ERROR')
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name'])
]
self.ofc.assert_has_calls(expected)
def test_update_network(self):
net = None
ctx = mock.ANY
with self.network() as network:
net = network['network']
self.assertEqual(network['network']['status'], 'ACTIVE')
net_ref = self._show('networks', net['id'])
self.assertEqual(net_ref['network']['status'], 'ACTIVE')
# Set admin_state_up to False
res = self._update_resource('network', net['id'],
{'admin_state_up': False})
self.assertFalse(res['admin_state_up'])
self.assertEqual(res['status'], 'DOWN')
net_ref = self._show('networks', net['id'])
self.assertEqual(net_ref['network']['status'], 'DOWN')
# Set admin_state_up to True
res = self._update_resource('network', net['id'],
{'admin_state_up': True})
self.assertTrue(res['admin_state_up'])
self.assertEqual(res['status'], 'ACTIVE')
net_ref = self._show('networks', net['id'])
self.assertEqual(net_ref['network']['status'], 'ACTIVE')
self._delete('networks', network['network']['id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_create_port_no_ofc_creation(self):
net = None
p1 = None
ctx = mock.ANY
with self.subnet() as subnet:
with self.port(subnet=subnet) as port:
p1 = port['port']
net_id = port['port']['network_id']
net = self._show_resource('network', net_id)
self.assertEqual(net['status'], 'ACTIVE')
self.assertEqual(p1['status'], 'DOWN')
p1_ref = self._show('ports', p1['id'])
self.assertEqual(p1_ref['port']['status'], 'DOWN')
self._delete('ports', port['port']['id'])
self._delete('networks', port['port']['network_id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_create_port_with_ofc_creation(self):
net = None
p1 = None
ctx = mock.ANY
with self.subnet() as subnet:
with self.port(subnet=subnet) as port:
p1 = port['port']
net_id = port['port']['network_id']
net = self._show_resource('network', net_id)
self.assertEqual(net['status'], 'ACTIVE')
self.assertEqual(p1['status'], 'DOWN')
p1_ref = self._show('ports', p1['id'])
self.assertEqual(p1_ref['port']['status'], 'DOWN')
# Check the port is not created on OFC
self.assertFalse(self.ofc.create_ofc_port.call_count)
# Register portinfo, then the port is created on OFC
portinfo = {'id': p1['id'], 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
p1_ref = self._show('ports', p1['id'])
self.assertEqual(p1_ref['port']['status'], 'ACTIVE')
self._delete('ports', port['port']['id'])
self._delete('networks', port['port']['network_id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.create_ofc_port(ctx, p1['id'], mock.ANY),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.delete_ofc_port(ctx, p1['id'], mock.ANY),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_delete_network_with_dhcp_port(self):
ctx = mock.ANY
with self.network() as network:
with self.subnet(network=network):
net = network['network']
p = self._create_resource(
'port',
{'network_id': net['id'],
'tenant_id': net['tenant_id'],
'device_owner': constants.DEVICE_OWNER_DHCP,
'device_id': 'dhcp-port1'})
# Make sure that the port is created on OFC.
portinfo = {'id': p['id'], 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
# In a case of dhcp port, the port is deleted automatically
# when delete_network.
self._delete('networks', network['network']['id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id,
net['id'], net['name']),
mock.call.exists_ofc_port(ctx, p['id']),
mock.call.create_ofc_port(ctx, p['id'], mock.ANY),
mock.call.exists_ofc_port(ctx, p['id']),
mock.call.delete_ofc_port(ctx, p['id'], mock.ANY),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_delete_network_with_error_status(self):
self.ofc.set_raise_exc('create_ofc_network',
nexc.OFCException(reason='fake error'))
with self.network() as net:
net_id = net['network']['id']
net_ref = self._show('networks', net_id)
self.assertEqual(net_ref['network']['status'], 'ERROR')
self._delete('networks', net['network']['id'])
ctx = mock.ANY
tenant_id = self._tenant_id
net_name = mock.ANY
net = mock.ANY
expected = [
mock.call.exists_ofc_tenant(ctx, tenant_id),
mock.call.create_ofc_tenant(ctx, tenant_id),
mock.call.create_ofc_network(ctx, tenant_id, net_id, net_name),
mock.call.exists_ofc_network(ctx, net_id),
mock.call.exists_ofc_tenant(ctx, tenant_id),
mock.call.delete_ofc_tenant(ctx, tenant_id),
]
self.ofc.assert_has_calls(expected)
self.assertFalse(self.ofc.delete_ofc_network.call_count)
def test_delete_network_with_ofc_deletion_failure(self):
self.ofc.set_raise_exc('delete_ofc_network',
nexc.OFCException(reason='hoge'))
with self.network() as net:
net_id = net['network']['id']
self._delete('networks', net_id,
expected_code=webob.exc.HTTPInternalServerError.code)
net_ref = self._show('networks', net_id)
self.assertEqual(net_ref['network']['status'], 'ERROR')
self.ofc.set_raise_exc('delete_ofc_network', None)
self._delete('networks', net['network']['id'])
ctx = mock.ANY
tenant = mock.ANY
net_name = mock.ANY
net = mock.ANY
expected = [
mock.call.create_ofc_network(ctx, tenant, net_id, net_name),
mock.call.exists_ofc_network(ctx, net_id),
mock.call.delete_ofc_network(ctx, net_id, net),
mock.call.exists_ofc_network(ctx, net_id),
mock.call.delete_ofc_network(ctx, net_id, net),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.delete_ofc_network.call_count, 2)
def test_delete_network_with_deactivating_auto_delete_port_failure(self):
self.ofc.set_raise_exc('delete_ofc_port',
nexc.OFCException(reason='hoge'))
with self.network() as net:
net_id = net['network']['id']
device_owner = db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS[0]
port = self._make_port(self.fmt, net_id, device_owner=device_owner)
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self._delete('networks', net_id,
expected_code=webob.exc.HTTPInternalServerError.code)
net_ref = self._show('networks', net_id)
self.assertEqual(net_ref['network']['status'], 'ACTIVE')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
self.ofc.set_raise_exc('delete_ofc_port', None)
self._delete('networks', net_id)
ctx = mock.ANY
tenant = mock.ANY
net_name = mock.ANY
net = mock.ANY
port = mock.ANY
expected = [
mock.call.create_ofc_network(ctx, tenant, net_id, net_name),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_network(ctx, net_id),
mock.call.delete_ofc_network(ctx, net_id, net)
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.delete_ofc_network.call_count, 1)
def test_update_port(self):
self._test_update_port_with_admin_state(resource='port')
def test_update_network_with_ofc_port(self):
self._test_update_port_with_admin_state(resource='network')
def _test_update_port_with_admin_state(self, resource='port'):
net = None
p1 = None
ctx = mock.ANY
if resource == 'network':
net_ini_admin_state = False
port_ini_admin_state = True
else:
net_ini_admin_state = True
port_ini_admin_state = False
with self.network(admin_state_up=net_ini_admin_state) as network:
with self.subnet(network=network) as subnet:
with self.port(subnet=subnet,
admin_state_up=port_ini_admin_state) as port:
p1 = port['port']
net_id = port['port']['network_id']
res_id = net_id if resource == 'network' else p1['id']
self.assertEqual(p1['status'], 'DOWN')
net = self._show_resource('network', net_id)
# Check the port is not created on OFC
self.assertFalse(self.ofc.create_ofc_port.call_count)
# Register portinfo, then the port is created on OFC
portinfo = {'id': p1['id'], 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.assertFalse(self.ofc.create_ofc_port.call_count)
res = self._update_resource(resource, res_id,
{'admin_state_up': True})
self.assertEqual(res['status'], 'ACTIVE')
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertFalse(self.ofc.delete_ofc_port.call_count)
res = self._update_resource(resource, res_id,
{'admin_state_up': False})
self.assertEqual(res['status'], 'DOWN')
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
self._delete('ports', port['port']['id'])
self._delete('networks', port['port']['network_id'])
expected = [
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_tenant(ctx, self._tenant_id),
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
net['name']),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.create_ofc_port(ctx, p1['id'], mock.ANY),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.delete_ofc_port(ctx, p1['id'], mock.ANY),
mock.call.exists_ofc_port(ctx, p1['id']),
mock.call.exists_ofc_network(ctx, net['id']),
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
]
self.ofc.assert_has_calls(expected)
def test_update_port_with_ofc_creation_failure(self):
with self.port(admin_state_up=False) as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.ofc.set_raise_exc('create_ofc_port',
nexc.OFCException(reason='hoge'))
body = {'port': {'admin_state_up': True}}
res = self._update('ports', port_id, body)
self.assertEqual(res['port']['status'], 'ERROR')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
body = {'port': {'admin_state_up': False}}
res = self._update('ports', port_id, body)
self.assertEqual(res['port']['status'], 'ERROR')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
self.ofc.set_raise_exc('create_ofc_port', None)
body = {'port': {'admin_state_up': True}}
res = self._update('ports', port_id, body)
self.assertEqual(res['port']['status'], 'ACTIVE')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ACTIVE')
self._delete('ports', port['port']['id'])
ctx = mock.ANY
port = mock.ANY
expected = [
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
def test_update_port_with_ofc_deletion_failure(self):
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self.ofc.set_raise_exc('delete_ofc_port',
nexc.OFCException(reason='hoge'))
body = {'port': {'admin_state_up': False}}
self._update('ports', port_id, body,
expected_code=webob.exc.HTTPInternalServerError.code)
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
body = {'port': {'admin_state_up': True}}
res = self._update('ports', port_id, body)
self.assertEqual(res['port']['status'], 'ERROR')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
self.ofc.set_raise_exc('delete_ofc_port', None)
body = {'port': {'admin_state_up': False}}
res = self._update('ports', port_id, body)
self.assertEqual(res['port']['status'], 'DOWN')
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'DOWN')
self._delete('ports', port['port']['id'])
ctx = mock.ANY
port = mock.ANY
expected = [
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 2)
def test_delete_port_with_error_status(self):
self.ofc.set_raise_exc('create_ofc_port',
nexc.OFCException(reason='fake'))
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
self._delete('ports', port['port']['id'])
ctx = mock.ANY
port = mock.ANY
expected = [
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
]
self.ofc.assert_has_calls(expected)
self.assertFalse(self.ofc.delete_ofc_port.call_count)
def test_delete_port_with_ofc_deletion_failure(self):
self.ofc.set_raise_exc('delete_ofc_port',
nexc.OFCException(reason='hoge'))
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self._delete('ports', port_id,
expected_code=webob.exc.HTTPInternalServerError.code)
port_ref = self._show('ports', port_id)
self.assertEqual(port_ref['port']['status'], 'ERROR')
self.ofc.set_raise_exc('delete_ofc_port', None)
self._delete('ports', port['port']['id'])
ctx = mock.ANY
port = mock.ANY
expected = [
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port)
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 2)
def _test_delete_port_for_disappeared_ofc_port(self, raised_exc):
self.ofc.set_raise_exc('delete_ofc_port', raised_exc)
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(added=[portinfo])
self._delete('ports', port_id)
# Check the port on neutron db is deleted. NotFound for
# neutron port itself should be handled by called. It is
# consistent with ML2 behavior, but it may need to be
# revisit.
self._show('ports', port_id,
expected_code=webob.exc.HTTPNotFound.code)
ctx = mock.ANY
port = mock.ANY
expected = [
mock.call.exists_ofc_port(ctx, port_id),
mock.call.create_ofc_port(ctx, port_id, port),
mock.call.exists_ofc_port(ctx, port_id),
mock.call.delete_ofc_port(ctx, port_id, port),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
def test_delete_port_for_nonexist_ofc_port(self):
self._test_delete_port_for_disappeared_ofc_port(
nexc.OFCResourceNotFound(resource='ofc_port'))
def test_delete_port_for_noofcmap_ofc_port(self):
self._test_delete_port_for_disappeared_ofc_port(
nexc.OFCMappingNotFound(resource='port', neutron_id='port1'))
class TestNecAllowedAddressPairs(NecPluginV2TestCase,
test_pair.TestAllowedAddressPairs):
pass

View File

@ -1,175 +0,0 @@
# Copyright 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.
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import requests
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.common import ofc_client
from neutron.tests import base
class FakeResponse(requests.Response):
def __init__(self, status_code=None, text=None, headers=None):
self._text = text
self.status_code = status_code
if headers is not None:
self.headers = headers
@property
def text(self):
return self._text
class OFCClientTest(base.BaseTestCase):
def _test_do_request(self, status, resbody, expected_data, exctype=None,
exc_checks=None, path_prefix=None):
req = mock.Mock(return_value=(FakeResponse(status, resbody)))
with mock.patch.object(requests, 'request', req):
client = ofc_client.OFCClient()
path = '/somewhere'
realpath = path_prefix + path if path_prefix else path
if exctype:
e = self.assertRaises(exctype, client.do_request,
'GET', path, body={})
self.assertEqual(expected_data, str(e))
if exc_checks:
for k, v in exc_checks.items():
self.assertEqual(v, getattr(e, k))
else:
response = client.do_request('GET', path, body={})
self.assertEqual(response, expected_data)
headers = {"Content-Type": "application/json"}
req.assert_called_with('GET', 'http://127.0.0.1:8888' + realpath,
verify=True, cert={}, data='{}',
headers=headers)
def test_do_request_200_json_value(self):
self._test_do_request(200, jsonutils.dumps([1, 2, 3]), [1, 2, 3])
def test_do_request_200_string(self):
self._test_do_request(200, 'abcdef', 'abcdef')
def test_do_request_200_no_body(self):
self._test_do_request(200, None, None)
def test_do_request_other_success_codes(self):
for status in [201, 202, 204]:
self._test_do_request(status, None, None)
def test_do_request_with_path_prefix(self):
config.CONF.set_override('path_prefix', '/dummy', group='OFC')
self._test_do_request(200, jsonutils.dumps([1, 2, 3]), [1, 2, 3],
path_prefix='/dummy')
def test_do_request_returns_404(self):
resbody = ''
errmsg = _("The specified OFC resource (/somewhere) is not found.")
self._test_do_request(404, resbody, errmsg, nexc.OFCResourceNotFound)
def test_do_request_error_no_body(self):
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
exc_checks = {'status': 400, 'err_code': None, 'err_msg': None}
self._test_do_request(400, None, errmsg, nexc.OFCException, exc_checks)
def test_do_request_error_string_body(self):
resbody = 'This is an error.'
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
exc_checks = {'status': 400, 'err_code': None,
'err_msg': 'This is an error.'}
self._test_do_request(400, resbody, errmsg, nexc.OFCException,
exc_checks)
def test_do_request_error_json_body(self):
resbody = jsonutils.dumps({'err_code': 40022,
'err_msg': 'This is an error.'})
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
exc_checks = {'status': 400, 'err_code': 40022,
'err_msg': 'This is an error.'}
self._test_do_request(400, resbody, errmsg, nexc.OFCException,
exc_checks)
def test_do_request_socket_error(self):
data = _("An OFC exception has occurred: Failed to connect OFC : ")
req = mock.Mock()
req.side_effect = requests.exceptions.RequestException
with mock.patch.object(requests, 'request', req):
client = ofc_client.OFCClient()
e = self.assertRaises(nexc.OFCException, client.do_request,
'GET', '/somewhere', body={})
self.assertEqual(data, str(e))
for k in ['status', 'err_code', 'err_msg']:
self.assertIsNone(getattr(e, k))
headers = {"Content-Type": "application/json"}
req.assert_called_with('GET', 'http://127.0.0.1:8888/somewhere',
verify=True, cert={}, data='{}',
headers=headers)
def test_do_request_retry_fail_after_one_attempts(self):
self._test_do_request_retry_after(1, api_max_attempts=1)
def test_do_request_retry_fail_with_max_attempts(self):
self._test_do_request_retry_after(3)
def test_do_request_retry_succeed_with_2nd_attempt(self):
self._test_do_request_retry_after(2, succeed_final=True)
def test_do_request_retry_succeed_with_1st_attempt(self):
self._test_do_request_retry_after(1, succeed_final=True)
def _test_do_request_retry_after(self, exp_request_count,
api_max_attempts=None,
succeed_final=False):
if api_max_attempts is not None:
cfg.CONF.set_override('api_max_attempts', api_max_attempts,
group='OFC')
res_unavail = FakeResponse(503, headers={'retry-after': '10'})
res_ok = FakeResponse(200)
req = mock.Mock()
if succeed_final:
req.side_effect = ([res_unavail] * (exp_request_count - 1)
+ [res_ok])
else:
req.side_effect = [res_unavail] * exp_request_count
with mock.patch.object(requests, 'request', req):
with mock.patch('time.sleep') as sleep:
client = ofc_client.OFCClient()
if succeed_final:
ret = client.do_request('GET', '/somewhere')
self.assertIsNone(ret)
else:
e = self.assertRaises(nexc.OFCServiceUnavailable,
client.do_request,
'GET', '/somewhere')
self.assertEqual('10', e.retry_after)
headers = {"Content-Type": "application/json"}
req.assert_called_with('GET', 'http://127.0.0.1:8888/somewhere',
verify=True, cert={}, data=None,
headers=headers)
self.assertEqual(exp_request_count, req.call_count)
self.assertEqual(exp_request_count - 1, sleep.call_count)

View File

@ -1,290 +0,0 @@
# 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.
import mock
from neutron import context
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import config
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec import ofc_manager
from neutron.tests.unit import testlib_api
class FakePortInfo(object):
def __init__(self, id, datapath_id, port_no=0,
vlan_id=65535, mac='00:11:22:33:44:55'):
self.data = {'id': id, 'datapath_id': datapath_id,
'port_no': port_no, 'vlan_id': vlan_id, 'mac': mac}
def __getattr__(self, name):
if name in self.fields:
return self[name]
else:
raise AttributeError(name)
class OFCManagerTestBase(testlib_api.SqlTestCase):
"""Class conisting of OFCManager unit tests."""
def setUp(self):
super(OFCManagerTestBase, self).setUp()
driver = "neutron.tests.unit.nec.stub_ofc_driver.StubOFCDriver"
config.CONF.set_override('driver', driver, 'OFC')
self.plugin = mock.Mock()
self.plugin.get_packet_filters_for_port.return_value = None
self.ofc = ofc_manager.OFCManager(self.plugin)
# NOTE: enable_autocheck() is a feature of StubOFCDriver
self.ofc.driver.enable_autocheck()
self.ctx = context.get_admin_context()
def get_random_params(self):
"""create random parameters for portinfo test."""
tenant = uuidutils.generate_uuid()
network = uuidutils.generate_uuid()
port = uuidutils.generate_uuid()
_filter = uuidutils.generate_uuid()
none = uuidutils.generate_uuid()
return tenant, network, port, _filter, none
class OFCManagerTest(OFCManagerTestBase):
def testa_create_ofc_tenant(self):
"""test create ofc_tenant."""
t, n, p, f, none = self.get_random_params()
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_tenant', t))
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_tenant', t))
tenant = ndb.get_ofc_item(self.ctx.session, 'ofc_tenant', t)
self.assertEqual(tenant.ofc_id, "ofc-" + t[:-4])
def testb_exists_ofc_tenant(self):
"""test exists_ofc_tenant."""
t, n, p, f, none = self.get_random_params()
self.assertFalse(self.ofc.exists_ofc_tenant(self.ctx, t))
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertTrue(self.ofc.exists_ofc_tenant(self.ctx, t))
def testc_delete_ofc_tenant(self):
"""test delete ofc_tenant."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_tenant', t))
self.ofc.delete_ofc_tenant(self.ctx, t)
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_tenant', t))
def testd_create_ofc_network(self):
"""test create ofc_network."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
network = ndb.get_ofc_item(self.ctx.session, 'ofc_network', n)
self.assertEqual(network.ofc_id, "ofc-" + n[:-4])
def teste_exists_ofc_network(self):
"""test exists_ofc_network."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertFalse(self.ofc.exists_ofc_network(self.ctx, n))
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertTrue(self.ofc.exists_ofc_network(self.ctx, n))
def testf_delete_ofc_network(self):
"""test delete ofc_network."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
self.ofc.delete_ofc_network(self.ctx, n, {'tenant_id': t})
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
def _mock_get_portinfo(self, port_id, datapath_id='0xabc', port_no=1):
get_portinfo = mock.patch.object(ndb, 'get_portinfo').start()
fake_portinfo = FakePortInfo(id=port_id, datapath_id=datapath_id,
port_no=port_no)
get_portinfo.return_value = fake_portinfo
return get_portinfo
def _test_create_ofc_port(self, with_filter=False):
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
get_portinfo = self._mock_get_portinfo(p)
port = {'tenant_id': t, 'network_id': n}
if with_filter:
_filters = ['filter1', 'filter2']
self.plugin.get_packet_filters_for_port.return_value = _filters
self.ofc.create_ofc_port(self.ctx, p, port)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
port = ndb.get_ofc_item(self.ctx.session, 'ofc_port', p)
self.assertEqual(port.ofc_id, "ofc-" + p[:-4])
get_portinfo.assert_called_once_with(mock.ANY, p)
portval = self.ofc.driver.ofc_port_dict[port.ofc_id]
if with_filter:
self.assertEqual(_filters, portval['filters'])
else:
self.assertFalse('filters' in portval)
def testg_create_ofc_port(self):
"""test create ofc_port."""
self._test_create_ofc_port(with_filter=False)
def testg_create_ofc_port_with_filters(self):
"""test create ofc_port."""
self._test_create_ofc_port(with_filter=True)
def testh_exists_ofc_port(self):
"""test exists_ofc_port."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertFalse(self.ofc.exists_ofc_port(self.ctx, p))
get_portinfo = self._mock_get_portinfo(p)
port = {'tenant_id': t, 'network_id': n}
self.ofc.create_ofc_port(self.ctx, p, port)
self.assertTrue(self.ofc.exists_ofc_port(self.ctx, p))
get_portinfo.assert_called_once_with(mock.ANY, p)
def testi_delete_ofc_port(self):
"""test delete ofc_port."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
get_portinfo = self._mock_get_portinfo(p)
port = {'tenant_id': t, 'network_id': n}
self.ofc.create_ofc_port(self.ctx, p, port)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
self.ofc.delete_ofc_port(self.ctx, p, port)
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
get_portinfo.assert_called_once_with(mock.ANY, p)
class OFCManagerFilterTest(OFCManagerTestBase):
def testj_create_ofc_packet_filter(self):
"""test create ofc_filter."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertFalse(ndb.get_ofc_item(self.ctx.session,
'ofc_packet_filter', f))
pf = {'tenant_id': t, 'network_id': n}
self.ofc.create_ofc_packet_filter(self.ctx, f, pf)
self.assertTrue(ndb.get_ofc_item(self.ctx.session,
'ofc_packet_filter', f))
_filter = ndb.get_ofc_item(self.ctx.session, 'ofc_packet_filter', f)
self.assertEqual(_filter.ofc_id, "ofc-" + f[:-4])
def testk_exists_ofc_packet_filter(self):
"""test exists_ofc_packet_filter."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertFalse(self.ofc.exists_ofc_packet_filter(self.ctx, f))
pf = {'tenant_id': t, 'network_id': n}
self.ofc.create_ofc_packet_filter(self.ctx, f, pf)
self.assertTrue(self.ofc.exists_ofc_packet_filter(self.ctx, f))
def testl_delete_ofc_packet_filter(self):
"""test delete ofc_filter."""
t, n, p, f, none = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_network(self.ctx, t, n)
pf = {'tenant_id': t, 'network_id': n}
self.ofc.create_ofc_packet_filter(self.ctx, f, pf)
self.assertTrue(ndb.get_ofc_item(self.ctx.session,
'ofc_packet_filter', f))
self.ofc.delete_ofc_packet_filter(self.ctx, f)
self.assertFalse(ndb.get_ofc_item(self.ctx.session,
'ofc_packet_filter', f))
class OFCManagerRouterTest(OFCManagerTestBase):
def get_random_params(self):
tenant = uuidutils.generate_uuid()
router = uuidutils.generate_uuid()
network = uuidutils.generate_uuid()
return (tenant, router, network)
def test_create_ofc_router(self):
"""test create ofc_router"""
t, r, _n = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
self.ofc.create_ofc_router(self.ctx, t, r, 'test router')
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
router = ndb.get_ofc_item(self.ctx.session, 'ofc_router', r)
self.assertEqual(router.ofc_id, "ofc-" + r[:-4])
def test_exists_ofc_router(self):
"""test exists_ofc_router"""
t, r, _n = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.assertFalse(self.ofc.exists_ofc_router(self.ctx, r))
self.ofc.create_ofc_router(self.ctx, t, r)
self.assertTrue(self.ofc.exists_ofc_router(self.ctx, r))
def test_delete_ofc_router(self):
"""test delete ofc_router"""
t, r, _n = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_router(self.ctx, t, r)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
self.ofc.delete_ofc_router(self.ctx, r, {'tenant_id': t})
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', r))
def test_router_interface(self):
t, r, n = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_router(self.ctx, t, r)
self.ofc.create_ofc_network(self.ctx, t, n)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
p = {'id': uuidutils.generate_uuid(),
'network_id': n, 'ip_address': '10.1.1.1', 'cidr': '10.1.0.0/20',
'mac_address': '11:22:33:44:55:66'}
self.ofc.add_ofc_router_interface(self.ctx, r, p['id'], p)
self.assertTrue(ndb.get_ofc_item(self.ctx.session,
'ofc_port', p['id']))
self.ofc.delete_ofc_router_interface(self.ctx, r, p['id'])
self.assertFalse(ndb.get_ofc_item(self.ctx.session,
'ofc_port', p['id']))
self.ofc.delete_ofc_router(self.ctx, r, {'tenant_id': t})
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', r))
def test_router_route(self):
t, r, _n = self.get_random_params()
self.ofc.create_ofc_tenant(self.ctx, t)
self.ofc.create_ofc_router(self.ctx, t, r)
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
routes = [{'destination': '2.2.2.0/24', 'nexthop': '1.1.1.10'}]
self.ofc.update_ofc_router_route(self.ctx, r, routes)
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 1)
routes = [{'destination': '3.3.3.0/24', 'nexthop': '1.1.1.11'},
{'destination': '4.4.4.0/24', 'nexthop': '1.1.1.11'}]
self.ofc.update_ofc_router_route(self.ctx, r, routes)
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 2)
routes = [{'destination': '2.2.2.0/24', 'nexthop': '1.1.1.10'}]
self.ofc.update_ofc_router_route(self.ctx, r, routes)
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 1)
routes = []
self.ofc.update_ofc_router_route(self.ctx, r, routes)
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 0)

View File

@ -1,719 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation.
#
# 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.
import contextlib
import mock
import webob.exc
from neutron.api.v2 import attributes
from neutron import context
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.extensions import packetfilter as ext_pf
from neutron.tests.unit.nec import test_nec_plugin
from neutron.tests.unit import test_db_plugin as test_plugin
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(ext_pf.Packetfilter):
@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': ext_pf.PACKET_FILTER_ATTR_MAP})
return super(PacketfilterExtensionManager, cls).get_resources()
class TestNecPluginPacketFilterBase(test_nec_plugin.NecPluginV2TestCase):
_nec_ini = NEC_PLUGIN_PF_INI
def setUp(self):
ext_mgr = PacketfilterExtensionManager()
super(TestNecPluginPacketFilterBase, self).setUp(ext_mgr=ext_mgr)
def _create_packet_filter(self, fmt, net_id, expected_res_status=None,
arg_list=None, **kwargs):
data = {'packet_filter': {'network_id': net_id,
'tenant_id': self._tenant_id,
'priority': '1',
'action': 'ALLOW'}}
for arg in (('name', 'admin_state_up', 'action', 'priority', 'in_port',
'src_mac', 'dst_mac', 'eth_type', 'src_cidr', 'dst_cidr',
'protocol', 'src_port', 'dst_port') +
(arg_list or ())):
# Arg must be present
if arg in kwargs:
data['packet_filter'][arg] = kwargs[arg]
pf_req = self.new_create_request('packet_filters', data, fmt)
if (kwargs.get('set_context') and 'tenant_id' in kwargs):
# create a specific auth context for this request
pf_req.environ['neutron.context'] = context.Context(
'', kwargs['tenant_id'])
pf_res = pf_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(pf_res.status_int, expected_res_status)
return pf_res
def _make_packet_filter(self, fmt, net_id, expected_res_status=None,
**kwargs):
res = self._create_packet_filter(fmt, net_id, expected_res_status,
**kwargs)
# Things can go wrong - raise HTTP exc with res code only
# so it can be caught by unit tests
if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int)
return self.deserialize(fmt, res)
@contextlib.contextmanager
def packet_filter_on_network(self, network=None, fmt=None, **kwargs):
with test_plugin.optional_ctx(network, self.network) as network_to_use:
net_id = network_to_use['network']['id']
pf = self._make_packet_filter(fmt or self.fmt, net_id, **kwargs)
yield pf
if not network:
self._delete('networks', network_to_use['network']['id'])
@contextlib.contextmanager
def packet_filter_on_port(self, port=None, fmt=None, 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'])
yield pf
class TestNecPluginPacketFilter(TestNecPluginPacketFilterBase):
def setUp(self):
super(TestNecPluginPacketFilter, self).setUp()
# Remove attributes explicitly from mock object to check
# a case where there are no update_filter and validate_*.
del self.ofc.driver.update_filter
del self.ofc.driver.validate_filter_create
del self.ofc.driver.validate_filter_update
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')
self._delete('packet_filters', pf_id)
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_protocol(self, protocol, expected_eth_type):
with self.packet_filter_on_network(protocol=protocol) as pf:
pf_data = pf['packet_filter']
self.assertEqual(protocol, pf_data['protocol'])
self.assertEqual(expected_eth_type, pf_data['eth_type'])
def test_create_pf_with_protocol_tcp(self):
self._test_create_pf_with_protocol('TCP', 0x800)
def test_create_pf_with_protocol_udp(self):
self._test_create_pf_with_protocol('UDP', 0x800)
def test_create_pf_with_protocol_icmp(self):
self._test_create_pf_with_protocol('ICMP', 0x800)
def test_create_pf_with_protocol_arp(self):
self._test_create_pf_with_protocol('ARP', 0x806)
def test_create_pf_with_inconsistent_protocol_and_eth_type(self):
with self.packet_filter_on_network(protocol='TCP') as pf:
pf_data = pf['packet_filter']
pf_id = pf_data['id']
self.assertEqual('TCP', pf_data['protocol'])
self.assertEqual(0x800, pf_data['eth_type'])
data = {'packet_filter': {'eth_type': 0x806}}
self._update('packet_filters', pf_id, data,
expected_code=409)
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 activate packet_filter (even if there is no change).
data = {'packet_filter': {}}
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',
'admin_state_up': False,
'action': 'DENY',
'priority': '102',
'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',
'src_port': '35001',
'dst_port': '22'
}
with self.packet_filter_on_network(**kwargs) as pf:
pf_id = pf['packet_filter']['id']
pf_ref = self._show('packet_filters', pf_id)
# convert string to int.
kwargs.update({'priority': 102, 'eth_type': 2048,
'src_port': 35001, 'dst_port': 22,
'in_port': None})
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_network_with_wildcards(self):
kwargs = {
'name': 'test-pf-net',
'admin_state_up': False,
'action': 'DENY',
'priority': '102',
}
with self.packet_filter_on_network(**kwargs) as pf:
pf_id = pf['packet_filter']['id']
pf_ref = self._show('packet_filters', pf_id)
# convert string to int.
kwargs.update({'priority': 102,
'in_port': None,
'src_mac': None,
'dst_mac': None,
'eth_type': None,
'src_cidr': None,
'dst_cidr': None,
'protocol': None,
'src_port': None,
'dst_port': None})
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,
'dst_port': 80,
# wildcard field is None in a response.
'src_port': None})
self.assertEqual(pf_id, pf_ref['packet_filter']['id'])
self.assertTrue(pf_ref['packet_filter']['in_port'])
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_delete_pf_with_error_status(self):
self.ofc.set_raise_exc('create_ofc_packet_filter',
nexc.OFCException(reason='fake'))
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')
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),
]
self.ofc.assert_has_calls(expected)
self.assertEqual(1, self.ofc.create_ofc_packet_filter.call_count)
self.assertEqual(0, self.ofc.delete_ofc_packet_filter.call_count)
def test_activate_pf_on_port_triggered_by_update_port(self):
ctx = mock.ANY
pf_dict = mock.ANY
self.ofc.set_raise_exc('create_ofc_packet_filter',
nexc.PortInfoNotFound(id='fake_id'))
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']
# create_ofc_packet_filter is now called even when
# in_port does not exists yet. In this case
# PortInfoNotFound exception is raised.
self.assertEqual(1, self.ofc.create_ofc_packet_filter.call_count)
portinfo = {'id': in_port_id, 'port_no': 123}
kw = {'added': [portinfo]}
self.ofc.set_raise_exc('create_ofc_packet_filter', None)
self.rpcapi_update_ports(**kw)
self.assertEqual(2, self.ofc.create_ofc_packet_filter.call_count)
self.ofc.assert_has_calls([
mock.call.create_ofc_packet_filter(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(2, self.ofc.create_ofc_packet_filter.call_count)
self.assertEqual(1, self.ofc.delete_ofc_packet_filter.call_count)
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,
expected_code=webob.exc.HTTPInternalServerError.code)
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.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_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,
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)
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) as pf:
pf_id = pf['packet_filter']['id']
self._show('packet_filters', pf_id,
expected_code=webob.exc.HTTPNotFound.code)
def test_auto_delete_pf_in_port_deletion(self):
with self.port() as port:
network = self._show('networks', port['port']['network_id'])
with self.packet_filter_on_network(network=network) as pfn:
with self.packet_filter_on_port(port=port,
set_portinfo=False) as pf:
pf_id = pf['packet_filter']['id']
in_port_id = pf['packet_filter']['in_port']
self._delete('ports', in_port_id)
# Check the packet filter on the port is deleted.
self._show('packet_filters', pf_id,
expected_code=webob.exc.HTTPNotFound.code)
# Check the packet filter on the network is not deleted.
self._show('packet_filters', pfn['packet_filter']['id'])
def test_no_pf_activation_while_port_operations(self):
with self.packet_filter_on_port() as pf:
in_port_id = pf['packet_filter']['in_port']
self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 0)
data = {'port': {'admin_state_up': False}}
self._update('ports', in_port_id, data)
self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 0)
data = {'port': {'admin_state_up': True}}
self._update('ports', in_port_id, data)
self.assertEqual(self.ofc.create_ofc_packet_filter.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_packet_filter.call_count, 0)
class TestNecPluginPacketFilterWithValidate(TestNecPluginPacketFilterBase):
def setUp(self):
super(TestNecPluginPacketFilterWithValidate, self).setUp()
# Remove attributes explicitly from mock object to check
# a case where there are no update_filter.
del self.ofc.driver.update_filter
self.validate_create = self.ofc.driver.validate_filter_create
self.validate_update = self.ofc.driver.validate_filter_update
def test_create_pf_on_network(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.driver.validate_filter_create(ctx, pf_dict),
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_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.ofc.driver.validate_filter_update.assert_called_once_with(
ctx, data['packet_filter'])
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)
self.assertEqual(
2, self.ofc.driver.validate_filter_update.call_count)
def test_create_pf_on_network_with_validation_error(self):
self.validate_create.side_effect = ext_pf.PacketFilterInvalidPriority(
min=1, max=65535)
with self.network() as net:
net_id = net['network']['id']
e = self.assertRaises(webob.exc.HTTPClientError,
self._make_packet_filter,
self.fmt, net_id, expected_res_status=400)
self.assertEqual(400, e.status_int)
def test_update_pf_on_network_with_validation_error(self):
self.validate_update.side_effect = (
ext_pf.PacketFilterUpdateNotSupported(field='priority'))
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'], 'ACTIVE')
data = {'packet_filter': {'priority': 1000}}
self._update('packet_filters', pf_id, data,
expected_code=400)
class TestNecPluginPacketFilterWithFilterUpdate(TestNecPluginPacketFilterBase):
def setUp(self):
super(TestNecPluginPacketFilterWithFilterUpdate, self).setUp()
# Remove attributes explicitly from mock object to check
# a case where there are no update_filter and validate_*.
del self.ofc.driver.validate_filter_create
del self.ofc.driver.validate_filter_update
def test_update_pf_toggle_admin_state(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_change_field(self):
ctx = mock.ANY
with self.packet_filter_on_network(admin_state_up=True) as pf:
pf_id = pf['packet_filter']['id']
self.assertTrue(self.ofc.create_ofc_packet_filter.called)
data = {'packet_filter': {'src_mac': '12:34:56:78:9a:bc'}}
self._update('packet_filters', pf_id, data)
self.ofc.update_ofc_packet_filter.assert_called_once_with(
ctx, pf_id, data['packet_filter'])
self.assertEqual(1, self.ofc.update_ofc_packet_filter.call_count)
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)
data = {'packet_filter': {'src_mac': '11:22:33:44:55:66'}}
self._update('packet_filters', pf_id, data)
self.assertEqual(1, self.ofc.update_ofc_packet_filter.call_count)
data = {'packet_filter': {'admin_state_up': True}}
self._update('packet_filters', pf_id, data)
data = {'packet_filter': {'src_mac': '66:55:44:33:22:11'}}
self._update('packet_filters', pf_id, data)
self.assertEqual(2, self.ofc.update_ofc_packet_filter.call_count)

View File

@ -1,726 +0,0 @@
# 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.
import random
import string
import uuid
import mock
import netaddr
from oslo_config import cfg
from neutron.common import constants
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import ofc_client as ofc
from neutron.plugins.nec.db import models as nmodels
from neutron.plugins.nec import drivers
from neutron.plugins.nec.drivers import pfc
from neutron.plugins.nec.extensions import packetfilter as ext_pf
from neutron.tests import base
class TestConfig(object):
"""Configuration for this test."""
host = '127.0.0.1'
port = 8888
use_ssl = False
key_file = None
cert_file = None
insecure_ssl = False
def _ofc(id):
"""OFC ID converter."""
return "ofc-%s" % id
class PFCDriverTestBase(base.BaseTestCase):
driver = 'neutron.plugins.nec.drivers.pfc.PFCDriverBase'
filter_supported = False
def setUp(self):
super(PFCDriverTestBase, self).setUp()
self.driver = drivers.get_driver(self.driver)(TestConfig)
self.do_request = mock.patch.object(ofc.OFCClient,
'do_request').start()
def get_ofc_item_random_params(self):
"""create random parameters for ofc_item test."""
tenant_id = uuidutils.generate_uuid()
network_id = uuidutils.generate_uuid()
port_id = uuidutils.generate_uuid()
portinfo = nmodels.PortInfo(id=port_id, datapath_id="0x123456789",
port_no=1234, vlan_id=321,
mac="11:22:33:44:55:66")
return tenant_id, network_id, portinfo
def _generate_ofc_tenant_id(self, tenant_id):
fields = tenant_id.split('-')
# Strip 1st character (UUID version) of 3rd field
fields[2] = fields[2][1:]
return ''.join(fields)
def get_ofc_description(self, desc):
"""OFC description consists of [A-Za-z0-9_]."""
return desc.replace('-', '_').replace(' ', '_')
def _create_tenant(self, t, ofc_t, post_id=False, post_desc=False):
tenant_path = '/tenants/%s' % ofc_t
path = "/tenants"
description = "desc of %s" % t
body = {}
if post_desc:
ofc_description = self.get_ofc_description(description)
body['description'] = ofc_description
if post_id:
body['id'] = ofc_t
self.do_request.return_value = None
else:
self.do_request.return_value = {'id': ofc_t}
ret = self.driver.create_tenant(description, t)
self.do_request.assert_called_once_with("POST", path, body=body)
self.assertEqual(ret, tenant_path)
def testa_create_tenant(self):
t, n, p = self.get_ofc_item_random_params()
ofc_t = self._generate_ofc_tenant_id(t)
self._create_tenant(t, ofc_t, post_id=True)
def testc_delete_tenant(self):
t, n, p = self.get_ofc_item_random_params()
path = "/tenants/%s" % _ofc(t)
self.driver.delete_tenant(path)
self.do_request.assert_called_once_with("DELETE", path)
def testd_create_network(self):
t, n, p = self.get_ofc_item_random_params()
description = "desc of %s" % n
ofc_description = self.get_ofc_description(description)
tenant_path = "/tenants/%s" % _ofc(t)
post_path = "%s/networks" % tenant_path
body = {'description': ofc_description}
network = {'id': _ofc(n)}
self.do_request.return_value = network
ret = self.driver.create_network(tenant_path, description, n)
self.do_request.assert_called_once_with("POST", post_path, body=body)
net_path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
self.assertEqual(ret, net_path)
def testf_delete_network(self):
t, n, p = self.get_ofc_item_random_params()
net_path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
self.driver.delete_network(net_path)
self.do_request.assert_called_once_with("DELETE", net_path)
def _test_create_port(self, call_filters_arg=None, send_filters_arg=None):
t, n, p = self.get_ofc_item_random_params()
net_path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
post_path = "%s/ports" % net_path
port_path = "/tenants/%s/networks/%s/ports/%s" % (_ofc(t), _ofc(n),
_ofc(p.id))
body = {'datapath_id': p.datapath_id,
'port': str(p.port_no),
'vid': str(p.vlan_id)}
if send_filters_arg is not None:
body['filters'] = send_filters_arg
port = {'id': _ofc(p.id)}
self.do_request.return_value = port
if call_filters_arg is not None:
ret = self.driver.create_port(net_path, p, p.id, call_filters_arg)
else:
ret = self.driver.create_port(net_path, p, p.id)
self.do_request.assert_called_once_with("POST", post_path, body=body)
self.assertEqual(ret, port_path)
def testg_create_port(self):
self._test_create_port()
def test_create_port_with_filters_argument(self):
# If no filter support, 'filters' argument is passed to OFC.
# Note that it will be overridden in a test class with filter support.
self._test_create_port(call_filters_arg=['dummy'],
send_filters_arg=None)
def testh_delete_port(self):
t, n, p = self.get_ofc_item_random_params()
port_path = "/tenants/%s/networks/%s/ports/%s" % (_ofc(t), _ofc(n),
_ofc(p.id))
self.driver.delete_port(port_path)
self.do_request.assert_called_once_with("DELETE", port_path)
def test_filter_supported(self):
self.assertEqual(self.filter_supported, self.driver.filter_supported())
class PFCDriverBaseTest(PFCDriverTestBase):
def test_extract_ofc_network_id(self):
network_id = '/tenants/tenant-a/networks/network-a'
self.assertEqual('network-a',
self.driver._extract_ofc_network_id(network_id))
def test_extract_ofc_network_id_failure(self):
network_id = '/tenants/tenant-a/networks/network-a/dummy'
self.assertRaises(pfc.InvalidOFCIdFormat,
self.driver._extract_ofc_network_id, network_id)
def test_extract_ofc_port_id(self):
port_id = '/tenants/tenant-a/networks/network-a/ports/port-a'
self.assertEqual({'tenant': 'tenant-a',
'network': 'network-a',
'port': 'port-a'},
self.driver._extract_ofc_port_id(port_id))
def test_extract_ofc_port_id_failure(self):
port_id = '/tenants/tenant-a/dummy/network-a/ports/port-a'
self.assertRaises(pfc.InvalidOFCIdFormat,
self.driver._extract_ofc_port_id, port_id)
class PFCV3DriverTest(PFCDriverTestBase):
driver = 'pfc_v3'
def testa_create_tenant(self):
t, n, p = self.get_ofc_item_random_params()
ret = self.driver.create_tenant('dummy_desc', t)
self.assertEqual(0, self.do_request.call_count)
ofc_t_path = "/tenants/" + self._generate_ofc_tenant_id(t)
self.assertEqual(ofc_t_path, ret)
def testc_delete_tenant(self):
t, n, p = self.get_ofc_item_random_params()
path = "/tenants/%s" % _ofc(t)
self.driver.delete_tenant(path)
self.assertEqual(0, self.do_request.call_count)
class PFCV4DriverTest(PFCDriverTestBase):
driver = 'pfc_v4'
class PFCV5DriverTest(PFCDriverTestBase):
driver = 'pfc_v5'
def test_create_router(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
description = 'dummy_router_desc'
tenant_path = "/tenants/%s" % _ofc(t)
post_path = "%s/routers" % tenant_path
router = {'id': _ofc(r)}
self.do_request.return_value = router
ret = self.driver.create_router(tenant_path, description, r)
self.do_request.assert_called_once_with("POST", post_path, body=None)
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
self.assertEqual(ret, router_path)
def test_delete_router(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
self.driver.delete_router(router_path)
self.do_request.assert_called_once_with("DELETE", router_path)
def test_add_router_interface(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
n = uuidutils.generate_uuid()
p = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
infs_path = router_path + "/interfaces"
net_path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
ip_address = '10.1.1.1/24'
mac_address = '11:22:33:44:55:66'
body = {'net_id': _ofc(n),
'ip_address': ip_address,
'mac_address': mac_address}
inf = {'id': _ofc(p)}
self.do_request.return_value = inf
ret = self.driver.add_router_interface(router_path, net_path,
ip_address, mac_address)
self.do_request.assert_called_once_with("POST", infs_path, body=body)
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
self.assertEqual(ret, inf_path)
def test_update_router_interface(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
p = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
ip_address = '10.1.1.1/24'
mac_address = '11:22:33:44:55:66'
self.driver.update_router_interface(inf_path, ip_address, mac_address)
self.driver.update_router_interface(inf_path, ip_address=ip_address)
self.driver.update_router_interface(inf_path, mac_address=mac_address)
self.do_request.assert_has_calls([
mock.call("PUT", inf_path, body={'ip_address': ip_address,
'mac_address': mac_address}),
mock.call("PUT", inf_path, body={'ip_address': ip_address}),
mock.call("PUT", inf_path, body={'mac_address': mac_address}),
])
def test_delete_router_interface(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
p = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
self.driver.delete_router_interface(inf_path)
self.do_request.assert_called_once_with("DELETE", inf_path)
def _get_route_id(self, dest, nexthop):
dest = netaddr.IPNetwork(dest)
return '-'.join([str(dest.network), nexthop, str(dest.netmask)])
def test_add_router_route(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
routes_path = router_path + "/routes"
dest = '10.1.1.0/24'
nexthop = '192.168.100.10'
body = {'destination': dest, 'nexthop': nexthop}
route_id = self._get_route_id(dest, nexthop)
self.do_request.return_value = {'id': route_id}
ret = self.driver.add_router_route(router_path, '10.1.1.0/24',
'192.168.100.10')
self.do_request.assert_called_once_with("POST", routes_path, body=body)
route_path = routes_path + '/' + route_id
self.assertEqual(ret, route_path)
def test_delete_router_route(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
routes_path = router_path + "/routes"
route_id = self._get_route_id('10.1.1.0/24', '192.168.100.10')
route_path = routes_path + '/' + route_id
self.driver.delete_router_route(route_path)
self.do_request.assert_called_once_with("DELETE", route_path)
def test_list_router_routes(self):
t = uuidutils.generate_uuid()
r = uuidutils.generate_uuid()
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
routes_path = router_path + "/routes"
routes = [('10.1.1.0/24', '192.168.100.10'),
('10.2.2.0/20', '192.168.100.20')]
data = {'routes': [{'id': self._get_route_id(route[0], route[1]),
'destination': route[0], 'nexthop': route[1]}
for route in routes]}
self.do_request.return_value = data
ret = self.driver.list_router_routes(router_path)
self.do_request.assert_called_once_with("GET", routes_path)
expected = [{'id': (routes_path + "/" +
self._get_route_id(route[0], route[1])),
'destination': route[0], 'nexthop': route[1]}
for route in routes]
self.assertEqual(len(routes), len(ret))
self.assertEqual(data['routes'], expected)
class PFCFilterDriverTestMixin(object):
def _test_create_filter(self, filter_dict=None, filter_post=None,
apply_ports=None):
t, n, p = self.get_ofc_item_random_params()
filter_id = uuidutils.generate_uuid()
f = {'priority': 123, 'action': "ACCEPT",
'network_id': n}
if filter_dict:
f.update(filter_dict)
body = {'action': 'pass', 'priority': 123}
if filter_post:
body.update(filter_post)
self.do_request.return_value = {'id': filter_id}
with mock.patch('neutron.plugins.nec.db.api.get_active_ports_on_ofc',
return_value=apply_ports) as active_ports:
ret = self.driver.create_filter(mock.sentinel.ctx, f)
self.do_request.assert_called_once_with("POST", "/filters",
body=body)
self.assertEqual(ret, '/filters/%s' % filter_id)
active_ports.assert_called_once_with(mock.sentinel.ctx, n,
filter_dict.get('in_port'))
def test_create_filter_accept(self):
self._test_create_filter(filter_dict={'action': 'ACCEPT'})
def test_create_filter_allow(self):
self._test_create_filter(filter_dict={'action': 'ALLOW'})
def test_create_filter_deny(self):
self._test_create_filter(filter_dict={'action': 'DENY'},
filter_post={'action': 'drop'})
def test_create_filter_drop(self):
self._test_create_filter(filter_dict={'action': 'DROP'},
filter_post={'action': 'drop'})
def test_create_filter_empty_field_not_post(self):
filter_dict = {'src_mac': '', 'src_cidr': '', 'src_port': 0,
'dst_mac': '', 'dst_cidr': '', 'dst_port': 0,
'protocol': '', 'eth_type': 0}
filter_post = {}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_none_field_not_post(self):
filter_dict = {'src_mac': None, 'src_cidr': None, 'src_port': None,
'dst_mac': None, 'dst_cidr': None, 'dst_port': None,
'protocol': None, 'eth_type': None}
filter_post = {}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_all_fields(self):
filter_dict = {'src_mac': '11:22:33:44:55:66',
'dst_mac': '77:88:99:aa:bb:cc',
'src_cidr': '192.168.3.0/24',
'dst_cidr': '10.11.240.0/20',
'src_port': 12345,
'dst_port': 23456,
'protocol': '0x10',
'eth_type': 0x800}
filter_post = filter_dict.copy()
filter_post['protocol'] = 16
filter_post['eth_type'] = '0x800'
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_cidr_ip_addr_32(self):
filter_dict = {'src_cidr': '192.168.3.1',
'dst_cidr': '10.11.240.2'}
filter_post = {'src_cidr': '192.168.3.1/32',
'dst_cidr': '10.11.240.2/32'}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_proto_tcp(self):
filter_dict = {'protocol': 'TCP'}
filter_post = {'protocol': constants.PROTO_NUM_TCP}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_proto_udp(self):
filter_dict = {'protocol': 'UDP'}
filter_post = {'protocol': constants.PROTO_NUM_UDP}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_proto_icmp(self):
filter_dict = {'protocol': 'ICMP'}
filter_post = {'protocol': constants.PROTO_NUM_ICMP}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_proto_arp_not_proto_post(self):
filter_dict = {'protocol': 'ARP'}
filter_post = {}
self._test_create_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_create_filter_apply_ports(self):
apply_ports = [
('p1', '/tenants/tenant-1/networks/network-1/ports/port-1'),
('p2', '/tenants/tenant-2/networks/network-2/ports/port-2')]
filter_post = {'apply_ports': [
{'tenant': 'tenant-1', 'network': 'network-1', 'port': 'port-1'},
{'tenant': 'tenant-2', 'network': 'network-2', 'port': 'port-2'}
]}
self._test_create_filter(filter_dict={}, apply_ports=apply_ports,
filter_post=filter_post)
def test_create_filter_apply_ports_with_router_interface(self):
apply_ports = [
('p1', '/tenants/tenant-1/networks/network-1/ports/port-1'),
('p2', '/tenants/tenant-2/routers/router-3/interfaces/if-4')]
filter_post = {'apply_ports': [
{'tenant': 'tenant-1', 'network': 'network-1', 'port': 'port-1'},
{'tenant': 'tenant-2', 'router': 'router-3', 'interface': 'if-4'}
]}
self._test_create_filter(filter_dict={}, apply_ports=apply_ports,
filter_post=filter_post)
def test_create_filter_apply_ports_no_router_interface_support(self):
cfg.CONF.set_override('support_packet_filter_on_ofc_router',
False, 'OFC')
apply_ports = [
('p1', '/tenants/tenant-1/networks/network-1/ports/port-1'),
('p2', '/tenants/tenant-2/routers/router-3/interfaces/if-4')]
filter_post = {'apply_ports': [
{'tenant': 'tenant-1', 'network': 'network-1', 'port': 'port-1'},
]}
self._test_create_filter(filter_dict={}, apply_ports=apply_ports,
filter_post=filter_post)
def _test_update_filter(self, filter_dict=None, filter_post=None):
filter_id = uuidutils.generate_uuid()
ofc_filter_id = '/filters/%s' % filter_id
self.driver.update_filter(ofc_filter_id, filter_dict)
self.do_request.assert_called_once_with("PUT", ofc_filter_id,
body=filter_post)
def test_update_filter_empty_fields(self):
filter_dict = {'src_mac': '', 'src_cidr': '', 'src_port': 0,
'dst_mac': '', 'dst_cidr': '', 'dst_port': 0,
'protocol': '', 'eth_type': 0}
filter_post = {'src_mac': '', 'src_cidr': '', 'src_port': '',
'dst_mac': '', 'dst_cidr': '', 'dst_port': '',
'protocol': '', 'eth_type': ''}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_none_fields(self):
filter_dict = {'src_mac': None, 'src_cidr': None, 'src_port': None,
'dst_mac': None, 'dst_cidr': None, 'dst_port': None,
'protocol': None, 'eth_type': None}
filter_post = {'src_mac': '', 'src_cidr': '', 'src_port': '',
'dst_mac': '', 'dst_cidr': '', 'dst_port': '',
'protocol': '', 'eth_type': ''}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_all_fields(self):
filter_dict = {'src_mac': '11:22:33:44:55:66',
'dst_mac': '77:88:99:aa:bb:cc',
'src_cidr': '192.168.3.0/24',
'dst_cidr': '10.11.240.0/20',
'src_port': 12345,
'dst_port': 23456,
'protocol': '0x10',
'eth_type': 0x800}
filter_post = filter_dict.copy()
filter_post['protocol'] = 16
filter_post['eth_type'] = '0x800'
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_cidr_ip_addr_32(self):
filter_dict = {'src_cidr': '192.168.3.1',
'dst_cidr': '10.11.240.2'}
filter_post = {'src_cidr': '192.168.3.1/32',
'dst_cidr': '10.11.240.2/32'}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_proto_tcp(self):
filter_dict = {'protocol': 'TCP'}
filter_post = {'protocol': constants.PROTO_NUM_TCP}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_proto_udp(self):
filter_dict = {'protocol': 'UDP'}
filter_post = {'protocol': constants.PROTO_NUM_UDP}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_proto_icmp(self):
filter_dict = {'protocol': 'ICMP'}
filter_post = {'protocol': constants.PROTO_NUM_ICMP}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_update_filter_proto_arp_post_empty(self):
filter_dict = {'protocol': 'ARP'}
filter_post = {'protocol': ''}
self._test_update_filter(filter_dict=filter_dict,
filter_post=filter_post)
def test_delete_filter(self):
t, n, p = self.get_ofc_item_random_params()
f_path = "/filters/%s" % uuidutils.generate_uuid()
self.driver.delete_filter(f_path)
self.do_request.assert_called_once_with("DELETE", f_path)
def _test_validate_filter_duplicate_priority(self, method, found_dup):
with mock.patch('neutron.manager.NeutronManager'
'.get_plugin') as get_plugin:
plugin = get_plugin.return_value
if found_dup:
plugin.get_packet_filters.return_value = ['found']
else:
plugin.get_packet_filters.return_value = []
network_id = str(uuid.uuid4())
filter_dict = {'network_id': network_id,
'priority': 12}
if found_dup:
self.assertRaises(ext_pf.PacketFilterDuplicatedPriority,
method, 'context', filter_dict)
else:
self.assertIsNone(method('context', filter_dict))
plugin.get_packet_filters.assert_called_once_with(
'context',
filters={'network_id': [network_id],
'priority': [12]},
fields=['id'])
def test_validate_filter_create_no_duplicate_priority(self):
self._test_validate_filter_duplicate_priority(
self.driver.validate_filter_create,
found_dup=False)
def test_validate_filter_create_duplicate_priority(self):
self._test_validate_filter_duplicate_priority(
self.driver.validate_filter_create,
found_dup=True)
def test_validate_filter_update_action_raises_error(self):
filter_dict = {'action': 'ALLOW'}
self.assertRaises(ext_pf.PacketFilterUpdateNotSupported,
self.driver.validate_filter_update,
'context', filter_dict)
def test_validate_filter_update_priority_raises_error(self):
filter_dict = {'priority': '13'}
self.assertRaises(ext_pf.PacketFilterUpdateNotSupported,
self.driver.validate_filter_update,
'context', filter_dict)
def _test_validate_filter_ipv6_not_supported(self, field, create=True):
if create:
filter_dict = {'network_id': 'net1', 'priority': 12}
method = self.driver.validate_filter_create
else:
filter_dict = {}
method = self.driver.validate_filter_update
filter_dict[field] = 'fe80::1'
self.assertRaises(ext_pf.PacketFilterIpVersionNonSupported,
method, 'context', filter_dict)
filter_dict[field] = '10.56.3.3'
self.assertIsNone(method('context', filter_dict))
def test_validate_filter_create_ipv6_not_supported(self):
with mock.patch('neutron.manager.NeutronManager'
'.get_plugin') as get_plugin:
plugin = get_plugin.return_value
plugin.get_packet_filters.return_value = []
self._test_validate_filter_ipv6_not_supported(
'src_cidr', create=True)
self._test_validate_filter_ipv6_not_supported(
'dst_cidr', create=True)
def test_validate_filter_update_ipv6_not_supported(self):
self._test_validate_filter_ipv6_not_supported('src_cidr', create=False)
self._test_validate_filter_ipv6_not_supported('dst_cidr', create=False)
def _test_validate_filter_priority_range_one(self, method, priority, ok):
filter_dict = {'priority': priority, 'network_id': 'net1'}
if ok:
self.assertIsNone(method('context', filter_dict))
else:
self.assertRaises(ext_pf.PacketFilterInvalidPriority,
method, 'context', filter_dict)
def test_validate_filter_create_priority_range(self):
with mock.patch('neutron.manager.NeutronManager'
'.get_plugin') as get_plugin:
plugin = get_plugin.return_value
plugin.get_packet_filters.return_value = []
method = self.driver.validate_filter_create
self._test_validate_filter_priority_range_one(method, 0, False)
self._test_validate_filter_priority_range_one(method, 1, True)
self._test_validate_filter_priority_range_one(method, 32766, True)
self._test_validate_filter_priority_range_one(method, 32767, False)
class PFCV51DriverTest(PFCFilterDriverTestMixin, PFCV5DriverTest):
driver = 'pfc_v51'
filter_supported = True
def test_create_port_with_filters_argument(self):
self._test_create_port(
call_filters_arg=[('neutron-id-1', '/filters/filter-1'),
('neutron-id-2', '/filters/filter-2')],
send_filters_arg=['filter-1', 'filter-2'])
class PFCDriverStringTest(base.BaseTestCase):
driver = 'neutron.plugins.nec.drivers.pfc.PFCDriverBase'
def setUp(self):
super(PFCDriverStringTest, self).setUp()
self.driver = drivers.get_driver(self.driver)(TestConfig)
def test_generate_pfc_id_uuid(self):
id_str = uuidutils.generate_uuid()
exp_str = (id_str[:14] + id_str[15:]).replace('-', '')[:31]
ret_str = self.driver._generate_pfc_id(id_str)
self.assertEqual(exp_str, ret_str)
def test_generate_pfc_id_uuid_no_hyphen(self):
# Keystone tenant_id style uuid
id_str = uuidutils.generate_uuid()
id_no_hyphen = id_str.replace('-', '')
exp_str = (id_str[:14] + id_str[15:]).replace('-', '')[:31]
ret_str = self.driver._generate_pfc_id(id_no_hyphen)
self.assertEqual(exp_str, ret_str)
def test_generate_pfc_id_string(self):
id_str = uuidutils.generate_uuid() + 'x'
exp_str = id_str[:31].replace('-', '_')
ret_str = self.driver._generate_pfc_id(id_str)
self.assertEqual(exp_str, ret_str)
def test_generate_pfc_desc(self):
random_list = [random.choice(string.printable) for x in range(128)]
random_str = ''.join(random_list)
accept_letters = string.letters + string.digits
exp_list = [x if x in accept_letters else '_' for x in random_list]
exp_str = ''.join(exp_list)[:127]
ret_str = self.driver._generate_pfc_description(random_str)
self.assertEqual(exp_str, ret_str)

View File

@ -1,345 +0,0 @@
# Copyright 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.
from oslo_config import cfg
from testtools import matchers
from webob import exc
from neutron.common import exceptions as n_exc
from neutron import context
from neutron.extensions import portbindings
from neutron.tests.unit import _test_extension_portbindings as test_bindings
from neutron.tests.unit.nec import test_nec_plugin
from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
class TestNecPortBinding(test_bindings.PortBindingsTestCase,
test_nec_plugin.NecPluginV2TestCase):
VIF_TYPE = portbindings.VIF_TYPE_OVS
VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True,
portbindings.OVS_HYBRID_PLUG: True}
ENABLE_SG = True
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_HYBRID_DRIVER
def setUp(self):
test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER)
cfg.CONF.set_override(
'enable_security_group', self.ENABLE_SG,
group='SECURITYGROUP')
super(TestNecPortBinding, self).setUp()
class TestNecPortBindingNoSG(TestNecPortBinding):
VIF_DETAILS = {portbindings.CAP_PORT_FILTER: False,
portbindings.OVS_HYBRID_PLUG: False}
ENABLE_SG = False
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
class TestNecPortBindingHost(
test_bindings.PortBindingsHostTestCaseMixin,
test_nec_plugin.NecPluginV2TestCase):
pass
class TestNecPortBindingPortInfo(test_nec_plugin.NecPluginV2TestCase):
def _get_portinfo(self, datapath_id=None, port_no=None):
if datapath_id is None:
datapath_id = '0xabc'
if port_no is None:
port_no = 1
return {'datapath_id': datapath_id,
'port_no': port_no}
def _check_response_portbinding_profile(self, port, datapath_id=None,
port_no=None):
expected = self._get_portinfo(datapath_id, port_no)
profile = port[portbindings.PROFILE]
self.assertEqual(len(profile), 2)
self.assertEqual(profile['datapath_id'],
expected['datapath_id'])
self.assertEqual(profile['port_no'],
expected['port_no'])
def _check_response_portbinding_no_profile(self, port):
self.assertIn('status', port)
self.assertNotIn(portbindings.PROFILE, port)
def _get_non_admin_context(self):
return context.Context(user_id=None,
tenant_id=self._tenant_id,
is_admin=False,
read_deleted="no")
def test_port_create_portinfo(self):
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
with self.port(arg_list=(portbindings.PROFILE,),
**profile_arg) as port:
port_id = port['port']['id']
# Check a response of create_port
self._check_response_portbinding_profile(port['port'])
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
# Check a response of get_port
ctx = context.get_admin_context()
port = self._show('ports', port_id, neutron_context=ctx)['port']
self._check_response_portbinding_profile(port)
# By default user is admin - now test non admin user
ctx = self._get_non_admin_context()
non_admin_port = self._show(
'ports', port_id, neutron_context=ctx)['port']
self._check_response_portbinding_no_profile(non_admin_port)
# port-update with non admin user should fail
self._update('ports', port_id,
{'port': profile_arg},
expected_code=exc.HTTPForbidden.code,
neutron_context=ctx)
def test_port_update_portinfo(self):
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
with self.port() as port:
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
port_id = port['port']['id']
# Check a response of create_port
self._check_response_portbinding_no_profile(port['port'])
# Check a response of update_port
ctx = context.get_admin_context()
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self._check_response_portbinding_profile(port)
port = self._show('ports', port_id, neutron_context=ctx)['port']
self._check_response_portbinding_profile(port)
def test_port_update_portinfo_detail(self):
with self.port() as port:
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
port_id = port['port']['id']
ctx = context.get_admin_context()
# add portinfo
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
# portinfo unchanged
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
# modify portinfo
profile_arg = {portbindings.PROFILE:
self._get_portinfo(datapath_id='0x1234567890',
port_no=99)}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
# delete portinfo with an empty dict
profile_arg = {portbindings.PROFILE: {}}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 2)
def test_port_update_portinfo_detail_clear_with_none(self):
with self.port() as port:
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
port_id = port['port']['id']
ctx = context.get_admin_context()
# add portinfo
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
# delete portinfo with None
profile_arg = {portbindings.PROFILE: None}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
def test_port_create_portinfo_with_empty_dict(self):
profile_arg = {portbindings.PROFILE: {}}
with self.port(arg_list=(portbindings.PROFILE,),
**profile_arg) as port:
port_id = port['port']['id']
# Check a response of create_port
self._check_response_portbinding_no_profile(port['port'])
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
# add portinfo
ctx = context.get_admin_context()
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self._check_response_portbinding_profile(port)
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
def test_port_create_portinfo_with_none(self):
profile_arg = {portbindings.PROFILE: None}
with self.port(arg_list=(portbindings.PROFILE,),
**profile_arg) as port:
port_id = port['port']['id']
# Check a response of create_port
self._check_response_portbinding_no_profile(port['port'])
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
# add portinfo
ctx = context.get_admin_context()
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
port = self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)['port']
self._check_response_portbinding_profile(port)
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
def test_port_update_for_existing_port_with_different_padding_dpid(self):
ctx = context.get_admin_context()
with self.port() as port:
port_id = port['port']['id']
portinfo = {'id': port_id, 'port_no': 123}
self.rpcapi_update_ports(datapath_id='0x000000000000abcd',
added=[portinfo])
self.assertEqual(1, self.ofc.create_ofc_port.call_count)
self.assertEqual(0, self.ofc.delete_ofc_port.call_count)
profile_arg = {portbindings.PROFILE:
self._get_portinfo(datapath_id='0xabcd',
port_no=123)}
self._update('ports', port_id, {'port': profile_arg},
neutron_context=ctx)
# Check create_ofc_port/delete_ofc_port are not called.
self.assertEqual(1, self.ofc.create_ofc_port.call_count)
self.assertEqual(0, self.ofc.delete_ofc_port.call_count)
def test_port_create_portinfo_non_admin(self):
with self.network(set_context=True, tenant_id='test') as net1:
with self.subnet(network=net1) as subnet1:
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
try:
with self.port(subnet=subnet1,
expected_res_status=403,
arg_list=(portbindings.PROFILE,),
set_context=True, tenant_id='test',
**profile_arg):
pass
except exc.HTTPClientError:
pass
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
def test_port_update_portinfo_non_admin(self):
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
with self.network() as net1:
with self.subnet(network=net1) as subnet1:
with self.port(subnet=subnet1) as port:
# By default user is admin - now test non admin user
port_id = port['port']['id']
ctx = self._get_non_admin_context()
port = self._update('ports', port_id,
{'port': profile_arg},
expected_code=exc.HTTPForbidden.code,
neutron_context=ctx)
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
def test_port_create_portinfo_validation_called(self):
# Check validate_portinfo is called.
profile_arg = {portbindings.PROFILE:
{'datapath_id': '0xabc',
'port_no': 0xffff + 1}}
try:
with self.port(arg_list=(portbindings.PROFILE,),
expected_res_status=400,
**profile_arg):
pass
except exc.HTTPClientError:
pass
class TestNecPortBindingValidatePortInfo(test_nec_plugin.NecPluginV2TestCase):
def test_validate_portinfo_ok(self):
profile = {'datapath_id': '0x1234567890abcdef',
'port_no': 123}
portinfo = self.plugin._validate_portinfo(profile)
# NOTE(mriedem): Handle long integer conversion universally.
self.assertEqual(
0x1234567890abcdef,
int(portinfo['datapath_id'].replace('L', ''), 16)
)
self.assertEqual(portinfo['port_no'], 123)
def test_validate_portinfo_ok_without_0x(self):
profile = {'datapath_id': '1234567890abcdef',
'port_no': 123}
portinfo = self.plugin._validate_portinfo(profile)
# NOTE(mriedem): Handle long integer conversion universally.
self.assertEqual(
0x1234567890abcdef,
int(portinfo['datapath_id'].replace('L', ''), 16)
)
self.assertEqual(portinfo['port_no'], 123)
def _test_validate_exception(self, profile, expected_msg):
e = self.assertRaises(n_exc.InvalidInput,
self.plugin._validate_portinfo, profile)
self.assertThat(str(e), matchers.StartsWith(expected_msg))
def test_validate_portinfo_dict_validation(self):
expected_msg = ("Invalid input for operation: "
"Validation of dictionary's keys failed.")
profile = {'port_no': 123}
self._test_validate_exception(profile, expected_msg)
profile = {'datapath_id': '0xabcdef'}
self._test_validate_exception(profile, expected_msg)
def test_validate_portinfo_negative_port_number(self):
profile = {'datapath_id': '0x1234567890abcdef',
'port_no': -1}
expected_msg = ("Invalid input for operation: "
"'-1' should be non-negative.")
self._test_validate_exception(profile, expected_msg)
def test_validate_portinfo_invalid_datapath_id(self):
expected_msg = ("Invalid input for operation: "
"datapath_id should be a hex string")
# non hexidecimal datapath_id
profile = {'datapath_id': 'INVALID',
'port_no': 123}
self._test_validate_exception(profile, expected_msg)
# Too big datapath_id
profile = {'datapath_id': '0x10000000000000000',
'port_no': 123}
self._test_validate_exception(profile, expected_msg)
def test_validate_portinfo_too_big_port_number(self):
profile = {'datapath_id': '0x1234567890abcdef',
'port_no': 65536}
expected_msg = ("Invalid input for operation: "
"port_no should be [0:65535]")
self._test_validate_exception(profile, expected_msg)

View File

@ -1,45 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation.
#
# 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.
import mock
from neutron import manager
from neutron.plugins.nec.common import config
from neutron.tests.unit.nec import test_nec_plugin
from neutron.tests.unit import test_extension_extraroute as test_ext_route
class NecRouterL3AgentTestCase(test_ext_route.ExtraRouteDBIntTestCase):
_plugin_name = test_nec_plugin.PLUGIN_NAME
def setUp(self):
mock.patch(test_nec_plugin.OFC_MANAGER).start()
super(NecRouterL3AgentTestCase, self).setUp(self._plugin_name)
plugin = manager.NeutronManager.get_plugin()
plugin.network_scheduler = None
plugin.router_scheduler = None
def test_floatingip_with_invalid_create_port(self):
self._test_floatingip_with_invalid_create_port(self._plugin_name)
class NecRouterOpenFlowTestCase(NecRouterL3AgentTestCase):
def setUp(self):
config.CONF.set_override('default_router_provider',
'openflow', 'PROVIDER')
super(NecRouterOpenFlowTestCase, self).setUp()

View File

@ -1,88 +0,0 @@
# Copyright 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.
import contextlib
import mock
from neutron.api.v2 import attributes
from neutron.extensions import securitygroup as ext_sg
from neutron import manager
from neutron.tests.unit.nec import test_nec_plugin
from neutron.tests.unit import test_extension_security_group as test_sg
from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
PLUGIN_NAME = test_nec_plugin.PLUGIN_NAME
OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager'
NOTIFIER = 'neutron.plugins.nec.nec_plugin.NECPluginV2AgentNotifierApi'
class NecSecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase):
def setUp(self, plugin=None):
test_sg_rpc.set_firewall_driver(test_sg_rpc.FIREWALL_HYBRID_DRIVER)
mock.patch(NOTIFIER).start()
mock.patch(OFC_MANAGER).start()
self._attribute_map_bk_ = {}
for item in attributes.RESOURCE_ATTRIBUTE_MAP:
self._attribute_map_bk_[item] = (attributes.
RESOURCE_ATTRIBUTE_MAP[item].
copy())
super(NecSecurityGroupsTestCase, self).setUp(PLUGIN_NAME)
plugin = manager.NeutronManager.get_plugin()
self.notifier = plugin.notifier
self.rpc = plugin.callback_sg
def tearDown(self):
super(NecSecurityGroupsTestCase, self).tearDown()
attributes.RESOURCE_ATTRIBUTE_MAP = self._attribute_map_bk_
class TestNecSGServerRpcCallBack(
test_sg_rpc.SGServerRpcCallBackTestCase,
NecSecurityGroupsTestCase):
pass
class TestNecSecurityGroups(NecSecurityGroupsTestCase,
test_sg.TestSecurityGroups,
test_sg_rpc.SGNotificationTestMixin):
def test_security_group_get_port_from_device(self):
with contextlib.nested(self.network(),
self.security_group()) as (n, sg):
with self.subnet(n):
res = self._create_port(self.fmt, n['network']['id'])
port = self.deserialize(self.fmt, res)
port_id = port['port']['id']
sg_id = sg['security_group']['id']
fixed_ips = port['port']['fixed_ips']
data = {'port': {'fixed_ips': fixed_ips,
'name': port['port']['name'],
ext_sg.SECURITYGROUPS: [sg_id]}}
req = self.new_update_request('ports', data, port_id)
res = self.deserialize(self.fmt,
req.get_response(self.api))
plugin = manager.NeutronManager.get_plugin()
port_dict = plugin.get_port_from_device(port_id)
self.assertEqual(port_id, port_dict['id'])
self.assertEqual([sg_id],
port_dict[ext_sg.SECURITYGROUPS])
self.assertEqual([], port_dict['security_group_rules'])
self.assertEqual([fixed_ips[0]['ip_address']],
port_dict['fixed_ips'])
self._delete('ports', port_id)

View File

@ -1,363 +0,0 @@
# 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.
import random
import mock
from six import moves
from neutron.openstack.common import uuidutils
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.common import ofc_client
from neutron.plugins.nec.db import models as nmodels
from neutron.plugins.nec import drivers
from neutron.tests import base
class TestConfig(object):
"""Configuration for this test."""
host = '127.0.0.1'
port = 8888
class TremaDriverTestBase(base.BaseTestCase):
driver_name = "trema"
def setUp(self):
super(TremaDriverTestBase, self).setUp()
self.driver = drivers.get_driver(self.driver_name)(TestConfig)
self.do_request = mock.patch.object(ofc_client.OFCClient,
'do_request').start()
def get_ofc_item_random_params(self):
"""create random parameters for ofc_item test."""
tenant_id = uuidutils.generate_uuid()
network_id = uuidutils.generate_uuid()
port_id = uuidutils.generate_uuid()
mac = ':'.join(['%x' % random.randint(0, 255)
for i in moves.xrange(6)])
portinfo = nmodels.PortInfo(id=port_id, datapath_id="0x123456789",
port_no=1234, vlan_id=321,
mac=mac)
return tenant_id, network_id, portinfo
class TremaDriverNetworkTestBase(TremaDriverTestBase):
def test_create_tenant(self):
t, n, p = self.get_ofc_item_random_params()
ret = self.driver.create_tenant('dummy_desc', t)
ofc_t_path = "/tenants/%s" % t
self.assertEqual(ofc_t_path, ret)
# There is no API call.
self.assertEqual(0, self.do_request.call_count)
def test_update_tenant(self):
t, n, p = self.get_ofc_item_random_params()
path = "/tenants/%s" % t
self.driver.update_tenant(path, 'dummy_desc')
# There is no API call.
self.assertEqual(0, self.do_request.call_count)
def testc_delete_tenant(self):
t, n, p = self.get_ofc_item_random_params()
path = "/tenants/%s" % t
self.driver.delete_tenant(path)
# There is no API call.
self.assertEqual(0, self.do_request.call_count)
def testa_create_network(self):
t, n, p = self.get_ofc_item_random_params()
description = "desc of %s" % n
body = {'id': n, 'description': description}
ret = self.driver.create_network(t, description, n)
self.do_request.assert_called_once_with("POST", "/networks", body=body)
self.assertEqual(ret, '/networks/%s' % n)
def testc_delete_network(self):
t, n, p = self.get_ofc_item_random_params()
net_path = "/networks/%s" % n
self.driver.delete_network(net_path)
self.do_request.assert_called_once_with("DELETE", net_path)
class TremaPortBaseDriverTest(TremaDriverNetworkTestBase):
driver_name = "trema_port"
def test_filter_supported(self):
self.assertTrue(self.driver.filter_supported())
def testd_create_port(self):
_t, n, p = self.get_ofc_item_random_params()
net_path = "/networks/%s" % n
body = {'id': p.id,
'datapath_id': p.datapath_id,
'port': str(p.port_no),
'vid': str(p.vlan_id)}
ret = self.driver.create_port(net_path, p, p.id)
self.do_request.assert_called_once_with(
"POST", "/networks/%s/ports" % n, body=body)
self.assertEqual(ret, '/networks/%s/ports/%s' % (n, p.id))
def testd_delete_port(self):
t, n, p = self.get_ofc_item_random_params()
p_path = "/networks/%s/ports/%s" % (n, p.id)
self.driver.delete_port(p_path)
self.do_request.assert_called_once_with("DELETE", p_path)
class TremaPortMACBaseDriverTest(TremaDriverNetworkTestBase):
driver_name = "trema_portmac"
def test_filter_supported(self):
self.assertTrue(self.driver.filter_supported())
def testd_create_port(self):
t, n, p = self.get_ofc_item_random_params()
dummy_port = "dummy-%s" % p.id
net_path = "/networks/%s" % n
path_1 = "/networks/%s/ports" % n
body_1 = {'id': dummy_port,
'datapath_id': p.datapath_id,
'port': str(p.port_no),
'vid': str(p.vlan_id)}
path_2 = "/networks/%s/ports/%s/attachments" % (n, dummy_port)
body_2 = {'id': p.id, 'mac': p.mac}
path_3 = "/networks/%s/ports/%s" % (n, dummy_port)
ret = self.driver.create_port(net_path, p, p.id)
self.do_request.assert_has_calls([
mock.call("POST", path_1, body=body_1),
mock.call("POST", path_2, body=body_2),
mock.call("DELETE", path_3)
])
port_path = "/networks/%s/ports/%s/attachments/%s" % (n, dummy_port,
p.id)
self.assertEqual(ret, port_path)
def testd_delete_port(self):
t, n, p = self.get_ofc_item_random_params()
dummy_port = "dummy-%s" % p.id
path = "/networks/%s/ports/%s/attachments/%s" % (n, dummy_port, p.id)
self.driver.delete_port(path)
self.do_request.assert_called_once_with("DELETE", path)
class TremaMACBaseDriverTest(TremaDriverNetworkTestBase):
driver_name = "trema_mac"
def test_filter_supported(self):
self.assertFalse(self.driver.filter_supported())
def testd_create_port(self):
t, n, p = self.get_ofc_item_random_params()
net_path = "/networks/%s" % n
path = "/networks/%s/attachments" % n
body = {'id': p.id, 'mac': p.mac}
ret = self.driver.create_port(net_path, p, p.id)
self.do_request.assert_called_once_with("POST", path, body=body)
self.assertEqual(ret, '/networks/%s/attachments/%s' % (n, p.id))
def testd_delete_port(self):
t, n, p = self.get_ofc_item_random_params()
path = "/networks/%s/attachments/%s" % (n, p.id)
self.driver.delete_port(path)
self.do_request.assert_called_once_with("DELETE", path)
class TremaFilterDriverTest(TremaDriverTestBase):
def _test_create_filter(self, filter_dict=None, filter_post=None,
filter_wildcards=None, no_portinfo=False):
t, n, p = self.get_ofc_item_random_params()
src_mac = ':'.join(['%x' % random.randint(0, 255)
for i in moves.xrange(6)])
if filter_wildcards is None:
filter_wildcards = []
f = {'tenant_id': t,
'id': uuidutils.generate_uuid(),
'network_id': n,
'priority': 123,
'action': "ACCEPT",
'in_port': p.id,
'src_mac': src_mac,
'dst_mac': "",
'eth_type': 0,
'src_cidr': "",
'dst_cidr': "",
'src_port': 0,
'dst_port': 0,
'protocol': "TCP",
'admin_state_up': True,
'status': "ACTIVE"}
if filter_dict:
f.update(filter_dict)
net_path = "/networks/%s" % n
all_wildcards_ofp = ['dl_vlan', 'dl_vlan_pcp', 'nw_tos',
'in_port', 'dl_src', 'dl_dst',
'nw_src', 'nw_dst',
'dl_type', 'nw_proto',
'tp_src', 'tp_dst']
all_wildcards_non_ofp = ['in_datapath_id', 'slice']
body = {'id': f['id'],
'action': 'ALLOW',
'priority': 123,
'slice': n,
'in_datapath_id': '0x123456789',
'in_port': 1234,
'nw_proto': '0x6',
'dl_type': '0x800',
'dl_src': src_mac}
if filter_post:
body.update(filter_post)
if no_portinfo:
filter_wildcards += ['in_datapath_id', 'in_port']
p = None
for field in filter_wildcards:
if field in body:
del body[field]
ofp_wildcards = ["%s:32" % _f if _f in ['nw_src', 'nw_dst'] else _f
for _f in all_wildcards_ofp if _f not in body]
body['ofp_wildcards'] = set(ofp_wildcards)
non_ofp_wildcards = [_f for _f in all_wildcards_non_ofp
if _f not in body]
if non_ofp_wildcards:
body['wildcards'] = set(non_ofp_wildcards)
ctx = mock.Mock()
ctx.session = mock.sentinel.session
with mock.patch('neutron.plugins.nec.db.api.get_portinfo',
return_value=p) as get_portinfo:
with mock.patch('neutron.plugins.nec.db.api.get_ofc_id',
return_value=net_path) as get_ofc_id:
ret = self.driver.create_filter(ctx, f, f['id'])
# The content of 'body' is checked below.
self.do_request.assert_called_once_with("POST", "/filters",
body=mock.ANY)
self.assertEqual(ret, '/filters/%s' % f['id'])
# ofp_wildcards and wildcards in body are comma-separated
# string but the order of elements are not considered,
# so we check these fields as set.
actual_body = self.do_request.call_args[1]['body']
if 'ofp_wildcards' in actual_body:
ofp_wildcards = actual_body['ofp_wildcards'].split(',')
actual_body['ofp_wildcards'] = set(ofp_wildcards)
if 'wildcards' in actual_body:
actual_body['wildcards'] = set(actual_body['wildcards'].split(','))
self.assertEqual(body, actual_body)
get_ofc_id.assert_called_once_with(mock.sentinel.session,
'ofc_network', n)
get_portinfo.assert_called_once_with(mock.sentinel.session, p.id)
def test_create_filter_accept(self):
self._test_create_filter(filter_dict={'action': 'ACCEPT'})
def test_create_filter_allow(self):
self._test_create_filter(filter_dict={'action': 'ALLOW'})
def test_create_filter_deny(self):
self._test_create_filter(filter_dict={'action': 'DENY'},
filter_post={'action': 'DENY'})
def test_create_filter_drop(self):
self._test_create_filter(filter_dict={'action': 'DROP'},
filter_post={'action': 'DENY'})
def test_create_filter_no_port(self):
self.assertRaises(nexc.PortInfoNotFound,
self._test_create_filter, no_portinfo=True)
def test_create_filter_src_mac_wildcard(self):
self._test_create_filter(filter_dict={'src_mac': ''},
filter_wildcards=['dl_src'])
def test_create_filter_dst_mac(self):
dst_mac = ':'.join(['%x' % random.randint(0, 255)
for i in moves.xrange(6)])
self._test_create_filter(filter_dict={'dst_mac': dst_mac},
filter_post={'dl_dst': dst_mac})
def test_create_filter_src_cidr(self):
src_cidr = '10.2.0.0/24'
self._test_create_filter(filter_dict={'src_cidr': src_cidr},
filter_post={'nw_src': src_cidr})
def test_create_filter_dst_cidr(self):
dst_cidr = '192.168.10.0/24'
self._test_create_filter(filter_dict={'dst_cidr': dst_cidr},
filter_post={'nw_dst': dst_cidr})
def test_create_filter_proto_icmp(self):
self._test_create_filter(
filter_dict={'protocol': 'icmp'},
filter_post={'dl_type': '0x800', 'nw_proto': '0x1'})
def test_create_filter_proto_tcp(self):
self._test_create_filter(
filter_dict={'protocol': 'tcp'},
filter_post={'dl_type': '0x800', 'nw_proto': '0x6'})
def test_create_filter_proto_udp(self):
self._test_create_filter(
filter_dict={'protocol': 'udp'},
filter_post={'dl_type': '0x800', 'nw_proto': '0x11'})
def test_create_filter_proto_arp(self):
self._test_create_filter(
filter_dict={'protocol': 'arp'},
filter_post={'dl_type': '0x806'},
filter_wildcards=['nw_proto'])
def test_create_filter_proto_misc(self):
self._test_create_filter(
filter_dict={'protocol': '0x33', 'eth_type': '0x900'},
filter_post={'dl_type': '0x900', 'nw_proto': '0x33'})
def test_create_filter_proto_misc_dl_type_wildcard(self):
self._test_create_filter(
filter_dict={'protocol': '0x33', 'ether_type': ''},
filter_post={'nw_proto': '0x33'},
filter_wildcards=['dl_type'])
def test_create_filter_proto_wildcard(self):
self._test_create_filter(
filter_dict={'protocol': ''},
filter_wildcards=['dl_type', 'nw_proto'])
def test_create_filter_src_dst_port(self):
self._test_create_filter(filter_dict={'src_port': 8192,
'dst_port': 4096},
filter_post={'tp_src': '0x2000',
'tp_dst': '0x1000'})
def testb_delete_filter(self):
t, n, p = self.get_ofc_item_random_params()
f_path = "/filters/%s" % uuidutils.generate_uuid()
self.driver.delete_filter(f_path)
self.do_request.assert_called_once_with("DELETE", f_path)

View File

@ -1,31 +0,0 @@
# Copyright 2014 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.
from neutron.plugins.nec.common import utils
from neutron.tests import base
class NecUtilsTest(base.BaseTestCase):
def test_cmp_dpid(self):
self.assertTrue(utils.cmp_dpid('0xabcd', '0xabcd'))
self.assertTrue(utils.cmp_dpid('abcd', '0xabcd'))
self.assertTrue(utils.cmp_dpid('0x000000000000abcd', '0xabcd'))
self.assertTrue(utils.cmp_dpid('0x000000000000abcd', '0x00abcd'))
self.assertFalse(utils.cmp_dpid('0x000000000000abcd', '0xabc0'))
self.assertFalse(utils.cmp_dpid('0x000000000000abcd', '0x00abc0'))
def test_cmp_dpid_with_exception(self):
self.assertFalse(utils.cmp_dpid('0xabcx', '0xabcx'))
self.assertFalse(utils.cmp_dpid(None, None))

View File

@ -98,7 +98,7 @@ console_scripts =
neutron-linuxbridge-agent = neutron.plugins.linuxbridge.agent.linuxbridge_neutron_agent:main
neutron-metadata-agent = neutron.cmd.eventlet.agents.metadata:main
neutron-mlnx-agent = neutron.plugins.mlnx.agent.eswitch_neutron_agent:main
neutron-nec-agent = neutron.plugins.nec.agent.nec_neutron_agent:main
neutron-nec-agent = neutron.cmd.eventlet.plugins.nec_neutron_agent:main
neutron-netns-cleanup = neutron.cmd.netns_cleanup:main
neutron-ns-metadata-proxy = neutron.cmd.eventlet.agents.metadata_proxy:main
neutron-nvsd-agent = neutron.plugins.oneconvergence.agent.nvsd_neutron_agent:main