diff --git a/quantum/plugins/cisco/db/api.py b/quantum/plugins/cisco/db/api.py index 3e647ffc3..ed42f044a 100644 --- a/quantum/plugins/cisco/db/api.py +++ b/quantum/plugins/cisco/db/api.py @@ -94,6 +94,17 @@ def network_list(tenant_id): all() +def network_id(net_name): + session = get_session() + try: + return session.query(models.Network).\ + options(joinedload(models.Network.ports)). \ + filter_by(name=net_name).\ + all() + except exc.NoResultFound, e: + raise q_exc.NetworkNotFound(net_name=net_name) + + def network_get(net_id): session = get_session() try: diff --git a/quantum/plugins/cisco/db/l2network_db.py b/quantum/plugins/cisco/db/l2network_db.py index f9b981948..c6962f7dd 100644 --- a/quantum/plugins/cisco/db/l2network_db.py +++ b/quantum/plugins/cisco/db/l2network_db.py @@ -26,6 +26,7 @@ import logging as LOG import quantum.plugins.cisco.db.api as db import quantum.plugins.cisco.db.nexus_db as ndb import quantum.plugins.cisco.db.ucs_db as udb +import quantum.plugins.cisco.db.services_db as sdb def initialize(): diff --git a/quantum/plugins/cisco/db/services_db.py b/quantum/plugins/cisco/db/services_db.py new file mode 100644 index 000000000..7235cfb41 --- /dev/null +++ b/quantum/plugins/cisco/db/services_db.py @@ -0,0 +1,78 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011, Cisco Systems, Inc. +# +# 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: Edgar Magana, Cisco Systems, Inc. + +import logging as LOG + +from sqlalchemy.orm import exc + +import quantum.plugins.cisco.db.api as db + +from quantum.plugins.cisco.common import cisco_exceptions as c_exc +from quantum.plugins.cisco.db import services_models + + +def get_all_services_bindings(): + """Lists all the services bindings""" + LOG.debug("get_all_services_bindings() called") + session = db.get_session() + try: + bindings = session.query(services_models.ServicesBinding).\ + all() + return bindings + except exc.NoResultFound: + return [] + + +def get_service_bindings(service_id): + """Lists services bindings for a service_id""" + LOG.debug("get_service_bindings() called") + session = db.get_session() + try: + bindings = session.query(services_models.ServicesBinding).\ + filter_by(service_id=service_id).\ + one() + return bindings + except exc.NoResultFound: + return [] + + +def add_services_binding(service_id, mngnet_id, nbnet_id, sbnet_id): + """Adds a services binding""" + LOG.debug("add_services_binding() called") + session = db.get_session() + binding = services_models.ServicesBinding(service_id, mngnet_id, \ + nbnet_id, sbnet_id) + session.add(binding) + session.flush() + return binding + + +def remove_services_binding(service_id): + """Removes a services binding""" + LOG.debug("remove_services_binding() called") + session = db.get_session() + try: + binding = session.query(services_models.ServicesBinding).\ + filter_by(service_id=service_id).\ + all() + for bind in binding: + session.delete(bind) + session.flush() + return binding + except exc.NoResultFound: + pass diff --git a/quantum/plugins/cisco/db/services_models.py b/quantum/plugins/cisco/db/services_models.py new file mode 100644 index 000000000..8a3b927b1 --- /dev/null +++ b/quantum/plugins/cisco/db/services_models.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011, Cisco Systems, Inc. +# +# 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: Edgar Magana, Cisco Systems, Inc. + +from sqlalchemy import Column, Integer, String + +from quantum.plugins.cisco.db.l2network_models import L2NetworkBase +from quantum.plugins.cisco.db.models import BASE + + +class ServicesBinding(BASE, L2NetworkBase): + """Represents a binding of L2 services to networks""" + __tablename__ = 'services_bindings' + + id = Column(Integer, primary_key=True, autoincrement=True) + service_id = Column(String(255)) + mngnet_id = Column(String(255)) + nbnet_id = Column(String(255)) + sbnet_id = Column(String(255)) + + def __init__(self, service_id, mngnet_id, nbnet_id, sbnet_id): + self.service_id = service_id + self.mngnet_id = mngnet_id + self.nbnet_id = nbnet_id + self.sbnet_id = sbnet_id + + def __repr__(self): + return "" % \ + (self.service_id, self.mngnet_id, self.nbnet_id, self.sbnet_id) diff --git a/quantum/plugins/cisco/services/README b/quantum/plugins/cisco/services/README new file mode 100644 index 000000000..3936ca36c --- /dev/null +++ b/quantum/plugins/cisco/services/README @@ -0,0 +1,70 @@ +========================================================================================= +README: L2 Network Services Insertion Utility +========================================================================================= +:Authors: Edgar Magana, Mani Ramasamy, Ram Durairaj +:Collaborators: Deepak Khanorkar, Sumit Naiksatam, Nat Chidambaram, Dan Wendlandt +:Contact: netstack@lists.launchpad.net +:Web site: https://blueprints.launchpad.net/quantum/+spec/services-insertion-wrapper + +Introduction +------------ +This utility offers a simplify way to insert and remove network services +in the path of the traffic to the server VMs, by splitting the network into two, +and having the service bridge between the two, in the process applying the service. +This model is called In-Path (Bump in the Wire) + +Pre-requisites +-------------- +This integration uses Quantum APIs offered on diablo realease and Nova compute +functionality, basically to create new service instances. + +Instructions +------------------------------------------------------ +This utility offer four functionalities: + +1. insert_inpath_service + +Creates two networks and insert a service vm between them bridging the traffic +path. It also creates a management network to access the service configuration. + +2. delete_service +Deletes the service prevopusly inserted as well as the network dependencies. + +connect_vm +Instanciate a VM which is connected to the southbound network created by +insert_inpath_service. Facilitates the connections of server vms into the +tenant's network. + +4. disconnect_vm +Disconnect from the southbound network and terminates the server vm. + +Example +------------------------------------------------------ +Let's insert a Firewall service between northbound and southbound networks, +the management network will be called mng_net: + +#PYTHONPATH=. python quantum/services/service_insertion.py insert_inpath_service +naas ami-00000029 mng_net northnet southnet + +"ami-00000029" is the reference id provided by Glance for the Firewall image +service instance id: i-00000091 + +Now, we can connect a fresh web server in to the southbound network with: +#PYTHONPATH=. python quantum/services/service_insertion.py connect_vm +naas ami-0000002b i-00000091 + +"ami-0000002b" is the reference id provided by Glace for the Web Server image +and "i-00000091" is the instance id provided by Nova for the FW service instance +previously created. +server instance id: i-00000092 + +If we want to disconnect and shutdown the vm instance server: +#PYTHONPATH=. python quantum/plugins/cisco/services/service_insertion.py disconnect_vm i-00000092 + +We can delete the service instance and the network configuration with: +#PYTHONPATH=. python quantum/plugins/cisco/services/service_insertion.py delete_service naas i-00000091 + +Caveats +------------------------------------------------------ +nova-compute service should be running in the same server that Quantum. +Nova API calls will be implemented in the next release (essex-3) diff --git a/quantum/plugins/cisco/services/__init__.py b/quantum/plugins/cisco/services/__init__.py new file mode 100644 index 000000000..d6e7beb5d --- /dev/null +++ b/quantum/plugins/cisco/services/__init__.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 OpenStack LLC +# +# 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: Edgar Magana, Cisco Systems +""" +L2 Network Services Insertion Utility +""" diff --git a/quantum/plugins/cisco/services/service_insertion.py b/quantum/plugins/cisco/services/service_insertion.py new file mode 100644 index 000000000..a393cd166 --- /dev/null +++ b/quantum/plugins/cisco/services/service_insertion.py @@ -0,0 +1,318 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, 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: Edgar Magana, Cisco Systems +# +""" +Network Library to insert services using Quantum APIs +Currently has four functionalities: +1. insert_inpath_service + +2. delete_service +3. connect_vm +4. disconnect_vm +""" + + +import logging +import logging.handlers +import os +import subprocess +import re +import sys + +from optparse import OptionParser +from quantum.client import Client +from quantum.plugins.cisco.db import api as db +from quantum.plugins.cisco.db import l2network_db as l2db +from quantum.plugins.cisco.db import services_db as sdb +from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.services import services_constants as servconts +from quantum.plugins.cisco.services import services_logistics as servlogcs + + +LOG = logging.getLogger(__name__) + + +def insert_inpath_service(tenant_id, service_image_id, + management_net_name, northbound_net_name, + southbound_net_name, *args): + """Inserting a network service between two networks""" + print ("Creating Network for Services and Servers") + service_logic = servlogcs.ServicesLogistics() + net_list = {} + multiport_net_list = [] + networks_name_list = [management_net_name, northbound_net_name, \ + southbound_net_name] + client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id) + for net in networks_name_list: + data = {servconts.NETWORK: {servconts.NAME: net}} + net_list[net] = client.create_network(data) + net_list[net][servconts.PORTS] = [] + LOG.debug("Network %s Created with ID: %s " % (net, \ + net_list[net][servconts.NETWORK][servconts.ID])) + print "Completed" + print ("Creating Ports on Services and Server Networks") + LOG.debug("Operation 'create_port' executed.") + if not service_logic.verify_plugin(const.UCS_PLUGIN): + for net in networks_name_list: + net_list[net][servconts.PORTS].append + (client.create_port + (net_list[net][servconts.NETWORK][servconts.ID])) + LOG.debug("Operation 'create_port' executed.") + else: + for net in networks_name_list: + nets = net_list[net][servconts.NETWORK][servconts.ID] + multiport_net_list.append(nets) + data = create_multiport(tenant_id, multiport_net_list) + net_idx = 0 + for net in networks_name_list: + port_id = data[servconts.PORTS][net_idx][servconts.ID] + net_list[net][servconts.PORTS].append(port_id) + LOG.debug("Port UUID: %s on network: %s" % \ + (data[servconts.PORTS][net_idx][servconts.ID], net)) + net_idx = net_idx + 1 + print "Completed" + try: + create_vm_args = [] + create_vm_args.append(servconts.CREATE_VM_CMD) + create_vm_args.append(service_image_id) + print ("Creating VM with image: %s" % (service_image_id)) + process = subprocess.Popen(create_vm_args, stdout=subprocess.PIPE) + result = process.stdout.readlines() + tokens = re.search("i-[a-f0-9]*", str(result[1])) + service_vm_name = tokens.group(0) + print ("Image: %s instantiated successfully" % (service_vm_name)) + + except Exception as exc: + print exc + + service_logic.image_status(service_vm_name) + print "Completed" + print "Attaching Ports To VM Service interfaces" + try: + idx = 0 + for net in networks_name_list: + network_id = net_list[net][servconts.NETWORK][servconts.ID] + port_id = net_list[net][servconts.PORTS][idx] + attachment = client.show_port_attachment(network_id, port_id) + attachment = attachment[servconts.ATTACHMENT][servconts.ID][:36] + LOG.debug("Plugging virtual interface: %s of VM %s \ + into port: %s on network: %s" % + (attachment, service_vm_name, port_id, net)) + attach_data = {servconts.ATTACHMENT: {servconts.ID: '%s' % + attachment}} + client.attach_resource(network_id, port_id, attach_data) + except Exception as exc: + print exc + print "Completed" + try: + LOG.debug("Registering Service in DB") + l2db.initialize() + for uuid_net in db.network_id(networks_name_list[0]): + mngnet_id = str(uuid_net.uuid) + for uuid_net in db.network_id(networks_name_list[1]): + nbnet_id = str(uuid_net.uuid) + for uuid_net in db.network_id(networks_name_list[2]): + sbnet_id = str(uuid_net.uuid) + sdb.add_services_binding(service_vm_name, mngnet_id, nbnet_id, + sbnet_id) + except Exception as exc: + print exc + + +def delete_service(tenant_id, service_instance_id, *args): + """ + Removes a service and all the network configuration + """ + l2db.initialize() + print ("Terminating Service VM") + service_logic = servlogcs.ServicesLogistics() + vms_list = [] + vms_list.append(servconts.DELETE_VM_CMD) + vms_list.append(service_instance_id) + + if not service_logic.image_exist(service_instance_id): + print ("Service VM does not exist") + sys.exit() + + result = subprocess.call(vms_list) + service_logic.image_shutdown_verification(service_instance_id) + + client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id) + service_nets = sdb.get_service_bindings(service_instance_id) + print ("Terminating Ports and Networks") + network_name = db.network_get(service_nets.mngnet_id) + port_id_net = db.port_list(service_nets.mngnet_id) + for ports_uuid in port_id_net: + client.delete_port(service_nets.mngnet_id, ports_uuid.uuid) + client.delete_network(service_nets.mngnet_id) + network_name = db.network_get(service_nets.nbnet_id) + port_id_net = db.port_list(service_nets.nbnet_id) + for ports_uuid in port_id_net: + client.delete_port(service_nets.nbnet_id, ports_uuid.uuid) + client.delete_network(service_nets.nbnet_id) + network_name = db.network_get(service_nets.sbnet_id) + port_id_net = db.port_list(service_nets.sbnet_id) + for ports_uuid in port_id_net: + client.delete_port(service_nets.sbnet_id, ports_uuid.uuid) + client.delete_network(service_nets.sbnet_id) + service_list = sdb.remove_services_binding(service_instance_id) + print ("Configuration Removed Successfully") + + +def disconnect_vm(vm_instance_id, *args): + """ + Deletes VMs and Port connection + """ + l2db.initialize() + print ("Terminating Service VM") + service_logic = servlogcs.ServicesLogistics() + vms_list = [] + vms_list.append(servconts.DELETE_VM_CMD) + vms_list.append(vm_instance_id) + result = subprocess.call(vms_list) + service_logic.image_shutdown_verification(vm_instance_id) + print ("VM Server Off") + + +def connect_vm(tenant_id, vm_image_id, service_instance_id, *args): + """ + Starts a VMs and is connected to southbound network + """ + l2db.initialize() + client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id) + print ("Connecting %s to Service %s " % (vm_image_id, service_instance_id)) + service_logic = servlogcs.ServicesLogistics() + service_nets = sdb.get_service_bindings(service_instance_id) + client.create_port(service_nets.mngnet_id) + client.create_port(service_nets.nbnet_id) + sb_port_id = client.create_port(service_nets.sbnet_id) + LOG.debug("Operation 'create_port' executed.") + new_port_id = sb_port_id[servconts.PORT][servconts.ID] + try: + create_vm_args = [] + create_vm_args.append(servconts.CREATE_VM_CMD) + create_vm_args.append(vm_image_id) + print ("Creating VM with image: %s" % (vm_image_id)) + process = subprocess.Popen(create_vm_args, stdout=subprocess.PIPE) + result = process.stdout.readlines() + tokens = re.search("i-[a-f0-9]*", str(result[1])) + vm_name = tokens.group(0) + print ("Image: %s instantiated successfully" % (vm_name)) + except Exception as exc: + print exc + + service_logic.image_status(vm_name) + print "Completed" + print "Attaching Ports To VM Service interfaces" + south_net = service_nets.sbnet_id + attachment = client.show_port_attachment(south_net, new_port_id) + attachment = attachment[servconts.ATTACHMENT][servconts.ID][:36] + LOG.debug("Plugging virtual interface: %s of VM %s \ + into port: %s on network: %s" % + (attachment, vm_name, new_port_id, service_nets.sbnet_id)) + attach_data = {servconts.ATTACHMENT: {servconts.ID: '%s' % attachment}} + client.attach_resource(service_nets.sbnet_id, new_port_id, attach_data) + print ("Connect VM Ended") + + +def create_multiport(tenant_id, networks_list, *args): + """Creates ports on a single host""" + ports_info = {'multiport': \ + {'status': 'ACTIVE', + 'net_id_list': networks_list, + 'ports_desc': {'key': 'value'}}} + request_url = "/multiport" + client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id, + action_prefix=servconts.ACTION_PREFIX_CSCO) + data = client.do_request('POST', request_url, body=ports_info) + return data + + +def build_args(cmd, cmdargs, arglist): + """Building the list of args for a particular CLI""" + args = [] + orig_arglist = arglist[:] + try: + for cmdarg in cmdargs: + args.append(arglist[0]) + del arglist[0] + except: + LOG.debug("Not enough arguments for \"%s\" (expected: %d, got: %d)" + % (cmd, len(cmdargs), len(orig_arglist))) + print "Service Insertion Usage:\n %s %s" % (cmd, + " ".join(["<%s>" % y for y in SERVICE_COMMANDS[cmd]["args"]])) + sys.exit() + if len(arglist) > 0: + LOG.debug("Too many arguments for \"%s\" (expected: %d, got: %d)" \ + % (cmd, len(cmdargs), len(orig_arglist))) + print "Service Insertion Usage:\n %s %s" % (cmd, + " ".join(["<%s>" % y for y in SERVICE_COMMANDS[cmd]["args"]])) + sys.exit() + return args + + +SERVICE_COMMANDS = { + "insert_inpath_service": { + "func": insert_inpath_service, + "args": ["tenant_id", "service_image_id", + "management_net_name", "northbound_net_name", + "southbound_net_name"]}, + "delete_service": { + "func": delete_service, + "args": ["tenant_id", "service_instance_id"]}, + "connect_vm": { + "func": connect_vm, + "args": ["tenant_id", "vm_image_id", + "service_instance_id"]}, + "disconnect_vm": { + "func": disconnect_vm, + "args": ["vm_instance_id"]}} + + +if __name__ == "__main__": + os.system("clear") + usagestr = "Usage: %prog [OPTIONS] [args]" + PARSER = OptionParser(usage=usagestr) + PARSER.add_option("-H", "--host", dest="host", + type="string", default="127.0.0.1", help="ip address of api host") + PARSER.add_option("-p", "--port", dest="port", + type="int", default=9696, help="api port") + PARSER.add_option("-s", "--ssl", dest="ssl", + action="store_true", default=False, help="use ssl") + PARSER.add_option("-v", "--verbose", dest="verbose", + action="store_true", default=False, help="turn on verbose logging") + PARSER.add_option("-f", "--logfile", dest="logfile", + type="string", default="syslog", help="log file path") + options, args = PARSER.parse_args() + if options.verbose: + LOG.setLevel(logging.DEBUG) + else: + LOG.setLevel(logging.WARN) + if options.logfile == "syslog": + LOG.addHandler(logging.handlers.SysLogHandler(address='/dev/log')) + else: + LOG.addHandler(logging.handlers.WatchedFileHandler(options.logfile)) + os.chmod(options.logfile, 0644) + service_logic = servlogcs.ServicesLogistics() + HOST = options.host + PORT = options.port + USE_SSL = options.ssl + CMD = args[0] + args = build_args(CMD, SERVICE_COMMANDS[CMD]["args"], args[1:]) + SERVICE_COMMANDS[CMD]["func"](*args) + sys.exit(0) diff --git a/quantum/plugins/cisco/services/services_constants.py b/quantum/plugins/cisco/services/services_constants.py new file mode 100644 index 000000000..0ee80129c --- /dev/null +++ b/quantum/plugins/cisco/services/services_constants.py @@ -0,0 +1,35 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 OpenStack LLC +# +# 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: Edgar Magana, Cisco Systems +# +""" +Services Constants for the Services insertion Library +""" + + +FORMAT = 'json' +ACTION_PREFIX_EXT = '/v1.0' +ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \ + '/extensions/csco/tenants/{tenant_id}' +NETWORK = 'network' +ID = 'id' +PORTS = 'ports' +PORT = 'port' +NAME = 'name' +ATTACHMENT = 'attachment' +CREATE_VM_CMD = '/usr/bin/euca-run-instances' +DELETE_VM_CMD = '/usr/bin/euca-terminate-instances' +DESCRIBE_VM_CMD = '/usr/bin/euca-describe-instances' diff --git a/quantum/plugins/cisco/services/services_logistics.py b/quantum/plugins/cisco/services/services_logistics.py new file mode 100644 index 000000000..7550ac53e --- /dev/null +++ b/quantum/plugins/cisco/services/services_logistics.py @@ -0,0 +1,122 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, 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: Edgar Magana, Cisco Systems +""" +Logistic components for Service Insertion utility +""" + +import logging +import subprocess +import re +import time + +from quantum.common import utils +from quantum.plugins.cisco import l2network_plugin_configuration as conf +from quantum.plugins.cisco.db import services_db as sdb +from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.services import services_constants as servconts + +LOG = logging.getLogger(__name__) + + +class ServicesLogistics(): + """ + Services Logistics Modules + """ + def __init__(self): + pass + + def image_shutdown_verification(self, image_name): + """ + Verifies that the VM has been properly shutdown + """ + try: + service_args = [] + service_args.append(servconts.DESCRIBE_VM_CMD) + service_args.append(image_name) + counter = 0 + flag = False + while flag == False and counter <= 5: + counter = counter + 1 + time.sleep(2.5) + process = subprocess.Popen(service_args, \ + stdout=subprocess.PIPE) + result = process.stdout.readlines() + if not result: + flag = True + except Exception, exc: + print exc + + def image_status(self, image_name): + """ + Checks the status of the image + """ + try: + service_args = [] + service_args.append(servconts.DESCRIBE_VM_CMD) + service_args.append(image_name) + counter = 0 + flag = False + while flag == False and counter <= 10: + counter = counter + 1 + time.sleep(2.5) + process = subprocess.Popen(service_args, \ + stdout=subprocess.PIPE) + result = process.stdout.readlines() + if result: + tokens = re.search("running", str(result[1])) + if tokens: + service_status = tokens.group(0) + if service_status == "running": + flag = True + except Exception as exc: + print exc + + def image_exist(self, image_name): + """ + Verifies that the image id is available + """ + try: + service_vm = sdb.get_service_bindings(image_name) + if service_vm: + return True + else: + return False + except Exception as exc: + print exc + + def verify_plugin(self, plugin_key): + """ + Verifies the PlugIn available + """ + _plugins = {} + for key in conf.PLUGINS[const.PLUGINS].keys(): + _plugins[key] = \ + utils.import_object(conf.PLUGINS[const.PLUGINS][key]) + if not plugin_key in _plugins.keys(): + LOG.debug("No %s Plugin loaded" % plugin_key) + return False + else: + LOG.debug("Plugin %s founded" % const.UCS_PLUGIN) + return True + + def press_key(self): + """ + Waits for en external input + """ + key = raw_input("Press any key to continue") + return key diff --git a/quantum/plugins/cisco/tests/unit/test_database.py b/quantum/plugins/cisco/tests/unit/test_database.py index f8298deb8..f1e2f86f7 100644 --- a/quantum/plugins/cisco/tests/unit/test_database.py +++ b/quantum/plugins/cisco/tests/unit/test_database.py @@ -27,6 +27,7 @@ from quantum.plugins.cisco.common import cisco_constants as const import quantum.plugins.cisco.db.api as db import quantum.plugins.cisco.db.l2network_db as l2network_db import quantum.plugins.cisco.db.nexus_db as nexus_db +import quantum.plugins.cisco.db.services_db as services_db import quantum.plugins.cisco.db.ucs_db as ucs_db @@ -191,6 +192,59 @@ class NexusDB(object): % str(exc)) +class ServicesDB(object): + """Class consisting of methods to call services db methods""" + def get_all_servicesbindings(self): + """get all services port bindings""" + bindings = [] + try: + for bind in services_db.get_all_services_bindings(): + LOG.debug("Getting services bindings : %s" % bind.service_id) + bind_dict = {} + bind_dict["service_id"] = str(bind.service_id) + bind_dict["mngnet_id"] = str(bind.mngnet_id) + bind_dict["nbnet_id"] = str(bind.nbnet_id) + bind_dict["sbnet_id"] = str(bind.sbnet_id) + bindings.append(bind_dict) + except Exception, exc: + LOG.error("Failed to get all bindings: %s" % str(exc)) + return bindings + + def get_servicebindings(self, service_id): + """get service binding""" + try: + bind = services_db.get_service_bindings(service_id) + LOG.debug("Getting service binding : %s" % bind.service_id) + return bind + except Exception, exc: + LOG.error("Failed to get service binding: %s" % str(exc)) + + def create_servicebinding(self, service_id, mngnet_id, nbnet_id, sbnet_id): + """create service binding""" + bind_dict = {} + try: + res = services_db.add_services_binding(service_id, mngnet_id, \ + nbnet_id, sbnet_id) + LOG.debug("Created service binding : %s" % res.service_id) + bind_dict["service_id"] = str(res.service_id) + bind_dict["mngnet_id"] = str(res.mngnet_id) + bind_dict["nbnet_id"] = str(res.nbnet_id) + bind_dict["sbnet_id"] = str(res.sbnet_id) + return bind_dict + except Exception, exc: + LOG.error("Failed to create service binding: %s" % str(exc)) + + def delete_servicebinding(self, service_id): + """delete service binding""" + try: + bind = services_db.remove_services_binding(service_id) + for res in bind: + LOG.debug("Deleted service binding: %s" % res.service_id) + except Exception, exc: + raise Exception("Failed to delete service binding: %s" + % str(exc)) + + class L2networkDB(object): """Class conisting of methods to call L2network db methods""" def get_all_vlan_bindings(self): @@ -741,6 +795,72 @@ class NexusDBTest(unittest.TestCase): self.dbtest.delete_nexusportbinding(vlan_id) +class ServicesDBTest(unittest.TestCase): + """Class conisting of services DB unit tests""" + def setUp(self): + """Setup for services db tests""" + l2network_db.initialize() + self.dbtest = ServicesDB() + LOG.debug("Setup") + + def tearDown(self): + """Tear Down""" + db.clear_db() + + def testa_create_servicebinding(self): + """create service binding""" + service_id = self.dbtest.create_servicebinding("i-00001", \ + "mng_net", "northb_net", "northb_net") + self.assertTrue(service_id["service_id"] == "i-00001") + self.tearDown_servicebinding() + + def testb_get_servicesbindings(self): + """get all services binding""" + service_id = self.dbtest.create_servicebinding("i-00001", \ + "mng_net", "northb_net", "northb_net") + bindings = self.dbtest.get_servicebindings("i-00001") + count = 0 + if bindings: + count += 1 + self.assertTrue(count == 1) + self.tearDown_servicebinding() + + def testb_getall_servicesbindings(self): + """get all services binding""" + service_id = self.dbtest.create_servicebinding("i-00001", \ + "mng_net", "northb_net", "northb_net") + service_id = self.dbtest.create_servicebinding("i-00002", \ + "mng_net", "northb_net", "northb_net") + bindings = self.dbtest.get_all_servicesbindings() + count = 0 + for bind in bindings: + if "mng_net" in bind["mngnet_id"]: + count += 1 + self.assertTrue(count == 2) + self.tearDown_servicebinding() + + def testc_delete_servicesbinding(self): + """delete services binding""" + binding_serv = self.dbtest.create_servicebinding("i-00001", \ + "mng_net", "northb_net", "northb_net") + self.dbtest.delete_servicebinding("i-00001") + bindings = self.dbtest.get_all_servicesbindings() + count = 0 + for bind in bindings: + if "mng_net" in bind["mngnet_id"]: + count += 1 + self.assertTrue(count == 0) + self.tearDown_servicebinding() + + def tearDown_servicebinding(self): + """tear down nexusport binding table""" + LOG.debug("Tearing Down Nexus port Bindings") + binds = self.dbtest.get_all_servicesbindings() + for bind in binds: + service_id = bind["service_id"] + self.dbtest.delete_servicebinding(service_id) + + class L2networkDBTest(unittest.TestCase): """Class conisting of L2network DB unit tests""" def setUp(self):