]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Initial rework of cli to use the WS api
authorBrad Hall <bhall@nicira.com>
Fri, 3 Jun 2011 05:30:37 +0000 (22:30 -0700)
committerBrad Hall <bhall@nicira.com>
Fri, 3 Jun 2011 05:30:37 +0000 (22:30 -0700)
- Still need to implement the interface commands and also address TODO's in
  the code.

quantum/api/views/ports.py
quantum/cli.py

index 2d93a35f60bd6c849131b8b839b647be83498465..6077214b68fab9bdcdde6413743fd41a999b4193 100644 (file)
@@ -37,12 +37,13 @@ class ViewBuilder(object):
         else:
             port = self._build_simple(port_data)
         return port
-    
+
     def _build_simple(self, port_data):
         """Return a simple model of a server."""
         return dict(port=dict(id=port_data['port-id']))
-    
+
     def _build_detail(self, port_data):
         """Return a simple model of a server."""
         return dict(port=dict(id=port_data['port-id'],
-                              state=port_data['port-state']))
+          attachment=port_data['attachment'],
+          state=port_data['port-state']))
index 78f1a6b4811719ddcdbcbec6c3cebb5b283c5aa0..faa92f1b16803a2d2d95b4f48e79df39e78781f1 100644 (file)
@@ -1,6 +1,7 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2011, Nicira Networks, Inc.
+# Copyright 2011 Nicira Networks, Inc.
+# Copyright 2011 Citrix Systems
 #
 #    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
 #    License for the specific language governing permissions and limitations
 #    under the License.
 # @author: Somik Behera, Nicira Networks, Inc.
+# @author: Brad Hall, Nicira Networks, Inc.
 
+import httplib
+import logging as LOG
+import simplejson
+import socket
 import sys
+import urllib
 
 from manager import QuantumManager
+from optparse import OptionParser
+from quantum.common.wsgi import Serializer
 
+FORMAT = "json"
+CONTENT_TYPE = "application/" + FORMAT
 
-def usage():
-    print "\nUsage:"
-    print "list_nets <tenant-id>"
-    print "create_net <tenant-id> <net-name>"
-    print "delete_net <tenant-id> <net-id>"
-    print "detail_net <tenant-id> <net-id>"
-    print "rename_net <tenant-id> <net-id> <new name>"
-    print "list_ports <tenant-id> <net-id>"
-    print "create_port <tenant-id> <net-id>"
-    print "delete_port <tenant-id> <net-id> <port-id>"
-    print "detail_port <tenant-id> <net-id> <port-id>"
-    print "plug_iface <tenant-id> <net-id> <port-id> <iface-id>"
-    print "unplug_iface <tenant-id> <net-id> <port-id>"
-    print "detail_iface <tenant-id> <net-id> <port-id>"
-    print "list_iface <tenant-id> <net-id>\n"
-
-if len(sys.argv) < 2 or len(sys.argv) > 6: 
-    usage()
-    exit(1)
-
-quantum = QuantumManager()
-manager = quantum.get_manager()
-
-if sys.argv[1] == "list_nets" and len(sys.argv) == 3:
-    network_on_tenant = manager.get_all_networks(sys.argv[2])
-    print "Virtual Networks on Tenant:%s\n" % sys.argv[2]
-    for k, v in network_on_tenant.iteritems():
-        print"\tNetwork ID:%s \n\tNetwork Name:%s \n" % (k, v)
-elif sys.argv[1] == "create_net" and len(sys.argv) == 4:
-    new_net_id = manager.create_network(sys.argv[2], sys.argv[3])
+### --- Miniclient (taking from the test directory)
+### TODO(bgh): move this to a library within quantum
+class MiniClient(object):
+    """A base client class - derived from Glance.BaseClient"""
+    action_prefix = '/v0.1/tenants/{tenant_id}'
+    def __init__(self, host, port, use_ssl):
+        self.host = host
+        self.port = port
+        self.use_ssl = use_ssl
+        self.connection = None
+    def get_connection_type(self):
+        if self.use_ssl:
+            return httplib.HTTPSConnection
+        else:
+            return httplib.HTTPConnection
+    def do_request(self, tenant, method, action, body=None,
+                   headers=None, params=None):
+        action = MiniClient.action_prefix + action
+        action = action.replace('{tenant_id}',tenant)
+        if type(params) is dict:
+            action += '?' + urllib.urlencode(params)
+        try:
+            connection_type = self.get_connection_type()
+            headers = headers or {}
+            # Open connection and send request
+            c = connection_type(self.host, self.port)
+            c.request(method, action, body, headers)
+            res = c.getresponse()
+            status_code = self.get_status_code(res)
+            if status_code in (httplib.OK, httplib.CREATED,
+                               httplib.ACCEPTED, httplib.NO_CONTENT):
+                return res
+            else:
+                raise Exception("Server returned error: %s" % res.read())
+        except (socket.error, IOError), e:
+            raise Exception("Unable to connect to server. Got error: %s" % e)
+    def get_status_code(self, response):
+        if hasattr(response, 'status_int'):
+            return response.status_int
+        else:
+            return response.status
+### -- end of miniclient
+
+### -- Core CLI functions
+
+def list_nets(manager, *args):
+    tenant_id = args[0]
+    networks = manager.get_all_networks(tenant_id)
+    print "Virtual Networks on Tenant:%s\n" % tenant_id
+    for net in networks:
+        id = net["net-id"]
+        name = net["net-name"]
+        print "\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name)
+
+def api_list_nets(client, *args):
+    tenant_id = args[0]
+    res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT)
+    resdict = simplejson.loads(res.read())
+    LOG.debug(resdict)
+    print "Virtual Networks on Tenant:%s\n" % tenant_id
+    for n in resdict["networks"]:
+        net_id = n["id"]
+        print "\tNetwork ID:%s\n" % (net_id)
+        # TODO(bgh): we should make this call pass back the name too
+        # name = n["net-name"]
+        # LOG.info("\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name))
+
+def create_net(manager, *args):
+    tid, name = args
+    new_net_id = manager.create_network(tid, name)
     print "Created a new Virtual Network with ID:%s\n" % new_net_id
