Merge "Removing the SDN-VE monolithic plugin"

This commit is contained in:
Jenkins 2015-08-29 23:56:31 +00:00 committed by Gerrit Code Review
commit 61ab2ea304
17 changed files with 0 additions and 1950 deletions

@ -1,50 +0,0 @@
[sdnve]
# (ListOpt) The IP address of one (or more) SDN-VE controllers
# Default value is: controller_ips = 127.0.0.1
# Example: controller_ips = 127.0.0.1,127.0.0.2
# (StrOpt) The integration bridge for OF based implementation
# The default value for integration_bridge is None
# Example: integration_bridge = br-int
# (ListOpt) The interface mapping connecting the integration
# bridge to external network as a list of physical network names and
# interfaces: <physical_network_name>:<interface_name>
# Example: interface_mappings = default:eth2
# (BoolOpt) Used to reset the integration bridge, if exists
# The default value for reset_bridge is True
# Example: reset_bridge = False
# (BoolOpt) Used to set the OVS controller as out-of-band
# The default value for out_of_band is True
# Example: out_of_band = False
#
# (BoolOpt) The fake controller for testing purposes
# Default value is: use_fake_controller = False
# (StrOpt) The port number for use with controller
# The default value for the port is 8443
# Example: port = 8443
# (StrOpt) The userid for use with controller
# The default value for the userid is admin
# Example: userid = sdnve_user
# (StrOpt) The password for use with controller
# The default value for the password is admin
# Example: password = sdnve_password
#
# (StrOpt) The default type of tenants (and associated resources)
# Available choices are: OVERLAY or OF
# The default value for tenant type is OVERLAY
# Example: default_tenant_type = OVERLAY
# (StrOpt) The string in tenant description that indicates
# Default value for OF tenants: of_signature = SDNVE-OF
# (StrOpt) The string in tenant description that indicates
# Default value for OVERLAY tenants: overlay_signature = SDNVE-OVERLAY
[sdnve_agent]
# (IntOpt) Agent's polling interval in seconds
# polling_interval = 2
# (StrOpt) What to use for root helper
# The default value: root_helper = 'sudo'
# (BoolOpt) Whether to use rpc or not
# The default value: rpc = True
[securitygroup]
# The security group is not supported:
# firewall_driver = neutron.agent.firewall.NoopFirewallDriver

@ -1,6 +0,0 @@
IBM SDN-VE Neutron Plugin
This plugin implements Neutron v2 APIs.
For more details on how to use it please refer to the following page:
http://wiki.openstack.org/wiki/IBM-Neutron

@ -1,265 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import socket
import sys
import time
import eventlet
eventlet.monkey_patch()
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_service import loopingcall
import six
from neutron.agent.common import ovs_lib
from neutron.agent.linux import ip_lib
from neutron.agent import rpc as agent_rpc
from neutron.common import config as common_config
from neutron.common import constants as n_const
from neutron.common import topics
from neutron.common import utils as n_utils
from neutron.i18n import _LE, _LI
from neutron import context
from neutron.plugins.ibm.common import constants
LOG = logging.getLogger(__name__)
cfg.CONF.import_group('SDNVE', 'neutron.plugins.ibm.common.config')
cfg.CONF.import_group('SDNVE_AGENT', 'neutron.plugins.ibm.common.config')
AGENT_TYPE_SDNVE = 'IBM SDN-VE agent'
class SdnvePluginApi(agent_rpc.PluginApi):
def sdnve_info(self, context, info):
cctxt = self.client.prepare()
return cctxt.call(context, 'sdnve_info', info=info)
class SdnveNeutronAgent(object):
target = oslo_messaging.Target(version='1.1')
def __init__(self, integ_br, interface_mappings,
info, polling_interval,
controller_ip, reset_br, out_of_band):
'''The agent initialization.
Sets the following parameters and sets up the integration
bridge and physical interfaces if need be.
:param integ_br: name of the integration bridge.
:param interface_mappings: interfaces to physical networks.
:param info: local IP address of this hypervisor.
:param polling_interval: interval (secs) to poll DB.
:param controller_ip: Ip address of SDN-VE controller.
'''
super(SdnveNeutronAgent, self).__init__()
self.int_bridge_name = integ_br
self.controller_ip = controller_ip
self.interface_mappings = interface_mappings
self.polling_interval = polling_interval
self.info = info
self.reset_br = reset_br
self.out_of_band = out_of_band
self.agent_state = {
'binary': 'neutron-sdnve-agent',
'host': cfg.CONF.host,
'topic': n_const.L2_AGENT_TOPIC,
'configurations': {'interface_mappings': interface_mappings,
'reset_br': self.reset_br,
'out_of_band': self.out_of_band,
'controller_ip': self.controller_ip},
'agent_type': AGENT_TYPE_SDNVE,
'start_flag': True}
if self.int_bridge_name:
self.int_br = self.setup_integration_br(integ_br, reset_br,
out_of_band,
self.controller_ip)
self.setup_physical_interfaces(self.interface_mappings)
else:
self.int_br = None
self.setup_rpc()
def _report_state(self):
try:
self.state_rpc.report_state(self.context,
self.agent_state)
self.agent_state.pop('start_flag', None)
except Exception:
LOG.exception(_LE("Failed reporting state!"))
def setup_rpc(self):
if self.int_br:
mac = self.int_br.get_local_port_mac()
self.agent_id = '%s%s' % ('sdnve', (mac.replace(":", "")))
else:
nameaddr = socket.gethostbyname(socket.gethostname())
self.agent_id = '%s%s' % ('sdnve_', (nameaddr.replace(".", "_")))
self.topic = topics.AGENT
self.plugin_rpc = SdnvePluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
self.context = context.get_admin_context_without_session()
self.endpoints = [self]
consumers = [[constants.INFO, topics.UPDATE]]
self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic,
consumers)
if self.polling_interval:
heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
heartbeat.start(interval=self.polling_interval)
# Plugin calls the agents through the following
def info_update(self, context, **kwargs):
LOG.debug("info_update received")
info = kwargs.get('info', {})
new_controller = info.get('new_controller')
out_of_band = info.get('out_of_band')
if self.int_br and new_controller:
LOG.debug("info_update received. New controller "
"is to be set to: %s", new_controller)
self.int_br.set_controller(["tcp:" + new_controller])
if out_of_band:
LOG.debug("info_update received. New controller "
"is set to be out of band")
self.int_br.set_db_attribute("Controller",
self.int_bridge_name,
"connection-mode",
"out-of-band")
def setup_integration_br(self, bridge_name, reset_br, out_of_band,
controller_ip=None):
'''Sets up the integration bridge.
Create the bridge and remove all existing flows if reset_br is True.
Otherwise, creates the bridge if not already existing.
:param bridge_name: the name of the integration bridge.
:param reset_br: A boolean to rest the bridge if True.
:param out_of_band: A boolean indicating controller is out of band.
:param controller_ip: IP address to use as the bridge controller.
:returns: the integration bridge
'''
int_br = ovs_lib.OVSBridge(bridge_name)
if reset_br:
int_br.reset_bridge()
int_br.remove_all_flows()
else:
int_br.create()
# set the controller
if controller_ip:
int_br.set_controller(["tcp:" + controller_ip])
if out_of_band:
int_br.set_db_attribute("Controller", bridge_name,
"connection-mode", "out-of-band")
return int_br
def setup_physical_interfaces(self, interface_mappings):
'''Sets up the physical network interfaces.
Link physical interfaces to the integration bridge.
:param interface_mappings: map physical net names to interface names.
'''
for physical_network, interface in six.iteritems(interface_mappings):
LOG.info(_LI("Mapping physical network %(physical_network)s to "
"interface %(interface)s"),
{'physical_network': physical_network,
'interface': interface})
# Connect the physical interface to the bridge
if not ip_lib.device_exists(interface):
LOG.error(_LE("Interface %(interface)s for physical network "
"%(physical_network)s does not exist. Agent "
"terminated!"),
{'physical_network': physical_network,
'interface': interface})
raise SystemExit(1)
self.int_br.add_port(interface)
def sdnve_info(self):
details = self.plugin_rpc.sdnve_info(
self.context,
{'info': self.info})
return details
def rpc_loop(self):
while True:
start = time.time()
LOG.debug("Agent in the rpc loop.")
# sleep till end of polling interval
elapsed = (time.time() - start)
if (elapsed < self.polling_interval):
time.sleep(self.polling_interval - elapsed)
else:
LOG.info(_LI("Loop iteration exceeded interval "
"(%(polling_interval)s vs. %(elapsed)s)!"),
{'polling_interval': self.polling_interval,
'elapsed': elapsed})
def daemon_loop(self):
self.rpc_loop()
def create_agent_config_map(config):
interface_mappings = n_utils.parse_mappings(
config.SDNVE.interface_mappings)
controller_ips = config.SDNVE.controller_ips
LOG.info(_LI("Controller IPs: %s"), controller_ips)
controller_ip = controller_ips[0]
return {
'integ_br': config.SDNVE.integration_bridge,
'interface_mappings': interface_mappings,
'controller_ip': controller_ip,
'info': config.SDNVE.info,
'polling_interval': config.SDNVE_AGENT.polling_interval,
'reset_br': config.SDNVE.reset_bridge,
'out_of_band': config.SDNVE.out_of_band}
def main():
cfg.CONF.register_opts(ip_lib.OPTS)
common_config.init(sys.argv[1:])
common_config.setup_logging()
try:
agent_config = create_agent_config_map(cfg.CONF)
except ValueError as e:
LOG.exception(_LE("%s Agent terminated!"), e)
raise SystemExit(1)
plugin = SdnveNeutronAgent(**agent_config)
# Start everything.
LOG.info(_LI("Agent initialized successfully, now running... "))
plugin.daemon_loop()

