fixes to test scripts, added filter option to command line tool
[sfa.git] / cmdline / genicli.py
index 06cb466..b54d4f7 100644 (file)
 
 import getopt
 import sys
-from clientstub import *
+import os
+from cert import *
+from geniclient import *
+from geniticket import *
 
-long_opts = ["username=", "usertype=", "help", "outfile=", "credfile="]
+long_opts = ["keyfile=", "help", "outfile=", "credfile=", "ticketfile=",
+             "username=", "email=", "ip=", "dns=", "dump_parents", "server=",
+             "filter="]
 
 # default command line options
-username = "planetlab.scott.pl.smbaker"
-usertype = "user"
-opname = "lookup"
+username = "client"
+opname = None
 type = None
 hrn = None
-cred_name = None
-id_file = None
+
 key_file = None
-acc_file = None
 cred_file = None
-leaf_name = None
-server_host = "127.0.0.1"
-server_port = 8002
+cert_file = None
 out_file = None
+ticket_file = None
+
+ip = None
+dns = None
+email = None
+uuid = None
+gid_pkey_fn = None
+gid_fn = None
+filter = None
+
+dump_parents = False
+
+leaf_name = None
+server_url = "https://localhost:12345/"
+
+def get_leaf(hrn):
+    parts = hrn.split(".")\r
+    return parts[-1]
 
 def showhelp():
    print "syntax: cli <options> command <args>"
    print "options:"
-   print "    --username       ... hrn of user performing op"
-   print "    --usertype       ... kind of user performing op (user, slice, ...)"
-   print "    --outfile        ... write reply to file"
-   print "    --credfile       ... credential to pass"
+   print "    --username       ... username (or hrn) of user making call"
+   print "    --outfile       ... save response to a file"
+   print "    --credfile       ... credential of user making call (or 'None')"
+   print "    --keyfile        ... private key file of user making call"
+   print "    --ticketfile     ... filename of ticket (for redeemticket)"
+   print "    --email          ... email address (for registering users)"
+   print "    --ip             ... IP address (for registering nodes)"
+   print "    --dns            ... DNS address (for registering nodes)"
+   print "    --dump_parents   ... dump parents"
+   print "    --server         ... geni server (registry/component) to connect to"
+   print "    --filter <type>  ... filter the results of a list operation (user | slice | node ...)"
    print "commands:"
-   print "    lookup <type> <hrn>"
+   print "    resolve <hrn>"
    print "    dumpCredential"
-   print "    getCredential <cred_name>"
+   print "    getCredential <type> <hrn>"
    print "    start <hrn>"
+   print "    createKey <filename>"
+   print "    createGid <hrn> <uuid|None> <pubkey_fn>"
+   print "    register <type> <hrn> <gid_filename>"
+   print "    remove <type> <hrn>"
+   print "    update <type> <hrn>"
+   print "    startSlice"
+   print "    stopSlice"
+   print "    listSlices"
 
 def process_options():
-   global username, usertype, opname
-   global type, hrn, cred_name
-   global leaf_name
-   global id_file, cred_file
-   global acc_file, key_file, out_file
+   global username
+   global opname
+   global type, hrn
+   global cert_file, cred_file
+   global key_file, out_file, ticket_file
+   global uuid, pkey_fn, gid_fn, email, gid_pkey_fn, ip, dns
+   global dump_parents
+   global server_url
+   global filter
 
    (options, args) = getopt.getopt(sys.argv[1:], '', long_opts)
    for opt in options:
        name = opt[0]
        val = opt[1]
 
-       if name == "--username":
-           username = val
-       elif name == "--usertype":
-           usertype = val
-       elif name == "--help":
+       if name == "--help":
            showhelp()
            sys.exit(0)
+       elif name == "--username":
+           username = val
        elif name == "--outfile":
            out_file = val
        elif name == "--credfile":
            cred_file = val
