Re-activate and fix command "trusted" in sfi.py
[sfa.git] / sfa / client / sfi.py
index 8fa380b..14122e1 100644 (file)
@@ -13,10 +13,12 @@ import datetime
 import codecs
 import pickle
 import json
 import codecs
 import pickle
 import json
+import shutil
 from lxml import etree
 from StringIO import StringIO
 from optparse import OptionParser
 from pprint import PrettyPrinter
 from lxml import etree
 from StringIO import StringIO
 from optparse import OptionParser
 from pprint import PrettyPrinter
+from tempfile import mkstemp
 
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.gid import GID
 
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.gid import GID
@@ -41,33 +43,12 @@ from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
 from sfa.client.return_value import ReturnValue
 from sfa.client.candidates import Candidates
 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
 from sfa.client.return_value import ReturnValue
 from sfa.client.candidates import Candidates
+from sfa.client.manifolduploader import ManifoldUploader
 
 CM_PORT=12346
 
 
 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)
+from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
+    terminal_render, filter_records 
 
 # display methods
 def display_rspec(rspec, format='rspec'):
 
 # display methods
 def display_rspec(rspec, format='rspec'):
@@ -113,14 +94,15 @@ def filter_records(type, records):
     return filtered_records
 
 
     return filtered_records
 
 
-def credential_printable (credential_string):
-    credential=Credential(string=credential_string)
+def credential_printable (cred):
+    credential=Credential(cred=cred)
     result=""
     result += credential.get_summary_tostring()
     result += "\n"
     rights = credential.get_privileges()
     result=""
     result += credential.get_summary_tostring()
     result += "\n"
     rights = credential.get_privileges()
-    result += "rights=%s"%rights
-    result += "\n"
+    result += "type=%s\n" % credential.type    
+    result += "version=%s\n" % credential.version    
+    result += "rights=%s\n"%rights
     return result
 
 def show_credentials (cred_s):
     return result
 
 def show_credentials (cred_s):
@@ -243,6 +225,36 @@ def load_record_from_file(filename):
 import uuid
 def unique_call_id(): return uuid.uuid4().urn
 
 import uuid
 def unique_call_id(): return uuid.uuid4().urn
 
+########## a simple model for maintaing 3 doc attributes per command (instead of just one)
+# essentially for the methods that implement a subcommand like sfi list
+# we need to keep track of
+# (*) doc         a few lines that tell what it does, still located in __doc__
+# (*) args_string a simple one-liner that describes mandatory arguments
+# (*) example     well, one or several releant examples
+# 
+# since __doc__ only accounts for one, we use this simple mechanism below
+# however we keep doc in place for easier migration
+
+from functools import wraps
+
+# we use a list as well as a dict so we can keep track of the order
+commands_list=[]
+commands_dict={}
+
+def register_command (args_string, example):
+    def wrap(m): 
+        name=getattr(m,'__name__')
+        doc=getattr(m,'__doc__',"-- missing doc --")
+        doc=doc.strip(" \t\n")
+        commands_list.append(name)
+        commands_dict[name]=(doc, args_string, example)
+        @wraps(m)
+        def new_method (*args, **kwds): return m(*args, **kwds)
+        return new_method
+    return wrap
+
+##########
+
 class Sfi:
     
     # dirty hack to make this class usable from the outside
 class Sfi:
     
     # dirty hack to make this class usable from the outside
@@ -270,36 +282,15 @@ class Sfi:
         self.authority = None
         self.logger = sfi_logger
         self.logger.enable_console()
         self.authority = None
         self.logger = sfi_logger
         self.logger.enable_console()
-        self.available_names = [ tuple[0] for tuple in Sfi.available ]
-        self.available_dict = dict (Sfi.available)
-   
-    # 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"),
-        ("gid", "[name]"),
-        ("trusted", "cred"),
-        ("config", ""),
-        ]
-
-    def print_command_help (self, options):
+        ### various auxiliary material that we keep at hand 
+        self.command=None
+        # need to call this other than just 'config' as we have a command/method with that name
+        self.config_instance=None
+        self.config_file=None
+        self.client_bootstrap=None
+
+    ### suitable if no reasonable command has been provided
+    def print_commands_help (self, options):
         verbose=getattr(options,'verbose')
         format3="%18s %-15s %s"
         line=80*'-'
         verbose=getattr(options,'verbose')
         format3="%18s %-15s %s"
         line=80*'-'
@@ -308,66 +299,128 @@ class Sfi:
             print line
         else:
             print line
             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*' ')
+            self.create_parser_global().print_help()
+        # preserve order from the code
+        for command in commands_list:
+            (doc, args_string, example) = commands_dict[command]
             if verbose:
                 print line
             if verbose:
                 print line
-            print format3%(command,args,doc)
+            doc=doc.replace("\n","\n"+35*' ')
+            print format3%(command,args_string,doc)
             if verbose:
             if verbose:
-                self.create_command_parser(command).print_help()
+                self.create_parser_command(command).print_help()
+            
+    ### now if a known command was found we can be more verbose on that one
+    def print_help (self):
+        print "==================== Generic sfi usage"
+        self.sfi_parser.print_help()
+        (doc,_,example)=commands_dict[self.command]
+        print "\n==================== Purpose of %s"%self.command
+        print doc
+        print "\n==================== Specific usage for %s"%self.command
+        self.command_parser.print_help()
+        if example:
+            print "\n==================== %s example(s)"%self.command
+            print example
+
+    def create_parser_global(self):
+        # Generate command line parser
+        parser = OptionParser(add_help_option=False,
+                              usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
+                              description="Commands: %s"%(" ".join(commands_list)))
+        parser.add_option("-r", "--registry", dest="registry",
+                         help="root registry", metavar="URL", default=None)
+        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",
+                         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",
+                         help="authority name", metavar="HRN", default=None)
+        parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
+                         help="verbose mode - cumulative")
+        parser.add_option("-D", "--debug",
+                          action="store_true", dest="debug", default=False,
+                          help="Debug (xml-rpc) protocol messages")
+        # 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,
+                         help="Amout of time to wait before timing out the request")
+        parser.add_option("-h", "--help", 
+                         action="store_true", dest="help", default=False,
+                         help="one page summary on commands & exit")
+        parser.disable_interspersed_args()
+
+        return parser
+        
 
 
-    def create_command_parser(self, command):
-        if command not in self.available_dict:
+    def create_parser_command(self, command):
+        if command not in commands_dict:
             msg="Invalid command\n"
             msg+="Commands: "
             msg="Invalid command\n"
             msg+="Commands: "
-            msg += ','.join(self.available_names)            
+            msg += ','.join(commands_list)            
             self.logger.critical(msg)
             sys.exit(2)
 
             self.logger.critical(msg)
             sys.exit(2)
 
-        parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
-                                     % (command, self.available_dict[command]))
+        # retrieve args_string
+        (_, args_string, __) = commands_dict[command]
+
+        parser = OptionParser(add_help_option=False,
+                              usage="sfi [sfi_options] %s [cmd_options] %s"
+                              % (command, args_string))
+        parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
+                           help="Summary of one command usage")
+
+        if command in ("config"):
+            parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
+                              help='how myslice config variables as well')
+
+        if command in ("version"):
+            parser.add_option("-R","--registry-version",
+                              action="store_true", dest="version_registry", default=False,
+                              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")
 
         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)") 
 
         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('-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',
+            parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
                               default='', type="str", action='callback', callback=optparse_listvalue_callback)
             parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>', 
                               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', 
+                              help='Set/replace slice researchers', default='', type="str", action='callback', 
                               callback=optparse_listvalue_callback)
                               callback=optparse_listvalue_callback)
-            parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Principal Investigators/Project Managers',
+            parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
                               default='', type="str", action='callback', callback=optparse_listvalue_callback)
                               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                          
             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"):
+        if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision", 
+                       "action", "shutdown", "renew", "status"):
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
                                   "authority in set of credentials for this call")
 
         # show_credential option
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
                                   "authority in set of credentials for this call")
 
         # show_credential option
-        if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"):
+        if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","delete","status","renew"):
             parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
                               help="show credential(s) used in human-readable form")
         # registy filter option
             parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
                               help="show credential(s) used in human-readable form")
         # registy filter option
@@ -379,7 +432,7 @@ class Sfi:
         if command in ("show"):
             parser.add_option("-k","--key",dest="keys",action="append",default=[],
                               help="specify specific keys to be displayed from record")
         if command in ("show"):
             parser.add_option("-k","--key",dest="keys",action="append",default=[],
                               help="specify specific keys to be displayed from record")
-        if command in ("resources"):
+        if command in ("resources", "describe"):
             # rspec version
             parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
                               help="schema type and version of resulting RSpec")
             # rspec version
             parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
                               help="schema type and version of resulting RSpec")
@@ -400,8 +453,7 @@ class Sfi:
                                 choices=("all", "resources", "leases"), default="resources")
 
 
                                 choices=("all", "resources", "leases"), default="resources")
 
 
-        # 'create' does return the new rspec, makes sense to save that too
-        if command in ("resources", "show", "list", "gid", 'create'):
+        if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
            parser.add_option("-o", "--output", dest="file",
                             help="output XML to file", metavar="FILE", default=None)
 
            parser.add_option("-o", "--output", dest="file",
                             help="output XML to file", metavar="FILE", default=None)
 
@@ -416,83 +468,49 @@ class Sfi:
         if command == 'list':
            parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
                              help="list all child records", default=False)
         if command == 'list':
            parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
                              help="list all child records", default=False)
+           parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
+                             help="gives details, like user keys", default=False)
         if command in ("delegate"):
            parser.add_option("-u", "--user",
         if command in ("delegate"):
            parser.add_option("-u", "--user",
-                            action="store_true", dest="delegate_user", default=False,
-                            help="delegate user credential")
-           parser.add_option("-s", "--slice", dest="delegate_slice",
-                            help="delegate slice credential", metavar="HRN", default=None)
+                             action="store_true", dest="delegate_user", default=False,
+                             help="delegate your own credentials; default if no other option is provided")
+           parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
+                             metavar="slice_hrn", help="delegate cred. for slice HRN")
+           parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
+                             metavar='auth_hrn', help="delegate cred for auth HRN")
+           # this primarily is a shorthand for -a my_hrn
+           parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
+                             help="delegate your PI credentials, so s.t. like -a your_hrn^")
+           parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
+                             help="""by default the mandatory argument is expected to be a user, 
+use this if you mean an authority instead""")
+
+        if command in ("myslice"):
+            parser.add_option("-p","--password",dest='password',action='store',default=None,
+                              help="specify mainfold password on the command line")
+            parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
+                             metavar="slice_hrn", help="delegate cred. for slice HRN")
+            parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
+                             metavar='auth_hrn', help="delegate PI cred for auth HRN")
         
         
-        if command in ("version"):
-            parser.add_option("-R","--registry-version",
-                              action="store_true", dest="version_registry", default=False,
-                              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")
-
         return parser
 
         
         return parser
 
         
-    def create_parser(self):
-
-        # Generate command line parser
-        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("-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",
-                         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",
-                         help="authority name", metavar="HRN", default=None)
-        parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
-                         help="verbose mode - cumulative")
-        parser.add_option("-D", "--debug",
-                          action="store_true", dest="debug", default=False,
-                          help="Debug (xml-rpc) protocol messages")
-        # 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,
-                         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
-        
-
-    def print_help (self):
-        print "==================== Generic sfi usage"
-        self.sfi_parser.print_help()
-        print "==================== Specific command usage"
-        self.command_parser.print_help()
-
     #
     # Main: parse arguments and dispatch to command
     #
     def dispatch(self, command, command_options, command_args):
     #
     # Main: parse arguments and dispatch to command
     #
     def dispatch(self, command, command_options, command_args):