@ -1,71 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
DEFAULT_INTERFACE_MAPPINGS = []
DEFAULT_CONTROLLER_IPS = ['127.0.0.1']
sdnve_opts = [
cfg.BoolOpt('use_fake_controller', default=False,
help=_("Whether to use a fake controller.")),
cfg.StrOpt('base_url', default='/one/nb/v2/',
help=_("Base URL for SDN-VE controller REST API.")),
cfg.ListOpt('controller_ips', default=DEFAULT_CONTROLLER_IPS,
help=_("List of IP addresses of SDN-VE controller(s).")),
cfg.StrOpt('info', default='sdnve_info_string',
help=_("SDN-VE RPC subject.")),
cfg.StrOpt('port', default='8443',
help=_("SDN-VE controller port number.")),
cfg.StrOpt('format', default='json',
help=_("SDN-VE request/response format.")),
cfg.StrOpt('userid', default='admin',
help=_("SDN-VE administrator user ID.")),
cfg.StrOpt('password', default='admin', secret=True,
help=_("SDN-VE administrator password.")),
cfg.StrOpt('integration_bridge',
help=_("Integration bridge to use.")),
cfg.BoolOpt('reset_bridge', default=True,
help=_("Whether to reset the integration bridge before use.")),
cfg.BoolOpt('out_of_band', default=True,
help=_("Indicating if controller is out of band or not.")),
cfg.ListOpt('interface_mappings',
default=DEFAULT_INTERFACE_MAPPINGS,
help=_("List of <physical_network_name>:<interface_name> "
"mappings.")),
cfg.StrOpt('default_tenant_type', default='OVERLAY',
help=_("Tenant type: OVERLAY (default) or OF.")),
cfg.StrOpt('overlay_signature', default='SDNVE-OVERLAY',
help=_("The string in tenant description that indicates "
"the tenant is a OVERLAY tenant.")),
cfg.StrOpt('of_signature', default='SDNVE-OF',
help=_("The string in tenant description that indicates "
"the tenant is a OF tenant.")),
]
sdnve_agent_opts = [
cfg.IntOpt('polling_interval', default=2,
help=_("Agent polling interval if necessary.")),
cfg.BoolOpt('rpc', default=True,
help=_("Whether to use rpc.")),
]
cfg.CONF.register_opts(sdnve_opts, "SDNVE")
cfg.CONF.register_opts(sdnve_agent_opts, "SDNVE_AGENT")

@ -1,30 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from six.moves import http_client as httplib
# Topic for info notifications between the plugin and agent
INFO = 'info'
TENANT_TYPE_OF = 'OF'
TENANT_TYPE_OVERLAY = 'OVERLAY'
HTTP_ACCEPTABLE = [httplib.OK,
httplib.CREATED,
httplib.ACCEPTED,
httplib.NO_CONTENT
]

@ -1,26 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import exceptions
class SdnveException(exceptions.NeutronException):
message = _("An unexpected error occurred in the SDN-VE Plugin. "
"Here is the error message: %(msg)s")
class BadInputException(exceptions.BadRequest):
message = _("The input does not contain nececessary info: %(msg)s")

