NEC OpenFlow plugin support.

blueprint quantum-nec-openflow-plugin

Change-Id: Ib6d6f658bbf6d653527fa7820685f9bed1412a18
This commit is contained in:
Ryota MIBU 2012-08-02 01:42:20 +09:00
parent 7404f16682
commit 7aebe2468b
32 changed files with 3007 additions and 1 deletions

View 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

View 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

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

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

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

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

View 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

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

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

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

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

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

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

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

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

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

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

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

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

View 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

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

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

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

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

View 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

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

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

View 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

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

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

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

View File

@ -49,6 +49,7 @@ linuxbridge_plugin_config_path = 'etc/quantum/plugins/linuxbridge'
nvp_plugin_config_path = 'etc/quantum/plugins/nicira'
ryu_plugin_config_path = 'etc/quantum/plugins/ryu'
meta_plugin_config_path = 'etc/quantum/plugins/metaplugin'
nec_plugin_config_path = 'etc/quantum/plugins/nec'
DataFiles = [
(config_path,
@ -72,7 +73,8 @@ DataFiles = [
['etc/quantum/plugins/nicira/nvp.ini']),
(ryu_plugin_config_path, ['etc/quantum/plugins/ryu/ryu.ini']),
(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(
@ -102,6 +104,8 @@ setuptools.setup(
'quantum.plugins.openvswitch.agent.ovs_quantum_agent:main',
'quantum-ryu-agent = '
'quantum.plugins.ryu.agent.ryu_quantum_agent:main',
'quantum-nec-agent = '
'quantum.plugins.nec.agent.nec_quantum_agent:main',
'quantum-server = quantum.server:main',
]
},