-elif sys.argv[1] == "delete_net" and len(sys.argv) == 4:
-    manager.delete_network(sys.argv[2], sys.argv[3])
-    print "Deleted Virtual Network with ID:%s" % sys.argv[3]
-elif sys.argv[1] == "detail_net" and len(sys.argv) == 4:
-    vif_list = manager.get_network_details(sys.argv[2], sys.argv[3])
-    print "Remote Interfaces on Virtual Network:%s\n" % sys.argv[3]
-    for iface in vif_list:
-        print "\tRemote interface :%s" % iface
-elif sys.argv[1] == "rename_net" and len(sys.argv) == 5:
-    manager.rename_network(sys.argv[2], sys.argv[3], sys.argv[4])
-    print "Renamed Virtual Network with ID:%s" % sys.argv[3]
-elif sys.argv[1] == "list_ports" and len(sys.argv) == 4:
-    ports = manager.get_all_ports(sys.argv[2], sys.argv[3])
-    print " Virtual Ports on Virtual Network:%s\n" % sys.argv[3]
+
+def api_create_net(client, *args):
+    tid, name = args
+    data = {'network': {'network-name': '%s' % name}}
+    body = Serializer().serialize(data, CONTENT_TYPE)
+    res = client.do_request(tid, 'POST', "/networks." + FORMAT, body=body)
+    rd = simplejson.loads(res.read())
+    LOG.debug(rd)
+    nid = None
+    try:
+        nid = rd["networks"]["network"]["id"]
+    except Exception, e:
+        print "Failed to create network"
+        # TODO(bgh): grab error details from ws request result
+        return
+    print "Created a new Virtual Network with ID:%s\n" % nid
+
+def delete_net(manager, *args):
+    tid, nid = args
+    manager.delete_network(tid, nid)
+    print "Deleted Virtual Network with ID:%s" % nid
+
+def api_delete_net(client, *args):
+    tid, nid = args
+    res = client.do_request(tid, 'DELETE', "/networks/" + nid + "." + FORMAT)
+    status = res.status
+    if status != 202:
+        print "Failed to delete network"
+        output = res.read()
+        print output
+    else:
+        print "Deleted Virtual Network with ID:%s" % nid
+
+def detail_net(manager, *args):
+    tid, nid = args
+    network = manager.get_network_details(tid, nid)
+    network_id = network["net-id"]
+    network_name = network["net-name"]
+    print "\tNetwork id:%s\n\tNetwork name:%s\n" % (network_id, network_name)
+
+def api_detail_net(client, *args):
+    tid, nid = args
+    res = client.do_request(tid, 'GET', "/networks/" + nid + "." + FORMAT)
+    output = res.read()
+    rd = simplejson.loads(output)
+    LOG.debug(rd)
+    network_id = rd["networks"]["network"]["id"]
+    network_name = rd["networks"]["network"]["name"]
+    print "\tNetwork id:%s\n\tNetwork name:%s\n" % (network_id, network_name)
+
+def rename_net(manager, *args):
+    tid, nid, name = args
+    manager.rename_network(tid, nid, name)
+    print "Renamed Virtual Network with ID:%s" % nid
+
+def api_rename_net(client, *args):
+    tid, nid, name = args
+    data = {'network': {'network-name': '%s' % name}}
+    body = Serializer().serialize(data, CONTENT_TYPE)
+    res = client.do_request(tid, 'PUT', "/networks/%s.%s" % (nid, FORMAT),
+      body=body)
+    resdict = simplejson.loads(res.read())
+    LOG.debug(resdict)
+    print "Renamed Virtual Network with ID:%s" % nid
+
+# TODO(bgh): fix this command
+def list_ports(manager, *args):
+    tid, nid = args
+    ports = manager.get_all_ports(tid, nid)
+    print "Ports on Virtual Network:%s\n" % nid
     for port in ports:
-        print "\tVirtual Port:%s" % port
-elif sys.argv[1] == "create_port" and len(sys.argv) == 4:
-    new_port = manager.create_port(sys.argv[2], sys.argv[3])
+        "\tVirtual Port:%s" % port
+
+def api_list_ports(client, *args):
+    tid, nid = args
+    res = client.do_request(tid, 'GET',
+      "/networks/%s/ports.%s" % (nid, FORMAT))
+    output = res.read()
+    if res.status != 200:
+        LOG.error("Failed to list ports: %s" % output)
+        return
+    rd = simplejson.loads(output)
+    LOG.debug(rd)
+    print "Ports on Virtual Network:%s\n" % nid
+    for port in rd["ports"]:
+        print "\tVirtual Port:%s" % port["id"]
+
+def create_port(manager, *args):
+    tid, nid = args
+    new_port = manager.create_port(tid, nid)
+    print "Created Virtual Port:%s " \
+          "on Virtual Network:%s" % (new_port, nid)
+
+def api_create_port(client, *args):
+    tid, nid = args
+    res = client.do_request(tid, 'POST',
+      "/networks/%s/ports.%s" % (nid, FORMAT))
+    output = res.read()
+    if res.status != 200:
+        LOG.error("Failed to create port: %s" % output)
+        return
+    rd = simplejson.loads(output)
+    new_port = rd["ports"]["port"]["id"]
     print "Created Virtual Port:%s " \
-          "on Virtual Network:%s" % (new_port, sys.argv[3])
-elif sys.argv[1] == "delete_port" and len(sys.argv) == 5:
-    manager.delete_port(sys.argv[2], sys.argv[3], sys.argv[4])
-    print "Deleted Virtual Port:%s " \
-          "on Virtual Network:%s" % (sys.argv[3], sys.argv[4])
-elif sys.argv[1] == "detail_port" and len(sys.argv) == 5:
-    port_detail = manager.get_port_details(sys.argv[2],
-                                           sys.argv[3], sys.argv[4])
+          "on Virtual Network:%s" % (new_port, nid)
+
+def delete_port(manager, *args):
+    tid, nid, pid = args
+    LOG.info("Deleted Virtual Port:%s " \
+          "on Virtual Network:%s" % (pid, nid))
+
+def api_delete_port(client, *args):
+    tid, nid, pid = args
+    res = client.do_request(tid, 'DELETE',
+      "/networks/%s/ports/%s.%s" % (nid, pid, FORMAT))
+    output = res.read()
+    if res.status != 202:
+        LOG.error("Failed to delete port: %s" % output)
+        return
+    LOG.info("Deleted Virtual Port:%s " \
+          "on Virtual Network:%s" % (pid, nid))
+
+def detail_port(manager, *args):
+    tid, nid, pid = args
+    port_detail = manager.get_port_details(tid, nid, pid)
+    print "Virtual Port:%s on Virtual Network:%s " \
+          "contains remote interface:%s" % (pid, nid, port_detail)
+
+def api_detail_port(client, *args):
+    tid, nid, pid = args
+    res = client.do_request(tid, 'GET',
+      "/networks/%s/ports/%s.%s" % (nid, pid, FORMAT))
+    output = res.read()
+    if res.status != 200:
+        LOG.error("Failed to get port details: %s" % output)
+        return
+    rd = simplejson.loads(output)
+    port = rd["ports"]["port"]
+    id = port["id"]
+    attachment = port["attachment"]
+    LOG.debug(port)
     print "Virtual Port:%s on Virtual Network:%s " \
