Merge branch 'master' into senslab2
[sfa.git] / sfa / client / sfi.py
old mode 100755 (executable)
new mode 100644 (file)
index b4aac4f..43e5b98
@@ -1,47 +1,73 @@
-#! /usr/bin/env python
-
-# sfi -- slice-based facility interface
-
-# xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
+#
+# sfi.py - basic SFA command-line client
+# this module is also used in sfascan
+#
 
 import sys
 sys.path.append('.')
 
 import os, os.path
 
 import sys
 sys.path.append('.')
 
 import os, os.path
-import tempfile
 import socket
 import socket
+import re
 import datetime
 import codecs
 import pickle
 import datetime
 import codecs
 import pickle
+import json
 from lxml import etree
 from StringIO import StringIO
 from optparse import OptionParser
 from lxml import etree
 from StringIO import StringIO
 from optparse import OptionParser
+from pprint import PrettyPrinter
 
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.gid import GID
 from sfa.trust.credential import Credential
 from sfa.trust.sfaticket import SfaTicket
 
 
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.gid import GID
 from sfa.trust.credential import Credential
 from sfa.trust.sfaticket import SfaTicket
 
+from sfa.util.faults import SfaInvalidArgument
 from sfa.util.sfalogging import sfi_logger
 from sfa.util.sfalogging import sfi_logger
-from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
+from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
 from sfa.util.config import Config
 from sfa.util.version import version_core
 from sfa.util.cache import Cache
 
 from sfa.util.config import Config
 from sfa.util.version import version_core
 from sfa.util.cache import Cache
 
-from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
+from sfa.storage.record import Record
 
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.rspec_converter import RSpecConverter
 from sfa.rspecs.version_manager import VersionManager
 
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.rspec_converter import RSpecConverter
 from sfa.rspecs.version_manager import VersionManager
-from sfa.client.return_value import ReturnValue
 
 
-import sfa.client.sfaprotocol as sfaprotocol
+from sfa.client.sfaclientlib import SfaClientBootstrap
+from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
+from sfa.client.return_value import ReturnValue
 
 
-AGGREGATE_PORT=12346
 CM_PORT=12346
 
 # utility methods here
 CM_PORT=12346
 
 # utility methods here
+def optparse_listvalue_callback(option, option_string, value, parser):
+    setattr(parser.values, option.dest, value.split(','))
+
+# a code fragment that could be helpful for argparse which unfortunately is 
+# available with 2.7 only, so this feels like too strong a requirement for the client side
+#class ExtraArgAction  (argparse.Action):
+#    def __call__ (self, parser, namespace, values, option_string=None):
+# would need a try/except of course
+#        (k,v)=values.split('=')
+#        d=getattr(namespace,self.dest)
+#        d[k]=v
+#####
+#parser.add_argument ("-X","--extra",dest='extras', default={}, action=ExtraArgAction,
+#                     help="set extra flags, testbed dependent, e.g. --extra enabled=true")
+    
+def optparse_dictvalue_callback (option, option_string, value, parser):
+    try:
+        (k,v)=value.split('=',1)
+        d=getattr(parser.values, option.dest)
+        d[k]=v
+    except:
+        parser.print_help()
+        sys.exit(1)
+
 # display methods
 def display_rspec(rspec, format='rspec'):
     if format in ['dns']:
 # display methods
 def display_rspec(rspec, format='rspec'):
     if format in ['dns']:
@@ -71,7 +97,7 @@ def display_records(recordList, dump=False):
 
 def display_record(record, dump=False):
     if dump:
 
 def display_record(record, dump=False):
     if dump:
-        record.dump()
+        record.dump(sort=True)
     else:
         info = record.getdict()
         print "%s (%s)" % (info['hrn'], info['type'])
     else:
         info = record.getdict()
         print "%s (%s)" % (info['hrn'], info['type'])
@@ -87,16 +113,28 @@ def filter_records(type, records):
 
 
 # save methods
 
 
 # save methods
-def save_variable_to_file(var, filename, format="text"):
-    f = open(filename, "w")
+def save_raw_to_file(var, filename, format="text", banner=None):
+    if filename == "-":
+        # if filename is "-", send it to stdout
+        f = sys.stdout
+    else:
+        f = open(filename, "w")
+    if banner:
+        f.write(banner+"\n")
     if format == "text":
         f.write(str(var))
     elif format == "pickled":
         f.write(pickle.dumps(var))
     if format == "text":
         f.write(str(var))
     elif format == "pickled":
         f.write(pickle.dumps(var))
+    elif format == "json":
+        if hasattr(json, "dumps"):
+            f.write(json.dumps(var))   # python 2.6
+        else:
+            f.write(json.write(var))   # python 2.5
     else:
         # this should never happen
         print "unknown output format", format
     else:
         # this should never happen
         print "unknown output format", format
-
+    if banner:
+        f.write('\n'+banner+"\n")
 
 def save_rspec_to_file(rspec, filename):
     if not filename.endswith(".rspec"):
 
 def save_rspec_to_file(rspec, filename):
     if not filename.endswith(".rspec"):
@@ -106,58 +144,84 @@ def save_rspec_to_file(rspec, filename):
     f.close()
     return
 
     f.close()
     return
 
-def save_records_to_file(filename, recordList, format="xml"):
+def save_records_to_file(filename, record_dicts, format="xml"):
     if format == "xml":
         index = 0
     if format == "xml":
         index = 0
-        for record in recordList:
+        for record_dict in record_dicts:
             if index > 0:
             if index > 0:
-                save_record_to_file(filename + "." + str(index), record)
+                save_record_to_file(filename + "." + str(index), record_dict)
             else:
             else:
-                save_record_to_file(filename, record)
+                save_record_to_file(filename, record_dict)
             index = index + 1
     elif format == "xmllist":
         f = open(filename, "w")
         f.write("<recordlist>\n")
             index = index + 1
     elif format == "xmllist":
         f = open(filename, "w")
         f.write("<recordlist>\n")
-        for record in recordList:
-            record = SfaRecord(dict=record)
-            f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
+        for record_dict in record_dicts:
+            record_obj=Record(dict=record_dict)
+            f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
         f.write("</recordlist>\n")
         f.close()
     elif format == "hrnlist":
         f = open(filename, "w")
         f.write("</recordlist>\n")
         f.close()
     elif format == "hrnlist":
         f = open(filename, "w")
-        for record in recordList:
-            record = SfaRecord(dict=record)
-            f.write(record.get_name() + "\n")
+        for record_dict in record_dicts:
+            record_obj=Record(dict=record_dict)
+            f.write(record_obj.hrn + "\n")
         f.close()
     else:
         # this should never happen
         print "unknown output format", format
 
         f.close()
     else:
         # this should never happen
         print "unknown output format", format
 
-def save_record_to_file(filename, record):
-    if record['type'] in ['user']:
-        record = UserRecord(dict=record)
-    elif record['type'] in ['slice']:
-        record = SliceRecord(dict=record)
-    elif record['type'] in ['node']:
-        record = NodeRecord(dict=record)
-    elif record['type'] in ['authority', 'ma', 'sa']:
-        record = AuthorityRecord(dict=record)
-    else:
-        record = SfaRecord(dict=record)
-    str = record.save_to_string()
+def save_record_to_file(filename, record_dict):
+    record = Record(dict=record_dict)
+    xml = record.save_as_xml()
     f=codecs.open(filename, encoding='utf-8',mode="w")
     f=codecs.open(filename, encoding='utf-8',mode="w")
-    f.write(str)
+    f.write(xml)
     f.close()
     return
 
     f.close()
     return
 
+# minimally check a key argument
+def check_ssh_key (key):
+    good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
+    return re.match(good_ssh_key, key, re.IGNORECASE)
 
 # load methods
 
 # load methods
+def load_record_from_opts(options):
+    record_dict = {}
+    if hasattr(options, 'xrn') and options.xrn:
+        if hasattr(options, 'type') and options.type:
+            xrn = Xrn(options.xrn, options.type)
+        else:
+            xrn = Xrn(options.xrn)
+        record_dict['urn'] = xrn.get_urn()
+        record_dict['hrn'] = xrn.get_hrn()
+        record_dict['type'] = xrn.get_type()
+    if hasattr(options, 'key') and options.key:
+        try:
+            pubkey = open(options.key, 'r').read()
+        except IOError:
+            pubkey = options.key
+        if not check_ssh_key (pubkey):
+            raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
+        record_dict['keys'] = [pubkey]
+    if hasattr(options, 'slices') and options.slices:
+        record_dict['slices'] = options.slices
+    if hasattr(options, 'researchers') and options.researchers:
+        record_dict['researcher'] = options.researchers
+    if hasattr(options, 'email') and options.email:
+        record_dict['email'] = options.email
+    if hasattr(options, 'pis') and options.pis:
+        record_dict['pi'] = options.pis
+
+    # handle extra settings
+    record_dict.update(options.extras)
+    
+    return Record(dict=record_dict)
+
 def load_record_from_file(filename):
     f=codecs.open(filename, encoding="utf-8", mode="r")
 def load_record_from_file(filename):
     f=codecs.open(filename, encoding="utf-8", mode="r")
