#
-# $Id$
-# $URL$
# Thierry Parmentelat -- INRIA
#
# Utilities for filtering on leases
-#
-from types import StringTypes
-from PLC.Faults import *
+# w0622: we keep on using 'filter' as a variable name
+# pylint: disable=c0111, c0103, w0622
+
+import time
+
+from PLC.Faults import * # pylint: disable=w0401, w0614
from PLC.Filter import Filter
from PLC.Parameter import Parameter, Mixed
from PLC.Timestamp import Timestamp
# supersede the generic Filter class to support time intersection
-class LeaseFilter (Filter):
+
+
+class LeaseFilter(Filter):
# general notes on input parameters
# int_timestamp: number of seconds since the epoch
# str_timestamp: see Timestamp.sql_validate
- # timeslot: a tuple (from,until), each being either int_timestamp or str_timestamp
-
- local_fields = { 'alive': Mixed ( Parameter (int, "int_timestamp: leases alive at that time"),
- Parameter (str, "str_timestamp: leases alive at that time"),
- Parameter (tuple,"timeslot: the leases alive during this timeslot")),
- 'clip': Mixed ( Parameter (int, "int_timestamp: leases alive after that time"),
- Parameter (str, "str_timestamp: leases alive after at that time"),
- Parameter (tuple,"timeslot: the leases alive during this timeslot")),
- }
-
- def __init__(self, fields = {}, filter = {},
- doc = "Lease filter -- adds the 'alive' and 'clip' capabilities for filtering on leases"):
- Filter.__init__(self,fields,filter,doc)
- self.fields.update (LeaseFilter.local_fields)
-
-
- ## canonical type
+ # timeslot: a tuple (from, until), each being either int_timestamp or
+ # str_timestamp
+
+ local_fields = {
+ 'alive': Mixed(
+ Parameter(int, "int_timestamp: leases alive at that time"),
+ Parameter(str, "str_timestamp: leases alive at that time"),
+ Parameter(tuple, "timeslot: the leases alive during this timeslot")),
+ 'clip': Mixed(
+ Parameter(int, "int_timestamp: leases alive after that time"),
+ Parameter(str, "str_timestamp: leases alive after that time"),
+ Parameter(tuple, "timeslot: the leases alive during this timeslot")),
+ ########## macros
+ # {'day' : 0} : all leases from today and on
+ # {'day' : 1} : all leases today (localtime at the myplc)
+ # {'day' : 2} : all leases today and tomorrow (localtime at the myplc)
+ # etc..
+ 'day': Parameter(
+ int,
+ "clip on a number of days from today and on;"
+ " 0 means no limit in the future"),
+ }
+
+ def __init__(self, fields=None, filter=None,
+ doc="Lease filter -- adds the 'alive' and 'clip'"
+ "capabilities for filtering on leases"):
+ if fields is None:
+ fields = {}
+ if filter is None:
+ filter = {}
+ Filter.__init__(self, fields, filter, doc)
+ self.fields.update(LeaseFilter.local_fields)
+
+ # canonical type
@staticmethod
- def quote (timestamp): return Timestamp.cast_long(timestamp)
+ def quote(timestamp):
+ return Timestamp.cast_long(timestamp)
- ## basic SQL utilities
+ # basic SQL utilities
@staticmethod
- def sql_time_intersect (f1,u1,f2,u2):
+ def sql_time_intersect(f1, u1, f2, u2):
# either f2 is in [f1,u1], or u2 is in [f1,u1], or f2<=f1<=u1<=u2
- return ("((%(f1)s <= %(f2)s) AND (%(f2)s <= %(u1)s)) " + \
- "OR ((%(f1)s <= %(u2)s) AND (%(u2)s <= %(u1)s)) " + \
- "OR ((%(f2)s<=%(f1)s) AND (%(u1)s<=%(u2)s))")%locals()
+ return (
+ "(({f1} <= {f2}) AND ({f2} <= {u1})) "
+ "OR (({f1} <= {u2}) AND ({u2} <= {u1})) "
+ "OR (({f2}<={f1}) AND ({u1}<={u2}))"
+ .format(f1=f1, u1=u1, f2=f2, u2=u2))
@staticmethod
- def time_in_range (timestamp,f1,u1):
+ def time_in_range(timestamp, f1, u1):
return Timestamp.cast_long(f1) <= Timestamp.cast_long(timestamp) \
- and Timestamp.cast_long(timestamp) <= Timestamp.cast_long(u1)
+ and Timestamp.cast_long(timestamp) <= Timestamp.cast_long(u1)
@staticmethod
- def sql_time_in_range (timestamp,f1,u1):
- # is timestamp in [f1,u1]
- return "((%(f1)s <= %(timestamp)s) AND (%(timestamp)s <= %(u1)s))"%locals()
+ def sql_time_in_range(timestamp, f1, u1):
+ # is timestamp in [f1, u1]
+ return (
+ "(({f1} <= {timestamp}) AND ({timestamp} <= {u1}))"
+ .format(timestamp=timestamp, f1=f1, u1=u1))
@staticmethod
- def sql_timeslot_after (f1,u1,mark):
+ def sql_timeslot_after(f1, u1, mark):
# is the lease alive after mark, i.e. u1 >= mark
- return "(%(u1)s >= %(mark)s)"%locals()
-
+ return "({u1} >= {mark})".format(u1=u1, mark=mark)
- ## hooks for the local fields
- def sql_alive (self, alive):
- if isinstance (alive,int) or isinstance (alive, StringTypes):
+ # hooks for the local fields
+ def sql_alive(self, alive):
+ if isinstance(alive, (int, str)):
# the lease is alive at that time if from <= alive <= until
- alive=LeaseFilter.quote(alive)
- return LeaseFilter.sql_time_in_range(alive,'t_from','t_until')
- elif isinstance (alive,tuple):
- (f,u)=alive
- f=LeaseFilter.quote(f)
- u=LeaseFilter.quote(u)
- return LeaseFilter.sql_time_intersect (f,u,'t_from','t_until')
- else: raise PLCInvalidArgument ("LeaseFilter: alive field %r"%alive)
-
- def sql_clip (self, clip):
- if isinstance (clip,int) or isinstance (clip, StringTypes):
- start=LeaseFilter.quote(clip)
- return LeaseFilter.sql_timeslot_after('t_from','t_until',start)
- elif isinstance (clip,tuple):
- (f,u)=clip
- f=LeaseFilter.quote(f)
- u=LeaseFilter.quote(u)
- return LeaseFilter.sql_time_intersect(f,u,'t_from','t_until')
- else: raise PLCInvalidArgument ("LeaseFilter: clip field %r"%clip)
-
-
- ## supersede the generic Filter 'sql' method
- def sql(self, api, join_with = "AND"):
+ alive = LeaseFilter.quote(alive)
+ return LeaseFilter.sql_time_in_range(alive, 't_from', 't_until')
+ elif isinstance(alive, tuple):
+ (f, u) = alive
+ f = LeaseFilter.quote(f)
+ u = LeaseFilter.quote(u)
+ return LeaseFilter.sql_time_intersect(f, u, 't_from', 't_until')
+ else:
+ raise PLCInvalidArgument("LeaseFilter: alive field {}"
+ .format(alive))
+
+ def sql_clip(self, clip):
+ if isinstance(clip, int) or isinstance(clip, str):
+ start = LeaseFilter.quote(clip)
+ return LeaseFilter.sql_timeslot_after('t_from', 't_until', start)
+ elif isinstance(clip, tuple):
+ (f, u) = clip
+ f = LeaseFilter.quote(f)
+ u = LeaseFilter.quote(u)
+ return LeaseFilter.sql_time_intersect(f, u, 't_from', 't_until')
+ else:
+ raise PLCInvalidArgument("LeaseFilter: clip field {}"
+ .format(clip))
+
+ # the whole key to implementing day is to compute today's beginning
+ def today_start(self):
+ # a struct_time
+ st = time.localtime()
+ seconds_today = st.tm_hour * 3600 + st.tm_min * 60 + st.tm_sec
+ return int(time.time()) - seconds_today
+
+ # supersede the generic Filter 'sql' method
+ def sql(self, api, join_with="AND"):
+ # implement 'day' as a clip
+ if 'day' in self:
+ if 'clip' in self:
+ raise PLCInvalidArgument("LeaseFilter cannot have both 'clip' and 'day'")
+ today = self.today_start()
+ nb_days = self['day']
+ if nb_days == 0:
+ self['clip'] = today
+ else:
+ self['clip'] = (today, today + nb_days * 24 * 3600)
+ del self['day']
+
# preserve locally what belongs to us, hide it from the superclass
# self.local is a dict local_key : user_value
# self.negation is a dict local_key : string
- self.local={}
- self.negation={}
- for (k,v) in LeaseFilter.local_fields.items():
- if self.has_key(k):
- self.local[k]=self[k]
+ self.local = {}
+ self.negation = {}
+ for (k, v) in list(LeaseFilter.local_fields.items()):
+ if k in self:
+ self.local[k] = self[k]
del self[k]
- self.negation[k]=""
- elif self.has_key('~'+k):
- self.local[k]=self['~'+k]
- del self['~'+k]
- self.negation[k]="NOT "
+ self.negation[k] = ""
+ elif ('~' + k) in self:
+ self.local[k] = self['~' + k]
+ del self['~' + k]
+ self.negation[k] = "NOT "
# run the generic filtering code
- (where_part,clip_part) = Filter.sql(self,api,join_with)
- for (k,v) in self.local.items():
+ (where_part, clip_part) = Filter.sql(self, api, join_with)
+ for (k, v) in list(self.local.items()):
try:
# locate hook function associated with key
- method=LeaseFilter.__dict__['sql_'+k]
- where_part += " %s %s(%s)" %(self.join_with,self.negation[k],method(self,self.local[k]))
- except Exception,e:
- raise PLCInvalidArgument,"LeaseFilter: something wrong with filter key %s, val was %r -- %r"%(k,v,e)
- if Filter.debug: print 'LeaseFilter.sql: where_part=',where_part,'clip_part',clip_part
- return (where_part,clip_part)
-
-######## xxx not sure where this belongs yet
+ method = LeaseFilter.__dict__['sql_' + k]
+ where_part += " {} {}({})"\
+ .format(self.join_with,
+ self.negation[k],
+ method(self, self.local[k]))
+ except Exception as e:
+ raise PLCInvalidArgument(
+ "LeaseFilter: something wrong with filter"
+ "key {}, val was {} -- {}".format(k, v, e))
+ return (where_part, clip_part)
+
+# xxx not sure where this belongs yet
# given a set of nodes, and a timeslot,
# returns the available leases that have at least a given duration
-def free_leases (api, node_ids, t_from, t_until, min_duration):
+
+
+def free_leases(api, node_ids, t_from, t_until, min_duration):
# get the leases for these nodes and timeslot
- filter = {'node_id':node_ids,
+ filter = {'node_id': node_ids,
'clip': (t_from, t_until),
# sort by node, and inside one node, chronologically
- '-SORT' : ('node_id','t_from'),
+ '-SORT': ('node_id', 't_from'),
}
- leases = Leases (api, filter)
+ leases = Leases(api, filter)
- result=[]
+ result = []
# sort node_ids
node_ids.sort()
# scan nodes from the input
- input_node_id=0
+ input_node_id = 0
# scan nodes from the leases
- lease_node_id=0
+ lease_node_id = 0
return '?? what now ??'
-def node_free_leases (node_id, node_leases, t_from, t_until):
+
+def node_free_leases(node_id, node_leases, t_from, t_until):
# no lease yet : return one solid lease
if not node_leases:
- return [ {'node_id':node_id,
- 't_from':t_from,
- 't_until':t_until} ]
+ return [{'node_id': node_id,
+ 't_from': t_from,
+ 't_until': t_until}]
- result=[]
- current_time=t_from
- is_on=LeaseFilter.time_in_range(node_leases[0]['t_from'],t_from,t_until)
+ result = []
+ current_time = t_from
+ is_on = LeaseFilter.time_in_range(
+ node_leases[0]['t_from'], t_from, t_until)
while True:
-# print 'DBG','current_time',current_time,'is_on',is_on,'result',result
+ # print 'DBG','current_time',current_time,'is_on',is_on,'result',result
# lease is active
if is_on:
- current_time=node_leases[0]['t_until']
- is_on=False
+ current_time = node_leases[0]['t_until']
+ is_on = False
del node_leases[0]
- if not node_leases: return result
+ if not node_leases:
+ return result
# free, has no remaining lease
elif not node_leases:
- result.append( {'node_id':node_id, 't_from':current_time, 't_until': t_until} )
+ result.append(
+ {'node_id': node_id,
+ 't_from': current_time, 't_until': t_until})
return result
# free and has remaining leases
else:
next_time = node_leases[0]['t_from']
- result.append( {'node_id':node_id,'t_from':current_time,'t_until':next_time})
+ result.append(
+ {'node_id': node_id,
+ 't_from': current_time, 't_until': next_time})
current_time = next_time
- is_on=True
+ is_on = True