@ -1,387 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from six.moves import http_client as httplib
import httplib2
from keystoneclient.v2_0 import client as keyclient
from oslo_config import cfg
from oslo_log import log as logging
from six.moves.urllib import parse
from neutron.api.v2 import attributes
from neutron.common import utils
from neutron.i18n import _LE, _LI
from neutron.plugins.ibm.common import config # noqa
from neutron.plugins.ibm.common import constants
from neutron import wsgi
LOG = logging.getLogger(__name__)
SDNVE_VERSION = '2.0'
SDNVE_ACTION_PREFIX = '/sdnve'
SDNVE_RETRIES = 0
SDNVE_RETRIY_INTERVAL = 1
SDNVE_TENANT_TYPE_OVERLAY = u'DOVE'
SDNVE_URL = 'https://%s:%s%s'
class RequestHandler(object):
'''Handles processing requests to and responses from controller.'''
def __init__(self, controller_ips=None, port=None, ssl=None,
base_url=None, userid=None, password=None,
timeout=10, formats=None):
'''Initializes the RequestHandler for communication with controller
Following keyword arguments are used; if not specified, default
values are used.
:param port: Username for authentication.
:param timeout: Time out for http requests.
:param userid: User id for accessing controller.
:param password: Password for accessing the controller.
:param base_url: The base url for the controller.
:param controller_ips: List of controller IP addresses.
:param formats: Supported formats.
'''
self.port = port or cfg.CONF.SDNVE.port
self.timeout = timeout
self._s_meta = None
self.connection = None
self.httpclient = httplib2.Http(
disable_ssl_certificate_validation=True)
self.cookie = None
userid = userid or cfg.CONF.SDNVE.userid
password = password or cfg.CONF.SDNVE.password
if (userid and password):
self.httpclient.add_credentials(userid, password)
self.base_url = base_url or cfg.CONF.SDNVE.base_url
self.controller_ips = controller_ips or cfg.CONF.SDNVE.controller_ips
LOG.info(_LI("The IP addr of available SDN-VE controllers: %s"),
self.controller_ips)
self.controller_ip = self.controller_ips[0]
LOG.info(_LI("The SDN-VE controller IP address: %s"),
self.controller_ip)
self.new_controller = False
self.format = formats or cfg.CONF.SDNVE.format
self.version = SDNVE_VERSION
self.action_prefix = SDNVE_ACTION_PREFIX
self.retries = SDNVE_RETRIES
self.retry_interval = SDNVE_RETRIY_INTERVAL
def serialize(self, data):
'''Serializes a dictionary with a single key.'''
if isinstance(data, dict):
return wsgi.Serializer().serialize(data, self.content_type())
elif data:
raise TypeError(_("unable to serialize object type: '%s'") %
type(data))
def deserialize(self, data, status_code):
'''Deserializes an xml or json string into a dictionary.'''
# NOTE(mb): Temporary fix for backend controller requirement
data = data.replace("router_external", "router:external")
if status_code == httplib.NO_CONTENT:
return data
try:
deserialized_data = wsgi.Serializer(
metadata=self._s_meta).deserialize(data, self.content_type())
deserialized_data = deserialized_data['body']
except Exception:
deserialized_data = data
return deserialized_data
def content_type(self, format=None):
'''Returns the mime-type for either 'xml' or 'json'.'''
return 'application/%s' % (format or self.format)
def delete(self, url, body=None, headers=None, params=None):
return self.do_request("DELETE", url, body=body,
headers=headers, params=params)
def get(self, url, body=None, headers=None, params=None):
return self.do_request("GET", url, body=body,
headers=headers, params=params)
def post(self, url, body=None, headers=None, params=None):
return self.do_request("POST", url, body=body,
headers=headers, params=params)
def put(self, url, body=None, headers=None, params=None):
return self.do_request("PUT", url, body=body,
headers=headers, params=params)
def do_request(self, method, url, body=None, headers=None,
params=None, connection_type=None):
status_code = -1
replybody_deserialized = ''
if body:
body = self.serialize(body)
self.headers = headers or {'Content-Type': self.content_type()}
if self.cookie:
self.headers['cookie'] = self.cookie
if self.controller_ip != self.controller_ips[0]:
controllers = [self.controller_ip]
else:
controllers = []
controllers.extend(self.controller_ips)
for controller_ip in controllers:
serverurl = SDNVE_URL % (controller_ip, self.port, self.base_url)
myurl = serverurl + url
if params and isinstance(params, dict):
myurl += '?' + parse.urlencode(params, doseq=1)
try:
LOG.debug("Sending request to SDN-VE. url: "
"%(myurl)s method: %(method)s body: "
"%(body)s header: %(header)s ",
{'myurl': myurl, 'method': method,
'body': body, 'header': self.headers})
resp, replybody = self.httpclient.request(
myurl, method=method, body=body, headers=self.headers)
LOG.debug("Response recd from SDN-VE. resp: %(resp)s "
"body: %(body)s",
{'resp': resp.status, 'body': replybody})
status_code = resp.status
except Exception as e:
LOG.error(_LE("Error: Could not reach server: %(url)s "
"Exception: %(excp)s."),
{'url': myurl, 'excp': e})
self.cookie = None
continue
if status_code not in constants.HTTP_ACCEPTABLE:
LOG.debug("Error message: %(reply)s -- Status: %(status)s",
{'reply': replybody, 'status': status_code})
else:
LOG.debug("Received response status: %s", status_code)
if resp.get('set-cookie'):
self.cookie = resp['set-cookie']
replybody_deserialized = self.deserialize(
replybody,
status_code)
LOG.debug("Deserialized body: %s", replybody_deserialized)
if controller_ip != self.controller_ip:
# bcast the change of controller
self.new_controller = True
self.controller_ip = controller_ip
return (status_code, replybody_deserialized)
return (httplib.REQUEST_TIMEOUT, 'Could not reach server(s)')
class Client(RequestHandler):
'''Client for SDNVE controller.'''
def __init__(self):
'''Initialize a new SDNVE client.'''
super(Client, self).__init__()
self.keystoneclient = KeystoneClient()
resource_path = {
'network': "ln/networks/",
'subnet': "ln/subnets/",
'port': "ln/ports/",
'tenant': "ln/tenants/",
'router': "ln/routers/",
'floatingip': "ln/floatingips/",
}
def process_request(self, body):
'''Processes requests according to requirements of controller.'''
if self.format == 'json':
body = dict(
(k.replace(':', '_'), v) for k, v in body.items()
if attributes.is_attr_set(v))
return body
def sdnve_list(self, resource, **params):
'''Fetches a list of resources.'''
res = self.resource_path.get(resource, None)
if not res:
LOG.info(_LI("Bad resource for forming a list request"))
return 0, ''
return self.get(res, params=params)
def sdnve_show(self, resource, specific, **params):
'''Fetches information of a certain resource.'''
res = self.resource_path.get(resource, None)
if not res:
LOG.info(_LI("Bad resource for forming a show request"))
return 0, ''
return self.get(res + specific, params=params)
def sdnve_create(self, resource, body):
'''Creates a new resource.'''
res = self.resource_path.get(resource, None)
if not res:
LOG.info(_LI("Bad resource for forming a create request"))
return 0, ''
body = self.process_request(body)
status, data = self.post(res, body=body)
return (status, data)
def sdnve_update(self, resource, specific, body=None):
'''Updates a resource.'''
res = self.resource_path.get(resource, None)
if not res:
LOG.info(_LI("Bad resource for forming a update request"))
return 0, ''
body = self.process_request(body)
return self.put(res + specific, body=body)
def sdnve_delete(self, resource, specific):
'''Deletes the specified resource.'''
res = self.resource_path.get(resource, None)
if not res:
LOG.info(_LI("Bad resource for forming a delete request"))
return 0, ''
return self.delete(res + specific)
def _tenant_id_conversion(self, osid):
return osid
def sdnve_get_tenant_byid(self, os_tenant_id):
sdnve_tenant_id = self._tenant_id_conversion(os_tenant_id)
resp, content = self.sdnve_show('tenant', sdnve_tenant_id)
if resp in constants.HTTP_ACCEPTABLE:
tenant_id = content.get('id')
tenant_type = content.get('network_type')
if tenant_type == SDNVE_TENANT_TYPE_OVERLAY:
tenant_type = constants.TENANT_TYPE_OVERLAY
return tenant_id, tenant_type
return None, None
def sdnve_check_and_create_tenant(self, os_tenant_id, network_type=None):
if not os_tenant_id:
return
tenant_id, tenant_type = self.sdnve_get_tenant_byid(os_tenant_id)
if tenant_id:
if not network_type:
return tenant_id
if tenant_type != network_type:
LOG.info(_LI("Non matching tenant and network types: "
"%(ttype)s %(ntype)s"),
{'ttype': tenant_type, 'ntype': network_type})
return
return tenant_id
# Have to create a new tenant
sdnve_tenant_id = self._tenant_id_conversion(os_tenant_id)
if not network_type:
network_type = self.keystoneclient.get_tenant_type(os_tenant_id)
if network_type == constants.TENANT_TYPE_OVERLAY:
network_type = SDNVE_TENANT_TYPE_OVERLAY
pinn_desc = ("Created by SDN-VE Neutron Plugin, OS project name = " +
self.keystoneclient.get_tenant_name(os_tenant_id))
res, content = self.sdnve_create('tenant',
{'id': sdnve_tenant_id,
'name': os_tenant_id,
'network_type': network_type,
'description': pinn_desc})
if res not in constants.HTTP_ACCEPTABLE:
return
return sdnve_tenant_id
def sdnve_get_controller(self):
if self.new_controller:
self.new_controller = False
return self.controller_ip
class KeystoneClient(object):
def __init__(self, username=None, tenant_name=None, password=None,
auth_url=None):
keystone_conf = cfg.CONF.keystone_authtoken
username = username or keystone_conf.admin_user
tenant_name = tenant_name or keystone_conf.admin_tenant_name
password = password or keystone_conf.admin_password
# FIXME(ihrachys): plugins should not construct keystone URL
# from configuration file and should instead rely on service
# catalog contents
auth_url = auth_url or utils.get_keystone_url(keystone_conf)
self.overlay_signature = cfg.CONF.SDNVE.overlay_signature
self.of_signature = cfg.CONF.SDNVE.of_signature
self.default_tenant_type = cfg.CONF.SDNVE.default_tenant_type
self.client = keyclient.Client(username=username,
password=password,
tenant_name=tenant_name,
auth_url=auth_url)
def get_tenant_byid(self, id):
try:
return self.client.tenants.get(id)
except Exception:
LOG.exception(_LE("Did not find tenant: %r"), id)
def get_tenant_type(self, id):
tenant = self.get_tenant_byid(id)
if tenant:
description = tenant.description
if description:
if (description.find(self.overlay_signature) >= 0):
return constants.TENANT_TYPE_OVERLAY
if (description.find(self.of_signature) >= 0):
return constants.TENANT_TYPE_OF
return self.default_tenant_type
def get_tenant_name(self, id):
tenant = self.get_tenant_byid(id)
if tenant:
return tenant.name
return 'not found'

