One Convergence Neutron Plugin Implementation
One Convergence Neutron Plugin implements Neutron API to provide a network virtualization solution. The plugin works with One Convergence NVSD controller to provide the functionality. This checkin implements the Neutron core APIs and the plugin will be extended to support the L3 and service plugin extension APIs. Change-Id: Ic8a0dc0f5950d41b9b253c0d61b6812dbfd161c7 Implements: blueprint oc-nvsd-neutron-plugin
This commit is contained in:
parent
cdec8e52a2
commit
683323f359
23
etc/neutron/plugins/oneconvergence/nvsdplugin.ini
Normal file
23
etc/neutron/plugins/oneconvergence/nvsdplugin.ini
Normal file
@ -0,0 +1,23 @@
|
||||
[nvsd]
|
||||
# Configure the NVSD controller. The plugin proxies the api calls using
|
||||
# to NVSD controller which implements the required functionality.
|
||||
|
||||
# IP address of NVSD controller api server
|
||||
# nvsd_ip = <ip address of nvsd controller>
|
||||
|
||||
# Port number of NVSD controller api server
|
||||
# nvsd_port = 8082
|
||||
|
||||
# Authentication credentials to access the api server
|
||||
# nvsd_user = <nvsd controller username>
|
||||
# nvsd_passwd = <password>
|
||||
|
||||
# API request timeout in seconds
|
||||
# request_timeout = <default request timeout>
|
||||
|
||||
# Maximum number of retry attempts to login to the NVSD controller
|
||||
# Specify 0 to retry until success (default)
|
||||
# nvsd_retries = 0
|
||||
|
||||
[database]
|
||||
# connection = mysql://root:<passwd>@127.0.0.1/<neutron_db>?charset=utf8
|
@ -42,7 +42,8 @@ migration_for_plugins = [
|
||||
'neutron.plugins.vmware.plugin.NsxPlugin',
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.plugins.embrane.plugins.embrane_ovs_plugin.EmbraneOvsPlugin',
|
||||
'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2'
|
||||
'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -38,6 +38,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.vmware.plugin.NsxPlugin',
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -38,7 +38,8 @@ migration_for_plugins = [
|
||||
'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin',
|
||||
'neutron.plugins.ryu.ryu_neutron_plugin.RyuNeutronPluginV2',
|
||||
'neutron.plugins.vmware.plugin.NsxPlugin',
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin'
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -38,6 +38,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.services.loadbalancer.plugin.LoadBalancerPlugin',
|
||||
'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -44,6 +44,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.nec.nec_plugin.NECPluginV2',
|
||||
'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
|
||||
'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2',
|
||||
'neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin.'
|
||||
'NeutronPluginPLUMgridV2',
|
||||
|
@ -38,6 +38,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.ryu.ryu_neutron_plugin.RyuNeutronPluginV2',
|
||||
'neutron.plugins.vmware.plugin.NsxPlugin',
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -38,6 +38,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.nec.nec_plugin.NECPluginV2',
|
||||
'neutron.plugins.vmware.plugin.NsxPlugin',
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -40,6 +40,7 @@ migration_for_plugins = [
|
||||
'neutron.plugins.vmware.plugin.NsxServicePlugin',
|
||||
'neutron.services.loadbalancer.plugin.LoadBalancerPlugin',
|
||||
'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2',
|
||||
'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
|
@ -33,6 +33,7 @@ PLUGINS = {
|
||||
'ml2': 'neutron.plugins.ml2.plugin.Ml2Plugin',
|
||||
'nec': 'neutron.plugins.nec.nec_plugin.NECPluginV2',
|
||||
'nvp': 'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
|
||||
'ocnvsd': 'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
|
||||
'ovs': 'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2',
|
||||
'plumgrid': 'neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin.'
|
||||
'NeutronPluginPLUMgridV2',
|
||||
@ -45,6 +46,7 @@ L3_CAPABLE = [
|
||||
PLUGINS['meta'],
|
||||
PLUGINS['ml2'],
|
||||
PLUGINS['nec'],
|
||||
PLUGINS['ocnvsd'],
|
||||
PLUGINS['ovs'],
|
||||
PLUGINS['ryu'],
|
||||
PLUGINS['brocade'],
|
||||
@ -56,6 +58,7 @@ FOLSOM_QUOTA = [
|
||||
PLUGINS['lbr'],
|
||||
PLUGINS['ml2'],
|
||||
PLUGINS['nvp'],
|
||||
PLUGINS['ocnvsd'],
|
||||
PLUGINS['ovs'],
|
||||
]
|
||||
|
||||
|
32
neutron/plugins/oneconvergence/README
Normal file
32
neutron/plugins/oneconvergence/README
Normal file
@ -0,0 +1,32 @@
|
||||
One Convergence Neutron Plugin to implement the Neutron v2.0 API. The plugin
|
||||
works with One Convergence NVSD controller to provide network virtualization
|
||||
functionality.
|
||||
|
||||
The plugin is enabled with the following configuration line in neutron.conf:
|
||||
|
||||
core_plugin = neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2
|
||||
|
||||
The configuration parameters required for the plugin are specified in the file
|
||||
etc/neutron/plugins/oneconvergence/nvsdplugin.ini. The configuration file contains
|
||||
description of the different parameters.
|
||||
|
||||
To enable One Convergence Neutron Plugin with devstack and configure the required
|
||||
parameters, use the following lines in localrc:
|
||||
|
||||
Q_PLUGIN=oneconvergence
|
||||
|
||||
disable_service n-net
|
||||
disable_service q-agt
|
||||
enable_service q-dhcp
|
||||
enable_service q-svc
|
||||
enable_service q-l3
|
||||
enable_service q-meta
|
||||
enable_service neutron
|
||||
|
||||
NVSD_IP=
|
||||
NVSD_PORT=
|
||||
NVSD_USER=
|
||||
NVSD_PASSWD=
|
||||
|
||||
The NVSD controller configuration should be specified in nvsdplugin.ini before
|
||||
invoking stack.sh.
|
0
neutron/plugins/oneconvergence/__init__.py
Normal file
0
neutron/plugins/oneconvergence/__init__.py
Normal file
0
neutron/plugins/oneconvergence/lib/__init__.py
Normal file
0
neutron/plugins/oneconvergence/lib/__init__.py
Normal file
41
neutron/plugins/oneconvergence/lib/config.py
Normal file
41
neutron/plugins/oneconvergence/lib/config.py
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright 2014 OneConvergence, 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.
|
||||
#
|
||||
|
||||
""" Register the configuration options"""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
NVSD_OPT = [
|
||||
cfg.StrOpt('nvsd_ip',
|
||||
default='127.0.0.1',
|
||||
help=_("NVSD Controller IP address")),
|
||||
cfg.IntOpt('nvsd_port',
|
||||
default=8082,
|
||||
help=_("NVSD Controller Port number")),
|
||||
cfg.StrOpt('nvsd_user',
|
||||
default='ocplugin',
|
||||
help=_("NVSD Controller username")),
|
||||
cfg.StrOpt('nvsd_passwd',
|
||||
default='oc123', secret=True,
|
||||
help=_("NVSD Controller password")),
|
||||
cfg.IntOpt('request_timeout',
|
||||
default=30,
|
||||
help=_("NVSD controller REST API request timeout in seconds")),
|
||||
cfg.IntOpt('nvsd_retries', default=0,
|
||||
help=_("Number of login retries to NVSD controller"))
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(NVSD_OPT, "nvsd")
|
55
neutron/plugins/oneconvergence/lib/exception.py
Normal file
55
neutron/plugins/oneconvergence/lib/exception.py
Normal file
@ -0,0 +1,55 @@
|
||||
# Copyright 2014 OneConvergence, 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.
|
||||
#
|
||||
|
||||
"""NVSD Exception Definitions."""
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
|
||||
|
||||
class NVSDAPIException(n_exc.NeutronException):
|
||||
'''Base NVSDplugin Exception.'''
|
||||
message = _("An unknown nvsd plugin exception occurred: %(reason)s")
|
||||
|
||||
|
||||
class RequestTimeout(NVSDAPIException):
|
||||
message = _("The request has timed out.")
|
||||
|
||||
|
||||
class UnAuthorizedException(NVSDAPIException):
|
||||
message = _("Invalid access credentials to the Server.")
|
||||
|
||||
|
||||
class NotFoundException(NVSDAPIException):
|
||||
message = _("A resource is not found: %(reason)s")
|
||||
|
||||
|
||||
class BadRequestException(NVSDAPIException):
|
||||
message = _("Request sent to server is invalid: %(reason)s")
|
||||
|
||||
|
||||
class ServerException(NVSDAPIException):
|
||||
message = _("Internal Server Error: %(reason)s")
|
||||
|
||||
|
||||
class ConnectionClosedException(NVSDAPIException):
|
||||
message = _("Connection is closed by the server.")
|
||||
|
||||
|
||||
class ForbiddenException(NVSDAPIException):
|
||||
message = _("The request is forbidden access to the resource: %(reason)s")
|
||||
|
||||
|
||||
class InternalServerError(NVSDAPIException):
|
||||
message = _("Internal Server Error from NVSD controller: %(reason)s")
|
262
neutron/plugins/oneconvergence/lib/nvsdlib.py
Normal file
262
neutron/plugins/oneconvergence/lib/nvsdlib.py
Normal file
@ -0,0 +1,262 @@
|
||||
# Copyright 2014 OneConvergence, 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: Kedar Kulkarni, One Convergence, Inc.
|
||||
|
||||
"""Intermidiate NVSD Library."""
|
||||
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import jsonutils as json
|
||||
from neutron.openstack.common import log as logging
|
||||
import neutron.plugins.oneconvergence.lib.exception as nvsdexception
|
||||
from neutron.plugins.oneconvergence.lib import plugin_helper
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NETWORKS_URI = "/pluginhandler/ocplugin/tenant/%s/lnetwork/"
|
||||
NETWORK_URI = NETWORKS_URI + "%s"
|
||||
GET_ALL_NETWORKS = "/pluginhandler/ocplugin/tenant/getallnetworks"
|
||||
|
||||
SUBNETS_URI = NETWORK_URI + "/lsubnet/"
|
||||
SUBNET_URI = SUBNETS_URI + "%s"
|
||||
GET_ALL_SUBNETS = "/pluginhandler/ocplugin/tenant/getallsubnets"
|
||||
|
||||
PORTS_URI = NETWORK_URI + "/lport/"
|
||||
PORT_URI = PORTS_URI + "%s"
|
||||
|
||||
METHODS = {"POST": "create",
|
||||
"PUT": "update",
|
||||
"DELETE": "delete",
|
||||
"GET": "get"}
|
||||
|
||||
|
||||
class NVSDApi(object):
|
||||
|
||||
def build_error_msg(self, method, resource, tenant_id, resource_id):
|
||||
if method == "POST":
|
||||
msg = _("Could not create a %(resource)s under tenant "
|
||||
"%(tenant_id)s") % {'resource': resource,
|
||||
'tenant_id': tenant_id}
|
||||
elif resource_id:
|
||||
msg = _("Failed to %(method)s %(resource)s "
|
||||
"id=%(resource_id)s") % {'method': METHODS[method],
|
||||
'resource': resource,
|
||||
'resource_id': resource_id
|
||||
}
|
||||
else:
|
||||
msg = _("Failed to %(method)s %(resource)s") % {
|
||||
'method': METHODS[method], 'resource': resource}
|
||||
return msg
|
||||
|
||||
def set_connection(self):
|
||||
self.nvsdcontroller = plugin_helper.initialize_plugin_helper()
|
||||
self.nvsdcontroller.login()
|
||||
|
||||
def send_request(self, method, uri, body=None, resource=None,
|
||||
tenant_id='', resource_id=None):
|
||||
"""Issue a request to NVSD controller."""
|
||||
|
||||
try:
|
||||
result = self.nvsdcontroller.request(method, uri, body=body)
|
||||
except nvsdexception.NVSDAPIException as e:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
msg = self.build_error_msg(method, resource, tenant_id,
|
||||
resource_id)
|
||||
LOG.error(msg)
|
||||
# Modifying the reason message without disturbing the exception
|
||||
# info
|
||||
ctxt.value = type(e)(reason=msg)
|
||||
return result
|
||||
|
||||
def create_network(self, network):
|
||||
|
||||
tenant_id = network['tenant_id']
|
||||
router_external = network['router:external'] is True
|
||||
|
||||
network_obj = {
|
||||
"name": network['name'],
|
||||
"tenant_id": tenant_id,
|
||||
"shared": network['shared'],
|
||||
"admin_state_up": network['admin_state_up'],
|
||||
"router:external": router_external
|
||||
}
|
||||
|
||||
uri = NETWORKS_URI % tenant_id
|
||||
|
||||
response = self.send_request("POST", uri, body=json.dumps(network_obj),
|
||||
resource='network', tenant_id=tenant_id)
|
||||
|
||||
nvsd_net = response.json()
|
||||
|
||||
LOG.debug(_("Network %(id)s created under tenant %(tenant_id)s"),
|
||||
{'id': nvsd_net['id'], 'tenant_id': tenant_id})
|
||||
|
||||
return nvsd_net
|
||||
|
||||
def update_network(self, network, network_update):
|
||||
|
||||
tenant_id = network['tenant_id']
|
||||
network_id = network['id']
|
||||
|
||||
uri = NETWORK_URI % (tenant_id, network_id)
|
||||
|
||||
self.send_request("PUT", uri,
|
||||
body=json.dumps(network_update),
|
||||
resource='network', tenant_id=tenant_id,
|
||||
resource_id=network_id)
|
||||
|
||||
LOG.debug(_("Network %(id)s updated under tenant %(tenant_id)s"),
|
||||
{'id': network_id, 'tenant_id': tenant_id})
|
||||
|
||||
def delete_network(self, network, subnets=[]):
|
||||
|
||||
tenant_id = network['tenant_id']
|
||||
network_id = network['id']
|
||||
|
||||
ports = self._get_ports(tenant_id, network_id)
|
||||
|
||||
for port in ports:
|
||||
self.delete_port(port['id'], port)
|
||||
|
||||
for subnet in subnets:
|
||||
self.delete_subnet(subnet)
|
||||
|
||||
path = NETWORK_URI % (tenant_id, network_id)
|
||||
|
||||
self.send_request("DELETE", path, resource='network',
|
||||
tenant_id=tenant_id, resource_id=network_id)
|
||||
|
||||
LOG.debug(_("Network %(id)s deleted under tenant %(tenant_id)s"),
|
||||
{'id': network_id, 'tenant_id': tenant_id})
|
||||
|
||||
def create_subnet(self, subnet):
|
||||
|
||||
tenant_id = subnet['tenant_id']
|
||||
network_id = subnet['network_id']
|
||||
|
||||
uri = SUBNETS_URI % (tenant_id, network_id)
|
||||
|
||||
self.send_request("POST", uri, body=json.dumps(subnet),
|
||||
resource='subnet', tenant_id=tenant_id)
|
||||
|
||||
LOG.debug(_("Subnet %(id)s created under tenant %(tenant_id)s"),
|
||||
{'id': subnet['id'], 'tenant_id': tenant_id})
|
||||
|
||||
def delete_subnet(self, subnet):
|
||||
|
||||
tenant_id = subnet['tenant_id']
|
||||
network_id = subnet['network_id']
|
||||
subnet_id = subnet['id']
|
||||
|
||||
uri = SUBNET_URI % (tenant_id, network_id, subnet_id)
|
||||
|
||||
self.send_request("DELETE", uri, resource='subnet',
|
||||
tenant_id=tenant_id, resource_id=subnet_id)
|
||||
|
||||
LOG.debug(_("Subnet %(id)s deleted under tenant %(tenant_id)s"),
|
||||
{'id': subnet_id, 'tenant_id': tenant_id})
|
||||
|
||||
def update_subnet(self, subnet, subnet_update):
|
||||
|
||||
tenant_id = subnet['tenant_id']
|
||||
network_id = subnet['network_id']
|
||||
subnet_id = subnet['id']
|
||||
|
||||
uri = SUBNET_URI % (tenant_id, network_id, subnet_id)
|
||||
|
||||
self.send_request("PUT", uri,
|
||||
body=json.dumps(subnet_update),
|
||||
resource='subnet', tenant_id=tenant_id,
|
||||
resource_id=subnet_id)
|
||||
|
||||
LOG.debug(_("Subnet %(id)s updated under tenant %(tenant_id)s"),
|
||||
{'id': subnet_id, 'tenant_id': tenant_id})
|
||||
|
||||
def create_port(self, tenant_id, port):
|
||||
|
||||
network_id = port["network_id"]
|
||||
fixed_ips = port.get("fixed_ips")
|
||||
ip_address = None
|
||||
subnet_id = None
|
||||
|
||||
if fixed_ips:
|
||||
ip_address = fixed_ips[0].get("ip_address")
|
||||
subnet_id = fixed_ips[0].get("subnet_id")
|
||||
|
||||
lport = {
|
||||
"id": port["id"],
|
||||
"name": port["name"],
|
||||
"device_id": port["device_id"],
|
||||
"device_owner": port["device_owner"],
|
||||
"mac_address": port["mac_address"],
|
||||
"ip_address": ip_address,
|
||||
"subnet_id": subnet_id,
|
||||
"admin_state_up": port["admin_state_up"],
|
||||
"network_id": network_id,
|
||||
"status": port["status"]
|
||||
}
|
||||
|
||||
path = PORTS_URI % (tenant_id, network_id)
|
||||
|
||||
self.send_request("POST", path, body=json.dumps(lport),
|
||||
resource='port', tenant_id=tenant_id)
|
||||
|
||||
LOG.debug(_("Port %(id)s created under tenant %(tenant_id)s"),
|
||||
{'id': port['id'], 'tenant_id': tenant_id})
|
||||
|
||||
def update_port(self, tenant_id, port, port_update):
|
||||
|
||||
network_id = port['network_id']
|
||||
port_id = port['id']
|
||||
|
||||
lport = {}
|
||||
for k in ('admin_state_up', 'name', 'device_id', 'device_owner'):
|
||||
if k in port_update:
|
||||
lport[k] = port_update[k]
|
||||
|
||||
fixed_ips = port_update.get('fixed_ips', None)
|
||||
if fixed_ips:
|
||||
lport["ip_address"] = fixed_ips[0].get("ip_address")
|
||||
lport["subnet_id"] = fixed_ips[0].get("subnet_id")
|
||||
|
||||
uri = PORT_URI % (tenant_id, network_id, port_id)
|
||||
|
||||
self.send_request("PUT", uri, body=json.dumps(lport),
|
||||
resource='port', tenant_id=tenant_id,
|
||||
resource_id=port_id)
|
||||
|
||||
LOG.debug(_("Port %(id)s updated under tenant %(tenant_id)s"),
|
||||
{'id': port_id, 'tenant_id': tenant_id})
|
||||
|
||||
def delete_port(self, port_id, port):
|
||||
|
||||
tenant_id = port['tenant_id']
|
||||
network_id = port['network_id']
|
||||
|
||||
uri = PORT_URI % (tenant_id, network_id, port_id)
|
||||
|
||||
self.send_request("DELETE", uri, resource='port', tenant_id=tenant_id,
|
||||
resource_id=port_id)
|
||||
|
||||
LOG.debug(_("Port %(id)s deleted under tenant %(tenant_id)s"),
|
||||
{'id': port_id, 'tenant_id': tenant_id})
|
||||
|
||||
def _get_ports(self, tenant_id, network_id):
|
||||
|
||||
uri = PORTS_URI % (tenant_id, network_id)
|
||||
|
||||
response = self.send_request("GET", uri, resource='ports',
|
||||
tenant_id=tenant_id)
|
||||
|
||||
return response.json()
|
186
neutron/plugins/oneconvergence/lib/plugin_helper.py
Normal file
186
neutron/plugins/oneconvergence/lib/plugin_helper.py
Normal file
@ -0,0 +1,186 @@
|
||||
# Copyright 2014 OneConvergence, 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: Kedar Kulkarni, One Convergence, Inc.
|
||||
|
||||
"""Library to talk to NVSD controller."""
|
||||
|
||||
import httplib
|
||||
import time
|
||||
from urlparse import urljoin
|
||||
|
||||
from oslo.config import cfg
|
||||
import requests
|
||||
|
||||
from neutron.openstack.common import jsonutils as json
|
||||
from neutron.openstack.common import log as logging
|
||||
import neutron.plugins.oneconvergence.lib.exception as exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def initialize_plugin_helper():
|
||||
nvsdcontroller = NVSDController()
|
||||
return nvsdcontroller
|
||||
|
||||
|
||||
class NVSDController(object):
|
||||
|
||||
"""Encapsulates the NVSD Controller details."""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._host = cfg.CONF.nvsd.nvsd_ip
|
||||
self._port = cfg.CONF.nvsd.nvsd_port
|
||||
self._user = cfg.CONF.nvsd.nvsd_user
|
||||
self._password = cfg.CONF.nvsd.nvsd_passwd
|
||||
self._retries = cfg.CONF.nvsd.nvsd_retries
|
||||
self._request_timeout = float(cfg.CONF.nvsd.request_timeout)
|
||||
self.api_url = 'http://' + self._host + ':' + str(self._port)
|
||||
|
||||
self.pool = requests.Session()
|
||||
|
||||
self.auth_token = None
|
||||
|
||||
def do_request(self, method, url=None, headers=None, data=None,
|
||||
timeout=10):
|
||||
response = self.pool.request(method, url=url,
|
||||
headers=headers, data=data,
|
||||
timeout=self._request_timeout)
|
||||
return response
|
||||
|
||||
def login(self):
|
||||
"""Login to NVSD Controller."""
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
login_url = urljoin(self.api_url,
|
||||
"/pluginhandler/ocplugin/authmgmt/login")
|
||||
|
||||
data = json.dumps({"user_name": self._user, "passwd": self._password})
|
||||
|
||||
attempts = 0
|
||||
|
||||
while True:
|
||||
if attempts < self._retries:
|
||||
attempts += 1
|
||||
elif self._retries == 0:
|
||||
attempts = 0
|
||||
else:
|
||||
msg = _("Unable to connect to NVSD controller. Exiting after "
|
||||
"%(retries)s attempts") % {'retries': self._retries}
|
||||
LOG.error(msg)
|
||||
raise exception.ServerException(reason=msg)
|
||||
try:
|
||||
response = self.do_request("POST", url=login_url,
|
||||
headers=headers, data=data,
|
||||
timeout=self._request_timeout)
|
||||
break
|
||||
except Exception as e:
|
||||
LOG.error(_("Login Failed: %s"), e)
|
||||
LOG.error(_("Unable to establish connection"
|
||||
" with Controller %s"), self.api_url)
|
||||
LOG.error(_("Retrying after 1 second..."))
|
||||
time.sleep(1)
|
||||
|
||||
if response.status_code == requests.codes.ok:
|
||||
LOG.debug(_("Login Successful %(uri)s "
|
||||
"%(status)s"), {'uri': self.api_url,
|
||||
'status': response.status_code})
|
||||
self.auth_token = json.loads(response.content)["session_uuid"]
|
||||
LOG.debug(_("AuthToken = %s"), self.auth_token)
|
||||
else:
|
||||
LOG.error(_("login failed"))
|
||||
|
||||
return
|
||||
|
||||
def request(self, method, url, body="", content_type="application/json"):
|
||||
"""Issue a request to NVSD controller."""
|
||||
|
||||
if self.auth_token is None:
|
||||
LOG.warning(_("No Token, Re-login"))
|
||||
self.login()
|
||||
|
||||
headers = {"Content-Type": content_type}
|
||||
|
||||
uri = urljoin(url, "?authToken=%s" % self.auth_token)
|
||||
|
||||
url = urljoin(self.api_url, uri)
|
||||
|
||||
request_ok = False
|
||||
response = None
|
||||
|
||||
try:
|
||||
response = self.do_request(method, url=url,
|
||||
headers=headers, data=body,
|
||||
timeout=self._request_timeout)
|
||||
|
||||
LOG.debug(_("request: %(method)s %(uri)s successful"),
|
||||
{'method': method, 'uri': self.api_url + uri})
|
||||
request_ok = True
|
||||
except httplib.IncompleteRead as e:
|
||||
response = e.partial
|
||||
request_ok = True
|
||||
except Exception as e:
|
||||
LOG.error(_("request: Request failed from "
|
||||
"Controller side :%s"), e)
|
||||
|
||||
if response is None:
|
||||
# Timeout.
|
||||
LOG.error(_("Response is Null, Request timed out: %(method)s to "
|
||||
"%(uri)s"), {'method': method, 'uri': uri})
|
||||
self.auth_token = None
|
||||
raise exception.RequestTimeout()
|
||||
|
||||
status = response.status_code
|
||||
if status == requests.codes.unauthorized:
|
||||
self.auth_token = None
|
||||
# Raise an exception to inform that the request failed.
|
||||
raise exception.UnAuthorizedException()
|
||||
|
||||
if status in self.error_codes:
|
||||
LOG.error(_("Request %(method)s %(uri)s body = %(body)s failed "
|
||||
"with status %(status)s"), {'method': method,
|
||||
'uri': uri, 'body': body,
|
||||
'status': status})
|
||||
LOG.error(_("%s"), response.reason)
|
||||
raise self.error_codes[status]()
|
||||
elif status not in (requests.codes.ok, requests.codes.created,
|
||||
requests.codes.no_content):
|
||||
LOG.error(_("%(method)s to %(url)s, unexpected response code: "
|
||||
"%(status)d"), {'method': method, 'url': url,
|
||||
'status': status})
|
||||
return
|
||||
|
||||
if not request_ok:
|
||||
LOG.error(_("Request failed from Controller side with "
|
||||
"Status=%s"), status)
|
||||
raise exception.ServerException()
|
||||
else:
|
||||
LOG.debug(_("Success: %(method)s %(url)s status=%(status)s"),
|
||||
{'method': method, 'url': self.api_url + uri,
|
||||
'status': status})
|
||||
response.body = response.content
|
||||
return response
|
||||
|
||||
error_codes = {
|
||||
404: exception.NotFoundException,
|
||||
409: exception.BadRequestException,
|
||||
500: exception.InternalServerError,
|
||||
503: exception.ServerException,
|
||||
403: exception.ForbiddenException,
|
||||
301: exception.NVSDAPIException,
|
||||
307: exception.NVSDAPIException,
|
||||
400: exception.NVSDAPIException,
|
||||
}
|
300
neutron/plugins/oneconvergence/plugin.py
Normal file
300
neutron/plugins/oneconvergence/plugin.py
Normal file
@ -0,0 +1,300 @@
|
||||
# Copyright 2014 OneConvergence, 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: Kedar Kulkarni, One Convergence, Inc.
|
||||
|
||||
"""Implementation of OneConvergence Neutron Plugin."""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.common import constants as q_const
|
||||
from neutron.common import exceptions as nexception
|
||||
from neutron.common import rpc as q_rpc
|
||||
from neutron.common import topics
|
||||
from neutron.db import agents_db
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import dhcp_rpc_base
|
||||
from neutron.db import external_net_db
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_agentschedulers_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import l3_rpc_base
|
||||
from neutron.db import portbindings_base
|
||||
from neutron.db import quota_db # noqa
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import rpc
|
||||
from neutron.plugins.common import constants as svc_constants
|
||||
import neutron.plugins.oneconvergence.lib.config # noqa
|
||||
import neutron.plugins.oneconvergence.lib.exception as nvsdexception
|
||||
from neutron.plugins.oneconvergence.lib import nvsdlib as nvsd_lib
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
IPv6 = 6
|
||||
|
||||
|
||||
class NVSDRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
|
||||
l3_rpc_base.L3RpcCallbackMixin):
|
||||
|
||||
"""Agent callback."""
|
||||
|
||||
RPC_API_VERSION = '1.1'
|
||||
|
||||
def create_rpc_dispatcher(self):
|
||||
"""Get the rpc dispatcher for this manager."""
|
||||
return q_rpc.PluginRpcDispatcher([self,
|
||||
agents_db.AgentExtRpcCallback()])
|
||||
|
||||
|
||||
class OneConvergencePluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
l3_agentschedulers_db.L3AgentSchedulerDbMixin,
|
||||
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
||||
external_net_db.External_net_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
portbindings_base.PortBindingBaseMixin):
|
||||
|
||||
"""L2 Virtual Network Plugin.
|
||||
|
||||
OneConvergencePluginV2 is a Neutron plugin that provides L2 Virtual Network
|
||||
functionality.
|
||||
"""
|
||||
|
||||
__native_bulk_support = True
|
||||
__native_pagination_support = True
|
||||
__native_sorting_support = True
|
||||
|
||||
supported_extension_aliases = ['agent',
|
||||
'binding',
|
||||
'dhcp_agent_scheduler',
|
||||
'ext-gw-mode',
|
||||
'external-net',
|
||||
'extraroute',
|
||||
'l3_agent_scheduler',
|
||||
'quotas',
|
||||
'router',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super(OneConvergencePluginV2, self).__init__()
|
||||
|
||||
self.oneconvergence_init()
|
||||
|
||||
self.base_binding_dict = {
|
||||
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS}
|
||||
|
||||
portbindings_base.register_port_dict_function()
|
||||
|
||||
self.setup_rpc()
|
||||
|
||||
self.network_scheduler = importutils.import_object(
|
||||
cfg.CONF.network_scheduler_driver)
|
||||
self.router_scheduler = importutils.import_object(
|
||||
cfg.CONF.router_scheduler_driver)
|
||||
|
||||
def oneconvergence_init(self):
|
||||
"""Initialize the connections and set the log levels for the plugin."""
|
||||
|
||||
self.nvsdlib = nvsd_lib.NVSDApi()
|
||||
self.nvsdlib.set_connection()
|
||||
|
||||
def setup_rpc(self):
|
||||
# RPC support
|
||||
self.service_topics = {svc_constants.CORE: topics.PLUGIN,
|
||||
svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
|
||||
self.conn = rpc.create_connection(new=True)
|
||||
self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = (
|
||||
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
||||
)
|
||||
self.agent_notifiers[q_const.AGENT_TYPE_L3] = (
|
||||
l3_rpc_agent_api.L3AgentNotify
|
||||
)
|
||||
self.callbacks = NVSDRpcCallbacks()
|
||||
self.dispatcher = self.callbacks.create_rpc_dispatcher()
|
||||
for svc_topic in self.service_topics.values():
|
||||
self.conn.create_consumer(svc_topic, self.dispatcher, fanout=False)
|
||||
|
||||
# Consume from all consumers in a thread
|
||||
self.conn.consume_in_thread()
|
||||
|
||||
def create_network(self, context, network):
|
||||
|
||||
net = self.nvsdlib.create_network(network['network'])
|
||||
|
||||
network['network']['id'] = net['id']
|
||||
|
||||
try:
|
||||
neutron_net = super(OneConvergencePluginV2,
|
||||
self).create_network(context, network)
|
||||
|
||||
#following call checks whether the network is external or not and
|
||||
#if it is external then adds this network to externalnetworks
|
||||
#table of neutron db
|
||||
self._process_l3_create(context, neutron_net, network['network'])
|
||||
except nvsdexception.NVSDAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.nvsdlib.delete_network(net)
|
||||
|
||||
return neutron_net
|
||||
|
||||
def update_network(self, context, net_id, network):
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
neutron_net = super(OneConvergencePluginV2,
|
||||
self).update_network(context, net_id, network)
|
||||
|
||||
self.nvsdlib.update_network(neutron_net, network['network'])
|
||||
# updates neutron database e.g. externalnetworks table.
|
||||
self._process_l3_update(context, neutron_net, network['network'])
|
||||
|
||||
return neutron_net
|
||||
|
||||
def delete_network(self, context, net_id):
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
network = self._get_network(context, net_id)
|
||||
#get all the subnets under the network to delete them
|
||||
subnets = self._get_subnets_by_network(context, net_id)
|
||||
|
||||
super(OneConvergencePluginV2, self).delete_network(context,
|
||||
net_id)
|
||||
|
||||
self.nvsdlib.delete_network(network, subnets)
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
|
||||
if subnet['subnet']['ip_version'] == IPv6:
|
||||
raise nexception.InvalidInput(
|
||||
error_message="NVSDPlugin doesn't support IPv6.")
|
||||
|
||||
neutron_subnet = super(OneConvergencePluginV2,
|
||||
self).create_subnet(context, subnet)
|
||||
|
||||
try:
|
||||
self.nvsdlib.create_subnet(neutron_subnet)
|
||||
except nvsdexception.NVSDAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
#Log the message and delete the subnet from the neutron
|
||||
super(OneConvergencePluginV2,
|
||||
self).delete_subnet(context, neutron_subnet['id'])
|
||||
LOG.error(_("Failed to create subnet, "
|
||||
"deleting it from neutron"))
|
||||
|
||||
return neutron_subnet
|
||||
|
||||
def delete_subnet(self, context, subnet_id):
|
||||
|
||||
neutron_subnet = self._get_subnet(context, subnet_id)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
super(OneConvergencePluginV2, self).delete_subnet(context,
|
||||
subnet_id)
|
||||
|
||||
self.nvsdlib.delete_subnet(neutron_subnet)
|
||||
|
||||
def update_subnet(self, context, subnet_id, subnet):
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
neutron_subnet = super(OneConvergencePluginV2,
|
||||
self).update_subnet(context, subnet_id,
|
||||
subnet)
|
||||
|
||||
self.nvsdlib.update_subnet(neutron_subnet, subnet)
|
||||
return neutron_subnet
|
||||
|
||||
def create_port(self, context, port):
|
||||
|
||||
network = {}
|
||||
|
||||
network_id = port['port']['network_id']
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
# Invoke the Neutron API for creating port
|
||||
neutron_port = super(OneConvergencePluginV2,
|
||||
self).create_port(context, port)
|
||||
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
neutron_port)
|
||||
|
||||
if port['port']['device_owner'] in ('network:router_gateway',
|
||||
'network:floatingip'):
|
||||
# for l3 requests, tenant_id will be None/''
|
||||
network = self._get_network(context, network_id)
|
||||
|
||||
tenant_id = network['tenant_id']
|
||||
else:
|
||||
tenant_id = port['port']['tenant_id']
|
||||
|
||||
port_id = neutron_port['id']
|
||||
|
||||
try:
|
||||
self.nvsdlib.create_port(tenant_id, neutron_port)
|
||||
except nvsdexception.NVSDAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("Deleting newly created "
|
||||
"neutron port %s"), port_id)
|
||||
super(OneConvergencePluginV2, self).delete_port(context,
|
||||
port_id)
|
||||
|
||||
return neutron_port
|
||||
|
||||
def update_port(self, context, port_id, port):
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
neutron_port = super(OneConvergencePluginV2,
|
||||
self).update_port(context, port_id, port)
|
||||
|
||||
if neutron_port['tenant_id'] == '':
|
||||
network = self._get_network(context,
|
||||
neutron_port['network_id'])
|
||||
tenant_id = network['tenant_id']
|
||||
else:
|
||||
tenant_id = neutron_port['tenant_id']
|
||||
|
||||
self.nvsdlib.update_port(tenant_id, neutron_port, port['port'])
|
||||
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
neutron_port)
|
||||
return neutron_port
|
||||
|
||||
def delete_port(self, context, port_id, l3_port_check=True):
|
||||
|
||||
if l3_port_check:
|
||||
self.prevent_l3_port_deletion(context, port_id)
|
||||
|
||||
neutron_port = self._get_port(context, port_id)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
|
||||
super(OneConvergencePluginV2, self).delete_port(context, port_id)
|
||||
|
||||
network = self._get_network(context, neutron_port['network_id'])
|
||||
neutron_port['tenant_id'] = network['tenant_id']
|
||||
|
||||
self.nvsdlib.delete_port(port_id, neutron_port)
|
0
neutron/tests/unit/oneconvergence/__init__.py
Normal file
0
neutron/tests/unit/oneconvergence/__init__.py
Normal file
109
neutron/tests/unit/oneconvergence/test_nvsd_plugin.py
Normal file
109
neutron/tests/unit/oneconvergence/test_nvsd_plugin.py
Normal file
@ -0,0 +1,109 @@
|
||||
# Copyright 2014 OneConvergence, 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.
|
||||
#
|
||||
|
||||
"""Test Library for OneConvergencePlugin."""
|
||||
|
||||
import contextlib
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron import context
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.oneconvergence import plugin as nvsd_plugin
|
||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
PLUGIN_NAME = 'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2'
|
||||
|
||||
|
||||
class OneConvergencePluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
_plugin_name = PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
def mocked_oneconvergence_init(self):
|
||||
def side_effect(*args, **kwargs):
|
||||
return {'id': str(uuid.uuid4())}
|
||||
|
||||
self.nvsdlib = mock.Mock()
|
||||
self.nvsdlib.create_network.side_effect = side_effect
|
||||
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
with mock.patch.object(nvsd_plugin.OneConvergencePluginV2,
|
||||
'oneconvergence_init',
|
||||
new=mocked_oneconvergence_init):
|
||||
super(OneConvergencePluginV2TestCase,
|
||||
self).setUp(self._plugin_name)
|
||||
|
||||
|
||||
class TestOneConvergencePluginNetworksV2(test_plugin.TestNetworksV2,
|
||||
OneConvergencePluginV2TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestOneConvergencePluginSubnetsV2(test_plugin.TestSubnetsV2,
|
||||
OneConvergencePluginV2TestCase):
|
||||
def test_update_subnet_inconsistent_ipv6_gatewayv4(self):
|
||||
self.skipTest("NVSD Plugin does not support IPV6.")
|
||||
|
||||
def test_create_subnet_with_v6_allocation_pool(self):
|
||||
self.skipTest("NVSD Plugin does not support IPV6.")
|
||||
|
||||
def test_update_subnet_inconsistent_ipv6_hostroute_dst_v4(self):
|
||||
self.skipTest("NVSD Plugin does not support IPV6.")
|
||||
|
||||
def test_update_subnet_inconsistent_ipv6_hostroute_np_v4(self):
|
||||
self.skipTest("NVSD Plugin does not support IPV6.")
|
||||
|
||||
|
||||
class TestOneConvergencePluginPortsV2(test_plugin.TestPortsV2,
|
||||
test_bindings.PortBindingsTestCase,
|
||||
OneConvergencePluginV2TestCase):
|
||||
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
||||
|
||||
def test_requested_subnet_id_v4_and_v6(self):
|
||||
self.skipTest("NVSD Plugin does not support IPV6.")
|
||||
|
||||
def test_port_vif_details(self):
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with self.port(name='name') as port1:
|
||||
ctx = context.get_admin_context()
|
||||
port = plugin.get_port(ctx, port1['port']['id'])
|
||||
self.assertEqual(port['binding:vif_type'],
|
||||
portbindings.VIF_TYPE_OVS)
|
||||
|
||||
def test_ports_vif_details(self):
|
||||
cfg.CONF.set_default('allow_overlapping_ips', True)
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with contextlib.nested(self.port(), self.port()) as (port1, port2):
|
||||
ctx = context.get_admin_context()
|
||||
ports = plugin.get_ports(ctx)
|
||||
self.assertEqual(len(ports), 2)
|
||||
for port in ports:
|
||||
self.assertEqual(port['binding:vif_type'],
|
||||
portbindings.VIF_TYPE_OVS)
|
||||
|
||||
|
||||
class TestOneConvergenceBasicGet(test_plugin.TestBasicGet,
|
||||
OneConvergencePluginV2TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestOneConvergenceV2HTTPResponse(test_plugin.TestV2HTTPResponse,
|
||||
OneConvergencePluginV2TestCase):
|
||||
pass
|
186
neutron/tests/unit/oneconvergence/test_nvsdlib.py
Normal file
186
neutron/tests/unit/oneconvergence/test_nvsdlib.py
Normal file
@ -0,0 +1,186 @@
|
||||
# Copyright 2014 OneConvergence, Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.openstack.common import jsonutils as json
|
||||
from neutron.plugins.oneconvergence.lib import nvsdlib
|
||||
from neutron.tests import base
|
||||
|
||||
NETWORKS_URI = "/pluginhandler/ocplugin/tenant/%s/lnetwork/"
|
||||
NETWORK_URI = NETWORKS_URI + "%s"
|
||||
GET_ALL_NETWORKS = "/pluginhandler/ocplugin/tenant/getallnetworks"
|
||||
|
||||
SUBNETS_URI = NETWORK_URI + "/lsubnet/"
|
||||
SUBNET_URI = SUBNETS_URI + "%s"
|
||||
GET_ALL_SUBNETS = "/pluginhandler/ocplugin/tenant/getallsubnets"
|
||||
|
||||
PORTS_URI = NETWORK_URI + "/lport/"
|
||||
PORT_URI = PORTS_URI + "%s"
|
||||
|
||||
TEST_NET = 'test-network'
|
||||
TEST_SUBNET = 'test-subnet'
|
||||
TEST_PORT = 'test-port'
|
||||
TEST_TENANT = 'test-tenant'
|
||||
|
||||
|
||||
class TestNVSDApi(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNVSDApi, self).setUp()
|
||||
self.nvsdlib = nvsdlib.NVSDApi()
|
||||
|
||||
def test_create_network(self):
|
||||
network_obj = {
|
||||
"name": 'test-net',
|
||||
"tenant_id": TEST_TENANT,
|
||||
"shared": False,
|
||||
"admin_state_up": True,
|
||||
"router:external": False
|
||||
}
|
||||
resp = mock.Mock()
|
||||
resp.json.return_value = {'id': 'uuid'}
|
||||
with mock.patch.object(self.nvsdlib, 'send_request',
|
||||
return_value=resp) as send_request:
|
||||
uri = NETWORKS_URI % TEST_TENANT
|
||||
net = self.nvsdlib.create_network(network_obj)
|
||||
send_request.assert_called_once_with("POST", uri,
|
||||
body=json.dumps(network_obj),
|
||||
resource='network',
|
||||
tenant_id=TEST_TENANT)
|
||||
self.assertEqual(net, {'id': 'uuid'})
|
||||
|
||||
def test_update_network(self):
|
||||
network = {'id': TEST_NET,
|
||||
'tenant_id': TEST_TENANT}
|
||||
update_network = {'name': 'new_name'}
|
||||
uri = NETWORK_URI % (TEST_TENANT, TEST_NET)
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.update_network(network, update_network)
|
||||
send_request.assert_called_once_with(
|
||||
"PUT", uri, body=json.dumps(update_network),
|
||||
resource='network', tenant_id=TEST_TENANT,
|
||||
resource_id=TEST_NET)
|
||||
|
||||
def test_delete_network(self):
|
||||
network = {'id': TEST_NET,
|
||||
'tenant_id': TEST_TENANT}
|
||||
|
||||
uri = NETWORK_URI % (TEST_TENANT, TEST_NET)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
with mock.patch.object(self.nvsdlib, '_get_ports'):
|
||||
self.nvsdlib.delete_network(network)
|
||||
send_request.assert_called_once_with(
|
||||
"DELETE", uri, resource='network',
|
||||
tenant_id=TEST_TENANT, resource_id=TEST_NET)
|
||||
|
||||
def test_create_port(self):
|
||||
path = PORTS_URI % (TEST_TENANT, TEST_NET)
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
fixed_ips = [{'ip_address': '10.0.0.2',
|
||||
'subnet_id': TEST_SUBNET}]
|
||||
|
||||
lport = {
|
||||
"id": TEST_PORT,
|
||||
"name": 'test',
|
||||
"device_id": "device_id",
|
||||
"device_owner": "device_owner",
|
||||
"mac_address": "mac_address",
|
||||
"fixed_ips": fixed_ips,
|
||||
"admin_state_up": True,
|
||||
"network_id": TEST_NET,
|
||||
"status": 'ACTIVE'
|
||||
}
|
||||
self.nvsdlib.create_port(TEST_TENANT, lport)
|
||||
expected = {"id": TEST_PORT, "name": 'test',
|
||||
"device_id": "device_id",
|
||||
"device_owner": "device_owner",
|
||||
"mac_address": "mac_address",
|
||||
"ip_address": '10.0.0.2',
|
||||
"subnet_id": TEST_SUBNET,
|
||||
"admin_state_up": True,
|
||||
"network_id": TEST_NET,
|
||||
"status": 'ACTIVE'}
|
||||
send_request.assert_called_once_with("POST", path,
|
||||
body=json.dumps(expected),
|
||||
resource='port',
|
||||
tenant_id=TEST_TENANT)
|
||||
|
||||
def test_update_port(self):
|
||||
port = {'id': TEST_PORT,
|
||||
'network_id': TEST_NET}
|
||||
|
||||
port_update = {'name': 'new-name'}
|
||||
uri = PORT_URI % (TEST_TENANT, TEST_NET, TEST_PORT)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.update_port(TEST_TENANT, port, port_update)
|
||||
send_request.assert_called_once_with("PUT", uri,
|
||||
body=json.dumps(port_update),
|
||||
resource='port',
|
||||
resource_id='test-port',
|
||||
tenant_id=TEST_TENANT)
|
||||
|
||||
def test_delete_port(self):
|
||||
port = {'network_id': TEST_NET,
|
||||
'tenant_id': TEST_TENANT}
|
||||
uri = PORT_URI % (TEST_TENANT, TEST_NET, TEST_PORT)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.delete_port(TEST_PORT, port)
|
||||
send_request.assert_called_once_with("DELETE", uri,
|
||||
resource='port',
|
||||
tenant_id=TEST_TENANT,
|
||||
resource_id=TEST_PORT)
|
||||
|
||||
def test_create_subnet(self):
|
||||
subnet = {'id': TEST_SUBNET,
|
||||
'tenant_id': TEST_TENANT,
|
||||
'network_id': TEST_NET}
|
||||
uri = SUBNETS_URI % (TEST_TENANT, TEST_NET)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.create_subnet(subnet)
|
||||
send_request.assert_called_once_with("POST", uri,
|
||||
body=json.dumps(subnet),
|
||||
resource='subnet',
|
||||
tenant_id=TEST_TENANT)
|
||||
|
||||
def test_update_subnet(self):
|
||||
subnet = {'id': TEST_SUBNET,
|
||||
'tenant_id': TEST_TENANT,
|
||||
'network_id': TEST_NET}
|
||||
subnet_update = {'name': 'new-name'}
|
||||
uri = SUBNET_URI % (TEST_TENANT, TEST_NET, TEST_SUBNET)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.update_subnet(subnet, subnet_update)
|
||||
send_request.assert_called_once_with(
|
||||
"PUT", uri, body=json.dumps(subnet_update), resource='subnet',
|
||||
tenant_id=TEST_TENANT, resource_id=TEST_SUBNET)
|
||||
|
||||
def test_delete_subnet(self):
|
||||
subnet = {'id': TEST_SUBNET,
|
||||
'tenant_id': TEST_TENANT,
|
||||
'network_id': TEST_NET}
|
||||
uri = SUBNET_URI % (TEST_TENANT, TEST_NET, TEST_SUBNET)
|
||||
|
||||
with mock.patch.object(self.nvsdlib, 'send_request') as send_request:
|
||||
self.nvsdlib.delete_subnet(subnet)
|
||||
send_request.assert_called_once_with("DELETE", uri,
|
||||
resource='subnet',
|
||||
tenant_id=TEST_TENANT,
|
||||
resource_id=TEST_SUBNET)
|
60
neutron/tests/unit/oneconvergence/test_plugin_helper.py
Normal file
60
neutron/tests/unit/oneconvergence/test_plugin_helper.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright 2014 OneConvergence, 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: Kedar Kulkarni, One Convergence, Inc.
|
||||
import mock
|
||||
import requests
|
||||
|
||||
from neutron.openstack.common import jsonutils as json
|
||||
from neutron.plugins.oneconvergence.lib import config # noqa
|
||||
from neutron.plugins.oneconvergence.lib import plugin_helper as client
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class TestPluginHelper(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestPluginHelper, self).setUp()
|
||||
self.nvsdcontroller = client.NVSDController()
|
||||
|
||||
def get_response(self, *args, **kwargs):
|
||||
response = mock.Mock()
|
||||
response.status_code = requests.codes.ok
|
||||
response.content = json.dumps({'session_uuid': 'new_auth_token'})
|
||||
return response
|
||||
|
||||
def test_login(self):
|
||||
login_url = ('http://127.0.0.1:8082/pluginhandler/ocplugin/'
|
||||
'authmgmt/login')
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
data = json.dumps({"user_name": "ocplugin", "passwd": "oc123"})
|
||||
timeout = 30.0
|
||||
|
||||
with mock.patch.object(self.nvsdcontroller, 'do_request',
|
||||
side_effect=self.get_response) as do_request:
|
||||
self.nvsdcontroller.login()
|
||||
do_request.assert_called_once_with('POST', url=login_url,
|
||||
headers=headers, data=data,
|
||||
timeout=timeout)
|
||||
|
||||
def test_request(self):
|
||||
with mock.patch.object(self.nvsdcontroller, 'do_request',
|
||||
side_effect=self.get_response) as do_request:
|
||||
self.nvsdcontroller.login()
|
||||
self.nvsdcontroller.request("POST", "/some_url")
|
||||
self.assertEqual(do_request.call_count, 2)
|
||||
do_request.assert_called_with(
|
||||
'POST',
|
||||
url='http://127.0.0.1:8082/some_url?authToken=new_auth_token',
|
||||
headers={'Content-Type': 'application/json'}, data='',
|
||||
timeout=30.0)
|
@ -68,6 +68,7 @@ data_files =
|
||||
etc/neutron/plugins/mlnx = etc/neutron/plugins/mlnx/mlnx_conf.ini
|
||||
etc/neutron/plugins/nec = etc/neutron/plugins/nec/nec.ini
|
||||
etc/neutron/plugins/nicira = etc/neutron/plugins/nicira/nvp.ini
|
||||
etc/neutron/plugins/oneconvergence = etc/neutron/plugins/oneconvergence/nvsdplugin.ini
|
||||
etc/neutron/plugins/openvswitch = etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini
|
||||
etc/neutron/plugins/plumgrid = etc/neutron/plugins/plumgrid/plumgrid.ini
|
||||
etc/neutron/plugins/ryu = etc/neutron/plugins/ryu/ryu.ini
|
||||
@ -144,6 +145,7 @@ neutron.core_plugins =
|
||||
mlnx = neutron.plugins.mlnx.mlnx_plugin:MellanoxEswitchPlugin
|
||||
nec = neutron.plugins.nec.nec_plugin:NECPluginV2
|
||||
nicira = neutron.plugins.nicira.NeutronPlugin:NvpPluginV2
|
||||
oneconvergence = neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2
|
||||
openvswitch = neutron.plugins.openvswitch.ovs_neutron_plugin:OVSNeutronPluginV2
|
||||
plumgrid = neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin:NeutronPluginPLUMgridV2
|
||||
ryu = neutron.plugins.ryu.ryu_neutron_plugin:RyuNeutronPluginV2
|
||||
|
Loading…
Reference in New Issue
Block a user