-    str = f.read()
+    xml_string = f.read()
     f.close()
     f.close()
-    record = SfaRecord(string=str)
-    return record
+    return Record(xml=xml_string)
 
 
 import uuid
 
 
 import uuid
@@ -165,7 +229,15 @@ def unique_call_id(): return uuid.uuid4().urn
 
 class Sfi:
     
 
 class Sfi:
     
-    required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user']
+    # dirty hack to make this class usable from the outside
+    required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user', 'user_private_key']
+
+    @staticmethod
+    def default_sfi_dir ():
+        if os.path.isfile("./sfi_config"): 
+            return os.getcwd()
+        else:
+            return os.path.expanduser("~/.sfi/")
 
     # dummy to meet Sfi's expectations for its 'options' field
     # i.e. s/t we can do setattr on
 
     # dummy to meet Sfi's expectations for its 'options' field
     # i.e. s/t we can do setattr on
@@ -176,68 +248,103 @@ class Sfi:
         if options is None: options=Sfi.DummyOptions()
         for opt in Sfi.required_options:
             if not hasattr(options,opt): setattr(options,opt,None)
         if options is None: options=Sfi.DummyOptions()
         for opt in Sfi.required_options:
             if not hasattr(options,opt): setattr(options,opt,None)
-        if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
-        # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
-        # would be safer to remove self.sfi_dir altogether
-        self.sfi_dir = options.sfi_dir
+        if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
         self.options = options
         self.options = options
-        self.slicemgr = None
-        self.registry = None
         self.user = None
         self.authority = None
         self.user = None
         self.authority = None
-        self.hashrequest = False
         self.logger = sfi_logger
         self.logger.enable_console()
         self.logger = sfi_logger
         self.logger.enable_console()
+        self.available_names = [ tuple[0] for tuple in Sfi.available ]
+        self.available_dict = dict (Sfi.available)
    
    
-    def create_cmd_parser(self, command, additional_cmdargs=None):
-        cmdargs = {"list": "authority",
-                  "show": "name",
-                  "remove": "name",
-                  "add": "record",
-                  "update": "record",
-                  "aggregates": "[name]",
-                  "registries": "[name]",
-                  "create_gid": "[name]",
-                  "get_gid": [],  
-                  "get_trusted_certs": "cred",
-                  "slices": "",
-                  "resources": "[name]",
-                  "create": "name rspec",
-                  "get_ticket": "name rspec",
-                  "redeem_ticket": "ticket",
-                  "delete": "name",
-                  "reset": "name",
-                  "start": "name",
-                  "stop": "name",
-                  "delegate": "name",
-                  "status": "name",
-                  "renew": "name",
-                  "shutdown": "name",
-                  "version": "",  
-                 }
-
-        if additional_cmdargs:
-            cmdargs.update(additional_cmdargs)
-
-        if command not in cmdargs:
+    # tuples command-name expected-args in the order in which they should appear in the help
+    available = [ 
+        ("version", ""),  
+        ("list", "authority"),
+        ("show", "name"),
+        ("add", "record"),
+        ("update", "record"),
+        ("remove", "name"),
+        ("slices", ""),
+        ("resources", "[slice_hrn]"),
+        ("create", "slice_hrn rspec"),
+        ("delete", "slice_hrn"),
+        ("status", "slice_hrn"),
+        ("start", "slice_hrn"),
+        ("stop", "slice_hrn"),
+        ("reset", "slice_hrn"),
+        ("renew", "slice_hrn time"),
+        ("shutdown", "slice_hrn"),
+        ("get_ticket", "slice_hrn rspec"),
+        ("redeem_ticket", "ticket"),
+        ("delegate", "name"),
+        ("create_gid", "[name]"),
+        ("get_trusted_certs", "cred"),
+        ("config", ""),
+        ]
+
+    def print_command_help (self, options):
+        verbose=getattr(options,'verbose')
+        format3="%18s %-15s %s"
+        line=80*'-'
+        if not verbose:
+            print format3%("command","cmd_args","description")
+            print line
+        else:
+            print line
+            self.create_parser().print_help()
+        for command in self.available_names:
+            args=self.available_dict[command]
+            method=getattr(self,command,None)
+            doc=""
+            if method: doc=getattr(method,'__doc__',"")
+            if not doc: doc="*** no doc found ***"
+            doc=doc.strip(" \t\n")
+            doc=doc.replace("\n","\n"+35*' ')
+            if verbose:
+                print line
+            print format3%(command,args,doc)
+            if verbose:
+                self.create_command_parser(command).print_help()
+
+    def create_command_parser(self, command):
+        if command not in self.available_dict:
             msg="Invalid command\n"
             msg+="Commands: "
             msg="Invalid command\n"
             msg+="Commands: "
-            msg += ','.join(cmdargs.keys())            
+            msg += ','.join(self.available_names)            
             self.logger.critical(msg)
             sys.exit(2)
 
             self.logger.critical(msg)
             sys.exit(2)
 
-        parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
-                                     % (command, cmdargs[command]))
+        parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
+                                     % (command, self.available_dict[command]))
+
+        if command in ("add", "update"):
+            parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
+            parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
+            parser.add_option('-e', '--email', dest='email', default="",  help="email (mandatory for users)") 
+# use --extra instead
+#            parser.add_option('-u', '--url', dest='url', metavar='<url>', default=None, help="URL, useful for slices") 
+#            parser.add_option('-d', '--description', dest='description', metavar='<description>', 
+#                              help='Description, useful for slices', default=None)
+            parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file', 
+                              default=None)
+            parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns',
+                              default='', type="str", action='callback', callback=optparse_listvalue_callback)
+            parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>', 
+                              help='slice researchers', default='', type="str", action='callback', 
+                              callback=optparse_listvalue_callback)
+            parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Principal Investigators/Project Managers',
+                              default='', type="str", action='callback', callback=optparse_listvalue_callback)
+# use --extra instead
+#            parser.add_option('-f', '--firstname', dest='firstname', metavar='<firstname>', help='user first name')
+#            parser.add_option('-l', '--lastname', dest='lastname', metavar='<lastname>', help='user last name')
+            parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
+                               action="callback", callback=optparse_dictvalue_callback, nargs=1,
+                               help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
 
         # user specifies remote aggregate/sm/component                          
         if command in ("resources", "slices", "create", "delete", "start", "stop", 
                        "restart", "shutdown",  "get_ticket", "renew", "status"):
 
         # user specifies remote aggregate/sm/component                          
         if command in ("resources", "slices", "create", "delete", "start", "stop", 
                        "restart", "shutdown",  "get_ticket", "renew", "status"):
-            parser.add_option("-a", "--aggregate", dest="aggregate",
-                             default=None, help="aggregate host")
-            parser.add_option("-p", "--port", dest="port",
-                             default=AGGREGATE_PORT, help="aggregate port")
-            parser.add_option("-c", "--component", dest="component", default=None,
-                             help="component hrn")
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
@@ -249,16 +356,25 @@ class Sfi:
                             help="type filter ([all]|user|slice|authority|node|aggregate)",
                             choices=("all", "user", "slice", "authority", "node", "aggregate"),
                             default="all")
                             help="type filter ([all]|user|slice|authority|node|aggregate)",
                             choices=("all", "user", "slice", "authority", "node", "aggregate"),
                             default="all")
-        # display formats
         if command in ("resources"):
         if command in ("resources"):
+            # rspec version
             parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
                               help="schema type and version of resulting RSpec")
             parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
                               help="schema type and version of resulting RSpec")
+            # disable/enable cached rspecs
+            parser.add_option("-c", "--current", dest="current", default=False,
+                              action="store_true",  
+                              help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
+            # display formats
             parser.add_option("-f", "--format", dest="format", type="choice",
                              help="display format ([xml]|dns|ip)", default="xml",
                              choices=("xml", "dns", "ip"))
             #panos: a new option to define the type of information about resources a user is interested in
             parser.add_option("-f", "--format", dest="format", type="choice",
                              help="display format ([xml]|dns|ip)", default="xml",
                              choices=("xml", "dns", "ip"))
             #panos: a new option to define the type of information about resources a user is interested in
-           parser.add_option("-i", "--info", dest="info",
+            parser.add_option("-i", "--info", dest="info",
                                 help="optional component information", default=None)
                                 help="optional component information", default=None)
+            # a new option to retreive or not reservation-oriented RSpecs (leases)
+            parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
+                                help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
+                                choices=("all", "resources", "leases"), default="resources")
 
 
         # 'create' does return the new rspec, makes sense to save that too
 
 
         # 'create' does return the new rspec, makes sense to save that too
@@ -274,14 +390,9 @@ class Sfi:
            parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
                              help="output file format ([xml]|xmllist|hrnlist)", default="xml",
                              choices=("xml", "xmllist", "hrnlist"))
            parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
                              help="output file format ([xml]|xmllist|hrnlist)", default="xml",
                              choices=("xml", "xmllist", "hrnlist"))