@ -1,64 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron.i18n import _LI
from neutron.plugins.ibm.common import constants
LOG = logging.getLogger(__name__)
HTTP_OK = 200
class FakeClient(object):
'''Fake Client for SDNVE controller.'''
def __init__(self, **kwargs):
LOG.info(_LI('Fake SDNVE controller initialized'))
def sdnve_list(self, resource, **_params):
LOG.info(_LI('Fake SDNVE controller: list'))
return (HTTP_OK, None)
def sdnve_show(self, resource, specific, **_params):
LOG.info(_LI('Fake SDNVE controller: show'))
return (HTTP_OK, None)
def sdnve_create(self, resource, body):
LOG.info(_LI('Fake SDNVE controller: create'))
return (HTTP_OK, None)
def sdnve_update(self, resource, specific, body=None):
LOG.info(_LI('Fake SDNVE controller: update'))
return (HTTP_OK, None)
def sdnve_delete(self, resource, specific):
LOG.info(_LI('Fake SDNVE controller: delete'))
return (HTTP_OK, None)
def sdnve_get_tenant_byid(self, id):
LOG.info(_LI('Fake SDNVE controller: get tenant by id'))
return id, constants.TENANT_TYPE_OF
def sdnve_check_and_create_tenant(self, id, network_type=None):
LOG.info(_LI('Fake SDNVE controller: check and create tenant'))
return id
def sdnve_get_controller(self):
LOG.info(_LI('Fake SDNVE controller: get controller'))
return None