-        return getattr(self, command)(command_options, command_args)
+        method=getattr(self, command, None)
+        if not method:
+            print "Unknown command %s"%command
+            return
+        return method(command_options, command_args)
 
     def main(self):
 
     def main(self):
-        self.sfi_parser = self.create_parser()
+        self.sfi_parser = self.create_parser_global()
         (options, args) = self.sfi_parser.parse_args()
         (options, args) = self.sfi_parser.parse_args()
-        if options.command_help: 
-            self.print_command_help(options)
+        if options.help: 
+            self.print_commands_help(options)
             sys.exit(1)
         self.options = options
 
             sys.exit(1)
         self.options = options
 
@@ -500,47 +518,74 @@ class Sfi:
 
         if len(args) <= 0:
             self.logger.critical("No command given. Use -h for help.")
 
         if len(args) <= 0:
             self.logger.critical("No command given. Use -h for help.")
-            self.print_command_help(options)
+            self.print_commands_help(options)
             return -1
     
         # complete / find unique match with command set
             return -1
     
         # complete / find unique match with command set
-        command_candidates = Candidates (self.available_names)
+        command_candidates = Candidates (commands_list)
         input = args[0]
         command = command_candidates.only_match(input)
         if not command:
         input = args[0]
         command = command_candidates.only_match(input)
         if not command:
-            self.print_command_help(options)
+            self.print_commands_help(options)
             sys.exit(1)
         # second pass options parsing
             sys.exit(1)
         # second pass options parsing
-        self.command_parser = self.create_command_parser(command)
+        self.command=command
+        self.command_parser = self.create_parser_command(command)
         (command_options, command_args) = self.command_parser.parse_args(args[1:])
         (command_options, command_args) = self.command_parser.parse_args(args[1:])
+        if command_options.help:
+            self.print_help()
+            sys.exit(1)
         self.command_options = command_options
 
         self.read_config () 
         self.bootstrap ()
         self.command_options = command_options
 
         self.read_config () 
         self.bootstrap ()
-        self.logger.info("Command=%s" % command)
+        self.logger.debug("Command=%s" % self.command)
 
         try:
             self.dispatch(command, command_options, command_args)
 
         try:
             self.dispatch(command, command_options, command_args)
-        except KeyError:
-            self.logger.critical ("Unknown command %s"%command)
-            sys.exit(1)
+        except SystemExit:
+            return 1
+        except:
+            self.logger.log_exc ("sfi command %s failed"%command)
+            return 1
 
 
-        return
+        return 0
     
     ####################
     def read_config(self):
         config_file = os.path.join(self.options.sfi_dir,"sfi_config")
     
     ####################
     def read_config(self):
         config_file = os.path.join(self.options.sfi_dir,"sfi_config")
+        shell_config_file  = os.path.join(self.options.sfi_dir,"sfi_config.sh")
         try:
         try:
-           config = Config (config_file)
+            if Config.is_ini(config_file):
+                config = Config (config_file)
+            else:
+                # try upgrading from shell config format
+                fp, fn = mkstemp(suffix='sfi_config', text=True)  
+                config = Config(fn)
+                # we need to preload the sections we want parsed 
+                # from the shell config
+                config.add_section('sfi')
+                # sface users should be able to use this same file to configure their stuff
+                config.add_section('sface')
+                # manifold users should be able to specify the details 
+                # of their backend server here for 'sfi myslice'
+                config.add_section('myslice')
+                config.load(config_file)
+                # back up old config
+                shutil.move(config_file, shell_config_file)
+                # write new config
+                config.save(config_file)
+                 
         except:
         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)
+            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)
      
      
+        self.config_instance=config
         errors = 0
         # Set SliceMgr URL
         if (self.options.sm is not None):
         errors = 0
         # Set SliceMgr URL
         if (self.options.sm is not None):
@@ -557,7 +602,7 @@ class Sfi:
         elif hasattr(config, "SFI_REGISTRY"):
            self.reg_url = config.SFI_REGISTRY
         else:
         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)
+           self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
            errors += 1 
 
         # Set user HRN
            errors += 1 
 
         # Set user HRN
@@ -566,7 +611,7 @@ class Sfi:
         elif hasattr(config, "SFI_USER"):
            self.user = config.SFI_USER
         else:
         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)
+           self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
            errors += 1 
 
         # Set authority HRN
            errors += 1 
 
         # Set authority HRN
@@ -582,17 +627,6 @@ class Sfi:
         if errors:
            sys.exit(1)
 
         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
     #
     #
     # Get various credential and spec files
     #
@@ -619,7 +653,7 @@ class Sfi:
             if not os.path.isfile(client_bootstrap.private_key_filename()):
                 self.logger.info ("private key not found, trying legacy name")
                 try:
             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))
+                    legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(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)
                     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)