-
-        if command in ("status", "version"):
-           parser.add_option("-o", "--output", dest="file",
-                            help="output dictionary to file", metavar="FILE", default=None)
-           parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
-                             help="output file format ([text]|pickled)", default="text",
-                             choices=("text","pickled"))
-
+        if command == 'list':
+           parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
+                             help="list all child records", default=False)
         if command in ("delegate"):
            parser.add_option("-u", "--user",
                             action="store_true", dest="delegate_user", default=False,
         if command in ("delegate"):
            parser.add_option("-u", "--user",
                             action="store_true", dest="delegate_user", default=False,
@@ -290,13 +401,9 @@ class Sfi:
                             help="delegate slice credential", metavar="HRN", default=None)
         
         if command in ("version"):
                             help="delegate slice credential", metavar="HRN", default=None)
         
         if command in ("version"):
-            parser.add_option("-a", "--aggregate", dest="aggregate",
-                             default=None, help="aggregate host")
-            parser.add_option("-p", "--port", dest="port",
-                             default=AGGREGATE_PORT, help="aggregate port")
             parser.add_option("-R","--registry-version",
                               action="store_true", dest="version_registry", default=False,
             parser.add_option("-R","--registry-version",
                               action="store_true", dest="version_registry", default=False,
-                              help="probe registry version instead of slicemgr")
+                              help="probe registry version instead of sliceapi")
             parser.add_option("-l","--local",
                               action="store_true", dest="version_local", default=False,
                               help="display version of the local client")
             parser.add_option("-l","--local",
                               action="store_true", dest="version_local", default=False,
                               help="display version of the local client")
@@ -307,16 +414,22 @@ class Sfi:
     def create_parser(self):
 
         # Generate command line parser
     def create_parser(self):
 
         # Generate command line parser
-        parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
-                             description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
+        parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
+                             description="Commands: %s"%(" ".join(self.available_names)))
         parser.add_option("-r", "--registry", dest="registry",
                          help="root registry", metavar="URL", default=None)
         parser.add_option("-r", "--registry", dest="registry",
                          help="root registry", metavar="URL", default=None)
-        parser.add_option("-s", "--slicemgr", dest="sm",
-                         help="slice manager", metavar="URL", default=None)
-        default_sfi_dir = os.path.expanduser("~/.sfi/")
+        parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
+                         help="slice API - in general a SM URL, but can be used to talk to an aggregate")
+        parser.add_option("-R", "--raw", dest="raw", default=None,
+                          help="Save raw, unparsed server response to a file")
+        parser.add_option("", "--rawformat", dest="rawformat", type="choice",
+                          help="raw file format ([text]|pickled|json)", default="text",
+                          choices=("text","pickled","json"))
+        parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
+                          help="text string to write before and after raw output")
         parser.add_option("-d", "--dir", dest="sfi_dir",
         parser.add_option("-d", "--dir", dest="sfi_dir",
-                         help="config & working directory - default is " + default_sfi_dir,
-                         metavar="PATH", default=default_sfi_dir)
+                         help="config & working directory - default is %default",
+                         metavar="PATH", default=Sfi.default_sfi_dir())
         parser.add_option("-u", "--user", dest="user",
                          help="user name", metavar="HRN", default=None)
         parser.add_option("-a", "--auth", dest="auth",
         parser.add_option("-u", "--user", dest="user",
                          help="user name", metavar="HRN", default=None)
         parser.add_option("-a", "--auth", dest="auth",
@@ -326,90 +439,235 @@ class Sfi:
         parser.add_option("-D", "--debug",
                           action="store_true", dest="debug", default=False,
                           help="Debug (xml-rpc) protocol messages")
         parser.add_option("-D", "--debug",
                           action="store_true", dest="debug", default=False,
                           help="Debug (xml-rpc) protocol messages")
-        parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
-                         help="RPC protocol (xmlrpc or soap)")
-        parser.add_option("-k", "--hashrequest",
-                         action="store_true", dest="hashrequest", default=False,
-                         help="Create a hash of the request that will be authenticated on the server")
+        # would it make sense to use ~/.ssh/id_rsa as a default here ?
+        parser.add_option("-k", "--private-key",
+                         action="store", dest="user_private_key", default=None,
+                         help="point to the private key file to use if not yet installed in sfi_dir")
         parser.add_option("-t", "--timeout", dest="timeout", default=None,
         parser.add_option("-t", "--timeout", dest="timeout", default=None,
-                         help="Amout of time tom wait before timing out the request")
+                         help="Amout of time to wait before timing out the request")
+        parser.add_option("-?", "--commands", 
+                         action="store_true", dest="command_help", default=False,
+                         help="one page summary on commands & exit")
         parser.disable_interspersed_args()
 
         return parser
         
 
         parser.disable_interspersed_args()
 
         return parser
         
 
-    def read_config(self):
-       config_file = os.path.join(self.options.sfi_dir,"sfi_config")
-       try:
-          config = Config (config_file)
-       except:
-          self.logger.critical("Failed to read configuration file %s"%config_file)
-          self.logger.info("Make sure to remove the export clauses and to add quotes")
-          if self.options.verbose==0:
-              self.logger.info("Re-run with -v for more details")
-          else:
-              self.logger.log_exc("Could not read config file %s"%config_file)
-          sys.exit(1)
+    def print_help (self):
+        self.sfi_parser.print_help()
+        self.command_parser.print_help()
+
+    #
+    # Main: parse arguments and dispatch to command
+    #
+    def dispatch(self, command, command_options, command_args):
+        return getattr(self, command)(command_options, command_args)
+
+    def main(self):
+        self.sfi_parser = self.create_parser()
+        (options, args) = self.sfi_parser.parse_args()
+        if options.command_help: 
+            self.print_command_help(options)
+            sys.exit(1)
+        self.options = options
+
+        self.logger.setLevelFromOptVerbose(self.options.verbose)
+
+        if len(args) <= 0:
+            self.logger.critical("No command given. Use -h for help.")
+            self.print_command_help(options)
+            return -1
     
     
-       errors = 0
-       # Set SliceMgr URL
-       if (self.options.sm is not None):
-          self.sm_url = self.options.sm
-       elif hasattr(config, "SFI_SM"):
-          self.sm_url = config.SFI_SM
-       else:
-          self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
-          errors += 1 
+        command = args[0]
+        self.command_parser = self.create_command_parser(command)
+        (command_options, command_args) = self.command_parser.parse_args(args[1:])
+        self.command_options = command_options
+
+        self.read_config () 
+        self.bootstrap ()
+        self.logger.info("Command=%s" % command)
+
+        try:
+            self.dispatch(command, command_options, command_args)
+        except KeyError:
+            self.logger.critical ("Unknown command %s"%command)
+            raise
+            sys.exit(1)
     
     
-       # Set Registry URL
-       if (self.options.registry is not None):
-          self.reg_url = self.options.registry
-       elif hasattr(config, "SFI_REGISTRY"):
-          self.reg_url = config.SFI_REGISTRY
-       else:
-          self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
-          errors += 1 
-          
-
-       # Set user HRN
-       if (self.options.user is not None):
-          self.user = self.options.user
-       elif hasattr(config, "SFI_USER"):
-          self.user = config.SFI_USER
-       else:
-          self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
-          errors += 1 
+        return
     
     
-       # Set authority HRN
-       if (self.options.auth is not None):
-          self.authority = self.options.auth
-       elif hasattr(config, "SFI_AUTH"):
-          self.authority = config.SFI_AUTH
-       else:
-          self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
-          errors += 1 
+    ####################
+    def read_config(self):
+        config_file = os.path.join(self.options.sfi_dir,"sfi_config")
+        try:
+           config = Config (config_file)
+        except:
+           self.logger.critical("Failed to read configuration file %s"%config_file)
+           self.logger.info("Make sure to remove the export clauses and to add quotes")
+           if self.options.verbose==0:
+               self.logger.info("Re-run with -v for more details")
+           else:
+               self.logger.log_exc("Could not read config file %s"%config_file)
+           sys.exit(1)
+     
+        errors = 0
+        # Set SliceMgr URL
+        if (self.options.sm is not None):
+           self.sm_url = self.options.sm
+        elif hasattr(config, "SFI_SM"):
+           self.sm_url = config.SFI_SM
+        else:
+           self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
+           errors += 1 
+
+        # Set Registry URL
+        if (self.options.registry is not None):
+           self.reg_url = self.options.registry
+        elif hasattr(config, "SFI_REGISTRY"):
+           self.reg_url = config.SFI_REGISTRY
+        else:
+           self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
+           errors += 1 
+
+        # Set user HRN
+        if (self.options.user is not None):
+           self.user = self.options.user
+        elif hasattr(config, "SFI_USER"):
+           self.user = config.SFI_USER
+        else:
+           self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
+           errors += 1 
+
+        # Set authority HRN
+        if (self.options.auth is not None):
+           self.authority = self.options.auth
+        elif hasattr(config, "SFI_AUTH"):
+           self.authority = config.SFI_AUTH
+        else:
+           self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
+           errors += 1 
+
+        self.config_file=config_file
+        if errors:
+           sys.exit(1)
+
+    def show_config (self):
+        print "From configuration file %s"%self.config_file
+        flags=[ 
+            ('SFI_USER','user'),
+            ('SFI_AUTH','authority'),
+            ('SFI_SM','sm_url'),
+            ('SFI_REGISTRY','reg_url'),
+            ]
+        for (external_name, internal_name) in flags:
+            print "%s='%s'"%(external_name,getattr(self,internal_name))
+
+    #
+    # Get various credential and spec files
+    #
+    # Establishes limiting conventions
+    #   - conflates MAs and SAs
+    #   - assumes last token in slice name is unique
+    #
+    # Bootstraps credentials
+    #   - bootstrap user credential from self-signed certificate
+    #   - bootstrap authority credential from user credential
+    #   - bootstrap slice credential from user credential
+    #
     
     
-       if errors:
-          sys.exit(1)
+    # init self-signed cert, user credentials and gid
+    def bootstrap (self):
+        client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
+        # if -k is provided, use this to initialize private key
+        if self.options.user_private_key:
+            client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
+        else:
+            # trigger legacy compat code if needed 
+            # the name has changed from just <leaf>.pkey to <hrn>.pkey
+            if not os.path.isfile(client_bootstrap.private_key_filename()):
+                self.logger.info ("private key not found, trying legacy name")
+                try:
+                    legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
+                    self.logger.debug("legacy_private_key=%s"%legacy_private_key)
+                    client_bootstrap.init_private_key_if_missing (legacy_private_key)
+                    self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
+                except:
+                    self.logger.log_exc("Can't find private key ")
+                    sys.exit(1)
+            
+        # make it bootstrap
+        client_bootstrap.bootstrap_my_gid()
+        # extract what's needed
+        self.private_key = client_bootstrap.private_key()
+        self.my_credential_string = client_bootstrap.my_credential_string ()
+        self.my_gid = client_bootstrap.my_gid ()
+        self.client_bootstrap = client_bootstrap
 
 
 
 
+    def my_authority_credential_string(self):
+        if not self.authority:
+            self.logger.critical("no authority specified. Use -a or set SF_AUTH")
+            sys.exit(-1)
+        return self.client_bootstrap.authority_credential_string (self.authority)
+
+    def slice_credential_string(self, name):
+        return self.client_bootstrap.slice_credential_string (name)
+
+    # xxx should be supported by sfaclientbootstrap as well
+    def delegate_cred(self, object_cred, hrn, type='authority'):
+        # the gid and hrn of the object we are delegating
+        if isinstance(object_cred, str):
+            object_cred = Credential(string=object_cred) 
+        object_gid = object_cred.get_gid_object()
+        object_hrn = object_gid.get_hrn()
+    
+        if not object_cred.get_privileges().get_all_delegate():
+            self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
+            return
+
+        # the delegating user's gid
+        caller_gidfile = self.my_gid()
+  
+        # the gid of the user who will be delegated to
+        delegee_gid = self.client_bootstrap.gid(hrn,type)
+        delegee_hrn = delegee_gid.get_hrn()
+        dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
+        return dcred.save_to_string(save_parents=True)
+     
     #
     #
-    # Establish Connection to SliceMgr and Registry Servers
-    #
-    def set_servers(self):
-
-       self.read_config() 
-       # Get key and certificate
-       key_file = self.get_key_file()
-       cert_file = self.get_cert_file(key_file)
-       self.key_file = key_file
-       self.cert_file = cert_file
-       self.cert = GID(filename=cert_file)
-       self.logger.info("Contacting Registry at: %s"%self.reg_url)
-       self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)  
-       self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
-       self.slicemgr = sfaprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
-       return
+    # Management of the servers
+    # 
+
+    def registry (self):
+        # cache the result
+        if not hasattr (self, 'registry_proxy'):
+            self.logger.info("Contacting Registry at: %s"%self.reg_url)
+            self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid, 
+                                                 timeout=self.options.timeout, verbose=self.options.debug)  
+        return self.registry_proxy
+
+    def sliceapi (self):
+        # cache the result
+        if not hasattr (self, 'sliceapi_proxy'):
+            # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
+            if hasattr(self.command_options,'component') and self.command_options.component:
+                # resolve the hrn at the registry
+                node_hrn = self.command_options.component
+                records = self.registry().Resolve(node_hrn, self.my_credential_string)
+                records = filter_records('node', records)
+                if not records:
+                    self.logger.warning("No such component:%r"% opts.component)
+                record = records[0]
+                cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
+                self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
+            else:
+                # otherwise use what was provided as --sliceapi, or SFI_SM in the config
+                if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
+                    self.sm_url = 'http://' + self.sm_url
+                self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
+                self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid, 
+                                                     timeout=self.options.timeout, verbose=self.options.debug)  
+        return self.sliceapi_proxy
 
     def get_cached_server_version(self, server):
         # check local cache first
 
     def get_cached_server_version(self, server):
         # check local cache first
@@ -429,194 +687,56 @@ class Sfi:
         if not version: 
             result = server.GetVersion()
             version= ReturnValue.get_value(result)
         if not version: 
             result = server.GetVersion()
             version= ReturnValue.get_value(result)
-            # cache version for 24 hours
-            cache.add(cache_key, version, ttl= 60*60*24)
+            # cache version for 20 minutes
+            cache.add(cache_key, version, ttl= 60*20)
             self.logger.info("Updating cache file %s" % cache_file)
             cache.save_to_file(cache_file)
 
         return version   
         
             self.logger.info("Updating cache file %s" % cache_file)
             cache.save_to_file(cache_file)
 
         return version   
         
-
+    ### resurrect this temporarily so we can support V1 aggregates for a while
     def server_supports_options_arg(self, server):
         """
         Returns true if server support the optional call_id arg, false otherwise. 
         """
         server_version = self.get_cached_server_version(server)
     def server_supports_options_arg(self, server):
         """
         Returns true if server support the optional call_id arg, false otherwise. 
         """
         server_version = self.get_cached_server_version(server)
+        result = False
+        # xxx need to rewrite this 
+        if int(server_version.get('geni_api')) >= 2:
+            result = True
+        return result
+
+    def server_supports_call_id_arg(self, server):
+        server_version = self.get_cached_server_version(server)
+        result = False      
         if 'sfa' in server_version and 'code_tag' in server_version:
             code_tag = server_version['code_tag']
             code_tag_parts = code_tag.split("-")
         if 'sfa' in server_version and 'code_tag' in server_version:
             code_tag = server_version['code_tag']
             code_tag_parts = code_tag.split("-")
-            
             version_parts = code_tag_parts[0].split(".")
             major, minor = version_parts[0], version_parts[1]
             rev = code_tag_parts[1]
             version_parts = code_tag_parts[0].split(".")
             major, minor = version_parts[0], version_parts[1]
             rev = code_tag_parts[1]