+       elif name == "--certfile":
+           cred_file = val
+       elif name == "--keyfile":
+           key_file = val
+       elif name == "--ticketfile":
+           ticket_file = val
+       elif name == "--email":
+           email = val
+       elif name == "--ip":
+           ip = val
+       elif name == "--dns":
+           dns = val
+       elif name == "--dump_parents":
+           dump_parents = True
+       elif name == "--server":
+           server_url = val
+       elif name == "--filter":
+           filter = val
 
    if not args:
-       report.error("no operation specified")
+       print "no operation specified"
        sys.exit(-1)
 
    opname = args[0]
 
-   if opname == "lookup":
+   if opname == "resolve":
+       if len(args) < 2:
+           print "syntax: resolve <hrn>"
+           sys.exit(-1)
+       hrn = args[1]
+
+   elif opname == "getCredential":
        if len(args) < 3:
-           report.error("syntax: lookup <type> <hrn>")
+           print "syntax: getcredential <type> <hrn>"
            sys.exit(-1)
        type = args[1]
        hrn = args[2]
 
-   elif opname == "getCredential":
-       if len(args) < 1:
-           report.error("syntax: getcredential <cred_name>")
-           sys.exit(-1)
-       cred_name = args[1]
+   elif opname == "createGid":
+       if len(args) < 4:
+           print "syntax: createGid <hrn> <uuid|None> <pubkey_fn>"
+       hrn = args[1]
+       if args[2]=="None":
+           uuid=None
+       else:
+           uuid = int(args[2])
+       gid_pkey_fn = args[3]
+
+   elif opname == "register":
+       if len(args) < 4:
+           print "syntax: register <type> <hrn> <gid_filename>"
+       type = args[1]
+       hrn = args[2]
+       gid_fn = args[3]
+
+   elif opname == "remove":
+       if len(args) < 3:
+           print "syntax: remove <type> <hrn>"
+       type = args[1]
+       hrn = args[2]
+
+   elif opname == "update":
+       if len(args) < 3:
+           print "syntax: update <type> <hrn>"
+       type = args[1]
+       hrn = args[2]
 
-   elif opname == "start":
-       if len(args) < 1:
-           report.error("syntax: start <hrn>")
+   elif opname == "getTicket":
+       if len(args) < 2:
+           print "syntax: getTicket <hrn>"
            sys.exit(-1)
        hrn = args[1]
 
-   if not leaf_name:
-       leaf_name = get_leaf(username)
+   leaf_name = get_leaf(username)
 
-   if id_file == None:
-       id_file = leaf_name + ".cert"
+   if cert_file == None:
+       cert_file = leaf_name + ".cert"
 
    if key_file == None:
        key_file = leaf_name + ".pkey"
 
-   if acc_file == None:
-       acc_file = "acc_file"
-
    if cred_file == None:
-       cred_file = "cred_file"
+       cred_file = leaf_name + ".cred"
 
 def show_options():
+   print "   server:", server_url
    print " username:", username
-   print "     leaf:", leaf_name
-   print " usertype:", usertype
-   print "  id_file:", id_file
+   print "cert_file:", cert_file
    print " key_file:", key_file
-   print " acc_file:", acc_file
    print "cred_file:", cred_file
    print "operation:", opname
    print "     type:", type
    print "      hrn:", hrn
-   print "cred_name:", cred_name
    print " out_file:", out_file
 
 def get_authority(x):
     parts = x.split(".")
     return ".".join(parts[:3])
 
-def compose_message():
-   g_params = {}
-   p_params = {}
-   dict = {"opname": opname}
-
-   if opname == "lookup":
-      g_params["hrn"] = hrn
-      g_params["type"] = type
-
-   elif opname == "getCredential":
-      g_params["cred_name"] = cred_name
-
-      parts = cred_name.split(":")
-      if len(parts) < 2:
-          report.error("bad format for getCredential (slice:hrn.of.slice, ...)")
-
-      # XXX smbaker: this looks redundant
-      if parts[0] == "slice":
-         g_params["hrn"] = get_authority(parts[1])
-         g_params["type"] = "slice"
-
-   elif opname == "start":
-      g_params["hrn"] = hrn
-      g_params["type"] = "slice"
-
-   dict["g_params"] = g_params
-   dict["p_params"] = p_params
-
-   return dict
-
-def do_remote_op():
-   message = compose_message()
-
-   client = GENIClient(username, usertype, id_file, key_file, acc_file, cred_file)
-
-   server = client.connect(server_host, server_port)
-   if not server:
-       report.error("failed to connect to server")
-       sys.exit(-1)
-
-   report.trace("message:" + str(message))
-
-   server.write(str(message))
+def dumpCredential():
+   pass
 
