]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Implementation of the BP services-insertion-wrapper inside the Cisco Plugin
authorEdgar Magana <eperdomo@cisco.com>
Tue, 13 Dec 2011 07:41:59 +0000 (23:41 -0800)
committerEdgar Magana <eperdomo@cisco.com>
Tue, 13 Dec 2011 21:35:11 +0000 (13:35 -0800)
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 networks,
in the process applying the service. This model is called In-Path
(Bump in the Wire)

Change-Id: If7e9ad2dcb8124e7d82ac837c5b62c0d09b00bcd

quantum/plugins/cisco/db/api.py
quantum/plugins/cisco/db/l2network_db.py
quantum/plugins/cisco/db/services_db.py [new file with mode: 0644]
quantum/plugins/cisco/db/services_models.py [new file with mode: 0644]
quantum/plugins/cisco/services/README [new file with mode: 0644]
quantum/plugins/cisco/services/__init__.py [new file with mode: 0644]
quantum/plugins/cisco/services/service_insertion.py [new file with mode: 0644]
quantum/plugins/cisco/services/services_constants.py [new file with mode: 0644]
quantum/plugins/cisco/services/services_logistics.py [new file with mode: 0644]
quantum/plugins/cisco/tests/unit/test_database.py

index 3e647ffc337a39313035c4765e9dfd1ac0ddbaad..ed42f044a68559dd96d0a126e5ca3310e2c84971 100644 (file)
@@ -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:
index f9b981948c3e893394a3f3d5ce892f27f09f41f2..c6962f7dd6f5af9c994619bbf6301d9ba02cc896 100644 (file)
@@ -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 (file)
index 0000000..7235cfb
--- /dev/null
@@ -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 (file)
index 0000000..8a3b927
--- /dev/null
@@ -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 "<ServicesBinding (%s,%d)>" % \
+          (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 (file)
index 0000000..3936ca3
--- /dev/null
@@ -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 <tenant_id> <service_image_id>
+    <management_net_name> <northbound_net_name> <southbound_net_name>
+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 <tenant_id> <service_instance_id>
+Deletes the service prevopusly inserted as well as the network dependencies.
+
+connect_vm <tenant_id> <vm_image_id> <service_instance_id>
+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 <vm_instance_id>
+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 (file)
index 0000000..d6e7beb
--- /dev/null
@@ -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 (file)
index 0000000..a393cd1
--- /dev/null
@@ -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 <tenant_id> <service_image_id>
+    <management_net_name> <northbound_net_name> <southbound_net_name>
+2. delete_service <tenant_id> <service_instance_id>
+3. connect_vm <tenant_id> <vm_image_id> <service_instance_id>
+4. disconnect_vm <vm_instance_id>
+"""
+
+
+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] <command> [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 (file)
index 0000000..0ee8012
--- /dev/null
@@ -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 (file)
index 0000000..7550ac5
--- /dev/null
@@ -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
index f8298deb8d80e1595ebe75ea5302e1bfdadaca0c..f1e2f86f73176b5fca92a3b2a58eeac6005594fb 100644 (file)
@@ -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):