Merge Master in geni-v3 conflict resolution
[sfa.git] / sfa / client / sfi.py
index 5d44cf3..53d655b 100644 (file)
@@ -46,30 +46,8 @@ from sfa.client.candidates import Candidates
 
 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'):
@@ -115,14 +93,15 @@ def filter_records(type, 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 += "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):
@@ -197,39 +176,6 @@ def save_record_to_file(filename, record_dict):
     f.close()
     return
 
-# used in sfi list
-def terminal_render (records,options):
-    # sort records by type
-    grouped_by_type={}
-    for record in records:
-        type=record['type']
-        if type not in grouped_by_type: grouped_by_type[type]=[]
-        grouped_by_type[type].append(record)
-    for (type, list) in grouped_by_type.items():
-#        print 20 * '-', type
-        try:    renderer=eval('terminal_render_'+type)
-        except: renderer=terminal_render_default
-        for record in list: renderer(record,options)
-
-def terminal_render_default (record,options):
-    print "%s (%s)" % (record['hrn'], record['type'])
-def terminal_render_user (record, options):
-    print "%s (User)"%record['hrn'],
-    if record.get('reg-pi-authorities',None): print " [PI at %s]"%(" and ".join(record['reg-pi-authorities'])),
-    if record.get('reg-slices',None): print " [IN slices %s]"%(" and ".join(record['reg-slices'])),
-    print ""
-def terminal_render_slice (record, options):
-    print "%s (Slice)"%record['hrn'],
-    if record.get('reg-researchers',None): print " [USERS %s]"%(" and ".join(record['reg-researchers'])),
-#    print record.keys()
-    print ""
-def terminal_render_authority (record, options):
-    print "%s (Authority)"%record['hrn'],
-    if record.get('reg-pis',None): print " [PIS %s]"%(" and ".join(record['reg-pis'])),
-    print ""
-def terminal_render_node (record, options):
-    print "%s (Node)"%record['hrn']
-
 # minimally check a key argument
 def check_ssh_key (key):
     good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
@@ -313,22 +259,22 @@ class Sfi:
         ("version", ""),  
         ("list", "authority"),
         ("show", "name"),
-        ("add", "record"),
-        ("update", "record"),
+        ("add", "[record]"),
+        ("update", "[record]"),
         ("remove", "name"),
-        ("slices", ""),
-        ("resources", "[slice_hrn]"),
+        ("resources", ""),
+        ("describe", "slice_hrn"),
         ("create", "slice_hrn rspec"),
+        ("allocate", "slice_hrn rspec"),
+        ("provision", "slice_hrn"),
+        ("action", "slice_hrn action"), 
         ("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"),
+        ("delegate", "to_hrn"),
         ("gid", "[name]"),
         ("trusted", "cred"),
         ("config", ""),
@@ -373,36 +319,29 @@ class Sfi:
             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',
+            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>', 
-                              help='slice researchers', default='', type="str", action='callback', 
+                              help='Set/replace 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',
+            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)
-# 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"):
+        if command in ("resources", "describe", "allocate", "provision", "create", "delete", "allocate", "provision", 
+                       "action", "shutdown",  "get_ticket", "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
-        if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"):
+        if command in ("list","resources", "describe", "provision", "allocate", "create","add","update","remove","slices","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
@@ -414,7 +353,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 ("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")
@@ -436,7 +375,7 @@ class Sfi:
 
 
         # '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", 'create'):
            parser.add_option("-o", "--output", dest="file",
                             help="output XML to file", metavar="FILE", default=None)
 
@@ -451,12 +390,22 @@ class Sfi:
         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",
-                            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 ("version"):
             parser.add_option("-R","--registry-version",
@@ -687,6 +636,9 @@ class Sfi:
         # 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.0', 
+                              'geni_value': self.my_credential_string}
         self.my_gid = client_bootstrap.my_gid ()
         self.client_bootstrap = client_bootstrap
 
@@ -697,9 +649,17 @@ class Sfi:
             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(self, name):
+        return {'geni_type': 'geni_sfa',
+                'geni_version': '3.0',
+                '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
@@ -932,15 +892,22 @@ or version information about sfi itself
         return
     
     def add(self, options, args):
-        "add record into registry from xml file (Register)"
+        "add record into registry by using the command options (Recommended) or from xml file (Register)"
         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
@@ -957,7 +924,7 @@ or version information about sfi itself
         return self.registry().Register(record_dict, auth_cred)
     
     def update(self, options, args):
-        "update record into registry from xml file (Update)"
+        "update record into registry by using the command options (Recommended) or from xml file (Update)"
         record_dict = {}
         if len(args) > 0:
             record_filepath = args[0]
@@ -1020,12 +987,9 @@ or version information about sfi itself
         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()
+        api_options['call_id']=unique_call_id()
         if options.show_credential:
             show_credentials(creds)
         result = server.ListSlices(creds, *self.ois(server,api_options))
@@ -1039,17 +1003,13 @@ or version information about sfi itself
     # show rspec for named slice
     def resources(self, options, args):
         """
-        with no arg, discover available resources, (ListResources)
+        discover available resources
 or with an slice hrn, shows currently provisioned resources
         """
         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:
@@ -1062,9 +1022,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
-        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:
@@ -1095,6 +1052,45 @@ or with an slice hrn, shows currently provisioned resources
 
         return
 
+    def describe(self, options, args):
+        """
+        Shows currently provisioned resources.
+        """
+        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.0'},
+                      }
+        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.0'}
+        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 
+
     def create(self, options, args):
         """
         create or update named slice with given rspec
@@ -1137,7 +1133,8 @@ or with an slice hrn, shows currently provisioned resources
         # xxx Thierry 2012 sept. 21
         # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here
         # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again
-        slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True})
+        slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
+        # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True})
         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']
@@ -1155,8 +1152,6 @@ or with an slice hrn, shows currently provisioned resources
         # 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()
@@ -1182,25 +1177,116 @@ or with an slice hrn, shows currently provisioned resources
         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]
-        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)
-        result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
+        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 
-  
+        return value
+
+    def allocate(self, options, args):
+        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
+        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']))
+
+        if options.show_credential:
+            show_credentials(creds)
+
+        # rspec
+        rspec_file = self.get_rspec_file(args[1])
+        rspec = open(rspec_file).read()
+        api_options = {}
+        api_options ['call_id'] = unique_call_id()
+        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)
+        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
+
+        return value
+        
+
+    def provision(self, options, args):
+        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
+        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']))
+
+        if options.show_credential:
+            show_credentials(creds)
+
+        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.0').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 '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, [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)
+        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
+        return value     
+
     def status(self, options, args):
         """
         retrieve slice status (SliverStatus)
@@ -1212,18 +1298,15 @@ or with an slice hrn, shows currently provisioned resources
         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]
-        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)
-        result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
+        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)
@@ -1243,9 +1326,6 @@ or with an slice hrn, shows currently provisioned resources
         # 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)
@@ -1266,9 +1346,6 @@ or with an slice hrn, shows currently provisioned resources
         # 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)
         value = ReturnValue.get_value(result)
         if self.options.raw:
@@ -1278,21 +1355,24 @@ or with an slice hrn, shows currently provisioned resources
         return value
     
     # reset named slice
-    def reset(self, options, args):
+    def action(self, options, args):
         """
-        reset named slice (reset_slice)
+        Perform the named operational action on the named slivers
         """
         server = self.sliceapi()
+        api_options = {}
         # 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
-        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)
-        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)
@@ -1313,17 +1393,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_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)
         # 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)
-        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)
@@ -1341,11 +1418,8 @@ or with an slice hrn, shows currently provisioned resources
         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]
-        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:
@@ -1366,15 +1440,12 @@ or with an slice hrn, shows currently provisioned resources
         # 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()
+        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
@@ -1432,7 +1503,8 @@ or with an slice hrn, shows currently provisioned resources
             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:
@@ -1441,31 +1513,51 @@ or with an slice hrn, shows currently provisioned resources
         GID(string=gid).save_to_file(filename)
          
 
-    def delegate(self, options, args):
+    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 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:
-            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))
+            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))
     
     def trusted(self, options, args):
         """