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:
parent
ddb546443f
commit
1fdbcb1343
|
@ -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:
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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'
|
|
@ -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.")
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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]
|
|
@ -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
|
|
@ -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'])
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
networking-nec>=2015.1,<2015.2
|
|
@ -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)
|
|
@ -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]
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
||||
])
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
@ -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))
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue