From 49aeb133ab3b334a2785886efedf5b9f2a3da23b Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Wed, 28 May 2014 16:37:46 +0200 Subject: [PATCH] substantial cleanup of the renew method and client * sfi -l aka --as-long-as-possible is supported in sfi * sfi renew <> +2d / +3w / +4m is now working as well as the other time formats (int, rfc3339...) * most importantly the final expiration time is trimmed to the min of credential expiration and max_slice_renewal, but goes on with these values instead of whining --- sfa/client/sfi.py | 12 ++++++++- sfa/managers/aggregate_manager.py | 8 ------ sfa/methods/Renew.py | 34 ++++++++++++++++++------- sfa/util/sfatime.py | 41 ++++++++++++++++++++++++++++--- 4 files changed, 74 insertions(+), 21 deletions(-) diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index fc4e609f..4cceb51a 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -448,6 +448,9 @@ class Sfi: if canonical in ("list","resources", "describe", "provision", "allocate", "register","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") + if canonical in ("renew"): + parser.add_option("-l","--as-long-as-possible",dest='alap',action='store_true',default=False, + help="renew as long as possible") # registy filter option if canonical in ("list", "show", "remove"): parser.add_option("-t", "--type", dest="type", type="choice", @@ -1396,7 +1399,12 @@ use this if you mean an authority instead""") print value return value - @declare_command("slice_hrn [...] time","sfi renew onelab.ple.heartbeat 2015-04-31") + @declare_command("slice_hrn [...] time", + "\n".join(["sfi renew onelab.ple.heartbeat 2015-04-31", + "sfi renew onelab.ple.heartbeat 2015-04-31T14:00:00Z", + "sfi renew onelab.ple.heartbeat +5d", + "sfi renew onelab.ple.heartbeat +3w", + "sfi renew onelab.ple.heartbeat +2m",])) def renew(self, options, args): """ renew slice (Renew) @@ -1423,6 +1431,8 @@ use this if you mean an authority instead""") # options and call_id when supported api_options = {} api_options['call_id']=unique_call_id() + if options.alap: + api_options['geni_extend_alap']=True if options.show_credential: show_credentials(creds) result = server.Renew(sliver_urns, creds, input_time, *self.ois(server,api_options)) diff --git a/sfa/managers/aggregate_manager.py b/sfa/managers/aggregate_manager.py index 28645675..15211035 100644 --- a/sfa/managers/aggregate_manager.py +++ b/sfa/managers/aggregate_manager.py @@ -147,14 +147,6 @@ class AggregateManager: call_id = options.get('call_id') if Callids().already_handled(call_id): return True - # extend as long as possible - if options.get('geni_extend_alap'): - now = datetime.datetime.now() - requested = utcparse(expiration_time) - max = adjust_datetime(now, days=int(api.config.SFA_MAX_SLICE_RENEW)) - if requested > max: - expiration_time = max - return api.driver.renew(xrns, expiration_time, options) def PerformOperationalAction(self, api, xrns, creds, action, options={}): diff --git a/sfa/methods/Renew.py b/sfa/methods/Renew.py index c75c7816..0a7ac1af 100644 --- a/sfa/methods/Renew.py +++ b/sfa/methods/Renew.py @@ -3,7 +3,7 @@ import datetime from sfa.util.faults import InsufficientRights from sfa.util.xrn import urn_to_hrn from sfa.util.method import Method -from sfa.util.sfatime import utcparse +from sfa.util.sfatime import utcparse, add_datetime from sfa.trust.credential import Credential @@ -14,7 +14,7 @@ class Renew(Method): Renews the resources in the specified slice or slivers by extending the lifetime. - @param surn ([string]) List of URNs of to renew + @param urns ([string]) List of URNs of to renew @param credentials ([string]) of credentials @param expiration_time (string) requested time of expiration @param options (dict) options @@ -37,16 +37,32 @@ class Renew(Method): options=options) the_credential = Credential(cred=valid_creds[0]) actual_caller_hrn = the_credential.actual_caller_hrn() - self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-urns: %s\texp:%s\tmethod-name: %s"%\ + self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-urns: %s\texpiration:%s\tmethod-name: %s"%\ (self.api.interface, actual_caller_hrn, urns, expiration_time,self.name)) + # extend as long as possible : take the min of requested and now+SFA_MAX_SLICE_RENEW + if options.get('geni_extend_alap'): + # ignore requested time and set to max + expiration_time = add_datetime(datetime.datetime.utcnow(), days=int(self.api.config.SFA_MAX_SLICE_RENEW)) + # Validate that the time does not go beyond the credential's expiration time - requested_time = utcparse(expiration_time) + requested_expire = utcparse(expiration_time) + self.api.logger.info("requested_expire = %s"%requested_expire) + credential_expire = the_credential.get_expiration() + self.api.logger.info("credential_expire = %s"%credential_expire) max_renew_days = int(self.api.config.SFA_MAX_SLICE_RENEW) - if requested_time > Credential(cred=valid_creds[0]).get_expiration(): - raise InsufficientRights('Renewsliver: Credential expires before requested expiration time') - if requested_time > datetime.datetime.utcnow() + datetime.timedelta(days=max_renew_days): - raise Exception('Cannot renew > %s days from now' % max_renew_days) - return self.api.manager.Renew(self.api, urns, creds, expiration_time, options) + max_expire = datetime.datetime.utcnow() + datetime.timedelta (days=max_renew_days) + if requested_expire > credential_expire: + # used to throw an InsufficientRights exception here, this was not right + self.api.logger.warning("Requested expiration %s, after credential expiration (%s) -> trimming to the latter/sooner"%\ + (requested_expire, credential_expire)) + requested_expire = credential_expire + if requested_expire > max_expire: + # likewise + self.api.logger.warning("Requested expiration %s, after maximal expiration %s days (%s) -> trimming to the latter/sooner"%\ + (requested_expire, self.api.config.SFA_MAX_SLICE_RENEW,max_expire)) + requested_expire = max_expire + + return self.api.manager.Renew(self.api, urns, creds, requested_expire, options) diff --git a/sfa/util/sfatime.py b/sfa/util/sfatime.py index d14f44b0..27418b2e 100644 --- a/sfa/util/sfatime.py +++ b/sfa/util/sfatime.py @@ -24,6 +24,7 @@ from types import StringTypes import dateutil.parser import datetime import time +import re from sfa.util.sfalogging import logger @@ -35,16 +36,36 @@ the timezone, so that it's compatible with normal datetime.datetime objects. For safety this can also handle inputs that are either timestamps, or datetimes """ + + def handle_shorthands (input): + """recognize string like +5d or +3w or +2m as + 2 days, 3 weeks or 2 months from now""" + if input.startswith('+'): + match=re.match (r"([0-9]+)([dwm])",input[1:]) + if match: + how_many=int(match.group(1)) + what=match.group(2) + if what == 'd': d=datetime.timedelta(days=how_many) + elif what == 'w': d=datetime.timedelta(weeks=how_many) + elif what == 'm': d=datetime.timedelta(weeks=4*how_many) + return datetime.datetime.utcnow()+d + # prepare the input for the checks below by # casting strings ('1327098335') to ints if isinstance(input, StringTypes): try: input = int(input) except ValueError: - pass + try: + new_input=handle_shorthands(input) + if new_input is not None: input=new_input + except: + import traceback + traceback.print_exc() + #################### here we go if isinstance (input, datetime.datetime): - logger.warn ("argument to utcparse already a datetime - doing nothing") + #logger.info ("argument to utcparse already a datetime - doing nothing") return input elif isinstance (input, StringTypes): t = dateutil.parser.parse(input) @@ -65,9 +86,23 @@ def datetime_to_utc(input): def datetime_to_epoch(input): return int(time.mktime(input.timetuple())) -def adjust_datetime(input, days=0, hours=0, minutes=0, seconds=0): +def add_datetime(input, days=0, hours=0, minutes=0, seconds=0): """ Adjust the input date by the specified delta (in seconds). """ dt = utcparse(input) return dt + datetime.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds) + +if __name__ == '__main__': + for input in [ + '+2d', + '+3w', + '+2m', + 1401282977.575632, + 1401282977, + '1401282977', + '2014-05-28', + '2014-05-28T15:18', + '2014-05-28T15:18:30', + ]: + print "input=%20s -> parsed %s"%(input,datetime_to_string(utcparse(input))) -- 2.43.0