-   reply = server.read(MAX_RESULT)
-   if not reply:
-      report.error("No reply")
-      sys.exit(-1)
+# creates a self-signed certificate and private key
+def createKey():
+   k = Keypair(create=True)
 
-   if out_file:
-      open(out_file, "w").write(reply)
+   self_signed = False
+   if self_signed:
+      ik = k
+      iname = username
    else:
-      print "////// RESULT: //////"
-      print reply
-
-def dumpCredential():
-    cred_str = open(cred_file).read()
-    c_pem = X509.load_cert_string(cred_str)
-    subjectAltName = c_pem.get_ext("subjectAltName").get_value()
-    info_cert = get_cred_info(subjectAltName)
-
-    print "subject:", c_pem.get_subject().CN
-    print "issuer:", c_pem.get_issuer().CN
-    print "cred_str:"
-    print " ", subjectAltName
-    print "rights:"
-    op_set = info_cert['operation_set']
-    for item in op_set.keys():
-       rights = op_set[item]
-       print " ", item, ", ".join(rights)
-
-    print "interfaces:"
-    interfaces = info_cert['on_interfaces']
-    for item in interfaces:
-       print " ", item['lbl'], item['type'], item['name']
+      ik = Keypair(create=True)
+      iname = "issuer"
+
+   print "writing private key to", key_file
+   k.save_to_file(key_file)
+
+   #cert = Certificate(subject=username)
+   #cert.set_pubkey(k)
+   #cert.set_issuer(ik, iname)
+   #cert.sign()
+   #print "writing self-signed cert to", cert_file
+   #cert.save_to_file(cert_file)
+
+def load_publickey_string(fn):
+   f = file(fn,"r")
+   key_string = f.read()
+
+   # if the filename is a private key file, then extract the public key
+   if "PRIVATE KEY" in key_string:
+       outfn = tempfile.mktemp()
+       cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
+       os.system(cmd)
+       f = file(outfn, "r")
+       key_string = f.read()
+       os.remove(outfn)
+
+   return key_string
 
 def main():
    process_options()
    show_options()
 
+   result = None
+
+   # if the operation is not a local operation, then create a geniclient to
+   # talk to the server
+   if (opname != "dumpCredential") and (opname != "help") and (opname != "createKey"):
+       if not os.path.exists(key_file):
+           print "key file", key_file, "does not exist"
+           sys.exit(-1)
+       if not os.path.exists(cert_file):
+           k = Keypair(filename = key_file)
+           cert = Certificate(subject=username)
+           cert.set_pubkey(k)
+           cert.set_issuer(k, username)
+           cert.sign()
+           print "writing self-signed cert to", cert_file
+           cert.save_to_file(cert_file)
+       client = GeniClient(server_url, key_file, cert_file)
+
+   # if a cred_file was specified, then load the credential
+   if (cred_file=="None") or (opname == "help") or (opname == "createKey") or \
+      (opname == "redeemTicket"):
+      cred = None
+   else:
+      cred = Credential(filename = cred_file)
+
    if opname == "dumpCredential":
       dumpCredential()
-      sys.exit(0)
-      
+
    elif opname == "help":
       showhelp()
-      sys.exit(0)
 