-          "contains remote interface:%s" % (sys.argv[3],
-                                            sys.argv[4],
-                                            port_detail)
-elif sys.argv[1] == "plug_iface" and len(sys.argv) == 6:
-    manager.plug_interface(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])
-    print "Plugged remote interface:%s " \
-          "into Virtual Network:%s" % (sys.argv[5], sys.argv[3])
-elif sys.argv[1] == "unplug_iface" and len(sys.argv) == 5:
-    manager.unplug_interface(sys.argv[2], sys.argv[3], sys.argv[4])
-    print "UnPlugged remote interface " \
-          "from Virtual Port:%s Virtual Network:%s" % (sys.argv[4], 
-                                                       sys.argv[3])
-elif sys.argv[1] == "detail_iface" and len(sys.argv) == 5:
-    remote_iface = manager.get_interface_details(sys.argv[2], 
-                                                 sys.argv[3], sys.argv[4])
-    print "Remote interface on Virtual Port:%s " \
-          "Virtual Network:%s is %s" % (sys.argv[4], 
-                                        sys.argv[3], remote_iface)
-elif sys.argv[1] == "list_iface" and len(sys.argv) == 4:
-    iface_list = manager.get_all_attached_interfaces(sys.argv[2], sys.argv[3])
-    print "Remote Interfaces on Virtual Network:%s\n" % sys.argv[3]
+          "contains remote interface:%s" % (pid, nid, attachment)
+
+# TODO(bgh): still need to implement the iface commands
+def plug_iface(manager, *args):
+    tid, nid, pid, vid = args
+    manager.plug_interface(tid, nid, pid, vid)
+    LOG.info("Plugged remote interface:%s " \
+      "into Virtual Network:%s" % (vid, nid))
+
+def unplug_iface(manager,  *args):
+    tid, nid, pid = args
+    manager.unplug_interface(tid, nid, pid)
+    LOG.info("UnPlugged remote interface " \
+          "from Virtual Port:%s Virtual Network:%s" % (pid, nid))
+
+def detail_iface(manager, *args):
+    tid, nid, pid = args
+    remote_iface = manager.get_interface_details(tid, nid, pid)
+    LOG.info("Remote interface on Virtual Port:%s " \
+          "Virtual Network:%s is %s" % (pid, nid, remote_iface))
+
+def list_iface(manager, *args):
+    tid, nid = args
+    iface_list = manager.get_all_attached_interfaces(tid, nid)
+    LOG.info("Remote Interfaces on Virtual Network:%s\n" % nid)
     for iface in iface_list:
-        print "\tRemote interface :%s" % iface
-elif sys.argv[1] == "all" and len(sys.argv) == 2:
-    print "Not Implemented"
-else:
-    print "invalid arguments: %s" % str(sys.argv)
-    usage()
+        LOG.info("\tRemote interface :%s" % iface)
+
+commands = {
+  "list_nets": {
+    "func": list_nets,
+    "api_func": api_list_nets,
+    "args": ["tenant-id"]
+    },
+  "create_net": {
+    "func": create_net,
+    "api_func": api_create_net,
+    "args": ["tenant-id", "net-name"]
+    },
+  "delete_net": {
+    "func": delete_net,
+    "api_func": api_delete_net,
+    "args": ["tenant-id", "net-id"]
+    },
+  "detail_net": {
+    "func": detail_net,
+    "api_func": api_detail_net,
+    "args": ["tenant-id", "net-id"]
+    },
+  "rename_net": {
+    "func": rename_net,
+    "api_func": api_rename_net,
+    "args": ["tenant-id", "net-id", "new-name"]
+    },
+  "list_ports": {
+    "func": list_ports,
+    "api_func": api_list_ports,
+    "args": ["tenant-id", "net-id"]
+    },
+  "create_port": {
+    "func": create_port,
+    "api_func": api_create_port,
+    "args": ["tenant-id", "net-id"]
+    },
+  "delete_port": {
+    "func": delete_port,
+    "api_func": api_delete_port,
+    "args": ["tenant-id", "net-id", "port-id"]
+    },
+  "detail_port": {
+    "func": detail_port,
+    "api_func": api_detail_port,
+    "args": ["tenant-id", "net-id", "port-id"]
+    },
+  "plug_iface": {
+    "func": plug_iface,
+    "args": ["tenant-id", "net-id", "port-id", "iface-id"]
+    },
+  "unplug_iface": {
+    "func": unplug_iface,
+    "args": ["tenant-id", "net-id", "port-id"]
+    },
+  "detail_iface": {
+    "func": detail_iface,
+    "args": ["tenant-id", "net-id", "port-id"]
+    },
+  "list_iface": {
+    "func": list_iface,
+    "args": ["tenant-id", "net-id"]
+    },
+  }
+
+def help():
+    print "\nCommands:"
+    for k in commands.keys():
+        print "    %s %s" % (k,
+          " ".join(["<%s>" % y for y in commands[k]["args"]]))
+
+def build_args(cmd, cmdargs, arglist):
+    args = []
+    orig_arglist = arglist[:]
+    try:
+        for x in cmdargs:
+            args.append(arglist[0])
+            del arglist[0]
+    except Exception, e:
+        LOG.error("Not enough arguments for \"%s\" (expected: %d, got: %d)" % (
+          cmd, len(cmdargs), len(orig_arglist)))
+        print "Usage:\n    %s %s" % (cmd,
+          " ".join(["<%s>" % y for y in commands[cmd]["args"]]))
+        return None
+    if len(arglist) > 0:
+        LOG.error("Too many arguments for \"%s\" (expected: %d, got: %d)" % (
+          cmd, len(cmdargs), len(orig_arglist)))
+        print "Usage:\n    %s %s" % (cmd,
+          " ".join(["<%s>" % y for y in commands[cmd]["args"]]))
+        return None
+    return args
+
+if __name__ == "__main__":
+    usagestr = "Usage: %prog [OPTIONS] <command> [args]"
+    parser = OptionParser(usage=usagestr)
+    parser.add_option("-a", "--use-api", dest="use_api",
+      action="store_true", default=False, help="Use WS API")
+    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 poort")
+    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")
+
+    options, args = parser.parse_args()
+
+    if options.verbose:
+        LOG.basicConfig(level=LOG.DEBUG)
+    else:
+        LOG.basicConfig(level=LOG.WARN)
+
+    if len(args) < 1:
+        parser.print_help()
+        sys.exit(1)
+
+    cmd = args[0]
+    if cmd not in commands.keys():
+        LOG.error("Unknown command: %s" % cmd)
+        help()
+        sys.exit(1)
 
+    args = build_args(cmd, commands[cmd]["args"], args[1:])
+    if not args:
+        sys.exit(1)
+    LOG.debug("Executing command \"%s\" with args: %s" % (cmd, args))
+    if options.use_api:
+        client = MiniClient(options.host, options.port, options.ssl)
+        if not commands[cmd].has_key("api_func"):
+            LOG.error("API version of \"%s\" is not yet implemented" % cmd)
+            sys.exit(1)
+        commands[cmd]["api_func"](client, *args)
+    else:
+        quantum = QuantumManager()
+        manager = quantum.get_manager()
+        commands[cmd]["func"](manager, *args)
+    sys.exit(0)