@@ -632,6 +666,9 @@ class Sfi:
         # extract what's needed
         self.private_key = client_bootstrap.private_key()
         self.my_credential_string = client_bootstrap.my_credential_string ()
         # extract what's needed
         self.private_key = client_bootstrap.private_key()
         self.my_credential_string = client_bootstrap.my_credential_string ()
+        self.my_credential = {'geni_type': 'geni_sfa',
+                              'geni_version': '3', 
+                              'geni_value': self.my_credential_string}
         self.my_gid = client_bootstrap.my_gid ()
         self.client_bootstrap = client_bootstrap
 
         self.my_gid = client_bootstrap.my_gid ()
         self.client_bootstrap = client_bootstrap
 
@@ -642,9 +679,17 @@ class Sfi:
             sys.exit(-1)
         return self.client_bootstrap.authority_credential_string (self.authority)
 
             sys.exit(-1)
         return self.client_bootstrap.authority_credential_string (self.authority)
 
+    def authority_credential_string(self, auth_hrn):
+        return self.client_bootstrap.authority_credential_string (auth_hrn)
+
     def slice_credential_string(self, name):
         return self.client_bootstrap.slice_credential_string (name)
 
     def slice_credential_string(self, name):
         return self.client_bootstrap.slice_credential_string (name)
 