@ -1,678 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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 functools
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_utils import excutils
from neutron.common import constants as n_const
from neutron.common import exceptions as n_exc
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron.db import agents_db
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import l3_gwmode_db
from neutron.db import portbindings_db
from neutron.extensions import portbindings
from neutron.i18n import _LE, _LI, _LW
from neutron.plugins.ibm.common import config # noqa
from neutron.plugins.ibm.common import constants
from neutron.plugins.ibm.common import exceptions as sdnve_exc
from neutron.plugins.ibm import sdnve_api as sdnve
from neutron.plugins.ibm import sdnve_api_fake as sdnve_fake
LOG = logging.getLogger(__name__)
class SdnveRpcCallbacks(object):
def __init__(self, notifier):
self.notifier = notifier # used to notify the agent
def sdnve_info(self, rpc_context, **kwargs):
'''Update new information.'''
info = kwargs.get('info')
# Notify all other listening agents
self.notifier.info_update(rpc_context, info)
return info
class AgentNotifierApi(object):
'''Agent side of the SDN-VE rpc API.'''
def __init__(self, topic):
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
self.topic_info_update = topics.get_topic_name(topic,
constants.INFO,
topics.UPDATE)
def info_update(self, context, info):
cctxt = self.client.prepare(topic=self.topic_info_update, fanout=True)
cctxt.cast(context, 'info_update', info=info)
def _ha(func):
'''Supports the high availability feature of the controller.'''
@functools.wraps(func)
def hawrapper(self, *args, **kwargs):
'''This wrapper sets the new controller if necessary
When a controller is detected to be not responding, and a
new controller is chosen to be used in its place, this decorator
makes sure the existing integration bridges are set to point
to the new controller by calling the set_controller method.
'''
ret_func = func(self, *args, **kwargs)
self.set_controller(args[0])
return ret_func
return hawrapper
class SdnvePluginV2(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin,
portbindings_db.PortBindingMixin,
l3_gwmode_db.L3_NAT_db_mixin,
agents_db.AgentDbMixin,
):
'''
Implement the Neutron abstractions using SDN-VE SDN Controller.
'''
__native_bulk_support = False
__native_pagination_support = False
__native_sorting_support = False
supported_extension_aliases = ["binding", "router", "external-net",
"agent", "quotas"]
def __init__(self, configfile=None):
self.base_binding_dict = {
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
portbindings.VIF_DETAILS: {portbindings.CAP_PORT_FILTER: False}}
super(SdnvePluginV2, self).__init__()
self.setup_rpc()
self.sdnve_controller_select()
if self.fake_controller:
self.sdnve_client = sdnve_fake.FakeClient()
else:
self.sdnve_client = sdnve.Client()
def sdnve_controller_select(self):
self.fake_controller = cfg.CONF.SDNVE.use_fake_controller
def setup_rpc(self):
# RPC support
self.topic = topics.PLUGIN
self.conn = n_rpc.create_connection(new=True)
self.notifier = AgentNotifierApi(topics.AGENT)
self.endpoints = [SdnveRpcCallbacks(self.notifier),
agents_db.AgentExtRpcCallback()]
self.conn.create_consumer(self.topic, self.endpoints,
fanout=False)
# Consume from all consumers in threads
self.conn.consume_in_threads()
def _update_base_binding_dict(self, tenant_type):
if tenant_type == constants.TENANT_TYPE_OVERLAY:
self.base_binding_dict[
portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE
if tenant_type == constants.TENANT_TYPE_OF:
self.base_binding_dict[
portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS
def set_controller(self, context):
LOG.info(_LI("Set a new controller if needed."))
new_controller = self.sdnve_client.sdnve_get_controller()
if new_controller:
self.notifier.info_update(
context,
{'new_controller': new_controller})
LOG.info(_LI("Set the controller to a new controller: %s"),
new_controller)
def _process_request(self, request, current):
new_request = dict(
(k, v) for k, v in request.items()
if v != current.get(k))
msg = _("Original SDN-VE HTTP request: %(orig)s; New request: %(new)s")
LOG.debug(msg, {'orig': request, 'new': new_request})
return new_request
#
# Network
#
@_ha
def create_network(self, context, network):
LOG.debug("Create network in progress: %r", network)
session = context.session
tenant_id = self._get_tenant_id_for_create(context, network['network'])
# Create a new SDN-VE tenant if need be
sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant(
tenant_id)
if sdnve_tenant is None:
raise sdnve_exc.SdnveException(
msg=_('Create net failed: no SDN-VE tenant.'))
with session.begin(subtransactions=True):
net = super(SdnvePluginV2, self).create_network(context, network)
self._process_l3_create(context, net, network['network'])
# Create SDN-VE network
(res, data) = self.sdnve_client.sdnve_create('network', net)
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).delete_network(context, net['id'])
raise sdnve_exc.SdnveException(
msg=(_('Create net failed in SDN-VE: %s') % res))
LOG.debug("Created network: %s", net['id'])
return net
@_ha
def update_network(self, context, id, network):
LOG.debug("Update network in progress: %r", network)
session = context.session
processed_request = {}
with session.begin(subtransactions=True):
original_network = super(SdnvePluginV2, self).get_network(
context, id)
processed_request['network'] = self._process_request(
network['network'], original_network)
net = super(SdnvePluginV2, self).update_network(
context, id, network)
self._process_l3_update(context, net, network['network'])
if processed_request['network']:
(res, data) = self.sdnve_client.sdnve_update(
'network', id, processed_request['network'])
if res not in constants.HTTP_ACCEPTABLE:
net = super(SdnvePluginV2, self).update_network(
context, id, {'network': original_network})
raise sdnve_exc.SdnveException(
msg=(_('Update net failed in SDN-VE: %s') % res))
return net
@_ha
def delete_network(self, context, id):
LOG.debug("Delete network in progress: %s", id)
session = context.session
with session.begin(subtransactions=True):
self._process_l3_delete(context, id)
super(SdnvePluginV2, self).delete_network(context, id)
(res, data) = self.sdnve_client.sdnve_delete('network', id)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(
_LE("Delete net failed after deleting the network in DB: %s"),
res)
@_ha
def get_network(self, context, id, fields=None):
LOG.debug("Get network in progress: %s", id)
return super(SdnvePluginV2, self).get_network(context, id, fields)
@_ha
def get_networks(self, context, filters=None, fields=None, sorts=None,
limit=None, marker=None, page_reverse=False):
LOG.debug("Get networks in progress")
return super(SdnvePluginV2, self).get_networks(
context, filters, fields, sorts, limit, marker, page_reverse)
#
# Port
#
@_ha
def create_port(self, context, port):
LOG.debug("Create port in progress: %r", port)
session = context.session
# Set port status as 'ACTIVE' to avoid needing the agent
port['port']['status'] = n_const.PORT_STATUS_ACTIVE
port_data = port['port']
with session.begin(subtransactions=True):
port = super(SdnvePluginV2, self).create_port(context, port)
if 'id' not in port:
return port
# If the tenant_id is set to '' by create_port, add the id to
# the request being sent to the controller as the controller
# requires a tenant id
tenant_id = port.get('tenant_id')
if not tenant_id:
LOG.debug("Create port does not have tenant id info")
original_network = super(SdnvePluginV2, self).get_network(
context, port['network_id'])
original_tenant_id = original_network['tenant_id']
port['tenant_id'] = original_tenant_id
LOG.debug(
"Create port does not have tenant id info; "
"obtained is: %s",
port['tenant_id'])
os_tenant_id = tenant_id
id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid(
os_tenant_id)
self._update_base_binding_dict(tenant_type)
self._process_portbindings_create_and_update(context,
port_data, port)
# NOTE(mb): Remove this block when controller is updated
# Remove the information that the controller does not accept
sdnve_port = port.copy()
sdnve_port.pop('device_id', None)
sdnve_port.pop('device_owner', None)
(res, data) = self.sdnve_client.sdnve_create('port', sdnve_port)
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).delete_port(context, port['id'])
raise sdnve_exc.SdnveException(
msg=(_('Create port failed in SDN-VE: %s') % res))
LOG.debug("Created port: %s", port.get('id', 'id not found'))
return port
@_ha
def update_port(self, context, id, port):
LOG.debug("Update port in progress: %r", port)
session = context.session
processed_request = {}
with session.begin(subtransactions=True):
original_port = super(SdnvePluginV2, self).get_port(
context, id)
processed_request['port'] = self._process_request(
port['port'], original_port)
updated_port = super(SdnvePluginV2, self).update_port(
context, id, port)
os_tenant_id = updated_port['tenant_id']
id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid(
os_tenant_id)
self._update_base_binding_dict(tenant_type)
self._process_portbindings_create_and_update(context,
port['port'],
updated_port)
if processed_request['port']:
(res, data) = self.sdnve_client.sdnve_update(
'port', id, processed_request['port'])
if res not in constants.HTTP_ACCEPTABLE:
updated_port = super(SdnvePluginV2, self).update_port(
context, id, {'port': original_port})
raise sdnve_exc.SdnveException(
msg=(_('Update port failed in SDN-VE: %s') % res))
return updated_port
@_ha
def delete_port(self, context, id, l3_port_check=True):
LOG.debug("Delete port in progress: %s", id)
# if needed, check to see if this is a port owned by
# an l3-router. If so, we should prevent deletion.
if l3_port_check:
self.prevent_l3_port_deletion(context, id)
self.disassociate_floatingips(context, id)
super(SdnvePluginV2, self).delete_port(context, id)
(res, data) = self.sdnve_client.sdnve_delete('port', id)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(
_LE("Delete port operation failed in SDN-VE "
"after deleting the port from DB: %s"), res)
#
# Subnet
#
@_ha
def create_subnet(self, context, subnet):
LOG.debug("Create subnet in progress: %r", subnet)
new_subnet = super(SdnvePluginV2, self).create_subnet(context, subnet)
# Note(mb): Use of null string currently required by controller
sdnve_subnet = new_subnet.copy()
if subnet.get('gateway_ip') is None:
sdnve_subnet['gateway_ip'] = 'null'
(res, data) = self.sdnve_client.sdnve_create('subnet', sdnve_subnet)
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).delete_subnet(context,
new_subnet['id'])
raise sdnve_exc.SdnveException(
msg=(_('Create subnet failed in SDN-VE: %s') % res))
LOG.debug("Subnet created: %s", new_subnet['id'])
return new_subnet
@_ha
def update_subnet(self, context, id, subnet):
LOG.debug("Update subnet in progress: %r", subnet)
session = context.session
processed_request = {}
with session.begin(subtransactions=True):
original_subnet = super(SdnvePluginV2, self).get_subnet(
context, id)
processed_request['subnet'] = self._process_request(
subnet['subnet'], original_subnet)
updated_subnet = super(SdnvePluginV2, self).update_subnet(
context, id, subnet)
if processed_request['subnet']:
# Note(mb): Use of string containing null required by controller
if 'gateway_ip' in processed_request['subnet']:
if processed_request['subnet'].get('gateway_ip') is None:
processed_request['subnet']['gateway_ip'] = 'null'
(res, data) = self.sdnve_client.sdnve_update(
'subnet', id, processed_request['subnet'])
if res not in constants.HTTP_ACCEPTABLE:
for key in subnet['subnet'].keys():
subnet['subnet'][key] = original_subnet[key]
super(SdnvePluginV2, self).update_subnet(
context, id, subnet)
raise sdnve_exc.SdnveException(
msg=(_('Update subnet failed in SDN-VE: %s') % res))
return updated_subnet
@_ha
def delete_subnet(self, context, id):
LOG.debug("Delete subnet in progress: %s", id)
super(SdnvePluginV2, self).delete_subnet(context, id)
(res, data) = self.sdnve_client.sdnve_delete('subnet', id)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(_LE("Delete subnet operation failed in SDN-VE after "
"deleting the subnet from DB: %s"), res)
#
# Router
#
@_ha
def create_router(self, context, router):
LOG.debug("Create router in progress: %r", router)
if router['router']['admin_state_up'] is False:
LOG.warning(_LW('Ignoring admin_state_up=False for router=%r. '
'Overriding with True'), router)
router['router']['admin_state_up'] = True
tenant_id = self._get_tenant_id_for_create(context, router['router'])
# Create a new SDN-VE tenant if need be
sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant(
tenant_id)
if sdnve_tenant is None:
raise sdnve_exc.SdnveException(
msg=_('Create router failed: no SDN-VE tenant.'))
new_router = super(SdnvePluginV2, self).create_router(context, router)
# Create SDN-VE router
(res, data) = self.sdnve_client.sdnve_create('router', new_router)
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).delete_router(context, new_router['id'])
raise sdnve_exc.SdnveException(
msg=(_('Create router failed in SDN-VE: %s') % res))
LOG.debug("Router created: %r", new_router)
return new_router
@_ha
def update_router(self, context, id, router):
LOG.debug("Update router in progress: id=%(id)s "
"router=%(router)r",
{'id': id, 'router': router})
session = context.session
processed_request = {}
if not router['router'].get('admin_state_up', True):
raise n_exc.NotImplementedError(_('admin_state_up=False '
'routers are not '
'supported.'))
with session.begin(subtransactions=True):
original_router = super(SdnvePluginV2, self).get_router(
context, id)
processed_request['router'] = self._process_request(
router['router'], original_router)
updated_router = super(SdnvePluginV2, self).update_router(
context, id, router)
if processed_request['router']:
egw = processed_request['router'].get('external_gateway_info')
# Check for existing empty set (different from None) in request
if egw == {}:
processed_request['router'][
'external_gateway_info'] = {'network_id': 'null'}
(res, data) = self.sdnve_client.sdnve_update(
'router', id, processed_request['router'])
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).update_router(
context, id, {'router': original_router})
raise sdnve_exc.SdnveException(
msg=(_('Update router failed in SDN-VE: %s') % res))
return updated_router
@_ha
def delete_router(self, context, id):
LOG.debug("Delete router in progress: %s", id)
super(SdnvePluginV2, self).delete_router(context, id)
(res, data) = self.sdnve_client.sdnve_delete('router', id)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(
_LE("Delete router operation failed in SDN-VE after "
"deleting the router in DB: %s"), res)
@_ha
def add_router_interface(self, context, router_id, interface_info):
LOG.debug("Add router interface in progress: "
"router_id=%(router_id)s "
"interface_info=%(interface_info)r",
{'router_id': router_id, 'interface_info': interface_info})
new_interface = super(SdnvePluginV2, self).add_router_interface(
context, router_id, interface_info)
LOG.debug(
"SdnvePluginV2.add_router_interface called. Port info: %s",
new_interface)
request_info = interface_info.copy()
request_info['port_id'] = new_interface['port_id']
# Add the subnet_id to the request sent to the controller
if 'subnet_id' not in interface_info:
request_info['subnet_id'] = new_interface['subnet_id']
(res, data) = self.sdnve_client.sdnve_update(
'router', router_id + '/add_router_interface', request_info)
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).remove_router_interface(
context, router_id, interface_info)
raise sdnve_exc.SdnveException(
msg=(_('Update router-add-interface failed in SDN-VE: %s') %
res))
LOG.debug("Added router interface: %r", new_interface)
return new_interface
def _add_router_interface_only(self, context, router_id, interface_info):
LOG.debug("Add router interface only called: "
"router_id=%(router_id)s "
"interface_info=%(interface_info)r",
{'router_id': router_id, 'interface_info': interface_info})
port_id = interface_info.get('port_id')
if port_id:
(res, data) = self.sdnve_client.sdnve_update(
'router', router_id + '/add_router_interface', interface_info)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(_LE("SdnvePluginV2._add_router_interface_only: "
"failed to add the interface in the roll back."
" of a remove_router_interface operation"))
def _find_router_port_by_subnet_id(self, ports, subnet_id):
for p in ports:
subnet_ids = [fip['subnet_id'] for fip in p['fixed_ips']]
if subnet_id in subnet_ids:
return p['id']
@_ha
def remove_router_interface(self, context, router_id, interface_info):
LOG.debug("Remove router interface in progress: "
"router_id=%(router_id)s "
"interface_info=%(interface_info)r",
{'router_id': router_id, 'interface_info': interface_info})
subnet_id = interface_info.get('subnet_id')
port_id = interface_info.get('port_id')
if not subnet_id:
if not port_id:
raise sdnve_exc.BadInputException(msg=_('No port ID'))
myport = super(SdnvePluginV2, self).get_port(context, port_id)
LOG.debug("SdnvePluginV2.remove_router_interface port: %s",
myport)
myfixed_ips = myport.get('fixed_ips')
if not myfixed_ips:
raise sdnve_exc.BadInputException(msg=_('No fixed IP'))
subnet_id = myfixed_ips[0].get('subnet_id')
if subnet_id:
interface_info['subnet_id'] = subnet_id
LOG.debug(
"SdnvePluginV2.remove_router_interface subnet_id: %s",
subnet_id)
else:
if not port_id:
# The backend requires port id info in the request
subnet = super(SdnvePluginV2, self).get_subnet(context,
subnet_id)
df = {'device_id': [router_id],
'device_owner': [n_const.DEVICE_OWNER_ROUTER_INTF],
'network_id': [subnet['network_id']]}
ports = self.get_ports(context, filters=df)
if ports:
pid = self._find_router_port_by_subnet_id(ports, subnet_id)
if not pid:
raise sdnve_exc.SdnveException(
msg=(_('Update router-remove-interface '
'failed SDN-VE: subnet %(sid) is not '
'associated with any ports on router '
'%(rid)'), {'sid': subnet_id,
'rid': router_id}))
interface_info['port_id'] = pid
msg = ("SdnvePluginV2.remove_router_interface "
"subnet_id: %(sid)s port_id: %(pid)s")
LOG.debug(msg, {'sid': subnet_id, 'pid': pid})
(res, data) = self.sdnve_client.sdnve_update(
'router', router_id + '/remove_router_interface', interface_info)
if res not in constants.HTTP_ACCEPTABLE:
raise sdnve_exc.SdnveException(
msg=(_('Update router-remove-interface failed SDN-VE: %s') %
res))
session = context.session
with session.begin(subtransactions=True):
try:
if not port_id:
# port_id was not originally given in interface_info,
# so we want to remove the interface by subnet instead
# of port
del interface_info['port_id']
info = super(SdnvePluginV2, self).remove_router_interface(
context, router_id, interface_info)
except Exception:
with excutils.save_and_reraise_exception():
self._add_router_interface_only(context,
router_id, interface_info)
return info
#
# Floating Ip
#
@_ha
def create_floatingip(self, context, floatingip):
LOG.debug("Create floatingip in progress: %r",
floatingip)
new_floatingip = super(SdnvePluginV2, self).create_floatingip(
context, floatingip)
(res, data) = self.sdnve_client.sdnve_create(
'floatingip', {'floatingip': new_floatingip})
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).delete_floatingip(
context, new_floatingip['id'])
raise sdnve_exc.SdnveException(
msg=(_('Creating floating ip operation failed '
'in SDN-VE controller: %s') % res))
LOG.debug("Created floatingip : %r", new_floatingip)
return new_floatingip
@_ha
def update_floatingip(self, context, id, floatingip):
LOG.debug("Update floatingip in progress: %r", floatingip)
session = context.session
processed_request = {}
with session.begin(subtransactions=True):
original_floatingip = super(
SdnvePluginV2, self).get_floatingip(context, id)
processed_request['floatingip'] = self._process_request(
floatingip['floatingip'], original_floatingip)
updated_floatingip = super(
SdnvePluginV2, self).update_floatingip(context, id, floatingip)
if processed_request['floatingip']:
(res, data) = self.sdnve_client.sdnve_update(
'floatingip', id,
{'floatingip': processed_request['floatingip']})
if res not in constants.HTTP_ACCEPTABLE:
super(SdnvePluginV2, self).update_floatingip(
context, id, {'floatingip': original_floatingip})
raise sdnve_exc.SdnveException(
msg=(_('Update floating ip failed in SDN-VE: %s') % res))
return updated_floatingip
@_ha
def delete_floatingip(self, context, id):
LOG.debug("Delete floatingip in progress: %s", id)
super(SdnvePluginV2, self).delete_floatingip(context, id)
(res, data) = self.sdnve_client.sdnve_delete('floatingip', id)
if res not in constants.HTTP_ACCEPTABLE:
LOG.error(_LE("Delete floatingip failed in SDN-VE: %s"), res)

