2 # Thierry Parmentelat -- INRIA
4 # Utilities for filtering on leases
7 from types import StringTypes
8 from PLC.Faults import *
9 from PLC.Filter import Filter
10 from PLC.Parameter import Parameter, Mixed
11 from PLC.Timestamp import Timestamp
13 # supersede the generic Filter class to support time intersection
16 class LeaseFilter (Filter):
18 # general notes on input parameters
19 # int_timestamp: number of seconds since the epoch
20 # str_timestamp: see Timestamp.sql_validate
21 # timeslot: a tuple (from, until), each being either int_timestamp or
27 int, "int_timestamp: leases alive at that time"),
29 str, "str_timestamp: leases alive at that time"),
31 tuple, "timeslot: the leases alive during this timeslot")),
34 int, "int_timestamp: leases alive after that time"),
36 str, "str_timestamp: leases alive after at that time"),
38 tuple, "timeslot: the leases alive during this timeslot")),
41 def __init__(self, fields={}, filter={},
42 doc="Lease filter -- adds the 'alive' and 'clip'"
43 "capabilities for filtering on leases"):
44 Filter.__init__(self, fields, filter, doc)
45 self.fields.update(LeaseFilter.local_fields)
49 def quote(timestamp): return Timestamp.cast_long(timestamp)
53 def sql_time_intersect(f1, u1, f2, u2):
54 # either f2 is in [f1,u1], or u2 is in [f1,u1], or f2<=f1<=u1<=u2
55 return ("(({f1} <= {f2}) AND ({f2} <= {u1})) " +
56 "OR (({f1} <= {u2}) AND ({u2} <= {u1})) " +
57 "OR (({f2}<={f1}) AND ({u1}<={u2}))").format(**locals())
60 def time_in_range(timestamp, f1, u1):
61 return Timestamp.cast_long(f1) <= Timestamp.cast_long(timestamp) \
62 and Timestamp.cast_long(timestamp) <= Timestamp.cast_long(u1)
65 def sql_time_in_range(timestamp, f1, u1):
66 # is timestamp in [f1, u1]
67 return "(({f1} <= {timestamp}) AND ({timestamp} <= {u1}))"\
71 def sql_timeslot_after(f1, u1, mark):
72 # is the lease alive after mark, i.e. u1 >= mark
73 return "({u1} >= {mark})".format(**locals())
75 # hooks for the local fields
76 def sql_alive(self, alive):
77 if isinstance(alive, int) or isinstance(alive, StringTypes):
78 # the lease is alive at that time if from <= alive <= until
79 alive = LeaseFilter.quote(alive)
80 return LeaseFilter.sql_time_in_range(alive, 't_from', 't_until')
81 elif isinstance(alive, tuple):
83 f = LeaseFilter.quote(f)
84 u = LeaseFilter.quote(u)
85 return LeaseFilter.sql_time_intersect(f, u, 't_from', 't_until')
87 raise PLCInvalidArgument("LeaseFilter: alive field {}"
90 def sql_clip(self, clip):
91 if isinstance(clip, int) or isinstance(clip, StringTypes):
92 start = LeaseFilter.quote(clip)
93 return LeaseFilter.sql_timeslot_after('t_from', 't_until', start)
94 elif isinstance(clip, tuple):
96 f = LeaseFilter.quote(f)
97 u = LeaseFilter.quote(u)
98 return LeaseFilter.sql_time_intersect(f, u, 't_from', 't_until')
100 raise PLCInvalidArgument("LeaseFilter: clip field {}"
103 # supersede the generic Filter 'sql' method
104 def sql(self, api, join_with="AND"):
105 # preserve locally what belongs to us, hide it from the superclass
106 # self.local is a dict local_key : user_value
107 # self.negation is a dict local_key : string
110 for (k, v) in LeaseFilter.local_fields.items():
112 self.local[k] = self[k]
114 self.negation[k] = ""
115 elif ('~' + k) in self:
116 self.local[k] = self['~' + k]
118 self.negation[k] = "NOT "
119 # run the generic filtering code
120 (where_part, clip_part) = Filter.sql(self, api, join_with)
121 for (k, v) in self.local.items():
123 # locate hook function associated with key
124 method = LeaseFilter.__dict__['sql_' + k]
125 where_part += " {} {}({})"\
126 .format(self.join_with,
128 method(self, self.local[k]))
130 raise PLCInvalidArgument(
131 "LeaseFilter: something wrong with filter"
132 "key {}, val was {} -- {}".format(k, v, e))
133 return (where_part, clip_part)
135 # xxx not sure where this belongs yet
136 # given a set of nodes, and a timeslot,
137 # returns the available leases that have at least a given duration
140 def free_leases(api, node_ids, t_from, t_until, min_duration):
142 # get the leases for these nodes and timeslot
143 filter = {'node_id': node_ids,
144 'clip': (t_from, t_until),
145 # sort by node, and inside one node, chronologically
146 '-SORT': ('node_id', 't_from'),
148 leases = Leases(api, filter)
155 # scan nodes from the input
157 # scan nodes from the leases
160 return '?? what now ??'
163 def node_free_leases(node_id, node_leases, t_from, t_until):
165 # no lease yet : return one solid lease
167 return [{'node_id': node_id,
172 current_time = t_from
173 is_on = LeaseFilter.time_in_range(
174 node_leases[0]['t_from'], t_from, t_until)
177 # print 'DBG','current_time',current_time,'is_on',is_on,'result',result
180 current_time = node_leases[0]['t_until']
185 # free, has no remaining lease
186 elif not node_leases:
189 't_from': current_time, 't_until': t_until})
191 # free and has remaining leases
193 next_time = node_leases[0]['t_from']
196 't_from': current_time, 't_until': next_time})
197 current_time = next_time