+    def slice_credential(self, name):
+        return {'geni_type': 'geni_sfa',
+                'geni_version': '3',
+                'geni_value': self.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
     # 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
@@ -798,10 +843,35 @@ class Sfi:
     # Registry-related commands
     #==========================================================================
 
     # Registry-related commands
     #==========================================================================
 
+    @register_command("","")
+    def config (self, options, args):
+        "Display contents of current config"
+        print "# From configuration file %s"%self.config_file
+        flags=[ ('sfi', [ ('registry','reg_url'),
+                          ('auth','authority'),
+                          ('user','user'),
+                          ('sm','sm_url'),
+                          ]),
+                ]
+        if options.myslice:
+            flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
+
+        for (section, tuples) in flags:
+            print "[%s]"%section
+            try:
+                for (external_name, internal_name) in tuples:
+                    print "%-20s = %s"%(external_name,getattr(self,internal_name))
+            except:
+                for name in tuples:
+                    varname="%s_%s"%(section.upper(),name.upper())
+                    value=getattr(self.config_instance,varname)
+                    print "%-20s = %s"%(name,value)
+
+    @register_command("","")
     def version(self, options, args):
         """
         display an SFA server version (GetVersion)
     def version(self, options, args):
         """
         display an SFA server version (GetVersion)
-or version information about sfi itself
+  or version information about sfi itself
         """
         if options.version_local:
             version=version_core()
         """
         if options.version_local:
             version=version_core()
@@ -818,6 +888,7 @@ or version information about sfi itself
             pprinter = PrettyPrinter(indent=4)
             pprinter.pprint(version)
 
             pprinter = PrettyPrinter(indent=4)
             pprinter.pprint(version)
 
+    @register_command("authority","")
     def list(self, options, args):
         """
         list entries in named authority registry (List)
     def list(self, options, args):
         """
         list entries in named authority registry (List)
@@ -838,14 +909,14 @@ or version information about sfi itself
             raise Exception, "Not enough parameters for the 'list' command"
 
         # filter on person, slice, site, node, etc.
             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...
+        # This really should be in the self.filter_records funct def comment...
         list = filter_records(options.type, list)
         list = filter_records(options.type, list)
-        for record in list:
-            print "%s (%s)" % (record['hrn'], record['type'])
+        terminal_render (list, options)
         if options.file:
             save_records_to_file(options.file, list, options.fileformat)
         return
     
         if options.file:
             save_records_to_file(options.file, list, options.fileformat)
         return
     
+    @register_command("name","")
     def show(self, options, args):
         """
         show details about named registry record (Resolve)
     def show(self, options, args):
         """
         show details about named registry record (Resolve)
@@ -854,7 +925,8 @@ or version information about sfi itself
             self.print_help()
             sys.exit(1)
         hrn = args[0]
             self.print_help()
             sys.exit(1)
         hrn = args[0]
-        record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
+        # explicitly require Resolve to run in details mode
+        record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
         record_dicts = filter_records(options.type, record_dicts)
         if not record_dicts:
             self.logger.error("No record of type %s"% options.type)
         record_dicts = filter_records(options.type, record_dicts)
         if not record_dicts:
             self.logger.error("No record of type %s"% options.type)
@@ -876,16 +948,27 @@ or version information about sfi itself
             save_records_to_file(options.file, record_dicts, options.fileformat)
         return
     
             save_records_to_file(options.file, record_dicts, options.fileformat)
         return
     
+    @register_command("[xml-filename]","")
     def add(self, options, args):
     def add(self, options, args):
-        "add record into registry from xml file (Register)"
+        """add record into registry (Register) 
+  from command line options (recommended) 
+  old-school method involving an xml file still supported"""
+
         auth_cred = self.my_authority_credential_string()
         if options.show_credential:
             show_credentials(auth_cred)
         record_dict = {}
         auth_cred = self.my_authority_credential_string()
         if options.show_credential:
             show_credentials(auth_cred)
         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 len(args) > 1:
+            self.print_help()
+            sys.exit(1)
+        if len(args)==1:
+            try:
+                record_filepath = args[0]
+                rec_file = self.get_record_file(record_filepath)
+                record_dict.update(load_record_from_file(rec_file).todict())
+            except:
+                print "Cannot load record file %s"%record_filepath
+                sys.exit(1)
         if options:
             record_dict.update(load_record_from_opts(options).todict())
         # we should have a type by now
         if options:
             record_dict.update(load_record_from_opts(options).todict())
         # we should have a type by now
@@ -901,8 +984,11 @@ or version information about sfi itself
                 record_dict['last_name'] = record_dict['hrn'] 
         return self.registry().Register(record_dict, auth_cred)
     
                 record_dict['last_name'] = record_dict['hrn'] 
         return self.registry().Register(record_dict, auth_cred)
     
+    @register_command("[xml-filename]","")
     def update(self, options, args):
     def update(self, options, args):
-        "update record into registry from xml file (Update)"
+        """update record into registry (Update) 
+  from command line options (recommended) 
+  old-school method involving an xml file still supported"""
         record_dict = {}
         if len(args) > 0:
             record_filepath = args[0]
         record_dict = {}
         if len(args) > 0:
             record_filepath = args[0]
@@ -942,6 +1028,7 @@ or version information about sfi itself
             show_credentials(cred)
         return self.registry().Update(record_dict, cred)
   
             show_credentials(cred)
         return self.registry().Update(record_dict, cred)
   
+    @register_command("hrn","")
     def remove(self, options, args):
         "remove registry record by name (Remove)"
         auth_cred = self.my_authority_credential_string()
     def remove(self, options, args):
         "remove registry record by name (Remove)"
         auth_cred = self.my_authority_credential_string()
@@ -960,41 +1047,16 @@ or version information about sfi itself
     # Slice-related commands
     # ==================================================================
 
     # Slice-related commands
     # ==================================================================
 
-    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)  
-        # options and call_id when supported
-        api_options = {}
-       api_options['call_id']=unique_call_id()
-        if options.show_credential:
-            show_credentials(creds)
-        result = server.ListSlices(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:
-            display_list(value)
-        return
-
     # show rspec for named slice
     # show rspec for named slice
+    @register_command("","")
     def resources(self, options, args):
         """
     def resources(self, options, args):
         """
-        with no arg, discover available resources, (ListResources)
-or with an slice hrn, shows currently provisioned resources
+        discover available resources (ListResources)
         """
         server = self.sliceapi()
 
         # set creds
         """
         server = self.sliceapi()
 
         # set creds
-        creds = []
-        if args:
-            creds.append(self.slice_credential_string(args[0]))
-        else:
-            creds.append(self.my_credential_string)
+        creds = [self.my_credential]
         if options.delegate:
             creds.append(self.delegate_cred(cred, get_authority(self.authority)))
         if options.show_credential:
         if options.delegate:
             creds.append(self.delegate_cred(cred, get_authority(self.authority)))
         if options.show_credential:
@@ -1007,9 +1069,6 @@ or with an slice hrn, shows currently provisioned resources
         api_options ['call_id'] = unique_call_id()
         # ask for cached value if available
         api_options ['cached'] = True
         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:
         if options.info:
             api_options['info'] = options.info
         if options.list_leases:
@@ -1026,9 +1085,9 @@ or with an slice hrn, shows currently provisioned resources
                 # just request the version the client wants
                 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
             else:
                 # just request the version the client wants
                 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
             else:
-                api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
+                api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
         else:
         else:
-            api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
+            api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
         result = server.ListResources (creds, api_options)
         value = ReturnValue.get_value(result)
         if self.options.raw:
         result = server.ListResources (creds, api_options)
         value = ReturnValue.get_value(result)
         if self.options.raw:
@@ -1040,22 +1099,89 @@ or with an slice hrn, shows currently provisioned resources
 
         return
 
 
         return
 
-    def create(self, options, args):
+    @register_command("slice_hrn","")
+    def describe(self, options, args):
         """
         """
-        create or update named slice with given rspec
+        shows currently allocated/provisioned resources 
+        of the named slice or set of slivers (Describe) 
+        """
+        server = self.sliceapi()
+
+        # set creds
+        creds = [self.slice_credential(args[0])]
+        if options.delegate:
+            creds.append(self.delegate_cred(cred, get_authority(self.authority)))
+        if options.show_credential:
+            show_credentials(creds)
+
+        api_options = {'call_id': unique_call_id(),
+                       'cached': True,
+                       'info': options.info,
+                       'list_leases': options.list_leases,
+                       'geni_rspec_version': {'type': 'geni', 'version': '3'},
+                      }
+        if options.rspec_version:
+            version_manager = VersionManager()
+            server_version = self.get_cached_server_version(server)
+            if 'sfa' in server_version:
+                # just request the version the client wants
+                api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
+            else:
+                api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
+        urn = Xrn(args[0], type='slice').get_urn()        
+        result = server.Describe([urn], creds, 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)
+        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 
+
+    @register_command("slice_hrn","")
+    def delete(self, options, args):
+        """
+        de-allocate and de-provision all or named slivers of the slice (Delete)
         """
         server = self.sliceapi()
 
         """
         server = self.sliceapi()
 
-        # xxx do we need to check usage (len(args)) ?
         # slice urn
         slice_hrn = args[0]
         # slice urn
         slice_hrn = args[0]
-        slice_urn = hrn_to_urn(slice_hrn, 'slice')
+        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
+
+        # creds
+        slice_cred = self.slice_credential(slice_hrn)
+        creds = [slice_cred]
+        
+        # options and call_id when supported
+        api_options = {}
+        api_options ['call_id'] = unique_call_id()
+        if options.show_credential:
+            show_credentials(creds)
+        result = server.Delete([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
+
+    @register_command("slice_hrn rspec","")
+    def allocate(self, options, args):
+        """
+         allocate resources to the named slice (Allocate)
+        """
+        server = self.sliceapi()
+        server_version = self.get_cached_server_version(server)
+        slice_hrn = args[0]
+        slice_urn = Xrn(slice_hrn, type='slice').get_urn()
 
         # credentials
 
         # credentials
-        creds = [self.slice_credential_string(slice_hrn)]
+        creds = [self.slice_credential(slice_hrn)]
 
         delegated_cred = None
 
         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
         if server_version.get('interface') == 'slicemgr':
             # delegate our cred to the slice manager
             # do not delegate cred to slicemgr...not working at the moment
@@ -1071,38 +1197,24 @@ or with an slice hrn, shows currently provisioned resources
         # rspec
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
         # rspec
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
-
+        api_options = {}
+        api_options ['call_id'] = unique_call_id()
         # users
         # 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 = []
+        sfa_users = []
+        geni_users = []
         slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
         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']!=[]:
+        if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
             slice_record = slice_records[0]
             slice_record = slice_records[0]
-            user_hrns = slice_record['researcher']
+            user_hrns = slice_record['reg-researchers']
             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
             user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
             user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
+            sfa_users = sfa_users_arg(user_records, slice_record)
+            geni_users = pg_users_arg(user_records)
 
 
-            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')
-            else:
-                users = sfa_users_arg(user_records, slice_record)
-
-        # do not append users, keys, or slice tags. Anything
-        # not contained in this request will be removed from the slice
+        api_options['sfa_users'] = sfa_users
+        api_options['geni_users'] = geni_users
 
 
-        # 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))
+        result = server.Allocate(slice_urn, creds, rspec, 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)
         value = ReturnValue.get_value(result)
         if self.options.raw:
             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
@@ -1110,42 +1222,72 @@ or with an slice hrn, shows currently provisioned resources
             save_rspec_to_file (value, options.file)
         if (self.options.raw is None) and (options.file is None):
             print value
             save_rspec_to_file (value, options.file)
         if (self.options.raw is None) and (options.file is None):
             print value
-
         return value
         return value
+        
 
 
-    def delete(self, options, args):
+    @register_command("slice_hrn","")
+    def provision(self, options, args):
         """
         """
-        delete named slice (DeleteSliver)
+        provision already allocated resources of named slice (Provision)
         """
         server = self.sliceapi()
         """
         server = self.sliceapi()
-
-        # slice urn
+        server_version = self.get_cached_server_version(server)
         slice_hrn = args[0]
         slice_hrn = args[0]
-        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
+        slice_urn = Xrn(slice_hrn, type='slice').get_urn()
+
+        # credentials
+        creds = [self.slice_credential(slice_hrn)]
+        delegated_cred = None
+        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']))
 
 
-        # creds
-        slice_cred = self.slice_credential_string(slice_hrn)
-        creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
-        
-        # options and call_id when supported
-        api_options = {}
-        api_options ['call_id'] = unique_call_id()
         if options.show_credential:
             show_credentials(creds)
         if options.show_credential:
             show_credentials(creds)
-        result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
+
+        api_options = {}
+        api_options ['call_id'] = unique_call_id()
+
+        # set the requtested rspec version
+        version_manager = VersionManager()
+        rspec_version = version_manager._get_version('geni', '3').to_dict()
+        api_options['geni_rspec_version'] = rspec_version
+
+        # 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, [self.my_credential_string])
+        if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
+            slice_record = slice_records[0]
+            user_hrns = slice_record['reg-researchers']
+            user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
+            user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
+            users = pg_users_arg(user_records)
+        
+        api_options['geni_users'] = users
+        result = server.Provision([slice_urn], creds, 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)
         value = ReturnValue.get_value(result)
         if self.options.raw:
             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
-        else:
+        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
-        return value 
-  
+        return value     
+
+    @register_command("slice_hrn","")
     def status(self, options, args):
         """
     def status(self, options, args):
         """
-        retrieve slice status (SliverStatus)
+        retrieve the status of the slivers belonging to tne named slice (Status)
         """
         server = self.sliceapi()
 
         """
         server = self.sliceapi()
 
@@ -1154,87 +1296,42 @@ or with an slice hrn, shows currently provisioned resources
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
 
         # creds 
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
 
         # creds 
-        slice_cred = self.slice_credential_string(slice_hrn)
+        slice_cred = self.slice_credential(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
 
         # options and call_id when supported
         api_options = {}
         api_options['call_id']=unique_call_id()
         if options.show_credential:
             show_credentials(creds)
 
         # options and call_id when supported
         api_options = {}
         api_options['call_id']=unique_call_id()
         if options.show_credential:
             show_credentials(creds)
-        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') 
-        
-        # cred
-        slice_cred = self.slice_credential_string(args[0])
-        creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
-        # 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
-    
-    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') 
-        # cred
-        slice_cred = self.slice_credential_string(args[0])
-        creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
-        result =  server.Stop(slice_urn, creds)
+        result = server.Status([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
         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
+        # Thierry: seemed to be missing
         return value
         return value
-    
-    # reset named slice
-    def reset(self, options, args):
+
+    @register_command("slice_hrn action","")
+    def action(self, options, args):
         """
         """
-        reset named slice (reset_slice)
+        Perform the named operational action on these slivers
         """
         server = self.sliceapi()
         """
         server = self.sliceapi()
+        api_options = {}
         # slice urn
         slice_hrn = args[0]
         # slice urn
         slice_hrn = args[0]
-        slice_urn = hrn_to_urn(slice_hrn, 'slice') 
+        action = args[1]
+        slice_urn = Xrn(slice_hrn, type='slice').get_urn() 
         # cred
         # cred
-        slice_cred = self.slice_credential_string(args[0])
+        slice_cred = self.slice_credential(args[0])
         creds = [slice_cred]
         if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
         creds = [slice_cred]
         if options.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        result = server.reset_slice(creds, slice_urn)
+        
+        result = server.PerformOperationalAction([slice_urn], creds, action , 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)
         value = ReturnValue.get_value(result)
         if self.options.raw:
             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
@@ -1242,6 +1339,7 @@ or with an slice hrn, shows currently provisioned resources
             print value
         return value
 
             print value
         return value
 
+    @register_command("slice_hrn time","")
     def renew(self, options, args):
         """
         renew slice (RenewSliver)
     def renew(self, options, args):
         """
         renew slice (RenewSliver)
@@ -1255,17 +1353,14 @@ or with an slice hrn, shows currently provisioned resources
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         # time: don't try to be smart on the time format, server-side will
         # creds
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         # time: don't try to be smart on the time format, server-side will
         # creds
-        slice_cred = self.slice_credential_string(args[0])
+        slice_cred = self.slice_credential(args[0])
         creds = [slice_cred]
         creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
         # options and call_id when supported
         api_options = {}
         # options and call_id when supported
         api_options = {}
-       api_options['call_id']=unique_call_id()
+        api_options['call_id']=unique_call_id()
         if options.show_credential:
             show_credentials(creds)
         if options.show_credential:
             show_credentials(creds)
-        result =  server.RenewSliver(slice_urn, creds, input_time, *self.ois(server,api_options))
+        result =  server.Renew([slice_urn], creds, input_time, *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)
         value = ReturnValue.get_value(result)
         if self.options.raw:
             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
@@ -1274,6 +1369,7 @@ or with an slice hrn, shows currently provisioned resources
         return value
 
 
         return value
 
 
+    @register_command("slice_hrn","")
     def shutdown(self, options, args):
         """
         shutdown named slice (Shutdown)
     def shutdown(self, options, args):
         """
         shutdown named slice (Shutdown)
@@ -1283,11 +1379,8 @@ or with an slice hrn, shows currently provisioned resources
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         # creds
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
         # creds
-        slice_cred = self.slice_credential_string(slice_hrn)
+        slice_cred = self.slice_credential(slice_hrn)
         creds = [slice_cred]
         creds = [slice_cred]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
         result = server.Shutdown(slice_urn, creds)
         value = ReturnValue.get_value(result)
         if self.options.raw:
         result = server.Shutdown(slice_urn, creds)
         value = ReturnValue.get_value(result)
         if self.options.raw:
@@ -1297,75 +1390,7 @@ or with an slice hrn, shows currently provisioned resources
         return value         
     
 
         return value         
     
 
-    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]
-        if options.delegate:
-            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
-            creds.append(delegated_cred)
-        # 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)
-
-    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)
-
-        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
-
+    @register_command("[name]","")
     def gid(self, options, args):
         """
         Create a GID (CreateGid)
     def gid(self, options, args):
         """
         Create a GID (CreateGid)
@@ -1374,7 +1399,8 @@ or with an slice hrn, shows currently provisioned resources
             self.print_help()
             sys.exit(1)
         target_hrn = args[0]
             self.print_help()
             sys.exit(1)
         target_hrn = args[0]
-        gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
+        my_gid_string = open(self.client_bootstrap.my_gid()).read() 
+        gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
         if options.file:
             filename = options.file
         else:
         if options.file:
             filename = options.file
         else:
@@ -1382,36 +1408,193 @@ or with an slice hrn, shows currently provisioned resources
         self.logger.info("writing %s gid to %s" % (target_hrn, filename))
         GID(string=gid).save_to_file(filename)
          
         self.logger.info("writing %s gid to %s" % (target_hrn, filename))
         GID(string=gid).save_to_file(filename)
          
-
-    def delegate(self, options, args):
+    ####################
+    @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
+
+  will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
+  the set of credentials in the scope for this call would be
+  (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
+      as per -u/--user
+  (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
+      as per -p/--pi
+  (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
+  (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
+      because of the two -s options
+
+""")
+    def delegate (self, options, args):
         """
         (locally) create delegate credential for use by given hrn
         """
         (locally) create delegate credential for use by given hrn
+  make sure to check for 'sfi myslice' instead if you plan
+  on using MySlice
         """
         """
-        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 len(args) != 1:
+            self.print_help()
+            sys.exit(1)
+        to_hrn = args[0]
+        # support for several delegations in the same call
+        # so first we gather the things to do
+        tuples=[]
+        for slice_hrn in options.delegate_slices:
+            message="%s.slice"%slice_hrn
+            original = self.slice_credential_string(slice_hrn)
+            tuples.append ( (message, original,) )
+        if options.delegate_pi:
+            my_authority=self.authority
+            message="%s.pi"%my_authority
+            original = self.my_authority_credential_string()
+            tuples.append ( (message, original,) )
+        for auth_hrn in options.delegate_auths:
+            message="%s.auth"%auth_hrn
+            original=self.authority_credential_string(auth_hrn)
+            tuples.append ( (message, original, ) )
+        # if nothing was specified at all at this point, let's assume -u
+        if not tuples: options.delegate_user=True
+        # this user cred
         if options.delegate_user:
         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")
+            message="%s.user"%self.user
+            original = self.my_credential_string
+            tuples.append ( (message, original, ) )
+
+        # default type for beneficial is user unless -A
+        if options.delegate_to_authority:       to_type='authority'
+        else:                                   to_type='user'
+
+        # let's now handle all this
+        # it's all in the filenaming scheme
+        for (message,original) in tuples:
+            delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
+            delegated_credential = Credential (string=delegated_string)
+            filename = os.path.join ( self.options.sfi_dir,
+                                      "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
+            delegated_credential.save_to_file(filename, save_parents=True)
+            self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
+    
+    ####################
+    @register_command("","""$ less +/myslice sfi_config
+[myslice]
+backend  = http://manifold.pl.sophia.inria.fr:7080
+# the HRN that myslice uses, so that we are delegating to
+delegate = ple.upmc.slicebrowser
+# platform - this is a myslice concept
+platform = ple
+# username - as of this writing (May 2013) a simple login name
+username = thierry
+
+$ sfi myslice
+  will first collect the slices that you are part of, then make sure
+  all your credentials are up-to-date (read: refresh expired ones)
+  then compute delegated credentials for user 'ple.upmc.slicebrowser'
+  and upload them all on myslice backend, using 'platform' and 'user'.
+  A password will be prompted for the upload part.
+
+$ sfi -v myslice  -- or sfi -vv myslice
+  same but with more and more verbosity
+
+$ sfi m
+  is synonym to sfi myslice as no other command starts with an 'm'
+"""
+) # register_command
+    def myslice (self, options, args):
+
+        """ This helper is for refreshing your credentials at myslice; it will
+  * compute all the slices that you currently have credentials on
+  * refresh all your credentials (you as a user and pi, your slices)
+  * upload them to the manifold backend server
+  for last phase, sfi_config is read to look for the [myslice] section, 
+  and namely the 'backend', 'delegate' and 'user' settings"""
+
+        ##########
+        if len(args)>0:
+            self.print_help()
+            sys.exit(1)
+        ### the rough sketch goes like this
+        # (a) rain check for sufficient config in sfi_config
+        # we don't allow to override these settings for now
+        myslice_dict={}
+        myslice_keys=['backend', 'delegate', 'platform', 'username']
+        for key in myslice_keys:
+            full_key="MYSLICE_" + key.upper()
+            value=getattr(self.config_instance,full_key,None)
+            if value:   myslice_dict[key]=value
+            else:       print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
+        if len(myslice_dict) != len(myslice_keys):
+            sys.exit(1)
 
 
-        delegated_cred.save_to_file(dest_fn, save_parents=True)
+        # (b) figure whether we are PI for the authority where we belong
+        self.logger.info("Resolving our own id")
+        my_records=self.registry().Resolve(self.user,self.my_credential_string)
+        if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
+        my_record=my_records[0]
+        my_auths_all = my_record['reg-pi-authorities']
+        self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all))
+        self.logger.debug("They are %s"%(my_auths_all))
+        
+        my_auths = my_auths_all
+        if options.delegate_auths:
+            my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths)))
+
+        self.logger.info("Delegate PI creds for authorities: %s"%my_auths        )
+        # (c) get the set of slices that we are in
+        my_slices_all=my_record['reg-slices']
+        self.logger.info("Found %d slices that we are member of"%len(my_slices_all))
+        self.logger.debug("They are: %s"%(my_slices_all))
+        my_slices = my_slices_all
+        if options.delegate_slices:
+            my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices)))
+
+        self.logger.info("Delegate slice creds for slices: %s"%my_slices)
+
+        # (d) make sure we have *valid* credentials for all these
+        hrn_credentials=[]
+        hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
+        for auth_hrn in my_auths:
+            hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
+        for slice_hrn in my_slices:
+            hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
+
+        # (e) check for the delegated version of these
+        # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever 
+        # switch to myslice using an authority instead of a user
+        delegatee_type='user'
+        delegatee_hrn=myslice_dict['delegate']
+        hrn_delegated_credentials = []
+        for (hrn, htype, credential) in hrn_credentials:
+            delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
+            hrn_delegated_credentials.append ((hrn, htype, delegated_credential, ))
+
+        # (f) and finally upload them to manifold server
+        # xxx todo add an option so the password can be set on the command line
+        # (but *NOT* in the config file) so other apps can leverage this
+        uploader = ManifoldUploader (logger=self.logger,
+                                     url=myslice_dict['backend'],
+                                     platform=myslice_dict['platform'],
+                                     username=myslice_dict['username'],
+                                     password=options.password)
+        uploader.prompt_all()
+        (count_all,count_success)=(0,0)
+        for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
+            # inspect
+            inspect=Credential(string=delegated_credential)
+            expire_datetime=inspect.get_expiration()
+            message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
+            if uploader.upload(delegated_credential,message=message):
+                count_success+=1
+            count_all+=1
+
+        self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
+        # at first I thought we would want to save these,
+        # like 'sfi delegate does' but on second thought
+        # it is probably not helpful as people would not
+        # need to run 'sfi delegate' at all anymore
+        if count_success != count_all: sys.exit(1)
+        return
 
 
-        self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
-    
+    @register_command("cred","")
     def trusted(self, options, args):
         """
     def trusted(self, options, args):
         """
-        return uhe trusted certs at this interface (get_trusted_certs)
+        return the trusted certs at this interface (get_trusted_certs)
         """ 
         trusted_certs = self.registry().get_trusted_certs()
         for trusted_cert in trusted_certs:
         """ 
         trusted_certs = self.registry().get_trusted_certs()
         for trusted_cert in trusted_certs:
@@ -1419,8 +1602,6 @@ or with an slice hrn, shows currently provisioned resources
             gid.dump()
             cert = Certificate(string=trusted_cert)
             self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
             gid.dump()
             cert = Certificate(string=trusted_cert)
             self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
+            print "Certificate:\n%s\n\n"%trusted_cert
         return 
 
         return 
 
-    def config (self, options, args):
-        "Display contents of current config"
-        self.show_config()