@ -1,107 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_config import cfg
from neutron.agent.linux import ip_lib
from neutron.plugins.ibm.agent import sdnve_neutron_agent
from neutron.tests import base
NOTIFIER = ('neutron.plugins.ibm.'
'sdnve_neutron_plugin.AgentNotifierApi')
class CreateAgentConfigMap(base.BaseTestCase):
def test_create_agent_config_map_succeeds(self):
self.assertTrue(sdnve_neutron_agent.create_agent_config_map(cfg.CONF))
def test_create_agent_config_using_controller_ips(self):
cfg.CONF.set_override('controller_ips',
['10.10.10.1', '10.10.10.2'], group='SDNVE')
cfgmap = sdnve_neutron_agent.create_agent_config_map(cfg.CONF)
self.assertEqual(cfgmap['controller_ip'], '10.10.10.1')
def test_create_agent_config_using_interface_mappings(self):
cfg.CONF.set_override('interface_mappings',
['interface1 : eth1', 'interface2 : eth2'],
group='SDNVE')
cfgmap = sdnve_neutron_agent.create_agent_config_map(cfg.CONF)
self.assertEqual(cfgmap['interface_mappings'],
{'interface1': 'eth1', 'interface2': 'eth2'})
class TestSdnveNeutronAgent(base.BaseTestCase):
def setUp(self):
super(TestSdnveNeutronAgent, self).setUp()
notifier_p = mock.patch(NOTIFIER)
notifier_cls = notifier_p.start()
self.notifier = mock.Mock()
notifier_cls.return_value = self.notifier
cfg.CONF.set_override('integration_bridge',
'br_int', group='SDNVE')
kwargs = sdnve_neutron_agent.create_agent_config_map(cfg.CONF)
class MockFixedIntervalLoopingCall(object):
def __init__(self, f):
self.f = f
def start(self, interval=0):
self.f()
with mock.patch('neutron.plugins.ibm.agent.sdnve_neutron_agent.'
'SdnveNeutronAgent.setup_integration_br',
return_value=mock.Mock()),\
mock.patch('oslo_service.loopingcall.'
'FixedIntervalLoopingCall',
new=MockFixedIntervalLoopingCall):
self.agent = sdnve_neutron_agent.SdnveNeutronAgent(**kwargs)
def test_setup_physical_interfaces(self):
with mock.patch.object(self.agent.int_br,
'add_port') as add_port_func:
with mock.patch.object(ip_lib,
'device_exists',
return_valxue=True):
self.agent.setup_physical_interfaces({"interface1": "eth1"})
add_port_func.assert_called_once_with('eth1')
def test_setup_physical_interfaces_none(self):
with mock.patch.object(self.agent.int_br,
'add_port') as add_port_func:
with mock.patch.object(ip_lib,
'device_exists',
return_valxue=True):
self.agent.setup_physical_interfaces({})
self.assertFalse(add_port_func.called)
def test_get_info_set_controller(self):
with mock.patch.object(self.agent.int_br,
'set_controller') as set_controller_func:
kwargs = {}
kwargs['info'] = {'new_controller': '10.10.10.1'}
self.agent.info_update('dummy', **kwargs)
set_controller_func.assert_called_once_with(['tcp:10.10.10.1'])
def test_get_info(self):
with mock.patch.object(self.agent.int_br,
'set_controller') as set_controller_func:
kwargs = {}
self.agent.info_update('dummy', **kwargs)
self.assertFalse(set_controller_func.called)