-   elif (opname == "lookup") or \
-        (opname == "getCredential") or \
-        (opname == "start"):
-      do_remote_op()
+   elif opname == "createKey":
+      createKey()
+
+   elif (opname == "resolve"):
+      result = client.resolve(cred, hrn)
+      if result:
+          for record in result:
+              print "RESULT:"
+              record.dump(dump_parents=dump_parents)
+      else:
+          print "NO RESULT"
+
+   elif (opname == "getCredential"):
+      result = client.get_credential(cred, type, hrn)
+      if result:
+          print "RESULT:"
+          result.dump(dump_parents=dump_parents)
+          if out_file:
+              file(out_file, "w").write(result.save_to_string(save_parents=True))
+      else:
+          print "NO RESULT"
+
+   elif (opname == "list"):
+      result = client.list(cred)
+      if result:
+          if filter:
+              result = [r for r in result if r.type==filter]
+          for record in result:
+              print "RESULT:"
+              record.dump(dump_parents=dump_parents)
+      else:
+          print "NO RESULT"
+
+   elif (opname == "createGid"):
+       # try loading it from a private or a public key file
+       pkey_string = load_publickey_string(gid_pkey_fn)
+
+       gid = client.create_gid(cred, hrn, uuid, pkey_string)
+       if gid:
+           print "RESULT:"
+           gid.dump(dump_parents=dump_parents)
+           if out_file:
+               file(out_file,"w").write(gid.save_to_string(save_parents=True))
+       else:
+           print "NO RESULT"
+
+   elif (opname == "register"):
+       geni_info = {}
+       if type == "user":
+           if not email:
+               print "ERROR: must specify --email <addr> when registering users"
+           geni_info['email'] = email
+
+       if type == "node":
+           if not ip:
+               print "ERROR: must specify --ip <addr> when registering nodes"
+           geni_info['ip'] = ip
+           if not dns:
+               print "ERROR: must specify --dns <addr> when registering nodes"
+           geni_info['dns'] = dns
+
+       gid = GID(filename=gid_fn)
+       record = GeniRecord(name=hrn, gid=gid, type=type, pointer=-1)
+       record.set_geni_info(geni_info)
+
+       result = client.register(cred, record)
+
+   elif (opname == "remove"):
+       record_list = client.resolve(cred, hrn)
+       if not record_list:
+           print "no records match hrn"
+
+       matching_records = []
+       for record in record_list:
+           if record.get_type() == type:
+               matching_records.append(record)
+
+       if not matching_records:
+           print "records match hrn, but no records match type"
+
+       for record in matching_records:
+           client.remove(cred,record)
+
+   elif (opname == "update"):
+       record_list = client.resolve(cred, hrn)
+       if not record_list:
+           print "no records match hrn"
+
+       matching_records = []
+       for record in record_list:
+           if record.get_type() == type:
+               matching_records.append(record)
+
+       if not matching_records:
+           print "records match hrn, but no records match type"
+
+       for record in matching_records:
+           geni_info = record.get_geni_info()
+
+           if email:
+               geni_info['email'] = email
+           if ip:
+               geni_info['ip'] = ip
+           if dns:
+               geni_info['dns'] = dns
+
+           client.update(cred, record)
+
+   elif (opname == "stopSlice"):
+       client.stop_slice(cred)
+
+   elif (opname == "startSlice"):
+       client.start_slice(cred)
+
+   elif (opname == "resetSlice"):
+       client.reset_slice(cred)
+
+   elif (opname == "deleteSlice"):
+       client.delete_slice(cred)
+
+   elif (opname == "listSlices"):
+       result = client.list_slices(cred)
+       print "RESULT:"
+       print "\n".join(result)
+       if out_file:
+           file(out_file,"w").write("\n".join(result))
+
+   elif (opname == "getTicket"):
+      result = client.get_ticket(cred, hrn, {})
+      if result:
+          print "RESULT:"
+          result.dump(dump_parents=dump_parents)
+          if out_file:
+              file(out_file,"w").write(result.save_to_string(save_parents=True))
+      else:
+          print "NO RESULT"
+
+   elif (opname == "redeemTicket"):
+       ticket = Ticket(filename = ticket_file)
+       result = client.redeem_ticket(ticket)
 
    else:
-      report.error("unknown operation: " + opname)
+      print "unknown operation: " + opname
 
 if __name__=="__main__":
    main()