-            if int(major) >= 1:
-                if int(minor) >= 2:
-                    return True
-        return False                
-        
-    #
-    # Get various credential and spec files
-    #
-    # Establishes limiting conventions
-    #   - conflates MAs and SAs
-    #   - assumes last token in slice name is unique
-    #
-    # Bootstraps credentials
-    #   - bootstrap user credential from self-signed certificate
-    #   - bootstrap authority credential from user credential
-    #   - bootstrap slice credential from user credential
-    #
-    
-    
-    def get_key_file(self):
-       file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
-       if (os.path.isfile(file)):
-          return file
-       else:
-          self.logger.error("Key file %s does not exist"%file)
-          sys.exit(-1)
-       return
-    
-    def get_cert_file(self, key_file):
-    
-        cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
-        if (os.path.isfile(cert_file)):
-            # we'd perfer to use Registry issued certs instead of self signed certs. 
-            # if this is a Registry cert (GID) then we are done 
-            gid = GID(filename=cert_file)
-            if gid.get_urn():
-                return cert_file
-
-        # generate self signed certificate
-        k = Keypair(filename=key_file)
-        cert = Certificate(subject=self.user)
-        cert.set_pubkey(k)
-        cert.set_issuer(k, self.user)
-        cert.sign()
-        self.logger.info("Writing self-signed certificate to %s"%cert_file)
-        cert.save_to_file(cert_file)
-        self.cert = cert
-        # try to get registry issued cert
-        try:
-            self.logger.info("Getting Registry issued cert")
-            self.read_config()
-            # *hack.  need to set registry before _get_gid() is called 
-            self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, 
-                                                     timeout=self.options.timeout, verbose=self.options.debug)
-            gid = self._get_gid(type='user')
-            self.registry = None 
-            self.logger.info("Writing certificate to %s"%cert_file)
-            gid.save_to_file(cert_file)
-        except:
-            self.logger.info("Failed to download Registry issued cert")
-
-        return cert_file
-
-    def get_cached_gid(self, file):
-        """
-        Return a cached gid    
-        """
-        gid = None 
-        if (os.path.isfile(file)):
-            gid = GID(filename=file)
-        return gid
-
-    # xxx opts unused
-    def get_gid(self, opts, args):
-        """
-        Get the specify gid and save it to file
-        """
-        hrn = None
-        if args:
-            hrn = args[0]
-        gid = self._get_gid(hrn)
-        self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
-        return gid
+            if int(major) == 1 and minor == 0 and build >= 22:
+                result = True
+        return result                 
+
+    ### ois = options if supported
+    # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
+    def ois (self, server, option_dict):
+        if self.server_supports_options_arg (server): 
+            return [option_dict]
+        elif self.server_supports_call_id_arg (server):
+            return [ unique_call_id () ]
+        else: 
+            return []
+
+    ### cis = call_id if supported - like ois
+    def cis (self, server):
+        if self.server_supports_call_id_arg (server):
+            return [ unique_call_id ]
+        else:
+            return []
 
 
-    def _get_gid(self, hrn=None, type=None):
-        """
-        git_gid helper. Retrive the gid from the registry and save it to file.
-        """
-        
-        if not hrn:
-            hrn = self.user
-        gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
-        gid = self.get_cached_gid(gidfile)
-        if not gid:
-            user_cred = self.get_user_cred()
-            records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
-            if not records:
-                raise RecordNotFound(args[0])
-            record = records[0]
-            if type:
-                record=None
-                for rec in records:
-                   if type == rec['type']:
-                        record = rec 
-            if not record:
-                raise RecordNotFound(args[0])
-            
-            gid = GID(string=record['gid'])
-            self.logger.info("Writing gid to %s"%gidfile)
-            gid.save_to_file(filename=gidfile)
-        return gid   
-                
-     
-    def get_cached_credential(self, file):
-        """
-        Return a cached credential only if it hasn't expired.
-        """
-        if (os.path.isfile(file)):
-            credential = Credential(filename=file)
-            # make sure it isnt expired 
-            if not credential.get_expiration or \
-               datetime.datetime.today() < credential.get_expiration():
-                return credential
-        return None 
-    def get_user_cred(self):
-        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
-        return self.get_cred(file, 'user', self.user)
-
-    def get_auth_cred(self):
-        if not self.authority:
-            self.logger.critical("no authority specified. Use -a or set SF_AUTH")
-            sys.exit(-1)
-        file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
-        return self.get_cred(file, 'authority', self.authority)
-
-    def get_slice_cred(self, name):
-        file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
-        return self.get_cred(file, 'slice', name)
-    def get_cred(self, file, type, hrn):
-        # attempt to load a cached credential 
-        cred = self.get_cached_credential(file)    
-        if not cred:
-            if type in ['user']:
-                cert_string = self.cert.save_to_string(save_parents=True)
-                user_name = self.user.replace(self.authority + ".", '')
-                if user_name.count(".") > 0:
-                    user_name = user_name.replace(".", '_')
-                    self.user = self.authority + "." + user_name
-                cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
-            else:
-                # bootstrap slice credential from user credential
-                user_cred = self.get_user_cred().save_to_string(save_parents=True)
-                cred_str = self.registry.GetCredential(user_cred, hrn, type)
-            
-            if not cred_str:
-                self.logger.critical("Failed to get %s credential" % type)
-                sys.exit(-1)
-                
-            cred = Credential(string=cred_str)
-            cred.save_to_file(file, save_parents=True)
-            self.logger.info("Writing %s credential to %s" %(type, file))
-
-        return cred
-    
+    ######################################## miscell utilities
     def get_rspec_file(self, rspec):
        if (os.path.isabs(rspec)):
           file = rspec
     def get_rspec_file(self, rspec):
        if (os.path.isabs(rspec)):
           file = rspec
@@ -638,612 +758,604 @@ class Sfi:
        else:
           self.logger.critical("No such registry record file %s"%record)
           sys.exit(1)
        else:
           self.logger.critical("No such registry record file %s"%record)
           sys.exit(1)
-    
-    def load_publickey_string(self, 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
-
-    # xxx opts undefined
-    def get_component_proxy_from_hrn(self, hrn):
-        # direct connection to the nodes component manager interface
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        records = self.registry.Resolve(hrn, user_cred)
-        records = filter_records('node', records)
-        if not records:
-            self.logger.warning("No such component:%r"% opts.component)
-        record = records[0]
-  
-        return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
-    def server_proxy(self, host, port, keyfile, certfile):
-        """
-        Return an instance of an xmlrpc server connection    
-        """
-        # port is appended onto the domain, before the path. Should look like:
-        # http://domain:port/path
-        host_parts = host.split('/')
-        host_parts[0] = host_parts[0] + ":" + str(port)
-        url =  "http://%s" %  "/".join(host_parts)    
-        return sfaprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, 
-                                        verbose=self.options.debug)
-
-    # xxx opts could be retrieved in self.options
-    def server_proxy_from_opts(self, opts):
-        """
-        Return instance of an xmlrpc connection to a slice manager, aggregate
-        or component server depending on the specified opts
-        """
-        server = self.slicemgr
-        # direct connection to an aggregate
-        if hasattr(opts, 'aggregate') and opts.aggregate:
-            server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
-        # direct connection to the nodes component manager interface
-        if hasattr(opts, 'component') and opts.component:
-            server = self.get_component_proxy_from_hrn(opts.component)    
-        return server
+
+
     #==========================================================================
     # Following functions implement the commands
     #
     # Registry-related commands
     #==========================================================================
     #==========================================================================
     # Following functions implement the commands
     #
     # Registry-related commands
     #==========================================================================
-  
-    def dispatch(self, command, cmd_opts, cmd_args):
-        return getattr(self, command)(cmd_opts, cmd_args)
 
 
-    def create_gid(self, opts, args):
-        if len(args) < 1:
-            self.print_help()
-            sys.exit(1)
-        target_hrn = args[0]
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
-        if opts.file:
-            filename = opts.file
+    def version(self, options, args):
+        """
+        display an SFA server version (GetVersion)
+or version information about sfi itself
+        """
+        if options.version_local:
+            version=version_core()
         else:
         else:
-            filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
-        self.logger.info("writing %s gid to %s" % (target_hrn, filename))
-        GID(string=gid).save_to_file(filename)
-         
-     
-    # list entires in named authority registry
-    def list(self, opts, args):
+            if options.version_registry:
+                server=self.registry()
+            else:
+                server = self.sliceapi()
+            result = server.GetVersion()
+            version = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            pprinter = PrettyPrinter(indent=4)
+            pprinter.pprint(version)
+
+    def list(self, options, args):
+        """
+        list entries in named authority registry (List)
+        """
         if len(args)!= 1:
             self.print_help()
             sys.exit(1)
         hrn = args[0]
         if len(args)!= 1:
             self.print_help()
             sys.exit(1)
         hrn = args[0]
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
+        opts = {}
+        if options.recursive:
+            opts['recursive'] = options.recursive
+        
         try:
         try:
-            list = self.registry.List(hrn, user_cred)
+            list = self.registry().List(hrn, self.my_credential_string, options)
         except IndexError:
             raise Exception, "Not enough parameters for the 'list' command"
 
         # filter on person, slice, site, node, etc.
         # THis really should be in the self.filter_records funct def comment...
         except IndexError:
             raise Exception, "Not enough parameters for the 'list' command"
 
         # filter on person, slice, site, node, etc.
         # THis really should be in the self.filter_records funct def comment...
-        list = filter_records(opts.type, list)
+        list = filter_records(options.type, list)
         for record in list:
             print "%s (%s)" % (record['hrn'], record['type'])
         for record in list:
             print "%s (%s)" % (record['hrn'], record['type'])
-        if opts.file:
-            save_records_to_file(opts.file, list, opts.fileformat)
+        if options.file:
+            save_records_to_file(options.file, list, options.fileformat)
         return
     
         return
     
-    # show named registry record
-    def show(self, opts, args):
+    def show(self, options, args):
+        """
+        show details about named registry record (Resolve)
+        """
         if len(args)!= 1:
             self.print_help()
             sys.exit(1)
         hrn = args[0]
         if len(args)!= 1:
             self.print_help()
             sys.exit(1)
         hrn = args[0]
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        records = self.registry.Resolve(hrn, user_cred)
-        records = filter_records(opts.type, records)
-        if not records:
-            self.logger.error("No record of type %s"% opts.type)
+        record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
+        record_dicts = filter_records(options.type, record_dicts)
+        if not record_dicts:
+            self.logger.error("No record of type %s"% options.type)
+        records = [ Record(dict=record_dict) for record_dict in record_dicts ]
         for record in records:
         for record in records:
-            if record['type'] in ['user']:
-                record = UserRecord(dict=record)
-            elif record['type'] in ['slice']:
-                record = SliceRecord(dict=record)
-            elif record['type'] in ['node']:
-                record = NodeRecord(dict=record)
-            elif record['type'].startswith('authority'):
-                record = AuthorityRecord(dict=record)
-            else:
-                record = SfaRecord(dict=record)
-            if (opts.format == "text"): 
-                record.dump()  
-            else:
-                print record.save_to_string() 
-        if opts.file:
-            save_records_to_file(opts.file, records, opts.fileformat)
+            if (options.format == "text"):      record.dump(sort=True)  
+            else:                               print record.save_as_xml() 
+        if options.file:
+            save_records_to_file(options.file, record_dicts, options.fileformat)
         return
     
         return
     
-    def delegate(self, opts, args):
-
-        delegee_hrn = args[0]
-        if opts.delegate_user:
-            user_cred = self.get_user_cred()
-            cred = self.delegate_cred(user_cred, delegee_hrn)
-        elif opts.delegate_slice:
-            slice_cred = self.get_slice_cred(opts.delegate_slice)
-            cred = self.delegate_cred(slice_cred, delegee_hrn)
-        else:
-            self.logger.warning("Must specify either --user or --slice <hrn>")
-            return
-        delegated_cred = Credential(string=cred)
-        object_hrn = delegated_cred.get_gid_object().get_hrn()
-        if opts.delegate_user:
-            dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
-                                  + get_leaf(object_hrn) + ".cred")
-        elif opts.delegate_slice:
-            dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
-                                  + get_leaf(object_hrn) + ".cred")
-
-        delegated_cred.save_to_file(dest_fn, save_parents=True)
-
-        self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
-    
-    def delegate_cred(self, object_cred, hrn):
-        # the gid and hrn of the object we are delegating
-        if isinstance(object_cred, str):
-            object_cred = Credential(string=object_cred) 
-        object_gid = object_cred.get_gid_object()
-        object_hrn = object_gid.get_hrn()
-    
-        if not object_cred.get_privileges().get_all_delegate():
-            self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
-            return
-
-        # the delegating user's gid
-        caller_gid = self._get_gid(self.user)
-        caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
-  
-        # the gid of the user who will be delegated to
-        delegee_gid = self._get_gid(hrn)
-        delegee_hrn = delegee_gid.get_hrn()
-        delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
-        delegee_gid.save_to_file(filename=delegee_gidfile)
-        dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
-        return dcred.save_to_string(save_parents=True)
-     
-    # removed named registry record
-    #   - have to first retrieve the record to be removed
-    def remove(self, opts, args):
-        auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
-        if len(args)!=1:
+    def add(self, options, args):
+        "add record into registry from xml file (Register)"
+        auth_cred = self.my_authority_credential_string()
+        record_dict = {}
+        if len(args) > 0:
+            record_filepath = args[0]
+            rec_file = self.get_record_file(record_filepath)
+            record_dict.update(load_record_from_file(rec_file).todict())
+        if options:
+            record_dict.update(load_record_from_opts(options).todict())
+        # we should have a type by now
+        if 'type' not in record_dict :
             self.print_help()
             sys.exit(1)
             self.print_help()
             sys.exit(1)
-        hrn = args[0]
-        type = opts.type 
-        if type in ['all']:
-            type = '*'
-        return self.registry.Remove(hrn, auth_cred, type)
+        # this is still planetlab dependent.. as plc will whine without that
+        # also, it's only for adding
+        if record_dict['type'] == 'user':
+            if not 'first_name' in record_dict:
+                record_dict['first_name'] = record_dict['hrn']
+            if 'last_name' not in record_dict:
+                record_dict['last_name'] = record_dict['hrn'] 
+        return self.registry().Register(record_dict, auth_cred)
     
     
-    # add named registry record
-    def add(self, opts, args):
-        auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
-        if len(args)!=1:
+    def update(self, options, args):
+        "update record into registry from xml file (Update)"
+        record_dict = {}
+        if len(args) > 0:
+            record_filepath = args[0]
+            rec_file = self.get_record_file(record_filepath)
+            record_dict.update(load_record_from_file(rec_file).todict())
+        if options:
+            record_dict.update(load_record_from_opts(options).todict())
+        # at the very least we need 'type' here
+        if 'type' not in record_dict:
             self.print_help()
             sys.exit(1)
             self.print_help()
             sys.exit(1)
-        record_filepath = args[0]
-        rec_file = self.get_record_file(record_filepath)
-        record = load_record_from_file(rec_file).as_dict()
-        return self.registry.Register(record, auth_cred)
-    
-    # update named registry entry
-    def update(self, opts, args):
-        user_cred = self.get_user_cred()
-        if len(args)!=1:
-            self.print_help()
-            sys.exit(1)
-        rec_file = self.get_record_file(args[0])
-        record = load_record_from_file(rec_file)
-        if record['type'] == "user":
-            if record.get_name() == user_cred.get_gid_object().get_hrn():
-                cred = user_cred.save_to_string(save_parents=True)
+
+        # don't translate into an object, as this would possibly distort
+        # user-provided data; e.g. add an 'email' field to Users
+        if record_dict['type'] == "user":
+            if record_dict['hrn'] == self.user:
+                cred = self.my_credential_string
             else:
             else:
-                cred = self.get_auth_cred().save_to_string(save_parents=True)
-        elif record['type'] in ["slice"]:
+                cred = self.my_authority_credential_string()
+        elif record_dict['type'] in ["slice"]:
             try:
             try:
-                cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
-            except sfaprotocol.ServerException, e:
+                cred = self.slice_credential_string(record_dict['hrn'])
+            except ServerException, e:
                # XXX smbaker -- once we have better error return codes, update this
                # to do something better than a string compare
                if "Permission error" in e.args[0]:
                # XXX smbaker -- once we have better error return codes, update this
                # to do something better than a string compare
                if "Permission error" in e.args[0]:
-                   cred = self.get_auth_cred().save_to_string(save_parents=True)
+                   cred = self.my_authority_credential_string()
                else:
                    raise
                else:
                    raise
-        elif record.get_type() in ["authority"]:
-            cred = self.get_auth_cred().save_to_string(save_parents=True)
-        elif record.get_type() == 'node':
-            cred = self.get_auth_cred().save_to_string(save_parents=True)
+        elif record_dict['type'] in ["authority"]:
+            cred = self.my_authority_credential_string()
+        elif record_dict['type'] == 'node':
+            cred = self.my_authority_credential_string()
         else:
         else:
-            raise "unknown record type" + record.get_type()
-        record = record.as_dict()
-        return self.registry.Update(record, cred)
+            raise "unknown record type" + record_dict['type']
+        return self.registry().Update(record_dict, cred)
   
   
-    def get_trusted_certs(self, opts, args):
-        """
-        return uhe trusted certs at this interface 
-        """ 
-        trusted_certs = self.registry.get_trusted_certs()
-        for trusted_cert in trusted_certs:
-            gid = GID(string=trusted_cert)
-            gid.dump()
-            cert = Certificate(string=trusted_cert)
-            self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
-        return 
-
-    def aggregates(self, opts, args):
-        """
-        return a list of details about known aggregates
-        """
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        hrn = None
-        if args:
-            hrn = args[0]
-
-        result = self.registry.get_aggregates(user_cred, hrn)
-        display_list(result)
-        return 
-
-    def registries(self, opts, args):
-        """
-        return a list of details about known registries
-        """
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        hrn = None
-        if args:
-            hrn = args[0]
-        result = self.registry.get_registries(user_cred, hrn)
-        display_list(result)
-        return
-
+    def remove(self, options, args):
+        "remove registry record by name (Remove)"
+        auth_cred = self.my_authority_credential_string()
+        if len(args)!=1:
+            self.print_help()
+            sys.exit(1)
+        hrn = args[0]
+        type = options.type 
+        if type in ['all']:
+            type = '*'
+        return self.registry().Remove(hrn, auth_cred, type)
+    
     # ==================================================================
     # Slice-related commands
     # ==================================================================
 
     # ==================================================================
     # Slice-related commands
     # ==================================================================
 
-    def version(self, opts, args):
-        if opts.version_local:
-            version=version_core()
-        else:
-            if opts.version_registry:
-                server=self.registry
-            else:
-                server = self.server_proxy_from_opts(opts)
-            result = server.GetVersion()
-            version = ReturnValue.get_value(result)
-        for (k,v) in version.iteritems():
-            print "%-20s: %s"%(k,v)
-        if opts.file:
-            save_variable_to_file(version, opts.file, opts.fileformat)
-
-    # list instantiated slices
-    def slices(self, opts, args):
-        """
-        list instantiated slices
-        """
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        creds = [user_cred]
-        if opts.delegate:
-            delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
+    def slices(self, options, args):
+        "list instantiated slices (ListSlices) - returns urn's"
+        server = self.sliceapi()
+        # creds
+        creds = [self.my_credential_string]
+        if options.delegate:
+            delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
             creds.append(delegated_cred)  
             creds.append(delegated_cred)  