@ -1,143 +0,0 @@
# Copyright 2014 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_utils import uuidutils
from neutron.plugins.ibm.common import constants
from neutron.plugins.ibm import sdnve_api
from neutron.tests import base
RESOURCE_PATH = {
'network': "ln/networks/",
}
RESOURCE = 'network'
HTTP_OK = 200
TENANT_ID = uuidutils.generate_uuid()
class TestSdnveApi(base.BaseTestCase):
def setUp(self):
super(TestSdnveApi, self).setUp()
class MockKeystoneClient(object):
def __init__(self, **kwargs):
pass
def get_tenant_name(self, id):
return 'test tenant name'
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'KeystoneClient',
new=MockKeystoneClient):
self.api = sdnve_api.Client()
def mock_do_request(self, method, url, body=None, headers=None,
params=None, connection_type=None):
return (HTTP_OK, url)
def mock_do_request_tenant(self, method, url, body=None, headers=None,
params=None, connection_type=None):
return (HTTP_OK, {'id': TENANT_ID,
'network_type': constants.TENANT_TYPE_OF})
def mock_do_request_no_tenant(self, method, url, body=None, headers=None,
params=None, connection_type=None):
return (None, None)
def mock_process_request(self, body):
return body
def test_sdnve_api_list(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request):
result = self.api.sdnve_list(RESOURCE)
self.assertEqual(result, (HTTP_OK, RESOURCE_PATH[RESOURCE]))
def test_sdnve_api_show(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request):
result = self.api.sdnve_show(RESOURCE, TENANT_ID)
self.assertEqual(result,
(HTTP_OK, RESOURCE_PATH[RESOURCE] + TENANT_ID))
def test_sdnve_api_create(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.process_request',
new=self.mock_process_request):
result = self.api.sdnve_create(RESOURCE, '')
self.assertEqual(result, (HTTP_OK, RESOURCE_PATH[RESOURCE]))
def test_sdnve_api_update(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.process_request',
new=self.mock_process_request):
result = self.api.sdnve_update(RESOURCE, TENANT_ID, '')
self.assertEqual(result,
(HTTP_OK,
RESOURCE_PATH[RESOURCE] + TENANT_ID))
def test_sdnve_api_delete(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request):
result = self.api.sdnve_delete(RESOURCE, TENANT_ID)
self.assertEqual(result,
(HTTP_OK, RESOURCE_PATH[RESOURCE] + TENANT_ID))
def test_sdnve_get_tenant_by_id(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request_tenant):
id = TENANT_ID
result = self.api.sdnve_get_tenant_byid(id)
self.assertEqual(result,
(TENANT_ID, constants.TENANT_TYPE_OF))
def test_sdnve_check_and_create_tenant(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request_tenant):
id = TENANT_ID
result = self.api.sdnve_check_and_create_tenant(id)
self.assertEqual(result, TENANT_ID)
def test_sdnve_check_and_create_tenant_fail(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.'
'Client.do_request',
new=self.mock_do_request_no_tenant):
id = TENANT_ID
result = self.api.sdnve_check_and_create_tenant(
id, constants.TENANT_TYPE_OF)
self.assertIsNone(result)
def test_process_request(self):
my_request = {'key_1': 'value_1', 'router:external': 'True',
'key_2': 'value_2'}
expected = {'key_1': 'value_1', 'router_external': 'True',
'key_2': 'value_2'}
result = self.api.process_request(my_request)
self.assertEqual(expected, result)

