Merge "One Convergence Neutron Plugin Implementation"
This commit is contained in:
commit
005fec677c
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)
|
@ -69,6 +69,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
|
||||
@ -145,6 +146,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