-        server = self.server_proxy_from_opts(opts)
-        call_args = [creds]
-        if self.server_supports_options_arg(server):
-            options = {'call_id': unique_call_id()}
-            call_args.append(options)
-        result = server.ListSlices(*call_args)
+        # options and call_id when supported
+        api_options = {}
+       api_options['call_id']=unique_call_id()
+        result = server.ListSlices(creds, *self.ois(server,api_options))
         value = ReturnValue.get_value(result)
         value = ReturnValue.get_value(result)
-        display_list(value)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            display_list(value)
         return
         return
-    
+
     # show rspec for named slice
     # show rspec for named slice
-    def resources(self, opts, args):
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        server = self.server_proxy_from_opts(opts)
-   
-        options = {'call_id': unique_call_id()}
-        #panos add info options
-        if opts.info:
-            options['info'] = opts.info
-        
+    def resources(self, options, args):
+        """
+        with no arg, discover available resources, (ListResources)
+or with an slice hrn, shows currently provisioned resources
+        """
+        server = self.sliceapi()
+
+        # set creds
+        creds = []
         if args:
         if args:
-            cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
-            hrn = args[0]
-            options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
+            creds.append(self.slice_credential_string(args[0]))
         else:
         else:
-            cred = user_cred
-     
-        creds = [cred]
-        if opts.delegate:
-            delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
-            creds.append(delegated_cred)
-        if opts.rspec_version:
+            creds.append(self.my_credential_string)
+        if options.delegate:
+            creds.append(self.delegate_cred(cred, get_authority(self.authority)))
+
+        # no need to check if server accepts the options argument since the options has
+        # been a required argument since v1 API
+        api_options = {}
+        # always send call_id to v2 servers
+        api_options ['call_id'] = unique_call_id()
+        # ask for cached value if available
+        api_options ['cached'] = True
+        if args:
+            hrn = args[0]
+            api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
+        if options.info:
+            api_options['info'] = options.info
+        if options.list_leases:
+            api_options['list_leases'] = options.list_leases
+        if options.current:
+            if options.current == True:
+                api_options['cached'] = False
+            else:
+                api_options['cached'] = True
+        if options.rspec_version:
             version_manager = VersionManager()
             server_version = self.get_cached_server_version(server)
             if 'sfa' in server_version:
             version_manager = VersionManager()
             server_version = self.get_cached_server_version(server)
             if 'sfa' in server_version:
-                # just request the version the client wants 
-                options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
+                # just request the version the client wants
+                api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
             else:
             else:
-                # this must be a protogeni aggregate. We should request a v2 ad rspec
-                # regardless of what the client user requested 
-                options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()     
+                api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
         else:
         else:
-            options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
-        call_args = [creds, options]
-        result = server.ListResources(*call_args)
+            api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
+        result = server.ListResources (creds, api_options)
         value = ReturnValue.get_value(result)
         value = ReturnValue.get_value(result)
-        if opts.file is None:
-            display_rspec(value, opts.format)
-        else:
-            save_rspec_to_file(value, opts.file)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        if options.file is not None:
+            save_rspec_to_file(value, options.file)
+        if (self.options.raw is None) and (options.file is None):
+            display_rspec(value, options.format)
+
         return
 
         return
 
-    # created named slice with given rspec
-    def create(self, opts, args):
-        server = self.server_proxy_from_opts(opts)
-        server_version = self.get_cached_server_version(server)
+    def create(self, options, args):
+        """
+        create or update named slice with given rspec
+        """
+        server = self.sliceapi()
+
+        # xxx do we need to check usage (len(args)) ?
+        # slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice')
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice')
-        user_cred = self.get_user_cred()
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
-
-        if hasattr(opts, 'aggregate') and opts.aggregate:
-            delegated_cred = None
-        else:
-            # delegate the cred to the callers root authority
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
-            #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
-            #creds.append(delegated_cred)
 
 
+        # credentials
+        creds = [self.slice_credential_string(slice_hrn)]
+        delegated_cred = None
+        server_version = self.get_cached_server_version(server)
+        if server_version.get('interface') == 'slicemgr':
+            # delegate our cred to the slice manager
+            # do not delegate cred to slicemgr...not working at the moment
+            pass
+            #if server_version.get('hrn'):
+            #    delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
+            #elif server_version.get('urn'):
+            #    delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
+
+        # rspec
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
 
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
 
+        # users
         # need to pass along user keys to the aggregate.
         # users = [
         #  { urn: urn:publicid:IDN+emulab.net+user+alice
         #    keys: [<ssh key A>, <ssh key B>]
         #  }]
         users = []
         # need to pass along user keys to the aggregate.
         # users = [
         #  { urn: urn:publicid:IDN+emulab.net+user+alice
         #    keys: [<ssh key A>, <ssh key B>]
         #  }]
         users = []
-        slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
+        slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
         if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
             slice_record = slice_records[0]
             user_hrns = slice_record['researcher']
             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
         if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
             slice_record = slice_records[0]
             user_hrns = slice_record['researcher']
             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
-            user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
+            user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
 
             if 'sfa' not in server_version:
                 users = pg_users_arg(user_records)
                 rspec = RSpec(rspec)
                 rspec.filter({'component_manager_id': server_version['urn']})
                 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
 
             if 'sfa' not in server_version:
                 users = pg_users_arg(user_records)
                 rspec = RSpec(rspec)
                 rspec.filter({'component_manager_id': server_version['urn']})
                 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
-                creds = [slice_cred]
             else:
             else:
+                print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO"
                 users = sfa_users_arg(user_records, slice_record)
                 users = sfa_users_arg(user_records, slice_record)
-                creds = [slice_cred]
-                if delegated_cred:
-                    creds.append(delegated_cred)
-        call_args = [slice_urn, creds, rspec, users]
-        if self.server_supports_options_arg(server):
-            options = {'call_id': unique_call_id()}
-            call_args.append(options)
-        result = server.CreateSliver(*call_args)
+
+        # do not append users, keys, or slice tags. Anything
+        # not contained in this request will be removed from the slice
+
+        # CreateSliver has supported the options argument for a while now so it should
+        # be safe to assume this server support it
+        api_options = {}
+        api_options ['append'] = False
+        api_options ['call_id'] = unique_call_id()
+        result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
         value = ReturnValue.get_value(result)
         value = ReturnValue.get_value(result)
-        if opts.file is None:
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        if options.file is not None:
+            save_rspec_to_file (value, options.file)
+        if (self.options.raw is None) and (options.file is None):
             print value
             print value
-        else:
-            save_rspec_to_file (value, opts.file)
+
         return value
 
         return value
 