@ -1,120 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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.extensions import portbindings
from neutron.tests.unit import _test_extension_portbindings as test_bindings
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
from neutron.tests.unit.extensions import test_l3 as test_l3
from neutron.plugins.ibm.common import constants
_plugin_name = ('neutron.plugins.ibm.'
'sdnve_neutron_plugin.SdnvePluginV2')
HTTP_OK = 200
class MockClient(object):
def sdnve_list(self, resource, **params):
return (HTTP_OK, 'body')
def sdnve_show(self, resource, specific, **params):
return (HTTP_OK, 'body')
def sdnve_create(self, resource, body):
return (HTTP_OK, 'body')
def sdnve_update(self, resource, specific, body=None):
return (HTTP_OK, 'body')
def sdnve_delete(self, resource, specific):
return (HTTP_OK, 'body')
def sdnve_get_tenant_byid(self, os_tenant_id):
return (os_tenant_id, constants.TENANT_TYPE_OF)
def sdnve_check_and_create_tenant(
self, os_tenant_id, network_type=None):
return os_tenant_id
def sdnve_get_controller(self):
return
class MockKeystoneClient(object):
def __init__(self, **kwargs):
pass
def get_tenant_type(self, id):
return constants.TENANT_TYPE_OF
def get_tenant_name(self, id):
return "tenant name"
class IBMPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
def setUp(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.' 'KeystoneClient',
new=MockKeystoneClient),\
mock.patch('neutron.plugins.ibm.sdnve_api.' 'Client',
new=MockClient):
super(IBMPluginV2TestCase, self).setUp(plugin=_plugin_name)
class TestIBMBasicGet(test_plugin.TestBasicGet,
IBMPluginV2TestCase):
pass
class TestIBMV2HTTPResponse(test_plugin.TestV2HTTPResponse,
IBMPluginV2TestCase):
pass
class TestIBMNetworksV2(test_plugin.TestNetworksV2,
IBMPluginV2TestCase):
pass
class TestIBMPortsV2(test_plugin.TestPortsV2,
IBMPluginV2TestCase):
pass
class TestIBMSubnetsV2(test_plugin.TestSubnetsV2,
IBMPluginV2TestCase):
pass
class TestIBMPortBinding(IBMPluginV2TestCase,
test_bindings.PortBindingsTestCase):
VIF_TYPE = portbindings.VIF_TYPE_OVS
class IBMPluginRouterTestCase(test_l3.L3NatDBIntTestCase):
def setUp(self):
with mock.patch('neutron.plugins.ibm.sdnve_api.' 'KeystoneClient',
new=MockKeystoneClient),\
mock.patch('neutron.plugins.ibm.sdnve_api.' 'Client',
new=MockClient):
super(IBMPluginRouterTestCase, self).setUp(plugin=_plugin_name)
def test_floating_port_status_not_applicable(self):
self.skipTest('Plugin changes floating port status')

@ -56,7 +56,6 @@ data_files =
etc/neutron/plugins/cisco/cisco_router_plugin.ini
etc/neutron/plugins/cisco/cisco_vpn_agent.ini
etc/neutron/plugins/embrane = etc/neutron/plugins/embrane/heleos_conf.ini
etc/neutron/plugins/ibm = etc/neutron/plugins/ibm/sdnve_neutron_plugin.ini
etc/neutron/plugins/midonet = etc/neutron/plugins/midonet/midonet.ini
etc/neutron/plugins/ml2 =
etc/neutron/plugins/bigswitch/restproxy.ini
@ -84,7 +83,6 @@ console_scripts =
neutron-dhcp-agent = neutron.cmd.eventlet.agents.dhcp:main
neutron-hyperv-agent = neutron.cmd.eventlet.plugins.hyperv_neutron_agent:main
neutron-keepalived-state-change = neutron.cmd.keepalived_state_change:main
neutron-ibm-agent = neutron.plugins.ibm.agent.sdnve_neutron_agent:main
neutron-ipset-cleanup = neutron.cmd.ipset_cleanup:main
neutron-l3-agent = neutron.cmd.eventlet.agents.l3:main
neutron-linuxbridge-agent = neutron.plugins.ml2.drivers.linuxbridge.agent.linuxbridge_neutron_agent:main
@ -110,7 +108,6 @@ neutron.core_plugins =
brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2
cisco = neutron.plugins.cisco.network_plugin:PluginV2
embrane = neutron.plugins.embrane.plugins.embrane_ml2_plugin:EmbraneMl2Plugin
ibm = neutron.plugins.ibm.sdnve_neutron_plugin:SdnvePluginV2
midonet = neutron.plugins.midonet.plugin:MidonetPluginV2
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin
nuage = neutron.plugins.nuage.plugin:NuagePlugin