NEC OpenFlow plugin support.
blueprint quantum-nec-openflow-plugin Change-Id: Ib6d6f658bbf6d653527fa7820685f9bed1412a18
This commit is contained in:
parent
99021f44ff
commit
1985e16d2e
39
etc/quantum/plugins/nec/nec.ini
Normal file
39
etc/quantum/plugins/nec/nec.ini
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Sample Configurations
|
||||||
|
|
||||||
|
[DATABASE]
|
||||||
|
# This line MUST be changed to actually run the plugin.
|
||||||
|
# Example:
|
||||||
|
# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum
|
||||||
|
# Replace 127.0.0.1 above with the IP address of the database used by the
|
||||||
|
# main quantum server. (Leave it as is if the database runs on this host.)
|
||||||
|
sql_connection = sqlite://
|
||||||
|
# Database reconnection retry times - in event connectivity is lost
|
||||||
|
# set to -1 implies an infinite retry count
|
||||||
|
# sql_max_retries = 10
|
||||||
|
# Database reconnection interval in seconds - in event connectivity is lost
|
||||||
|
reconnect_interval = 2
|
||||||
|
|
||||||
|
[OVS]
|
||||||
|
# Do not change this parameter unless you have a good reason to.
|
||||||
|
# This is the name of the OVS integration bridge. There is one per hypervisor.
|
||||||
|
# The integration bridge acts as a virtual "patch port". All VM VIFs are
|
||||||
|
# attached to this bridge and then "patched" according to their network
|
||||||
|
# connectivity.
|
||||||
|
integration_bridge = br-int
|
||||||
|
|
||||||
|
[AGENT]
|
||||||
|
# Agent's polling interval in seconds
|
||||||
|
polling_interval = 2
|
||||||
|
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
||||||
|
# as root.
|
||||||
|
root_helper = sudo
|
||||||
|
|
||||||
|
[OFC]
|
||||||
|
# Specify OpenFlow Controller Host, Port and Driver to connect.
|
||||||
|
host = 127.0.0.1
|
||||||
|
port = 8888
|
||||||
|
# Drivers are in quantum/plugins/nec/drivers/ .
|
||||||
|
driver = trema
|
||||||
|
# PacketFilter is available when it's enabled in this configuration
|
||||||
|
# and supported by the driver.
|
||||||
|
enable_packet_filter = true
|
20
quantum/plugins/nec/README
Normal file
20
quantum/plugins/nec/README
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Quantum NEC OpenFlow Plugin
|
||||||
|
|
||||||
|
|
||||||
|
# -- What's this?
|
||||||
|
|
||||||
|
http://wiki.openstack.org/Quantum-NEC-OpenFlow-Plugin
|
||||||
|
|
||||||
|
|
||||||
|
# -- Installation
|
||||||
|
|
||||||
|
Use QuickStart Script for this plugin. This provides you auto installation and
|
||||||
|
configuration of Nova, Quantum and Trema.
|
||||||
|
https://github.com/nec-openstack/quantum-openflow-plugin/tree/folsom
|
||||||
|
|
||||||
|
|
||||||
|
# -- Running Tests
|
||||||
|
|
||||||
|
To run tests of this plugin (run the following from the top level Quantum
|
||||||
|
directory):
|
||||||
|
PLUGIN_DIR=quantum/plugins/nec ./run_tests.sh -N
|
15
quantum/plugins/nec/__init__.py
Normal file
15
quantum/plugins/nec/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
15
quantum/plugins/nec/agent/__init__.py
Normal file
15
quantum/plugins/nec/agent/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
130
quantum/plugins/nec/agent/nec_quantum_agent.py
Executable file
130
quantum/plugins/nec/agent/nec_quantum_agent.py
Executable file
@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation.
|
||||||
|
# Based on ryu/openvswitch agents.
|
||||||
|
#
|
||||||
|
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
||||||
|
# Copyright 2011 Nicira Networks, 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.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from quantum.agent.linux import ovs_lib
|
||||||
|
from quantum.common import config as logging_config
|
||||||
|
from quantum.common import topics
|
||||||
|
from quantum.openstack.common import context
|
||||||
|
from quantum.openstack.common import rpc
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NECQuantumAgent(object):
|
||||||
|
|
||||||
|
def __init__(self, integ_br, root_helper, polling_interval):
|
||||||
|
'''Constructor.
|
||||||
|
|
||||||
|
:param integ_br: name of the integration bridge.
|
||||||
|
:param root_helper: utility to use when running shell cmds.
|
||||||
|
:param polling_interval: interval (secs) to check the bridge.
|
||||||
|
'''
|
||||||
|
self.int_br = ovs_lib.OVSBridge(integ_br, root_helper)
|
||||||
|
self.polling_interval = polling_interval
|
||||||
|
|
||||||
|
self.host = socket.gethostname()
|
||||||
|
self.agent_id = 'nec-q-agent.%s' % self.host
|
||||||
|
self.datapath_id = "0x%s" % self.int_br.get_datapath_id()
|
||||||
|
|
||||||
|
# RPC network init
|
||||||
|
self.context = context.RequestContext('quantum', 'quantum',
|
||||||
|
is_admin=False)
|
||||||
|
self.conn = rpc.create_connection(new=True)
|
||||||
|
|
||||||
|
def update_ports(self, port_added=[], port_removed=[]):
|
||||||
|
"""RPC to update information of ports on Quantum Server"""
|
||||||
|
LOG.info("update ports: added=%s, removed=%s" %
|
||||||
|
(port_added, port_removed))
|
||||||
|
try:
|
||||||
|
rpc.call(self.context,
|
||||||
|
topics.PLUGIN,
|
||||||
|
{'method': 'update_ports',
|
||||||
|
'args': {'topic': topics.AGENT,
|
||||||
|
'agent_id': self.agent_id,
|
||||||
|
'datapath_id': self.datapath_id,
|
||||||
|
'port_added': port_added,
|
||||||
|
'port_removed': port_removed}})
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warn("update_ports() failed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
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 daemon_loop(self):
|
||||||
|
"""Main processing loop for NEC Plugin Agent."""
|
||||||
|
old_ports = []
|
||||||
|
while True:
|
||||||
|
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 old_ports:
|
||||||
|
port_info = self._vif_port_to_port_info(vif_port)
|
||||||
|
port_added.append(port_info)
|
||||||
|
|
||||||
|
port_removed = []
|
||||||
|
for port_id in old_ports:
|
||||||
|
if port_id not in new_ports:
|
||||||
|
port_removed.append(port_id)
|
||||||
|
|
||||||
|
if port_added or port_removed:
|
||||||
|
self.update_ports(port_added, port_removed)
|
||||||
|
else:
|
||||||
|
LOG.debug("No port changed.")
|
||||||
|
|
||||||
|
old_ports = new_ports
|
||||||
|
time.sleep(self.polling_interval)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config.CONF(args=sys.argv, project='quantum')
|
||||||
|
|
||||||
|
logging_config.setup_logging(config.CONF)
|
||||||
|
|
||||||
|
# Determine which agent type to use.
|
||||||
|
integ_br = config.OVS.integration_bridge
|
||||||
|
root_helper = config.AGENT.root_helper
|
||||||
|
polling_interval = config.AGENT.polling_interval
|
||||||
|
|
||||||
|
agent = NECQuantumAgent(integ_br, root_helper, polling_interval)
|
||||||
|
|
||||||
|
# Start everything.
|
||||||
|
agent.daemon_loop()
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
15
quantum/plugins/nec/common/__init__.py
Normal file
15
quantum/plugins/nec/common/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
59
quantum/plugins/nec/common/config.py
Normal file
59
quantum/plugins/nec/common/config.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.openstack.common import cfg
|
||||||
|
# import rpc config options
|
||||||
|
from quantum.openstack.common import rpc
|
||||||
|
|
||||||
|
|
||||||
|
database_opts = [
|
||||||
|
cfg.StrOpt('sql_connection', default='sqlite://'),
|
||||||
|
cfg.IntOpt('sql_max_retries', default=-1),
|
||||||
|
cfg.IntOpt('reconnect_interval', default=2),
|
||||||
|
]
|
||||||
|
|
||||||
|
ovs_opts = [
|
||||||
|
cfg.StrOpt('integration_bridge', default='br-int'),
|
||||||
|
]
|
||||||
|
|
||||||
|
agent_opts = [
|
||||||
|
cfg.IntOpt('polling_interval', default=2),
|
||||||
|
cfg.StrOpt('root_helper', default='sudo'),
|
||||||
|
]
|
||||||
|
|
||||||
|
ofc_opts = [
|
||||||
|
cfg.StrOpt('host', default='127.0.0.1'),
|
||||||
|
cfg.StrOpt('port', default='8888'),
|
||||||
|
cfg.StrOpt('driver', default='trema'),
|
||||||
|
cfg.BoolOpt('enable_packet_filter', default=True),
|
||||||
|
cfg.BoolOpt('use_ssl', default=False),
|
||||||
|
cfg.StrOpt('key_file', default=None),
|
||||||
|
cfg.StrOpt('cert_file', default=None),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cfg.CONF.register_opts(database_opts, "DATABASE")
|
||||||
|
cfg.CONF.register_opts(ovs_opts, "OVS")
|
||||||
|
cfg.CONF.register_opts(agent_opts, "AGENT")
|
||||||
|
cfg.CONF.register_opts(ofc_opts, "OFC")
|
||||||
|
|
||||||
|
# shortcuts
|
||||||
|
CONF = cfg.CONF
|
||||||
|
DATABASE = cfg.CONF.DATABASE
|
||||||
|
OVS = cfg.CONF.OVS
|
||||||
|
AGENT = cfg.CONF.AGENT
|
||||||
|
OFC = cfg.CONF.OFC
|
39
quantum/plugins/nec/common/exceptions.py
Normal file
39
quantum/plugins/nec/common/exceptions.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.common import exceptions as qexc
|
||||||
|
|
||||||
|
|
||||||
|
class OFCException(qexc.QuantumException):
|
||||||
|
message = _("An OFC exception has occurred: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class NECDBException(qexc.QuantumException):
|
||||||
|
message = _("An exception occurred in NECPluginV2 DB: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class OFCConsistencyBroken(qexc.QuantumException):
|
||||||
|
message = _("Consistency of Quantum-OFC resource map is broken: "
|
||||||
|
"%(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class PortInfoNotFound(qexc.NotFound):
|
||||||
|
message = _("PortInfo %(id)s could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
class PacketFilterNotFound(qexc.NotFound):
|
||||||
|
message = _("PacketFilter %(id)s could not be found")
|
99
quantum/plugins/nec/common/ofc_client.py
Normal file
99
quantum/plugins/nec/common/ofc_client.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import httplib
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from quantum.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):
|
||||||
|
"""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
|
||||||
|
"""
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.use_ssl = use_ssl
|
||||||
|
self.key_file = key_file
|
||||||
|
self.cert_file = cert_file
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def get_connection_type(self):
|
||||||
|
"""Returns the proper connection type"""
|
||||||
|
if self.use_ssl:
|
||||||
|
return httplib.HTTPSConnection
|
||||||
|
else:
|
||||||
|
return httplib.HTTPConnection
|
||||||
|
|
||||||
|
def do_request(self, method, action, body=None):
|
||||||
|
LOG.debug("Client request: %s %s [%s]" % (method, action, str(body)))
|
||||||
|
|
||||||
|
if type(body) is dict:
|
||||||
|
body = json.dumps(body)
|
||||||
|
try:
|
||||||
|
connection_type = self.get_connection_type()
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
# Open connection and send request, handling SSL certs
|
||||||
|
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)
|
||||||
|
if self.use_ssl and len(certs):
|
||||||
|
conn = connection_type(self.host, self.port, **certs)
|
||||||
|
else:
|
||||||
|
conn = connection_type(self.host, self.port)
|
||||||
|
conn.request(method, action, body, headers)
|
||||||
|
res = conn.getresponse()
|
||||||
|
data = res.read()
|
||||||
|
LOG.debug("OFC returns [%s:%s]" % (str(res.status), data))
|
||||||
|
if res.status in (httplib.OK,
|
||||||
|
httplib.CREATED,
|
||||||
|
httplib.ACCEPTED,
|
||||||
|
httplib.NO_CONTENT):
|
||||||
|
if data and len(data) > 1:
|
||||||
|
return json.loads(data)
|
||||||
|
else:
|
||||||
|
reason = _("An operation on OFC is failed.")
|
||||||
|
raise nexc.OFCException(reason=reason)
|
||||||
|
except (socket.error, IOError), e:
|
||||||
|
reason = _("Failed to connect OFC : %s" % str(e))
|
||||||
|
LOG.error(reason)
|
||||||
|
raise nexc.OFCException(reason=reason)
|
||||||
|
|
||||||
|
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
quantum/plugins/nec/db/__init__.py
Normal file
15
quantum/plugins/nec/db/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
123
quantum/plugins/nec/db/api.py
Normal file
123
quantum/plugins/nec/db/api.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from quantum.db import api as db
|
||||||
|
from quantum.db import model_base
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
from quantum.plugins.nec.common import exceptions as nexc
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
OFP_VLAN_NONE = 0xffff
|
||||||
|
|
||||||
|
|
||||||
|
def initialize():
|
||||||
|
options = {"sql_connection": "%s" % config.DATABASE.sql_connection,
|
||||||
|
"sql_max_retries": config.DATABASE.sql_max_retries,
|
||||||
|
"reconnect_interval": config.DATABASE.reconnect_interval,
|
||||||
|
"base": model_base.BASEV2}
|
||||||
|
db.configure_db(options)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_db(base=model_base.BASEV2):
|
||||||
|
db.clear_db(base)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ofc_item(model, id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
return (session.query(model).
|
||||||
|
filter_by(id=id).
|
||||||
|
one())
|
||||||
|
except sa.orm.exc.NoResultFound:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_ofc_item(model, quantum_id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
return (session.query(model).
|
||||||
|
filter_by(quantum_id=quantum_id).
|
||||||
|
one())
|
||||||
|
except sa.orm.exc.NoResultFound:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def add_ofc_item(model, id, quantum_id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
item = model(id=id, quantum_id=quantum_id)
|
||||||
|
session.add(item)
|
||||||
|
session.flush()
|
||||||
|
except sa.exc.IntegrityError as exc:
|
||||||
|
LOG.exception(exc)
|
||||||
|
raise nexc.NECDBException
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def del_ofc_item(model, id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
item = (session.query(model).
|
||||||
|
filter_by(id=id).
|
||||||
|
one())
|
||||||
|
session.delete(item)
|
||||||
|
session.flush()
|
||||||
|
except sa.orm.exc.NoResultFound:
|
||||||
|
LOG.warning("_del_ofc_item(): NotFound item "
|
||||||
|
"(model=%s, id=%s) " % (model, id))
|
||||||
|
|
||||||
|
|
||||||
|
def get_portinfo(id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
return (session.query(nmodels.PortInfo).
|
||||||
|
filter_by(id=id).
|
||||||
|
one())
|
||||||
|
except sa.orm.exc.NoResultFound:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def add_portinfo(id, datapath_id='', port_no=0, vlan_id=OFP_VLAN_NONE, mac=''):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
portinfo = nmodels.PortInfo(id=id, datapath_id=datapath_id,
|
||||||
|
port_no=port_no, vlan_id=vlan_id, mac=mac)
|
||||||
|
session.add(portinfo)
|
||||||
|
session.flush()
|
||||||
|
except sa.exc.IntegrityError as exc:
|
||||||
|
LOG.exception(exc)
|
||||||
|
raise nexc.NECDBException
|
||||||
|
return portinfo
|
||||||
|
|
||||||
|
|
||||||
|
def del_portinfo(id):
|
||||||
|
session = db.get_session()
|
||||||
|
try:
|
||||||
|
portinfo = (session.query(nmodels.PortInfo).
|
||||||
|
filter_by(id=id).
|
||||||
|
one())
|
||||||
|
session.delete(portinfo)
|
||||||
|
session.flush()
|
||||||
|
except sa.orm.exc.NoResultFound:
|
||||||
|
LOG.warning("del_portinfo(): NotFound portinfo for "
|
||||||
|
"port_id: %s" % id)
|
72
quantum/plugins/nec/db/models.py
Normal file
72
quantum/plugins/nec/db/models.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from quantum.db import model_base
|
||||||
|
from quantum.db import models_v2
|
||||||
|
|
||||||
|
|
||||||
|
class HasQuantumId(object):
|
||||||
|
"""Logical ID on Quantum"""
|
||||||
|
quantum_id = sa.Column(sa.String(36), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class OFCTenant(model_base.BASEV2, models_v2.HasId, HasQuantumId):
|
||||||
|
"""Represents a Tenant on OpenFlow Network/Controller."""
|
||||||
|
|
||||||
|
|
||||||
|
class OFCNetwork(model_base.BASEV2, models_v2.HasId, HasQuantumId):
|
||||||
|
"""Represents a Network on OpenFlow Network/Controller."""
|
||||||
|
|
||||||
|
|
||||||
|
class OFCPort(model_base.BASEV2, models_v2.HasId, HasQuantumId):
|
||||||
|
"""Represents a Port on OpenFlow Network/Controller."""
|
||||||
|
|
||||||
|
|
||||||
|
class OFCFilter(model_base.BASEV2, models_v2.HasId, HasQuantumId):
|
||||||
|
"""Represents a Filter on OpenFlow Network/Controller."""
|
||||||
|
|
||||||
|
|
||||||
|
class PortInfo(model_base.BASEV2, models_v2.HasId):
|
||||||
|
"""Represents a Virtual Interface."""
|
||||||
|
datapath_id = sa.Column(sa.String(36), nullable=False)
|
||||||
|
port_no = sa.Column(sa.Integer, nullable=False)
|
||||||
|
vlan_id = sa.Column(sa.Integer, nullable=False)
|
||||||
|
mac = sa.Column(sa.String(32), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class PacketFilter(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||||
|
"""Represents a packet filter"""
|
||||||
|
network_id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('networks.id', ondelete="CASCADE"),
|
||||||
|
nullable=False)
|
||||||
|
priority = sa.Column(sa.Integer, nullable=False)
|
||||||
|
action = sa.Column(sa.String(16), nullable=False)
|
||||||
|
# condition
|
||||||
|
in_port = sa.Column(sa.String(36), nullable=False)
|
||||||
|
src_mac = sa.Column(sa.String(32), nullable=False)
|
||||||
|
dst_mac = sa.Column(sa.String(32), nullable=False)
|
||||||
|
eth_type = sa.Column(sa.Integer, nullable=False)
|
||||||
|
src_cidr = sa.Column(sa.String(64), nullable=False)
|
||||||
|
dst_cidr = sa.Column(sa.String(64), nullable=False)
|
||||||
|
protocol = sa.Column(sa.String(16), nullable=False)
|
||||||
|
src_port = sa.Column(sa.Integer, nullable=False)
|
||||||
|
dst_port = sa.Column(sa.Integer, nullable=False)
|
||||||
|
# status
|
||||||
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
||||||
|
status = sa.Column(sa.String(16), nullable=False)
|
118
quantum/plugins/nec/db/nec_plugin_base.py
Normal file
118
quantum/plugins/nec/db/nec_plugin_base.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.api.v2 import attributes
|
||||||
|
from quantum.common import utils
|
||||||
|
from quantum.db import db_base_plugin_v2
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
|
||||||
|
|
||||||
|
class NECPluginV2Base(db_base_plugin_v2.QuantumDbPluginV2):
|
||||||
|
|
||||||
|
""" Base class of plugins that handle packet filters. """
|
||||||
|
|
||||||
|
def _make_packet_filter_dict(self, packet_filter, fields=None):
|
||||||
|
res = {'id': packet_filter['id'],
|
||||||
|
'tenant_id': packet_filter['tenant_id'],
|
||||||
|
'network_id': packet_filter['network_id'],
|
||||||
|
'action': packet_filter['action'],
|
||||||
|
'priority': packet_filter['priority'],
|
||||||
|
'in_port': packet_filter['in_port'],
|
||||||
|
'src_mac': packet_filter['src_mac'],
|
||||||
|
'dst_mac': packet_filter['dst_mac'],
|
||||||
|
'eth_type': packet_filter['eth_type'],
|
||||||
|
'src_cidr': packet_filter['src_cidr'],
|
||||||
|
'dst_cidr': packet_filter['dst_cidr'],
|
||||||
|
'protocol': packet_filter['protocol'],
|
||||||
|
'src_port': packet_filter['src_port'],
|
||||||
|
'dst_port': packet_filter['dst_port'],
|
||||||
|
'admin_state_up': packet_filter['admin_state_up'],
|
||||||
|
'status': packet_filter['status']}
|
||||||
|
return self._fields(res, fields)
|
||||||
|
|
||||||
|
def _get_packet_filter(self, context, id, verbose=None):
|
||||||
|
try:
|
||||||
|
packet_filter = self._get_by_id(context, nmodels.PacketFilter, id,
|
||||||
|
verbose=verbose)
|
||||||
|
except exc.NoResultFound:
|
||||||
|
raise q_exc.PacketFilterNotFound(id=id)
|
||||||
|
except exc.MultipleResultsFound:
|
||||||
|
LOG.error('Multiple packet_filters match for %s' % id)
|
||||||
|
raise q_exc.PacketFilterNotFound(id=id)
|
||||||
|
return packet_filter
|
||||||
|
|
||||||
|
def get_packet_filter(self, context, id, fields=None, verbose=None):
|
||||||
|
packet_filter = self._get_packet_filter(context, id, verbose=verbose)
|
||||||
|
return self._make_packet_filter_dict(packet_filter, fields)
|
||||||
|
|
||||||
|
def get_packet_filters(self, context, filters=None, fields=None,
|
||||||
|
verbose=None):
|
||||||
|
return self._get_collection(context,
|
||||||
|
nmodels.PacketFilter,
|
||||||
|
self._make_packet_filter_dict,
|
||||||
|
filters=filters,
|
||||||
|
fields=fields,
|
||||||
|
verbose=verbose)
|
||||||
|
|
||||||
|
def create_packet_filter(self, context, packet_filter):
|
||||||
|
pf = packet_filter['packet_filter']
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context, pf)
|
||||||
|
|
||||||
|
# validate network ownership
|
||||||
|
super(NECPluginV2Base, self).get_network(context, pf['network_id'])
|
||||||
|
if pf.get('in_port') != attributes.ATTR_NOT_SPECIFIED:
|
||||||
|
# validate port ownership
|
||||||
|
super(NECPluginV2Base, self).get_port(context, pf['in_port'])
|
||||||
|
|
||||||
|
params = {'tenant_id': tenant_id,
|
||||||
|
'id': pf.get('id') or utils.str_uuid(),
|
||||||
|
'network_id': pf['network_id'],
|
||||||
|
'priority': pf['priority'],
|
||||||
|
'action': pf['action'],
|
||||||
|
'admin_state_up': pf.get('admin_state_up', True),
|
||||||
|
'status': "ACTIVE"}
|
||||||
|
conditions = {'in_port': '',
|
||||||
|
'src_mac': '',
|
||||||
|
'dst_mac': '',
|
||||||
|
'eth_type': 0,
|
||||||
|
'src_cidr': '',
|
||||||
|
'dst_cidr': '',
|
||||||
|
'src_port': 0,
|
||||||
|
'dst_port': 0,
|
||||||
|
'protocol': ''}
|
||||||
|
for key, default in conditions.items():
|
||||||
|
if pf.get(key) == attributes.ATTR_NOT_SPECIFIED:
|
||||||
|
params.update({key: default})
|
||||||
|
else:
|
||||||
|
params.update({key: pf.get(key)})
|
||||||
|
|
||||||
|
with context.session.begin():
|
||||||
|
pf_entry = nmodels.PacketFilter(**params)
|
||||||
|
context.session.add(pf_entry)
|
||||||
|
return self._make_packet_filter_dict(pf_entry)
|
||||||
|
|
||||||
|
def update_packet_filter(self, context, id, packet_filter):
|
||||||
|
pf = packet_filter['packet_filter']
|
||||||
|
with context.session.begin():
|
||||||
|
pf_entry = self._get_packet_filter(context, id)
|
||||||
|
pf_entry.update(pf)
|
||||||
|
return self._make_packet_filter_dict(pf_entry)
|
||||||
|
|
||||||
|
def delete_packet_filter(self, context, id):
|
||||||
|
with context.session.begin():
|
||||||
|
packet_filter = self._get_packet_filter(context, id)
|
||||||
|
context.session.delete(packet_filter)
|
36
quantum/plugins/nec/drivers/__init__.py
Normal file
36
quantum/plugins/nec/drivers/__init__.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from quantum.openstack.common import importutils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
DRIVER_PATH = "quantum.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.PFCDriver"}
|
||||||
|
|
||||||
|
|
||||||
|
def get_driver(driver_name):
|
||||||
|
LOG.info("Loading OFC driver: %s" % driver_name)
|
||||||
|
driver_klass = DRIVER_LIST.get(driver_name) or driver_name
|
||||||
|
return importutils.import_class(driver_klass)
|
101
quantum/plugins/nec/drivers/pfc.py
Normal file
101
quantum/plugins/nec/drivers/pfc.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.plugins.nec.common import ofc_client
|
||||||
|
from quantum.plugins.nec import ofc_driver_base
|
||||||
|
|
||||||
|
|
||||||
|
TENANTS_PATH = "/tenants"
|
||||||
|
TENANT_PATH = "/tenants/%s"
|
||||||
|
NETWORKS_PATH = "/tenants/%s/networks"
|
||||||
|
NETWORK_PATH = "/tenants/%s/networks/%s"
|
||||||
|
PORTS_PATH = "/tenants/%s/networks/%s/ports"
|
||||||
|
PORT_PATH = "/tenants/%s/networks/%s/ports/%s"
|
||||||
|
|
||||||
|
|
||||||
|
class PFCDriver(ofc_driver_base.OFCDriverBase):
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_supported(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_tenant(self, description, tenant_id=None):
|
||||||
|
body = {'description': description}
|
||||||
|
if tenant_id:
|
||||||
|
body.update({'id': tenant_id})
|
||||||
|
res = self.client.post(TENANTS_PATH, body=body)
|
||||||
|
ofc_tenant_id = res['id']
|
||||||
|
return ofc_tenant_id
|
||||||
|
|
||||||
|
def update_tenant(self, ofc_tenant_id, description):
|
||||||
|
path = TENANT_PATH % ofc_tenant_id
|
||||||
|
body = {'description': description}
|
||||||
|
res = self.client.put(path, body=body)
|
||||||
|
|
||||||
|
def delete_tenant(self, ofc_tenant_id):
|
||||||
|
path = TENANT_PATH % ofc_tenant_id
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
def create_network(self, ofc_tenant_id, description, network_id=None):
|
||||||
|
path = NETWORKS_PATH % ofc_tenant_id
|
||||||
|
body = {'description': description}
|
||||||
|
if network_id:
|
||||||
|
body.update({'id': network_id})
|
||||||
|
res = self.client.post(path, body=body)
|
||||||
|
ofc_network_id = res['id']
|
||||||
|
return ofc_network_id
|
||||||
|
|
||||||
|
def update_network(self, ofc_tenant_id, ofc_network_id, description):
|
||||||
|
path = NETWORK_PATH % (ofc_tenant_id, ofc_network_id)
|
||||||
|
body = {'description': description}
|
||||||
|
return self.client.put(path, body=body)
|
||||||
|
|
||||||
|
def delete_network(self, ofc_tenant_id, ofc_network_id):
|
||||||
|
path = NETWORK_PATH % (ofc_tenant_id, ofc_network_id)
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, portinfo,
|
||||||
|
port_id=None):
|
||||||
|
path = PORTS_PATH % (ofc_tenant_id, ofc_network_id)
|
||||||
|
body = {'datapath_id': portinfo.datapath_id,
|
||||||
|
'port': str(portinfo.port_no),
|
||||||
|
'vid': str(portinfo.vlan_id)}
|
||||||
|
if port_id:
|
||||||
|
body.update({'id': port_id})
|
||||||
|
res = self.client.post(path, body=body)
|
||||||
|
ofc_port_id = res['id']
|
||||||
|
return ofc_port_id
|
||||||
|
|
||||||
|
def update_port(self, ofc_tenant_id, ofc_network_id, portinfo, port_id):
|
||||||
|
path = PORT_PATH % (ofc_tenant_id, ofc_network_id, ofc_port_id)
|
||||||
|
body = {'datapath_id': portinfo.datapath_id,
|
||||||
|
'port': str(portinfo.port_no),
|
||||||
|
'vid': str(portinfo.vlan_id)}
|
||||||
|
res = self.client.put(path, body=body)
|
||||||
|
ofc_port_id = res['id']
|
||||||
|
return ofc_port_id
|
||||||
|
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
path = PORT_PATH % (ofc_tenant_id, ofc_network_id, ofc_port_id)
|
||||||
|
return self.client.delete(path)
|
248
quantum/plugins/nec/drivers/trema.py
Normal file
248
quantum/plugins/nec/drivers/trema.py
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from quantum.plugins.nec.common import ofc_client
|
||||||
|
from quantum.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"
|
||||||
|
|
||||||
|
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 create_tenant(self, description, tenant_id=None):
|
||||||
|
return tenant_id or str(uuid.uuid4())
|
||||||
|
|
||||||
|
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 str(uuid.uuid4())
|
||||||
|
body = {'id': ofc_network_id, 'description': description}
|
||||||
|
self.client.post(self.networks_path, body=body)
|
||||||
|
return ofc_network_id
|
||||||
|
|
||||||
|
def update_network(self, ofc_tenant_id, ofc_network_id, description):
|
||||||
|
path = self.network_path % ofc_network_id
|
||||||
|
body = {'description': description}
|
||||||
|
return self.client.put(path, body=body)
|
||||||
|
|
||||||
|
def delete_network(self, ofc_tenant_id, ofc_network_id):
|
||||||
|
path = self.network_path % ofc_network_id
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
def update_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id,
|
||||||
|
portinfo):
|
||||||
|
self.delete_port(ofc_tenant_id, ofc_network_id, ofc_port_id)
|
||||||
|
self.create_port(ofc_tenant_id, ofc_network_id, portinfo, ofc_port_id)
|
||||||
|
|
||||||
|
|
||||||
|
class TremaFilterDriver(object):
|
||||||
|
"""Trema (Sliceable Switch) PacketFilter Driver"""
|
||||||
|
filters_path = "/filters"
|
||||||
|
filter_path = "/filters/%s"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_supported(cls):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_filter(self, ofc_tenant_id, ofc_network_id, filter_dict,
|
||||||
|
portinfo=None, filter_id=None):
|
||||||
|
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': 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() in "ICMP":
|
||||||
|
body['dl_type'] = "0x800"
|
||||||
|
body['nw_proto'] = hex(1)
|
||||||
|
elif filter_dict['protocol'].upper() in "TCP":
|
||||||
|
body['dl_type'] = "0x800"
|
||||||
|
body['nw_proto'] = hex(6)
|
||||||
|
elif filter_dict['protocol'].upper() in "UDP":
|
||||||
|
body['dl_type'] = "0x800"
|
||||||
|
body['nw_proto'] = hex(17)
|
||||||
|
elif filter_dict['protocol'].upper() in "ARP":
|
||||||
|
body['dl_type'] = "0x806"
|
||||||
|
ofp_wildcards.append("nw_proto")
|
||||||
|
else:
|
||||||
|
body['nw_proto'] = filter_dict['protocol']
|
||||||
|
if filter_dict['eth_type']:
|
||||||
|
body['dl_type'] = filter_dict['eth_type']
|
||||||
|
else:
|
||||||
|
ofp_wildcards.append("dl_type")
|
||||||
|
else:
|
||||||
|
ofp_wildcards.append("dl_type")
|
||||||
|
ofp_wildcards.append("nw_proto")
|
||||||
|
|
||||||
|
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 str(uuid.uuid4())
|
||||||
|
body['id'] = ofc_filter_id
|
||||||
|
|
||||||
|
body['ofp_wildcards'] = ','.join(ofp_wildcards)
|
||||||
|
|
||||||
|
self.client.post(self.filters_path, body=body)
|
||||||
|
return ofc_filter_id
|
||||||
|
|
||||||
|
def delete_filter(self, ofc_tenant_id, ofc_network_id, ofc_filter_id):
|
||||||
|
path = self.filter_path % ofc_filter_id
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
|
||||||
|
class TremaPortBaseDriver(TremaDriverBase, TremaFilterDriver):
|
||||||
|
"""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 = "/networks/%s/ports"
|
||||||
|
port_path = "/networks/%s/ports/%s"
|
||||||
|
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, portinfo,
|
||||||
|
port_id=None):
|
||||||
|
ofc_port_id = port_id or str(uuid.uuid4())
|
||||||
|
path = self.ports_path % 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 ofc_port_id
|
||||||
|
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
path = self.port_path % (ofc_network_id, ofc_port_id)
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
|
||||||
|
class TremaPortMACBaseDriver(TremaDriverBase, TremaFilterDriver):
|
||||||
|
"""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 = "/networks/%s/ports"
|
||||||
|
port_path = "/networks/%s/ports/%s"
|
||||||
|
attachments_path = "/networks/%s/ports/%s/attachments"
|
||||||
|
attachment_path = "/networks/%s/ports/%s/attachments/%s"
|
||||||
|
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, portinfo,
|
||||||
|
port_id=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 str(uuid.uuid4())
|
||||||
|
dummy_port_id = "dummy-%s" % ofc_port_id
|
||||||
|
|
||||||
|
path = self.ports_path % 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 % (ofc_network_id, dummy_port_id)
|
||||||
|
body = {'id': ofc_port_id, 'mac': portinfo.mac}
|
||||||
|
self.client.post(path, body=body)
|
||||||
|
|
||||||
|
path = self.port_path % (ofc_network_id, dummy_port_id)
|
||||||
|
self.client.delete(path)
|
||||||
|
|
||||||
|
return ofc_port_id
|
||||||
|
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
dummy_port_id = "dummy-%s" % ofc_port_id
|
||||||
|
path = self.attachment_path % (ofc_network_id, dummy_port_id,
|
||||||
|
ofc_port_id)
|
||||||
|
return self.client.delete(path)
|
||||||
|
|
||||||
|
|
||||||
|
class TremaMACBaseDriver(TremaDriverBase):
|
||||||
|
"""Trema (Sliceable Switch) Driver for mac base binding
|
||||||
|
|
||||||
|
TremaPortBaseDriver uses mac base binding.
|
||||||
|
Ports are identified by mac.
|
||||||
|
"""
|
||||||
|
attachments_path = "/networks/%s/attachments"
|
||||||
|
attachment_path = "/networks/%s/attachments/%s"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_supported(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, portinfo,
|
||||||
|
port_id=None):
|
||||||
|
ofc_port_id = port_id or str(uuid.uuid4())
|
||||||
|
path = self.attachments_path % ofc_network_id
|
||||||
|
body = {'id': ofc_port_id, 'mac': portinfo.mac}
|
||||||
|
self.client.post(path, body=body)
|
||||||
|
return ofc_port_id
|
||||||
|
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
path = self.attachment_path % (ofc_network_id, ofc_port_id)
|
||||||
|
return self.client.delete(path)
|
15
quantum/plugins/nec/extensions/__init__.py
Normal file
15
quantum/plugins/nec/extensions/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
131
quantum/plugins/nec/extensions/packetfilter.py
Normal file
131
quantum/plugins/nec/extensions/packetfilter.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.api.v2 import attributes
|
||||||
|
from quantum.api.v2 import base
|
||||||
|
from quantum.extensions import extensions
|
||||||
|
from quantum.manager import QuantumManager
|
||||||
|
from quantum import quota
|
||||||
|
from quantum.openstack.common import cfg
|
||||||
|
|
||||||
|
|
||||||
|
quota_packet_filter_opts = [
|
||||||
|
cfg.IntOpt('quota_packet_filter',
|
||||||
|
default=100,
|
||||||
|
help="number of packet_filters allowed per tenant, "
|
||||||
|
"-1 for unlimited")
|
||||||
|
]
|
||||||
|
# Register the configuration options
|
||||||
|
cfg.CONF.register_opts(quota_packet_filter_opts, 'QUOTAS')
|
||||||
|
|
||||||
|
|
||||||
|
PACKET_FILTER_ACTION_REGEX = "(?i)^(allow|accept|drop|deny)$"
|
||||||
|
PACKET_FILTER_NUMBER_REGEX = "(?i)^(0x[0-9a-fA-F]+|[0-9]+)$"
|
||||||
|
PACKET_FILTER_PROTOCOL_REGEX = "(?i)^(icmp|tcp|udp|arp|0x[0-9a-fA-F]+|[0-9]+)$"
|
||||||
|
PACKET_FILTER_ATTR_MAP = {
|
||||||
|
'id': {'allow_post': False, 'allow_put': False,
|
||||||
|
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||||
|
'is_visible': True},
|
||||||
|
'name': {'allow_post': True, 'allow_put': True, 'default': '',
|
||||||
|
'is_visible': True},
|
||||||
|
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||||
|
'required_by_policy': True,
|
||||||
|
'is_visible': True},
|
||||||
|
'network_id': {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||||
|
'is_visible': True},
|
||||||
|
'admin_state_up': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': True,
|
||||||
|
'convert_to': attributes.convert_to_boolean,
|
||||||
|
'validate': {'type:boolean': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'status': {'allow_post': False, 'allow_put': False,
|
||||||
|
'is_visible': True},
|
||||||
|
'action': {'allow_post': True, 'allow_put': True,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_ACTION_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
'priority': {'allow_post': True, 'allow_put': True,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
'in_port': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||||
|
'is_visible': True},
|
||||||
|
'src_mac': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:mac_address': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'dst_mac': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:mac_address': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'eth_type': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
'src_cidr': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:subnet': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'dst_cidr': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:subnet': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'protocol': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_PROTOCOL_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
'src_port': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
'dst_port': {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
|
'validate': {'type:regex': PACKET_FILTER_NUMBER_REGEX},
|
||||||
|
'is_visible': True},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Packetfilter(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return "PacketFilters"
|
||||||
|
|
||||||
|
def get_alias(self):
|
||||||
|
return "PacketFilters"
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
return "PacketFilters"
|
||||||
|
|
||||||
|
def get_namespace(self):
|
||||||
|
return "http://www.nec.co.jp/api/ext/packet_filter/v2.0"
|
||||||
|
|
||||||
|
def get_updated(self):
|
||||||
|
return "2012-07-24T00:00:00+09:00"
|
||||||
|
|
||||||
|
def get_resources(self):
|
||||||
|
resource = base.create_resource('packet_filters', 'packet_filter',
|
||||||
|
QuantumManager.get_plugin(),
|
||||||
|
PACKET_FILTER_ATTR_MAP)
|
||||||
|
qresource = quota.CountableResource('packet_filter',
|
||||||
|
quota._count_resource,
|
||||||
|
'quota_packet_filter')
|
||||||
|
quota.QUOTAS.register_resource(qresource)
|
||||||
|
return [extensions.ResourceExtension('packet_filters', resource)]
|
505
quantum/plugins/nec/nec_plugin.py
Normal file
505
quantum/plugins/nec/nec_plugin.py
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from quantum import context
|
||||||
|
from quantum.common import topics
|
||||||
|
from quantum.openstack.common import rpc
|
||||||
|
from quantum.openstack.common.rpc import dispatcher
|
||||||
|
from quantum.plugins.nec import ofc_manager
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
from quantum.plugins.nec.common import exceptions as nexc
|
||||||
|
from quantum.plugins.nec.db import api as ndb
|
||||||
|
from quantum.plugins.nec.db import nec_plugin_base
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OperationalStatus:
|
||||||
|
"""Enumeration for operational status.
|
||||||
|
|
||||||
|
ACTIVE: The resource is available.
|
||||||
|
DOWN: The resource is not operational. This might indicate
|
||||||
|
admin_status_up=False, or lack of OpenFlow info for the port.
|
||||||
|
BUILD: The plugin is creating the resource.
|
||||||
|
ERROR: Some error occured.
|
||||||
|
"""
|
||||||
|
ACTIVE = "ACTIVE"
|
||||||
|
DOWN = "DOWN"
|
||||||
|
BUILD = "BUILD"
|
||||||
|
ERROR = "ERROR"
|
||||||
|
|
||||||
|
|
||||||
|
class NECPluginV2(nec_plugin_base.NECPluginV2Base):
|
||||||
|
"""NECPluginV2 controls an OpenFlow Controller.
|
||||||
|
|
||||||
|
The Quantum 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 Quantum API V2. Codes for V1.0 and V1.1 are available
|
||||||
|
at https://github.com/nec-openstack/quantum-openflow-plugin .
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
ndb.initialize()
|
||||||
|
self.ofc = ofc_manager.OFCManager()
|
||||||
|
|
||||||
|
self.packet_filter_enabled = (config.OFC.enable_packet_filter and
|
||||||
|
self.ofc.driver.filter_supported)
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
self.supported_extension_aliases = ["PacketFilters"]
|
||||||
|
|
||||||
|
self.setup_rpc()
|
||||||
|
|
||||||
|
def setup_rpc(self):
|
||||||
|
self.topic = topics.PLUGIN
|
||||||
|
self.conn = rpc.create_connection(new=True)
|
||||||
|
self.callbacks = NECPluginV2RPCCallbacks(self)
|
||||||
|
self.dispatcher = self.callbacks.create_rpc_dispatcher()
|
||||||
|
self.conn.create_consumer(self.topic, self.dispatcher, fanout=False)
|
||||||
|
# Consume from all consumers in a thread
|
||||||
|
self.conn.consume_in_thread()
|
||||||
|
|
||||||
|
def _update_resource_status(self, context, resource, id, status):
|
||||||
|
"""Update status of specified resource."""
|
||||||
|
request = {}
|
||||||
|
request[resource] = dict(status=status)
|
||||||
|
obj_updater = getattr(super(NECPluginV2, self), "update_%s" % resource)
|
||||||
|
obj_updater(context, id, request)
|
||||||
|
|
||||||
|
def activate_port_if_ready(self, context, port, network=None):
|
||||||
|
"""Activate port by creating port on OFC if ready.
|
||||||
|
|
||||||
|
Activate port and packet_filters associated with the port.
|
||||||
|
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'])
|
||||||
|
|
||||||
|
port_status = OperationalStatus.ACTIVE
|
||||||
|
if not port['admin_state_up']:
|
||||||
|
LOG.debug("activate_port_if_ready(): skip, "
|
||||||
|
"port.admin_state_up is False.")
|
||||||
|
port_status = OperationalStatus.DOWN
|
||||||
|
elif not network['admin_state_up']:
|
||||||
|
LOG.debug("activate_port_if_ready(): skip, "
|
||||||
|
"network.admin_state_up is False.")
|
||||||
|
port_status = OperationalStatus.DOWN
|
||||||
|
elif not ndb.get_portinfo(port['id']):
|
||||||
|
LOG.debug("activate_port_if_ready(): skip, "
|
||||||
|
"no portinfo for this port.")
|
||||||
|
port_status = OperationalStatus.DOWN
|
||||||
|
|
||||||
|
# activate packet_filters before creating port on OFC.
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
if port_status is OperationalStatus.ACTIVE:
|
||||||
|
filters = dict(in_port=[port['id']],
|
||||||
|
status=[OperationalStatus.DOWN],
|
||||||
|
admin_state_up=[True])
|
||||||
|
pfs = (super(NECPluginV2, self).
|
||||||
|
get_packet_filters(context, filters=filters))
|
||||||
|
for pf in pfs:
|
||||||
|
self._activate_packet_filter_if_ready(context, pf,
|
||||||
|
network=network,
|
||||||
|
in_port=port)
|
||||||
|
|
||||||
|
if port_status in [OperationalStatus.ACTIVE]:
|
||||||
|
if self.ofc.exists_ofc_port(port['id']):
|
||||||
|
LOG.debug("activate_port_if_ready(): skip, "
|
||||||
|
"ofc_port already exists.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.ofc.create_ofc_port(port['tenant_id'],
|
||||||
|
port['network_id'],
|
||||||
|
port['id'])
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = "create_ofc_port() failed due to %s" % exc
|
||||||
|
LOG.error(reason)
|
||||||
|
port_status = OperationalStatus.ERROR
|
||||||
|
|
||||||
|
if port_status is not port['status']:
|
||||||
|
self._update_resource_status(context, "port", port['id'],
|
||||||
|
port_status)
|
||||||
|
|
||||||
|
def deactivate_port(self, context, port):
|
||||||
|
"""Deactivate port by deleting port from OFC if exists.
|
||||||
|
|
||||||
|
Deactivate port and packet_filters associated with the port.
|
||||||
|
"""
|
||||||
|
port_status = OperationalStatus.DOWN
|
||||||
|
if self.ofc.exists_ofc_port(port['id']):
|
||||||
|
try:
|
||||||
|
self.ofc.delete_ofc_port(port['tenant_id'],
|
||||||
|
port['network_id'],
|
||||||
|
port['id'])
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = "delete_ofc_port() failed due to %s" % exc
|
||||||
|
LOG.error(reason)
|
||||||
|
port_status = OperationalStatus.ERROR
|
||||||
|
else:
|
||||||
|
LOG.debug("deactivate_port(): skip, ofc_port does not "
|
||||||
|
"exist.")
|
||||||
|
|
||||||
|
if port_status is not port['status']:
|
||||||
|
self._update_resource_status(context, "port", port['id'],
|
||||||
|
port_status)
|
||||||
|
|
||||||
|
# deactivate packet_filters after the port has deleted from OFC.
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
filters = dict(in_port=[port['id']],
|
||||||
|
status=[OperationalStatus.ACTIVE])
|
||||||
|
pfs = super(NECPluginV2, self).get_packet_filters(context,
|
||||||
|
filters=filters)
|
||||||
|
for pf in pfs:
|
||||||
|
self._deactivate_packet_filter(context, pf)
|
||||||
|
|
||||||
|
# Quantm Plugin Basic methods
|
||||||
|
|
||||||
|
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)
|
||||||
|
new_net = super(NECPluginV2, self).create_network(context, network)
|
||||||
|
self._update_resource_status(context, "network", new_net['id'],
|
||||||
|
OperationalStatus.BUILD)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not self.ofc.exists_ofc_tenant(new_net['tenant_id']):
|
||||||
|
self.ofc.create_ofc_tenant(new_net['tenant_id'])
|
||||||
|
self.ofc.create_ofc_network(new_net['tenant_id'], new_net['id'],
|
||||||
|
new_net['name'])
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = "create_network() failed due to %s" % exc
|
||||||
|
LOG.error(reason)
|
||||||
|
self._update_resource_status(context, "network", new_net['id'],
|
||||||
|
OperationalStatus.ERROR)
|
||||||
|
else:
|
||||||
|
self._update_resource_status(context, "network", new_net['id'],
|
||||||
|
OperationalStatus.ACTIVE)
|
||||||
|
|
||||||
|
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=%s network=%s ." % (id, network))
|
||||||
|
old_net = super(NECPluginV2, self).get_network(context, id)
|
||||||
|
new_net = super(NECPluginV2, self).update_network(context, id, network)
|
||||||
|
|
||||||
|
changed = (old_net['admin_state_up'] is not new_net['admin_state_up'])
|
||||||
|
if changed and not new_net['admin_state_up']:
|
||||||
|
self._update_resource_status(context, "network", id,
|
||||||
|
OperationalStatus.DOWN)
|
||||||
|
# disable all active ports and packet_filters of the network
|
||||||
|
filters = dict(network_id=[id], status=[OperationalStatus.ACTIVE])
|
||||||
|
ports = super(NECPluginV2, self).get_ports(context,
|
||||||
|
filters=filters)
|
||||||
|
for port in ports:
|
||||||
|
self.deactivate_port(context, port)
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
pfs = (super(NECPluginV2, self).
|
||||||
|
get_packet_filters(context, filters=filters))
|
||||||
|
for pf in pfs:
|
||||||
|
self._deactivate_packet_filter(context, pf)
|
||||||
|
elif changed and new_net['admin_state_up']:
|
||||||
|
self._update_resource_status(context, "network", id,
|
||||||
|
OperationalStatus.ACTIVE)
|
||||||
|
# enable ports and packet_filters of the network
|
||||||
|
filters = dict(network_id=[id], status=[OperationalStatus.DOWN],
|
||||||
|
admin_state_up=[True])
|
||||||
|
ports = super(NECPluginV2, self).get_ports(context,
|
||||||
|
filters=filters)
|
||||||
|
for port in ports:
|
||||||
|
self.activate_port_if_ready(context, port, new_net)
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
pfs = (super(NECPluginV2, self).
|
||||||
|
get_packet_filters(context, filters=filters))
|
||||||
|
for pf in pfs:
|
||||||
|
self._activate_packet_filter_if_ready(context, pf, new_net)
|
||||||
|
|
||||||
|
return new_net
|
||||||
|
|
||||||
|
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 = super(NECPluginV2, self).get_network(context, id)
|
||||||
|
tenant_id = net['tenant_id']
|
||||||
|
|
||||||
|
# get packet_filters associated with the network
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
filters = dict(network_id=[id])
|
||||||
|
pfs = (super(NECPluginV2, self).
|
||||||
|
get_packet_filters(context, filters=filters))
|
||||||
|
|
||||||
|
super(NECPluginV2, self).delete_network(context, id)
|
||||||
|
try:
|
||||||
|
self.ofc.delete_ofc_network(tenant_id, id)
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = "delete_network() failed due to %s" % exc
|
||||||
|
# NOTE: The OFC configuration of this network could be remained
|
||||||
|
# as an orphan resource. But, it does NOT harm any other
|
||||||
|
# resources, so this plugin just warns.
|
||||||
|
LOG.warn(reason)
|
||||||
|
|
||||||
|
# delete all packet_filters of the network
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
for pf in pfs:
|
||||||
|
self.delete_packet_filter(context, pf['id'])
|
||||||
|
|
||||||
|
# delete unnessary ofc_tenant
|
||||||
|
filters = dict(tenant_id=[tenant_id])
|
||||||
|
nets = super(NECPluginV2, self).get_networks(context, filters=filters)
|
||||||
|
if len(nets) == 0:
|
||||||
|
try:
|
||||||
|
self.ofc.delete_ofc_tenant(tenant_id)
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = "delete_ofc_tenant() failed due to %s" % exc
|
||||||
|
LOG.warn(reason)
|
||||||
|
|
||||||
|
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)
|
||||||
|
new_port = super(NECPluginV2, self).create_port(context, port)
|
||||||
|
self._update_resource_status(context, "port", new_port['id'],
|
||||||
|
OperationalStatus.BUILD)
|
||||||
|
|
||||||
|
self.activate_port_if_ready(context, new_port)
|
||||||
|
|
||||||
|
return 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=%s port=%s ." % (id, port))
|
||||||
|
old_port = super(NECPluginV2, self).get_port(context, id)
|
||||||
|
new_port = super(NECPluginV2, self).update_port(context, id, port)
|
||||||
|
|
||||||
|
changed = (old_port['admin_state_up'] is
|
||||||
|
not new_port['admin_state_up'])
|
||||||
|
if changed:
|
||||||
|
if new_port['admin_state_up']:
|
||||||
|
self.activate_port_if_ready(context, new_port)
|
||||||
|
else:
|
||||||
|
self.deactivate_port(context, old_port)
|
||||||
|
|
||||||
|
return new_port
|
||||||
|
|
||||||
|
def delete_port(self, context, id):
|
||||||
|
"""Delete port and packet_filters associated with the port."""
|
||||||
|
LOG.debug("NECPluginV2.delete_port() called, id=%s ." % id)
|
||||||
|
port = super(NECPluginV2, self).get_port(context, id)
|
||||||
|
|
||||||
|
self.deactivate_port(context, port)
|
||||||
|
|
||||||
|
# delete all packet_filters of the port
|
||||||
|
if self.packet_filter_enabled:
|
||||||
|
filters = dict(port_id=[id])
|
||||||
|
pfs = (super(NECPluginV2, self).
|
||||||
|
get_packet_filters(context, filters=filters))
|
||||||
|
for packet_filter in pfs:
|
||||||
|
self.delete_packet_filter(context, packet_filter['id'])
|
||||||
|
|
||||||
|
super(NECPluginV2, self).delete_port(context, id)
|
||||||
|
|
||||||
|
# For PacketFilter Extension
|
||||||
|
|
||||||
|
def _activate_packet_filter_if_ready(self, context, packet_filter,
|
||||||
|
network=None, in_port=None):
|
||||||
|
"""Activate packet_filter by creating filter on OFC if ready.
|
||||||
|
|
||||||
|
Conditions to create packet_filter on OFC are:
|
||||||
|
* packet_filter admin_state is UP
|
||||||
|
* network admin_state is UP
|
||||||
|
* (if 'in_port' is specified) portinfo is available
|
||||||
|
"""
|
||||||
|
net_id = packet_filter['network_id']
|
||||||
|
if not network:
|
||||||
|
network = super(NECPluginV2, self).get_network(context, net_id)
|
||||||
|
in_port_id = packet_filter.get("in_port")
|
||||||
|
if in_port_id and not in_port:
|
||||||
|
in_port = super(NECPluginV2, self).get_port(context, in_port_id)
|
||||||
|
|
||||||
|
pf_status = OperationalStatus.ACTIVE
|
||||||
|
if not packet_filter['admin_state_up']:
|
||||||
|
LOG.debug("_activate_packet_filter_if_ready(): skip, "
|
||||||
|
"packet_filter.admin_state_up is False.")
|
||||||
|
pf_status = OperationalStatus.DOWN
|
||||||
|
elif not network['admin_state_up']:
|
||||||
|
LOG.debug("_activate_packet_filter_if_ready(): skip, "
|
||||||
|
"network.admin_state_up is False.")
|
||||||
|
pf_status = OperationalStatus.DOWN
|
||||||
|
elif in_port_id and in_port_id is in_port.get('id'):
|
||||||
|
LOG.debug("_activate_packet_filter_if_ready(): skip, "
|
||||||
|
"invalid in_port_id.")
|
||||||
|
pf_status = OperationalStatus.DOWN
|
||||||
|
elif in_port_id and not ndb.get_portinfo(in_port_id):
|
||||||
|
LOG.debug("_activate_packet_filter_if_ready(): skip, "
|
||||||
|
"no portinfo for in_port.")
|
||||||
|
pf_status = OperationalStatus.DOWN
|
||||||
|
|
||||||
|
if pf_status in [OperationalStatus.ACTIVE]:
|
||||||
|
if self.ofc.exists_ofc_packet_filter(packet_filter['id']):
|
||||||
|
LOG.debug("_activate_packet_filter_if_ready(): skip, "
|
||||||
|
"ofc_packet_filter already exists.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
(self.ofc.
|
||||||
|
create_ofc_packet_filter(packet_filter['tenant_id'],
|
||||||
|
packet_filter['network_id'],
|
||||||
|
packet_filter['id'],
|
||||||
|
packet_filter))
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = ("create_ofc_packet_filter() failed due to "
|
||||||
|
"%s" % exc)
|
||||||
|
LOG.error(reason)
|
||||||
|
pf_status = OperationalStatus.ERROR
|
||||||
|
|
||||||
|
if pf_status is not packet_filter['status']:
|
||||||
|
self._update_resource_status(context, "packet_filter",
|
||||||
|
packet_filter['id'], pf_status)
|
||||||
|
|
||||||
|
def _deactivate_packet_filter(self, context, packet_filter):
|
||||||
|
"""Deactivate packet_filter by deleting filter from OFC if exixts."""
|
||||||
|
pf_status = OperationalStatus.DOWN
|
||||||
|
if not self.ofc.exists_ofc_packet_filter(packet_filter['id']):
|
||||||
|
LOG.debug("_deactivate_packet_filter(): skip, "
|
||||||
|
"ofc_packet_filter does not exist.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.ofc.delete_ofc_packet_filter(packet_filter['tenant_id'],
|
||||||
|
packet_filter['network_id'],
|
||||||
|
packet_filter['id'])
|
||||||
|
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||||
|
reason = ("delete_ofc_packet_filter() failed due to "
|
||||||
|
"%s" % exc)
|
||||||
|
LOG.error(reason)
|
||||||
|
pf_status = OperationalStatus.ERROR
|
||||||
|
|
||||||
|
if pf_status is not packet_filter['status']:
|
||||||
|
self._update_resource_status(context, "packet_filter",
|
||||||
|
packet_filter['id'], pf_status)
|
||||||
|
|
||||||
|
def create_packet_filter(self, context, packet_filter):
|
||||||
|
"""Create a new packet_filter entry on DB, then try to activate it."""
|
||||||
|
LOG.debug("NECPluginV2.create_packet_filter() called, "
|
||||||
|
"packet_filter=%s ." % packet_filter)
|
||||||
|
new_pf = super(NECPluginV2, self).create_packet_filter(context,
|
||||||
|
packet_filter)
|
||||||
|
self._update_resource_status(context, "packet_filter", new_pf['id'],
|
||||||
|
OperationalStatus.BUILD)
|
||||||
|
|
||||||
|
self._activate_packet_filter_if_ready(context, new_pf)
|
||||||
|
|
||||||
|
return new_pf
|
||||||
|
|
||||||
|
def update_packet_filter(self, context, id, packet_filter):
|
||||||
|
"""Update packet_filter entry on DB, and recreate it if changed.
|
||||||
|
|
||||||
|
If any rule of the packet_filter was changed, recreate it on OFC.
|
||||||
|
"""
|
||||||
|
LOG.debug("NECPluginV2.update_packet_filter() called, "
|
||||||
|
"id=%s packet_filter=%s ." % (id, packet_filter))
|
||||||
|
old_pf = super(NECPluginV2, self).get_packet_filter(context, id)
|
||||||
|
new_pf = super(NECPluginV2, self).update_packet_filter(context, id,
|
||||||
|
packet_filter)
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
exclude_items = ["id", "name", "tenant_id", "network_id", "status"]
|
||||||
|
for key in new_pf['packet_filter'].keys():
|
||||||
|
if key not in exclude_items:
|
||||||
|
if old_pf[key] is not new_pf[key]:
|
||||||
|
changed = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
self._deactivate_packet_filter(context, old_pf)
|
||||||
|
self._activate_packet_filter_if_ready(context, new_pf)
|
||||||
|
|
||||||
|
return new_pf
|
||||||
|
|
||||||
|
def delete_packet_filter(self, context, id):
|
||||||
|
"""Deactivate and delete packet_filter."""
|
||||||
|
LOG.debug("NECPluginV2.delete_packet_filter() called, id=%s ." % id)
|
||||||
|
pf = super(NECPluginV2, self).get_packet_filter(context, id)
|
||||||
|
self._deactivate_packet_filter(context, pf)
|
||||||
|
|
||||||
|
super(NECPluginV2, self).delete_packet_filter(context, id)
|
||||||
|
|
||||||
|
|
||||||
|
class NECPluginV2RPCCallbacks():
|
||||||
|
|
||||||
|
RPC_API_VERSION = '1.0'
|
||||||
|
|
||||||
|
def __init__(self, plugin):
|
||||||
|
self.plugin = plugin
|
||||||
|
self.admin_context = context.get_admin_context()
|
||||||
|
|
||||||
|
def create_rpc_dispatcher(self):
|
||||||
|
'''Get the rpc dispatcher for this manager.
|
||||||
|
|
||||||
|
If a manager would like to set an rpc API version, or support more than
|
||||||
|
one class as the target of rpc messages, override this method.
|
||||||
|
'''
|
||||||
|
return dispatcher.RpcDispatcher([self])
|
||||||
|
|
||||||
|
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)
|
||||||
|
topic = kwargs['topic']
|
||||||
|
datapath_id = kwargs['datapath_id']
|
||||||
|
for p in kwargs.get('port_added', []):
|
||||||
|
id = p['id']
|
||||||
|
port = self.plugin.get_port(self.admin_context, id)
|
||||||
|
if port and ndb.get_portinfo(id):
|
||||||
|
ndb.del_portinfo(id)
|
||||||
|
self.plugin.deactivate_port(self.admin_context, port)
|
||||||
|
ndb.add_portinfo(id, datapath_id, p['port_no'],
|
||||||
|
mac=p.get('mac', ''))
|
||||||
|
self.plugin.activate_port_if_ready(self.admin_context, port)
|
||||||
|
for id in kwargs.get('port_removed', []):
|
||||||
|
port = self.plugin.get_port(self.admin_context, id)
|
||||||
|
if port and ndb.get_portinfo(id):
|
||||||
|
ndb.del_portinfo(id)
|
||||||
|
self.plugin.deactivate_port(self.admin_context, port)
|
103
quantum/plugins/nec/ofc_driver_base.py
Normal file
103
quantum/plugins/nec/ofc_driver_base.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
|
@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: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete_tenant(self, ofc_tenant_id):
|
||||||
|
"""Delete a tenant at OpenFlow Controller.
|
||||||
|
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@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 a OFC network ID.
|
||||||
|
:returns: ID of the network created at OpenFlow Controller.
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_network(self, ofc_tenant_id, ofc_network_id, description):
|
||||||
|
"""Update description of specified network.
|
||||||
|
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete_network(self, ofc_tenant_id, ofc_network_id):
|
||||||
|
"""Delete a netwrok at OpenFlow Controller.
|
||||||
|
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, portinfo,
|
||||||
|
port_id=None):
|
||||||
|
"""Create a new port on specified tenant and 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 a OFC port ID.
|
||||||
|
|
||||||
|
:returns: ID of the port created at OpenFlow Controller.
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
"""Delete a port at OpenFlow Controller.
|
||||||
|
|
||||||
|
:raises: quantum.plugin.nec.common.exceptions.OFCException
|
||||||
|
"""
|
||||||
|
pass
|
150
quantum/plugins/nec/ofc_manager.py
Normal file
150
quantum/plugins/nec/ofc_manager.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.plugins.nec import drivers
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
from quantum.plugins.nec.common import exceptions as nexc
|
||||||
|
from quantum.plugins.nec.db import api as ndb
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
|
||||||
|
|
||||||
|
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 Quantum
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
resource_map = {'ofc_tenant': nmodels.OFCTenant,
|
||||||
|
'ofc_network': nmodels.OFCNetwork,
|
||||||
|
'ofc_port': nmodels.OFCPort,
|
||||||
|
'ofc_packet_filter': nmodels.OFCFilter}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.driver = drivers.get_driver(config.OFC.driver)(config.OFC)
|
||||||
|
|
||||||
|
def _get_ofc_id(self, resource, quantum_id):
|
||||||
|
model = self.resource_map[resource]
|
||||||
|
ofc_item = ndb.find_ofc_item(model, quantum_id)
|
||||||
|
if not ofc_item:
|
||||||
|
reason = "NotFound %s for quantum_id=%s." % (resource, quantum_id)
|
||||||
|
raise nexc.OFCConsistencyBroken(reason=reason)
|
||||||
|
return ofc_item.id
|
||||||
|
|
||||||
|
def _exists_ofc_item(self, resource, quantum_id):
|
||||||
|
model = self.resource_map[resource]
|
||||||
|
if ndb.find_ofc_item(model, quantum_id):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Tenant
|
||||||
|
|
||||||
|
def create_ofc_tenant(self, tenant_id):
|
||||||
|
desc = "ID=%s at OpenStack." % tenant_id
|
||||||
|
ofc_tenant_id = self.driver.create_tenant(desc, tenant_id)
|
||||||
|
ndb.add_ofc_item(nmodels.OFCTenant, ofc_tenant_id, tenant_id)
|
||||||
|
|
||||||
|
def exists_ofc_tenant(self, tenant_id):
|
||||||
|
return self._exists_ofc_item("ofc_tenant", tenant_id)
|
||||||
|
|
||||||
|
def delete_ofc_tenant(self, tenant_id):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
|
||||||
|
self.driver.delete_tenant(ofc_tenant_id)
|
||||||
|
ndb.del_ofc_item(nmodels.OFCTenant, ofc_tenant_id)
|
||||||
|
|
||||||
|
# Network
|
||||||
|
|
||||||
|
def create_ofc_network(self, tenant_id, network_id, network_name=None):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
|
||||||
|
desc = "ID=%s Name=%s at Quantum." % (network_id, network_name)
|
||||||
|
ofc_net_id = self.driver.create_network(ofc_tenant_id, desc,
|
||||||
|
network_id)
|
||||||
|
ndb.add_ofc_item(nmodels.OFCNetwork, ofc_net_id, network_id)
|
||||||
|
|
||||||
|
def update_ofc_network(self, tenant_id, network_id, network_name):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
|
||||||
|
desc = "ID=%s Name=%s at Quantum." % (network_id, network_name)
|
||||||
|
self.driver.update_network(ofc_tenant_id, ofc_net_id, desc)
|
||||||
|
|
||||||
|
def exists_ofc_network(self, network_id):
|
||||||
|
return self._exists_ofc_item("ofc_network", network_id)
|
||||||
|
|
||||||
|
def delete_ofc_network(self, tenant_id, network_id):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
|
||||||
|
self.driver.delete_network(ofc_tenant_id, ofc_net_id)
|
||||||
|
ndb.del_ofc_item(nmodels.OFCNetwork, ofc_net_id)
|
||||||
|
|
||||||
|
# Port
|
||||||
|
|
||||||
|
def create_ofc_port(self, tenant_id, network_id, port_id):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
portinfo = ndb.get_portinfo(port_id)
|
||||||
|
if not portinfo:
|
||||||
|
raise nexc.PortInfoNotFound(id=port_id)
|
||||||
|
|
||||||
|
ofc_port_id = self.driver.create_port(ofc_tenant_id, ofc_net_id,
|
||||||
|
portinfo, port_id)
|
||||||
|
ndb.add_ofc_item(nmodels.OFCPort, ofc_port_id, port_id)
|
||||||
|
|
||||||
|
def exists_ofc_port(self, port_id):
|
||||||
|
return self._exists_ofc_item("ofc_port", port_id)
|
||||||
|
|
||||||
|
def delete_ofc_port(self, tenant_id, network_id, port_id):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
ofc_port_id = self._get_ofc_id("ofc_port", port_id)
|
||||||
|
|
||||||
|
self.driver.delete_port(ofc_tenant_id, ofc_net_id, ofc_port_id)
|
||||||
|
ndb.del_ofc_item(nmodels.OFCPort, ofc_port_id)
|
||||||
|
|
||||||
|
# PacketFilter
|
||||||
|
|
||||||
|
def create_ofc_packet_filter(self, tenant_id, network_id, filter_id,
|
||||||
|
filter_dict):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
in_port_id = filter_dict.get('in_port')
|
||||||
|
portinfo = None
|
||||||
|
if in_port_id:
|
||||||
|
portinfo = ndb.get_portinfo(in_port_id)
|
||||||
|
if not portinfo:
|
||||||
|
raise nexc.PortInfoNotFound(id=in_port_id)
|
||||||
|
|
||||||
|
ofc_pf_id = self.driver.create_filter(ofc_tenant_id, ofc_net_id,
|
||||||
|
filter_dict, portinfo, filter_id)
|
||||||
|
ndb.add_ofc_item(nmodels.OFCFilter, ofc_pf_id, filter_id)
|
||||||
|
|
||||||
|
def exists_ofc_packet_filter(self, filter_id):
|
||||||
|
return self._exists_ofc_item("ofc_packet_filter", filter_id)
|
||||||
|
|
||||||
|
def delete_ofc_packet_filter(self, tenant_id, network_id, filter_id):
|
||||||
|
ofc_tenant_id = self._get_ofc_id("ofc_tenant", tenant_id)
|
||||||
|
ofc_net_id = self._get_ofc_id("ofc_network", network_id)
|
||||||
|
ofc_pf_id = self._get_ofc_id("ofc_packet_filter", filter_id)
|
||||||
|
|
||||||
|
res = self.driver.delete_filter(ofc_tenant_id, ofc_net_id, ofc_pf_id)
|
||||||
|
ndb.del_ofc_item(nmodels.OFCFilter, ofc_pf_id)
|
72
quantum/plugins/nec/run_tests.py
Executable file
72
quantum/plugins/nec/run_tests.py
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010 OpenStack, LLC
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
"""Unittest runner for quantum NEC OpenFlow plugin
|
||||||
|
|
||||||
|
This file should be run from the top dir in the quantum directory
|
||||||
|
|
||||||
|
To run all tests::
|
||||||
|
PLUGIN_DIR=quantum/plugins/nec ./run_tests.sh
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from nose import config
|
||||||
|
from nose import core
|
||||||
|
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
sys.path.append(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
from quantum.common.test_lib import run_tests, test_config
|
||||||
|
import quantum.tests.unit
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
exit_status = False
|
||||||
|
|
||||||
|
# if a single test case was specified,
|
||||||
|
# we should only invoked the tests once
|
||||||
|
invoke_once = len(sys.argv) > 1
|
||||||
|
|
||||||
|
test_config['plugin_name'] = "nec_plugin.NECPluginV2"
|
||||||
|
|
||||||
|
cwd = os.getcwd()
|
||||||
|
c = config.Config(stream=sys.stdout,
|
||||||
|
env=os.environ,
|
||||||
|
verbosity=3,
|
||||||
|
includeExe=True,
|
||||||
|
traverseNamespace=True,
|
||||||
|
plugins=core.DefaultPluginManager())
|
||||||
|
c.configureWhere(quantum.tests.unit.__path__)
|
||||||
|
exit_status = run_tests(c)
|
||||||
|
|
||||||
|
if invoke_once:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
working_dir = os.path.abspath("quantum/plugins/nec")
|
||||||
|
c = config.Config(stream=sys.stdout,
|
||||||
|
env=os.environ,
|
||||||
|
verbosity=3,
|
||||||
|
workingDir=working_dir)
|
||||||
|
exit_status = exit_status or run_tests(c)
|
||||||
|
|
||||||
|
sys.exit(exit_status)
|
15
quantum/plugins/nec/tests/__init__.py
Normal file
15
quantum/plugins/nec/tests/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
15
quantum/plugins/nec/tests/unit/__init__.py
Normal file
15
quantum/plugins/nec/tests/unit/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
56
quantum/plugins/nec/tests/unit/stub_ofc_driver.py
Normal file
56
quantum/plugins/nec/tests/unit/stub_ofc_driver.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
from quantum.plugins.nec import ofc_driver_base
|
||||||
|
|
||||||
|
|
||||||
|
class StubOFCDriver(ofc_driver_base.OFCDriverBase):
|
||||||
|
|
||||||
|
def __init__(self, conf):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_tenant(self, description, tenant_id=None):
|
||||||
|
return "ofc-" + tenant_id[:-4]
|
||||||
|
|
||||||
|
def delete_tenant(self, ofc_tenant_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_network(self, ofc_tenant_id, description, network_id=None):
|
||||||
|
return "ofc-" + network_id[:-4]
|
||||||
|
|
||||||
|
def update_network(self, ofc_tenant_id, ofc_network_id, description):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete_network(self, ofc_tenant_id, ofc_network_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_port(self, ofc_tenant_id, ofc_network_id, info, port_id=None):
|
||||||
|
return "ofc-" + port_id[:-4]
|
||||||
|
|
||||||
|
def delete_port(self, ofc_tenant_id, ofc_network_id, ofc_port_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_supported(cls):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_filter(self, ofc_tenant_id, ofc_network_id, filter_dict,
|
||||||
|
portinfo=None, filter_id=None):
|
||||||
|
return "ofc-" + filter_id[:-4]
|
||||||
|
|
||||||
|
def delete_filter(self, ofc_tenant_id, ofc_network_id, ofc_filter_id):
|
||||||
|
pass
|
39
quantum/plugins/nec/tests/unit/test_config.py
Normal file
39
quantum/plugins/nec/tests/unit/test_config.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurationTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_defaults(self):
|
||||||
|
self.assertEqual('sqlite://', config.DATABASE.sql_connection)
|
||||||
|
self.assertEqual('sqlite://', config.CONF.DATABASE.sql_connection)
|
||||||
|
self.assertEqual(-1, config.CONF.DATABASE.sql_max_retries)
|
||||||
|
self.assertEqual(2, config.CONF.DATABASE.reconnect_interval)
|
||||||
|
self.assertEqual('br-int', config.CONF.OVS.integration_bridge)
|
||||||
|
self.assertEqual(2, config.CONF.AGENT.polling_interval)
|
||||||
|
self.assertEqual('sudo', config.CONF.AGENT.root_helper)
|
||||||
|
self.assertEqual('127.0.0.1', config.CONF.OFC.host)
|
||||||
|
self.assertEqual('8888', config.CONF.OFC.port)
|
||||||
|
self.assertEqual('trema', config.CONF.OFC.driver)
|
||||||
|
self.assertTrue(config.CONF.OFC.enable_packet_filter)
|
||||||
|
self.assertFalse(config.CONF.OFC.use_ssl)
|
||||||
|
self.assertEqual(None, config.CONF.OFC.key_file)
|
||||||
|
self.assertEqual(None, config.CONF.OFC.cert_file)
|
141
quantum/plugins/nec/tests/unit/test_db.py
Normal file
141
quantum/plugins/nec/tests/unit/test_db.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import random
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from quantum.common import utils
|
||||||
|
from quantum.plugins.nec.common import exceptions as nexc
|
||||||
|
from quantum.plugins.nec.db import api as ndb
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
|
||||||
|
|
||||||
|
class NECPluginV2DBTest(unittest.TestCase):
|
||||||
|
"""Class conisting of NECPluginV2 DB unit tests"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Setup for tests"""
|
||||||
|
ndb.initialize()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Tear Down"""
|
||||||
|
ndb.clear_db()
|
||||||
|
|
||||||
|
def get_ofc_item_random_params(self):
|
||||||
|
"""create random parameters for ofc_item test"""
|
||||||
|
ofc_id = utils.str_uuid()
|
||||||
|
quantum_id = utils.str_uuid()
|
||||||
|
none = utils.str_uuid()
|
||||||
|
return ofc_id, quantum_id, none
|
||||||
|
|
||||||
|
def testa_add_ofc_item(self):
|
||||||
|
"""test add OFC item"""
|
||||||
|
o, q, n = self.get_ofc_item_random_params()
|
||||||
|
tenant = ndb.add_ofc_item(nmodels.OFCTenant, o, q)
|
||||||
|
self.assertEqual(tenant.id, o)
|
||||||
|
self.assertEqual(tenant.quantum_id, q)
|
||||||
|
|
||||||
|
exception_raised = False
|
||||||
|
try:
|
||||||
|
ndb.add_ofc_item(nmodels.OFCTenant, o, q)
|
||||||
|
except nexc.NECDBException:
|
||||||
|
exception_raised = True
|
||||||
|
self.assertTrue(exception_raised)
|
||||||
|
|
||||||
|
def testb_get_ofc_item(self):
|
||||||
|
"""test get OFC item"""
|
||||||
|
o, q, n = self.get_ofc_item_random_params()
|
||||||
|
ndb.add_ofc_item(nmodels.OFCTenant, o, q)
|
||||||
|
tenant = ndb.get_ofc_item(nmodels.OFCTenant, o)
|
||||||
|
self.assertEqual(tenant.id, o)
|
||||||
|
self.assertEqual(tenant.quantum_id, q)
|
||||||
|
|
||||||
|
tenant_none = ndb.get_ofc_item(nmodels.OFCTenant, n)
|
||||||
|
self.assertEqual(None, tenant_none)
|
||||||
|
|
||||||
|
def testc_find_ofc_item(self):
|
||||||
|
"""test find OFC item"""
|
||||||
|
o, q, n = self.get_ofc_item_random_params()
|
||||||
|
ndb.add_ofc_item(nmodels.OFCTenant, o, q)
|
||||||
|
tenant = ndb.find_ofc_item(nmodels.OFCTenant, q)
|
||||||
|
self.assertEqual(tenant.id, o)
|
||||||
|
self.assertEqual(tenant.quantum_id, q)
|
||||||
|
|
||||||
|
tenant_none = ndb.find_ofc_item(nmodels.OFCTenant, n)
|
||||||
|
self.assertEqual(None, tenant_none)
|
||||||
|
|
||||||
|
def testc_del_ofc_item(self):
|
||||||
|
"""test delete OFC item"""
|
||||||
|
o, q, n = self.get_ofc_item_random_params()
|
||||||
|
ndb.add_ofc_item(nmodels.OFCTenant, o, q)
|
||||||
|
ndb.del_ofc_item(nmodels.OFCTenant, o)
|
||||||
|
|
||||||
|
tenant_none = ndb.get_ofc_item(nmodels.OFCTenant, q)
|
||||||
|
self.assertEqual(None, tenant_none)
|
||||||
|
tenant_none = ndb.find_ofc_item(nmodels.OFCTenant, q)
|
||||||
|
self.assertEqual(None, tenant_none)
|
||||||
|
|
||||||
|
def get_portinfo_random_params(self):
|
||||||
|
"""create random parameters for portinfo test"""
|
||||||
|
port_id = utils.str_uuid()
|
||||||
|
datapath_id = hex(random.randint(0, 0xffffffff))
|
||||||
|
port_no = random.randint(1, 100)
|
||||||
|
vlan_id = random.randint(0, 4095)
|
||||||
|
mac = ':'.join(["%02x" % random.randint(0, 0xff) for x in range(6)])
|
||||||
|
none = utils.str_uuid()
|
||||||
|
return port_id, datapath_id, port_no, vlan_id, mac, none
|
||||||
|
|
||||||
|
def testd_add_portinfo(self):
|
||||||
|
"""test add portinfo"""
|
||||||
|
i, d, p, v, m, n = self.get_portinfo_random_params()
|
||||||
|
portinfo = ndb.add_portinfo(i, d, p, v, m)
|
||||||
|
self.assertEqual(portinfo.id, i)
|
||||||
|
self.assertEqual(portinfo.datapath_id, d)
|
||||||
|
self.assertEqual(portinfo.port_no, p)
|
||||||
|
self.assertEqual(portinfo.vlan_id, v)
|
||||||
|
self.assertEqual(portinfo.mac, m)
|
||||||
|
|
||||||
|
exception_raised = False
|
||||||
|
try:
|
||||||
|
ndb.add_portinfo(i, d, p, v, m)
|
||||||
|
except nexc.NECDBException:
|
||||||
|
exception_raised = True
|
||||||
|
self.assertTrue(exception_raised)
|
||||||
|
|
||||||
|
def teste_get_portinfo(self):
|
||||||
|
"""test get portinfo"""
|
||||||
|
i, d, p, v, m, n = self.get_portinfo_random_params()
|
||||||
|
ndb.add_portinfo(i, d, p, v, m)
|
||||||
|
portinfo = ndb.get_portinfo(i)
|
||||||
|
self.assertEqual(portinfo.id, i)
|
||||||
|
self.assertEqual(portinfo.datapath_id, d)
|
||||||
|
self.assertEqual(portinfo.port_no, p)
|
||||||
|
self.assertEqual(portinfo.vlan_id, v)
|
||||||
|
self.assertEqual(portinfo.mac, m)
|
||||||
|
|
||||||
|
portinfo_none = ndb.get_portinfo(n)
|
||||||
|
self.assertEqual(None, portinfo_none)
|
||||||
|
|
||||||
|
def testf_del_portinfo(self):
|
||||||
|
"""test delete portinfo"""
|
||||||
|
i, d, p, v, m, n = self.get_portinfo_random_params()
|
||||||
|
ndb.add_portinfo(i, d, p, v, m)
|
||||||
|
portinfo = ndb.get_portinfo(i)
|
||||||
|
self.assertEqual(portinfo.id, i)
|
||||||
|
ndb.del_portinfo(i)
|
||||||
|
portinfo_none = ndb.get_portinfo(i)
|
||||||
|
self.assertEqual(None, portinfo_none)
|
62
quantum/plugins/nec/tests/unit/test_nec_plugin_v2.py
Normal file
62
quantum/plugins/nec/tests/unit/test_nec_plugin_v2.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import quantum.openstack.common.rpc
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
from quantum.tests.unit import test_db_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class NECPluginTestBase(object):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Make sure at each test a new instance of the plugin is returned
|
||||||
|
test_db_plugin.QuantumManager._instance = None
|
||||||
|
|
||||||
|
self._tenant_id = 'test-tenant'
|
||||||
|
|
||||||
|
json_deserializer = test_db_plugin.JSONDeserializer()
|
||||||
|
self._deserializers = {
|
||||||
|
'application/json': json_deserializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin = 'quantum.plugins.nec.nec_plugin.NECPluginV2'
|
||||||
|
config.CONF.set_override('core_plugin', plugin)
|
||||||
|
driver = "quantum.plugins.nec.tests.unit.stub_ofc_driver.StubOFCDriver"
|
||||||
|
config.CONF.set_override('driver', driver, 'OFC')
|
||||||
|
config.CONF.set_override('rpc_backend',
|
||||||
|
'quantum.openstack.common.rpc.impl_fake')
|
||||||
|
self.api = test_db_plugin.APIRouter()
|
||||||
|
self._skip_native_bulk = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestPortsV2(NECPluginTestBase, test_db_plugin.TestPortsV2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworksV2(NECPluginTestBase, test_db_plugin.TestNetworksV2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: This plugin does not override methods for subnet.
|
||||||
|
#class TestSubnetsV2(NECPluginTestBase, test_db_plugin.TestSubnetsV2):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(r-mibu): write UT for packet_filters.
|
||||||
|
class TestPacketFiltersV2(NECPluginTestBase,
|
||||||
|
test_db_plugin.QuantumDbPluginV2TestCase):
|
||||||
|
pass
|
160
quantum/plugins/nec/tests/unit/test_ofc_manager.py
Normal file
160
quantum/plugins/nec/tests/unit/test_ofc_manager.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from quantum.common import utils
|
||||||
|
from quantum.plugins.nec.common import config
|
||||||
|
from quantum.plugins.nec.db import api as ndb
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
from quantum.plugins.nec import ofc_manager
|
||||||
|
|
||||||
|
|
||||||
|
class OFCManagerTest(unittest.TestCase):
|
||||||
|
"""Class conisting of OFCManager unit tests"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
driver = "quantum.plugins.nec.tests.unit.stub_ofc_driver.StubOFCDriver"
|
||||||
|
config.CONF.set_override('driver', driver, 'OFC')
|
||||||
|
ndb.initialize()
|
||||||
|
self.ofc = ofc_manager.OFCManager()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
ndb.clear_db()
|
||||||
|
|
||||||
|
def get_random_params(self):
|
||||||
|
"""create random parameters for portinfo test"""
|
||||||
|
tenant = utils.str_uuid()
|
||||||
|
network = utils.str_uuid()
|
||||||
|
port = utils.str_uuid()
|
||||||
|
_filter = utils.str_uuid()
|
||||||
|
none = utils.str_uuid()
|
||||||
|
return tenant, network, port, _filter, none
|
||||||
|
|
||||||
|
def testa_create_ofc_tenant(self):
|
||||||
|
"""test create ofc_tenant"""
|
||||||
|
t, n, p, f, none = self.get_random_params()
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCTenant, t))
|
||||||
|
self.ofc.create_ofc_tenant(t)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCTenant, t))
|
||||||
|
tenant = ndb.find_ofc_item(nmodels.OFCTenant, t)
|
||||||
|
self.assertEqual(tenant.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(t))
|
||||||
|
self.ofc.create_ofc_tenant(t)
|
||||||
|
self.assertTrue(self.ofc.exists_ofc_tenant(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(t)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCTenant, t))
|
||||||
|
self.ofc.delete_ofc_tenant(t)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCTenant, 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(t)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCNetwork, n))
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCNetwork, n))
|
||||||
|
network = ndb.find_ofc_item(nmodels.OFCNetwork, n)
|
||||||
|
self.assertEqual(network.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(t)
|
||||||
|
self.assertFalse(self.ofc.exists_ofc_network(n))
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.assertTrue(self.ofc.exists_ofc_network(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(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCNetwork, n))
|
||||||
|
self.ofc.delete_ofc_network(t, n)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCNetwork, n))
|
||||||
|
|
||||||
|
def testg_create_ofc_port(self):
|
||||||
|
"""test create ofc_port"""
|
||||||
|
t, n, p, f, none = self.get_random_params()
|
||||||
|
self.ofc.create_ofc_tenant(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
ndb.add_portinfo(p, "0xabc", 1, 65535, "00:11:22:33:44:55")
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCPort, p))
|
||||||
|
self.ofc.create_ofc_port(t, n, p)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCPort, p))
|
||||||
|
port = ndb.find_ofc_item(nmodels.OFCPort, p)
|
||||||
|
self.assertEqual(port.id, "ofc-" + p[:-4])
|
||||||
|
|
||||||
|
def testh_exists_ofc_port(self):
|
||||||
|
"""test exists_ofc_port"""
|
||||||
|
t, n, p, f, none = self.get_random_params()
|
||||||
|
self.ofc.create_ofc_tenant(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
ndb.add_portinfo(p, "0xabc", 2, 65535, "00:12:22:33:44:55")
|
||||||
|
self.assertFalse(self.ofc.exists_ofc_port(p))
|
||||||
|
self.ofc.create_ofc_port(t, n, p)
|
||||||
|
self.assertTrue(self.ofc.exists_ofc_port(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(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
ndb.add_portinfo(p, "0xabc", 3, 65535, "00:13:22:33:44:55")
|
||||||
|
self.ofc.create_ofc_port(t, n, p)
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCPort, p))
|
||||||
|
self.ofc.delete_ofc_port(t, n, p)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCPort, p))
|
||||||
|
|
||||||
|
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(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCFilter, f))
|
||||||
|
self.ofc.create_ofc_packet_filter(t, n, f, {})
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCFilter, f))
|
||||||
|
_filter = ndb.find_ofc_item(nmodels.OFCFilter, f)
|
||||||
|
self.assertEqual(_filter.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(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.assertFalse(self.ofc.exists_ofc_packet_filter(f))
|
||||||
|
self.ofc.create_ofc_packet_filter(t, n, f, {})
|
||||||
|
self.assertTrue(self.ofc.exists_ofc_packet_filter(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(t)
|
||||||
|
self.ofc.create_ofc_network(t, n)
|
||||||
|
self.ofc.create_ofc_packet_filter(t, n, f, {})
|
||||||
|
self.assertTrue(ndb.find_ofc_item(nmodels.OFCFilter, f))
|
||||||
|
self.ofc.delete_ofc_packet_filter(t, n, f)
|
||||||
|
self.assertFalse(ndb.find_ofc_item(nmodels.OFCFilter, f))
|
158
quantum/plugins/nec/tests/unit/test_pfc_driver.py
Normal file
158
quantum/plugins/nec/tests/unit/test_pfc_driver.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import mox
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from quantum.common import utils
|
||||||
|
from quantum.plugins.nec import drivers
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
from quantum.plugins.nec.common import ofc_client as ofc
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfig(object):
|
||||||
|
"""Configuration for this test"""
|
||||||
|
host = '127.0.0.1'
|
||||||
|
port = 8888
|
||||||
|
use_ssl = False
|
||||||
|
key_file = None
|
||||||
|
cert_file = None
|
||||||
|
|
||||||
|
|
||||||
|
def _ofc(id):
|
||||||
|
"""OFC ID converter"""
|
||||||
|
return "ofc-%s" % id
|
||||||
|
|
||||||
|
|
||||||
|
class PFCDriverTestBase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mox = mox.Mox()
|
||||||
|
self.driver = drivers.get_driver("pfc")(TestConfig)
|
||||||
|
self.mox.StubOutWithMock(ofc.OFCClient, 'do_request')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.mox.UnsetStubs()
|
||||||
|
|
||||||
|
def get_ofc_item_random_params(self):
|
||||||
|
"""create random parameters for ofc_item test"""
|
||||||
|
tenant_id = utils.str_uuid()
|
||||||
|
network_id = utils.str_uuid()
|
||||||
|
port_id = utils.str_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 testa_create_tenant(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "desc of %s" % t
|
||||||
|
|
||||||
|
path = "/tenants"
|
||||||
|
body = {'id': t, 'description': description}
|
||||||
|
tenant = {'id': _ofc(t)}
|
||||||
|
ofc.OFCClient.do_request("POST", path, body=body).AndReturn(tenant)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret = self.driver.create_tenant(description, t)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
self.assertEqual(ret, _ofc(t))
|
||||||
|
|
||||||
|
def testb_update_tenant(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "new desc of %s" % t
|
||||||
|
|
||||||
|
path = "/tenants/%s" % _ofc(t)
|
||||||
|
body = {'description': description}
|
||||||
|
ofc.OFCClient.do_request("PUT", path, body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.update_tenant(_ofc(t), description)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testc_delete_tenant(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/tenants/%s" % _ofc(t)
|
||||||
|
ofc.OFCClient.do_request("DELETE", path)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_tenant(_ofc(t))
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testd_create_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "desc of %s" % n
|
||||||
|
|
||||||
|
path = "/tenants/%s/networks" % _ofc(t)
|
||||||
|
body = {'id': n, 'description': description}
|
||||||
|
network = {'id': _ofc(n)}
|
||||||
|
ofc.OFCClient.do_request("POST", path, body=body).AndReturn(network)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret = self.driver.create_network(_ofc(t), description, n)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
self.assertEqual(ret, _ofc(n))
|
||||||
|
|
||||||
|
def teste_update_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "desc of %s" % n
|
||||||
|
|
||||||
|
path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
|
||||||
|
body = {'description': description}
|
||||||
|
ofc.OFCClient.do_request("PUT", path, body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.update_network(_ofc(t), _ofc(n), description)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testf_delete_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
|
||||||
|
ofc.OFCClient.do_request("DELETE", path)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_network(_ofc(t), _ofc(n))
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testg_create_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/tenants/%s/networks/%s/ports" % (_ofc(t), _ofc(n))
|
||||||
|
body = {'id': p.id,
|
||||||
|
'datapath_id': p.datapath_id,
|
||||||
|
'port': str(p.port_no),
|
||||||
|
'vid': str(p.vlan_id)}
|
||||||
|
port = {'id': _ofc(p.id)}
|
||||||
|
ofc.OFCClient.do_request("POST", path, body=body).AndReturn(port)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret = self.driver.create_port(_ofc(t), _ofc(n), p, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
self.assertEqual(ret, _ofc(p.id))
|
||||||
|
|
||||||
|
def testh_delete_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/tenants/%s/networks/%s/ports/%s" % (_ofc(t), _ofc(n),
|
||||||
|
_ofc(p.id))
|
||||||
|
ofc.OFCClient.do_request("DELETE", path)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_port(_ofc(t), _ofc(n), _ofc(p.id))
|
||||||
|
self.mox.VerifyAll()
|
236
quantum/plugins/nec/tests/unit/test_trema_driver.py
Normal file
236
quantum/plugins/nec/tests/unit/test_trema_driver.py
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 NEC Corporation. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import mox
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from quantum.common import utils
|
||||||
|
from quantum.plugins.nec import drivers
|
||||||
|
from quantum.plugins.nec.db import models as nmodels
|
||||||
|
from quantum.plugins.nec.common import ofc_client
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfig(object):
|
||||||
|
"""Configuration for this test"""
|
||||||
|
host = '127.0.0.1'
|
||||||
|
port = 8888
|
||||||
|
|
||||||
|
|
||||||
|
class TremaDriverTestBase():
|
||||||
|
|
||||||
|
driver_name = "trema"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mox = mox.Mox()
|
||||||
|
self.driver = drivers.get_driver(self.driver_name)(TestConfig)
|
||||||
|
self.mox.StubOutWithMock(ofc_client.OFCClient, 'do_request')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.mox.UnsetStubs()
|
||||||
|
|
||||||
|
def get_ofc_item_random_params(self):
|
||||||
|
"""create random parameters for ofc_item test"""
|
||||||
|
tenant_id = utils.str_uuid()
|
||||||
|
network_id = utils.str_uuid()
|
||||||
|
port_id = utils.str_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
|
||||||
|
|
||||||
|
|
||||||
|
class TremaDriverNetworkTestBase(TremaDriverTestBase):
|
||||||
|
|
||||||
|
def testa_create_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "desc of %s" % n
|
||||||
|
|
||||||
|
body = {'id': n, 'description': description}
|
||||||
|
ofc_client.OFCClient.do_request("POST", "/networks", body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.create_network(t, description, n)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testb_update_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
description = "desc of %s" % n
|
||||||
|
|
||||||
|
body = {'description': description}
|
||||||
|
ofc_client.OFCClient.do_request("PUT", "/networks/%s" % n, body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.update_network(t, n, description)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testc_delete_network(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
ofc_client.OFCClient.do_request("DELETE", "/networks/%s" % n)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_network(t, n)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
|
||||||
|
class TremaPortBaseDriverTest(TremaDriverNetworkTestBase, unittest.TestCase):
|
||||||
|
|
||||||
|
driver_name = "trema_port"
|
||||||
|
|
||||||
|
def testd_create_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
body = {'id': p.id,
|
||||||
|
'datapath_id': p.datapath_id,
|
||||||
|
'port': str(p.port_no),
|
||||||
|
'vid': str(p.vlan_id)}
|
||||||
|
ofc_client.OFCClient.do_request("POST",
|
||||||
|
"/networks/%s/ports" % n, body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.create_port(t, n, p, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testd_delete_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
ofc_client.OFCClient.do_request("DELETE",
|
||||||
|
"/networks/%s/ports/%s" % (n, p.id))
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_port(t, n, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
|
||||||
|
class TremaPortMACBaseDriverTest(TremaDriverNetworkTestBase,
|
||||||
|
unittest.TestCase):
|
||||||
|
|
||||||
|
driver_name = "trema_portmac"
|
||||||
|
|
||||||
|
def testd_create_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
dummy_port = "dummy-%s" % p.id
|
||||||
|
|
||||||
|
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)}
|
||||||
|
ofc_client.OFCClient.do_request("POST", path_1, body=body_1)
|
||||||
|
path_2 = "/networks/%s/ports/%s/attachments" % (n, dummy_port)
|
||||||
|
body_2 = {'id': p.id, 'mac': p.mac}
|
||||||
|
ofc_client.OFCClient.do_request("POST", path_2, body=body_2)
|
||||||
|
path_3 = "/networks/%s/ports/%s" % (n, dummy_port)
|
||||||
|
ofc_client.OFCClient.do_request("DELETE", path_3)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.create_port(t, n, p, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
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)
|
||||||
|
ofc_client.OFCClient.do_request("DELETE", path)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_port(t, n, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
|
||||||
|
class TremaMACBaseDriverTest(TremaDriverNetworkTestBase, unittest.TestCase):
|
||||||
|
|
||||||
|
driver_name = "trema_mac"
|
||||||
|
|
||||||
|
def testd_create_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/networks/%s/attachments" % n
|
||||||
|
body = {'id': p.id, 'mac': p.mac}
|
||||||
|
ofc_client.OFCClient.do_request("POST", path, body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.create_port(t, n, p, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testd_delete_port(self):
|
||||||
|
t, n, p = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
path = "/networks/%s/attachments/%s" % (n, p.id)
|
||||||
|
ofc_client.OFCClient.do_request("DELETE", path)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_port(t, n, p.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
|
||||||
|
class TremaFilterDriverTest(TremaDriverTestBase, unittest.TestCase):
|
||||||
|
|
||||||
|
def get_ofc_item_random_params(self):
|
||||||
|
"""create random parameters for ofc_item test"""
|
||||||
|
t, n, p = (super(TremaFilterDriverTest, self).
|
||||||
|
get_ofc_item_random_params())
|
||||||
|
filter_id = utils.str_uuid()
|
||||||
|
filter_dict = {'tenant_id': t,
|
||||||
|
'id': filter_id,
|
||||||
|
'network_id': n,
|
||||||
|
'priority': 123,
|
||||||
|
'action': "ACCEPT",
|
||||||
|
'in_port': p.id,
|
||||||
|
'src_mac': p.mac,
|
||||||
|
'dst_mac': "",
|
||||||
|
'eth_type': 0,
|
||||||
|
'src_cidr': "",
|
||||||
|
'dst_cidr': "",
|
||||||
|
'src_port': 0,
|
||||||
|
'dst_port': 0,
|
||||||
|
'protocol': "TCP",
|
||||||
|
'admin_state_up': True,
|
||||||
|
'status': "ACTIVE"}
|
||||||
|
filter_item = nmodels.PacketFilter(**filter_dict)
|
||||||
|
return t, n, p, filter_item
|
||||||
|
|
||||||
|
def testa_create_filter(self):
|
||||||
|
t, n, p, f = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
ofp_wildcards = 'dl_vlan,dl_vlan_pcp,nw_tos,dl_dst,' + \
|
||||||
|
'nw_src:32,nw_dst:32,tp_src,tp_dst'
|
||||||
|
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': p.mac,
|
||||||
|
'ofp_wildcards': ofp_wildcards}
|
||||||
|
ofc_client.OFCClient.do_request("POST", "/filters", body=body)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.create_filter(t, n, f, p, f.id)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def testb_delete_filter(self):
|
||||||
|
t, n, p, f = self.get_ofc_item_random_params()
|
||||||
|
|
||||||
|
ofc_client.OFCClient.do_request("DELETE", "/filters/%s" % f.id)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.driver.delete_filter(t, n, f.id)
|
||||||
|
self.mox.VerifyAll()
|
6
setup.py
6
setup.py
@ -49,6 +49,7 @@ linuxbridge_plugin_config_path = 'etc/quantum/plugins/linuxbridge'
|
|||||||
nvp_plugin_config_path = 'etc/quantum/plugins/nicira'
|
nvp_plugin_config_path = 'etc/quantum/plugins/nicira'
|
||||||
ryu_plugin_config_path = 'etc/quantum/plugins/ryu'
|
ryu_plugin_config_path = 'etc/quantum/plugins/ryu'
|
||||||
meta_plugin_config_path = 'etc/quantum/plugins/metaplugin'
|
meta_plugin_config_path = 'etc/quantum/plugins/metaplugin'
|
||||||
|
nec_plugin_config_path = 'etc/quantum/plugins/nec'
|
||||||
|
|
||||||
DataFiles = [
|
DataFiles = [
|
||||||
(config_path,
|
(config_path,
|
||||||
@ -72,7 +73,8 @@ DataFiles = [
|
|||||||
['etc/quantum/plugins/nicira/nvp.ini']),
|
['etc/quantum/plugins/nicira/nvp.ini']),
|
||||||
(ryu_plugin_config_path, ['etc/quantum/plugins/ryu/ryu.ini']),
|
(ryu_plugin_config_path, ['etc/quantum/plugins/ryu/ryu.ini']),
|
||||||
(meta_plugin_config_path,
|
(meta_plugin_config_path,
|
||||||
['etc/quantum/plugins/metaplugin/metaplugin.ini'])
|
['etc/quantum/plugins/metaplugin/metaplugin.ini']),
|
||||||
|
(nec_plugin_config_path, ['etc/quantum/plugins/nec/nec.ini']),
|
||||||
]
|
]
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
@ -102,6 +104,8 @@ setuptools.setup(
|
|||||||
'quantum.plugins.openvswitch.agent.ovs_quantum_agent:main',
|
'quantum.plugins.openvswitch.agent.ovs_quantum_agent:main',
|
||||||
'quantum-ryu-agent = '
|
'quantum-ryu-agent = '
|
||||||
'quantum.plugins.ryu.agent.ryu_quantum_agent:main',
|
'quantum.plugins.ryu.agent.ryu_quantum_agent:main',
|
||||||
|
'quantum-nec-agent = '
|
||||||
|
'quantum.plugins.nec.agent.nec_quantum_agent:main',
|
||||||
'quantum-server = quantum.server:main',
|
'quantum-server = quantum.server:main',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user