-    # get a ticket for the specified slice
-    def get_ticket(self, opts, args):
-        slice_hrn, rspec_path = args[0], args[1]
-        slice_urn = hrn_to_urn(slice_hrn, 'slice')
-        user_cred = self.get_user_cred()
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
+    def delete(self, options, args):
+        """
+        delete named slice (DeleteSliver)
+        """
+        server = self.sliceapi()
+
+        # slice urn
+        slice_hrn = args[0]
+        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
+
+        # creds
+        slice_cred = self.slice_credential_string(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        rspec_file = self.get_rspec_file(rspec_path) 
-        rspec = open(rspec_file).read()
-        server = self.server_proxy_from_opts(opts)
-        ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
-        file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
-        self.logger.info("writing ticket to %s"%file)
-        ticket = SfaTicket(string=ticket_string)
-        ticket.save_to_file(filename=file, save_parents=True)
-
-    def redeem_ticket(self, opts, args):
-        ticket_file = args[0]
-        
-        # get slice hrn from the ticket
-        # use this to get the right slice credential 
-        ticket = SfaTicket(filename=ticket_file)
-        ticket.decode()
-        slice_hrn = ticket.gidObject.get_hrn()
-        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        #slice_hrn = ticket.attributes['slivers'][0]['hrn']
-        user_cred = self.get_user_cred()
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
-        
-        # get a list of node hostnames from the RSpec 
-        tree = etree.parse(StringIO(ticket.rspec))
-        root = tree.getroot()
-        hostnames = root.xpath("./network/site/node/hostname/text()")
         
         
-        # create an xmlrpc connection to the component manager at each of these
-        # components and gall redeem_ticket
-        connections = {}
-        for hostname in hostnames:
-            try:
-                self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
-                server = self.server_proxy(hostname, CM_PORT, self.key_file, \
-                                         self.cert_file, self.options.debug)
-                server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
-                self.logger.info("Success")
-            except socket.gaierror:
-                self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
-            except Exception, e:
-                self.logger.log_exc(e.message)
-        return
-    # delete named slice
-    def delete(self, opts, args):
+        # options and call_id when supported
+        api_options = {}
+        api_options ['call_id'] = unique_call_id()
+        result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
+        value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+        return value 
+  
+    def status(self, options, args):
+        """
+        retrieve slice status (SliverStatus)
+        """
+        server = self.sliceapi()
+
+        # slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
+
+        # creds 
+        slice_cred = self.slice_credential_string(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.server_proxy_from_opts(opts)
-        call_args = [slice_urn, creds]
-        if self.server_supports_options_arg(server):
-            options = {'call_id': unique_call_id()}
-            call_args.append(options)
-        return server.DeleteSliver(*call_args) 
-  
-    # start named slice
-    def start(self, opts, args):
+
+        # options and call_id when supported
+        api_options = {}
+        api_options['call_id']=unique_call_id()
+        result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
+        value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+
+    def start(self, options, args):
+        """
+        start named slice (Start)
+        """
+        server = self.sliceapi()
+
+        # the slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
+        
+        # cred
+        slice_cred = self.slice_credential_string(args[0])
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.server_proxy_from_opts(opts)
-        return server.Start(slice_urn, creds)
+        # xxx Thierry - does this not need an api_options as well ?
+        result = server.Start(slice_urn, creds)
+        value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+        return value
     
     
-    # stop named slice
-    def stop(self, opts, args):
+    def stop(self, options, args):
+        """
+        stop named slice (Stop)
+        """
+        server = self.sliceapi()
+        # slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
+        # cred
+        slice_cred = self.slice_credential_string(args[0])
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.server_proxy_from_opts(opts)
-        return server.Stop(slice_urn, creds)
+        result =  server.Stop(slice_urn, creds)
+        value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+        return value
     
     # reset named slice
     
     # reset named slice
-    def reset(self, opts, args):
+    def reset(self, options, args):
+        """
+        reset named slice (reset_slice)
+        """
+        server = self.sliceapi()
+        # slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        server = self.server_proxy_from_opts(opts)
-        slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
+        # cred
+        slice_cred = self.slice_credential_string(args[0])
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        return server.reset_slice(creds, slice_urn)
+        result = server.reset_slice(creds, slice_urn)
+        value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+        return value
 
 
-    def renew(self, opts, args):
+    def renew(self, options, args):
+        """
+        renew slice (RenewSliver)
+        """
+        server = self.sliceapi()
+        # slice urn    
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        server = self.server_proxy_from_opts(opts)
-        slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
+        # creds
+        slice_cred = self.slice_credential_string(args[0])
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
+        # time
         time = args[1]
         time = args[1]
-        
-        call_args = [slice_urn, creds, time]
-        if self.server_supports_options_arg(server):
-            options = {'call_id': unique_call_id()}
-            call_args.append(options)
-        result =  server.RenewSliver(*call_args)
+        # options and call_id when supported
+        api_options = {}
+       api_options['call_id']=unique_call_id()
+        result =  server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
         value = ReturnValue.get_value(result)
         value = ReturnValue.get_value(result)
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
         return value
 
 
         return value
 
 
-    def status(self, opts, args):
+    def shutdown(self, options, args):
+        """
+        shutdown named slice (Shutdown)
+        """
+        server = self.sliceapi()
+        # slice urn
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
+        # creds
+        slice_cred = self.slice_credential_string(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.server_proxy_from_opts(opts)
-        call_args = [slice_urn, creds]
-        if self.server_supports_options_arg(server):
-            options = {'call_id': unique_call_id()}
-            call_args.append(options)
-        result = server.SliverStatus(*call_args)
+        result = server.Shutdown(slice_urn, creds)
         value = ReturnValue.get_value(result)
         value = ReturnValue.get_value(result)
-        print value
-        if opts.file:
-            save_variable_to_file(value, opts.file, opts.fileformat)
-
+        if self.options.raw:
+            save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
+        else:
+            print value
+        return value         
+    
 
 
-    def shutdown(self, opts, args):
-        slice_hrn = args[0]
-        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
+    def get_ticket(self, options, args):
+        """
+        get a ticket for the specified slice
+        """
+        server = self.sliceapi()
+        # slice urn
+        slice_hrn, rspec_path = args[0], args[1]
+        slice_urn = hrn_to_urn(slice_hrn, 'slice')
+        # creds
+        slice_cred = self.slice_credential_string(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if opts.delegate:
+        if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.server_proxy_from_opts(opts)
-        return server.Shutdown(slice_urn, creds)         
-    
-    def print_help (self):
-        self.sfi_parser.print_help()
-        self.cmd_parser.print_help()
-
-    #
-    # Main: parse arguments and dispatch to command
-    #
-    def main(self):
-        self.sfi_parser = self.create_parser()
-        (options, args) = self.sfi_parser.parse_args()
-        self.options = options
+        # rspec
+        rspec_file = self.get_rspec_file(rspec_path) 
+        rspec = open(rspec_file).read()
+        # options and call_id when supported
+        api_options = {}
+       api_options['call_id']=unique_call_id()
+        # get ticket at the server
+        ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
+        # save
+        file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
+        self.logger.info("writing ticket to %s"%file)
+        ticket = SfaTicket(string=ticket_string)
+        ticket.save_to_file(filename=file, save_parents=True)
 
 
-        self.logger.setLevelFromOptVerbose(self.options.verbose)
-        if options.hashrequest:
-            self.hashrequest = True
-        if len(args) <= 0:
-            self.logger.critical("No command given. Use -h for help.")
-            return -1
-    
-        command = args[0]
-        self.cmd_parser = self.create_cmd_parser(command)
-        (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
+    def redeem_ticket(self, options, args):
+        """
+        Connects to nodes in a slice and redeems a ticket
+(slice hrn is retrieved from the ticket)
+        """
+        ticket_file = args[0]
+        
+        # get slice hrn from the ticket
+        # use this to get the right slice credential 
+        ticket = SfaTicket(filename=ticket_file)
+        ticket.decode()
+        ticket_string = ticket.save_to_string(save_parents=True)
 
 
-        self.set_servers()
-        self.logger.info("Command=%s" % command)
-        if command in ("resources"):
-            self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
-        elif command in ("list", "show", "remove"):
-            self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
-        self.logger.debug('cmd_args %s' % cmd_args)
+        slice_hrn = ticket.gidObject.get_hrn()
+        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
+        #slice_hrn = ticket.attributes['slivers'][0]['hrn']
+        slice_cred = self.slice_credential_string(slice_hrn)
+        
+        # get a list of node hostnames from the RSpec 
+        tree = etree.parse(StringIO(ticket.rspec))
+        root = tree.getroot()
+        hostnames = root.xpath("./network/site/node/hostname/text()")
+        
+        # create an xmlrpc connection to the component manager at each of these
+        # components and gall redeem_ticket
+        connections = {}
+        for hostname in hostnames:
+            try:
+                self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
+                cm_url="http://%s:%s/"%(hostname,CM_PORT)
+                server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
+                server = self.server_proxy(hostname, CM_PORT, self.private_key, 
+                                           timeout=self.options.timeout, verbose=self.options.debug)
+                server.RedeemTicket(ticket_string, slice_cred)
+                self.logger.info("Success")
+            except socket.gaierror:
+                self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
+            except Exception, e:
+                self.logger.log_exc(e.message)
+        return
 
 
-        try:
-            self.dispatch(command, cmd_opts, cmd_args)
-        except KeyError:
-            self.logger.critical ("Unknown command %s"%command)
-            raise
+    def create_gid(self, options, args):
+        """
+        Create a GID (CreateGid)
+        """
+        if len(args) < 1:
+            self.print_help()
             sys.exit(1)
             sys.exit(1)
+        target_hrn = args[0]
+        gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
+        if options.file:
+            filename = options.file
+        else:
+            filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
+        self.logger.info("writing %s gid to %s" % (target_hrn, filename))
+        GID(string=gid).save_to_file(filename)
+         
+
+    def delegate(self, options, args):
+        """
+        (locally) create delegate credential for use by given hrn
+        """
+        delegee_hrn = args[0]
+        if options.delegate_user:
+            cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
+        elif options.delegate_slice:
+            slice_cred = self.slice_credential_string(options.delegate_slice)
+            cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
+        else:
+            self.logger.warning("Must specify either --user or --slice <hrn>")
+            return
+        delegated_cred = Credential(string=cred)
+        object_hrn = delegated_cred.get_gid_object().get_hrn()
+        if options.delegate_user:
+            dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
+                                  + get_leaf(object_hrn) + ".cred")
+        elif options.delegate_slice:
+            dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
+                                  + get_leaf(object_hrn) + ".cred")
+
+        delegated_cred.save_to_file(dest_fn, save_parents=True)
+
+        self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
     
     
-        return
-    
-if __name__ == "__main__":
-    Sfi().main()
+    def get_trusted_certs(self, options, args):
+        """
+        return uhe trusted certs at this interface (get_trusted_certs)
+        """ 
+        trusted_certs = self.registry().get_trusted_certs()
+        for trusted_cert in trusted_certs:
+            gid = GID(string=trusted_cert)
+            gid.dump()
+            cert = Certificate(string=trusted_cert)
+            self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
+        return 
+
+    def config (self, options, args):
+        "Display contents of current config"
+        self.show_config()