worked on splitting regions and implemented set_allocator / set_consumer
[sfa.git] / gacks / gackshandle.py
1 ##
2 # This module implements Gacks handles
3 ##
4
5 import sys
6
7 ##
8 # GacksHandle is an object representing a handle. A handle is the following
9 # tuple:
10 #
11 #    (id, unitstart, unitstop, timestart, timestop)
12 #
13 #    id is a text identifier for the resource, for example "CPU"
14 #
15 #    unitstart and unitstop form an interval [unitstart, unitstop) in unit
16 #    space. For example, [0, 10) could represent 10% of a CPU.
17 #
18 #    timestart and timestop form an interval [timestart, timestop) in time
19 #    space.
20 #
21 #    If unitstop==INFINITY, or timestop==INFINITY, then it is treated as infinity
22 #
23 # The GacksHandle also doubles as an RSPEC
24
25 INFINITY = "inf" #sys.maxint
26
27 def is_lesser_equal(x, y):
28     if x==y:
29         return True
30     if x==INFINITY:
31         return False
32     if y==INFINITY:
33         return True
34     return (x<y)
35
36 def is_greater_equal(x, y):
37     if x==y:
38         return True
39     if y==INFINITY:
40         return False
41     if x==INFINITY:
42         return True
43     return (x>y)
44
45 def interval_contains(start1, stop1, start2, stop2):
46     return is_lesser_equal(start1, start2) and is_greater_equal(stop1, stop2)
47
48 class GacksHandle:
49     def __init__(self, id=None, unitStart=0, unitStop=INFINITY, timeStart=0, timeStop=INFINITY, string=None):
50         self.id = id
51         self.unitStart = unitStart
52         self.unitStop = unitStop
53         self.timeStart = timeStart
54         self.timeStop = timeStop
55         if string:
56             self.load_from_string(string)
57
58     def as_string(self):
59         return str(self.id) + "#" + \
60                str(self.unitStart) + "-" + str(self.unitStop) + "#" + \
61                str(self.timeStart) + "-" + str(self.timeStop)
62
63     def parse_range(self, str):
64         parts = str.split("-")
65         if len(parts)!=2:
66             raise ValueError
67
68         if parts[0] != INFINITY:
69             parts[0] = int(parts[0])
70
71         if parts[1] != INFINITY:
72             parts[1] = int(parts[1])
73
74         return parts
75
76     def load_from_string(self, str):
77         parts = str.split("#")
78
79         self.id = parts[0]
80
81         if len(parts) > 1:
82             (self.unitStart, self.unitStop) = self.parse_range(parts[1])
83
84         if len(parts) > 2:
85             (self.timeStart, self.timeStop) = self.parse_range(parts[2])
86
87     def get_quantity(self):
88         if self.unitStop == INFINITY:
89             return INFINITY
90         else:
91             return self.unitStop-self.unitStart
92
93     def get_duration(self):
94         if self.timeStop == INFINITY:
95             return INFINITY
96         else:
97             return self.timeStop-self.timeStart
98
99     def dump(self):
100         print str(self.id) + ": " + \
101               "units " + str(self.unitStart) + "-" + str(self.unitStop) + \
102               "time " + str(self.timeStart) + "-" + str(self.timeStop)
103
104     def clone(self):
105         return GacksHandle(self.id, self.unitStart, self.unitStop,
106                            self.timeStart, self.timeStop)
107
108     def split_subset(self, suStart, suStop, stStart, stStop):
109         # an arbitrary rectangle can have a subset removed by slicing it into
110         # five pieces:
111         #    h1 = top
112         #    h2 = left
113         #    h3 = right
114         #    h4 = bottom
115         #    s = subset (middle) that was sliced out
116
117         h1 = self.clone()
118         h2 = self.clone()
119         h3 = self.clone()
120         h4 = self.clone()
121         s = self.clone()
122
123         if not suStart:
124             suStart = self.unitStart
125         if not suStop:
126             suStop = self.unitStop
127         if not stStart:
128             stStart = self.timeStart
129         if not stStop:
130             stStop = self.timeStop
131
132         h1.unitStop = suStart
133
134         h2.unitStart = suStart
135         h2.unitStop = suStop
136         h2.timeStop = stStart
137
138         h3.unitStart = suStart
139         h3.unitStop = suStop
140         h3.timeStart = stStop
141
142         h4.unitStart = suStop
143
144         s.unitStart = suStart
145         s.unitStop = suStop
146         s.timeStart = stStart
147         s.timeStop = stStop
148
149         results = [s, h1, h2, h3, h4]
150         valid_results = []
151         for result in results:
152             if result.get_quantity()>0 and result.get_duration()>0:
153                 valid_results.append(result)
154
155         return valid_results
156
157     def split_subset_old(self, uStart, uStop, tStart, tStop):
158         results = [self]
159         if uStart:
160             results1 = []
161             for i in results:
162                 results1.extend(i.split_unit(uStart))
163             results = results1
164         if uStop:
165             results1 = []
166             for i in results:
167                 results1.extend(i.split_unit(uStop))
168             results = results1
169         if tStart:
170             results1 = []
171             for i in results:
172                 results1.extend(i.split_time(tStart))
173             results = results1
174         if tStop:
175             results1 = []
176             for i in results:
177                 results1.extend(i.split_time(tStop))
178             results = results1
179         return results
180
181     def split_unit(self, unit):
182         if is_lesser_equal(unit, self.unitStart) or is_greater_equal(unit, self.unitStop):
183             return [self]
184
185         h2 = self.clone()
186
187         self.unitStop = unit
188         h2.unitStart = unit
189
190         return [self, h2]
191
192     def split_time(self, time):
193         if is_lesser_equal(time, self.timeStart) or is_greater_equal(time, self.timeStop):
194             return [self]
195
196         h2 = self.clone()
197
198         self.timeStop = time
199         h2.timeStart = time
200
201         return [self, h2]
202
203     def is_superset(self, handle):
204         if self.id != handle.id:
205             return False
206
207         if not interval_contains(self.timeStart, self.timeStop, handle.timeStart, handle.timeStop):
208             return False
209
210         if not interval_contains(self.unitStart, self.unitStop, self.timeStart, self.timeStop):
211             return False
212
213         return True
214
215     def is_proper_superset(self, handle):
216         return self.is_superset(handle) and (not self.is_same_cell(handle))
217
218     def is_same_cell(self, handle):
219         return (self.id == handle.id) and \
220                (self.unitStart == handle.unitStart) and \
221                (self.unitStop == handle.unitStop) and \
222                (self.timeStart == handle.timeStart) and \
223                (self.timeStop == handle.timeStop)
224
225     def is_same(self, handle):
226         return self.is_same_cell(handle)
227
228     def is_in_list(self, handle_list):
229         for handle in handle_list:
230             if is_same(self, handle):
231                 return True
232         return False
233
234 class GacksRecord(GacksHandle):
235     def __init__(self, id=None, unitStart=0, unitStop=INFINITY, timeStart=0, timeStop=INFINITY, allocatorHRNs=[], consumerHRN=None):
236         GacksHandle.__init__(self, id, unitStart, unitStop, timeStart, timeStop)
237         self.allocatorHRNs = allocatorHRNs
238         self.consumerHRN = consumerHRN
239
240     def dump(self):
241         GacksHandle.dump(self)
242         print "  allocators:", ", ".join(self.allocatorHRNs)
243         print "  consumer:", self.consumerHRN
244
245     def clone(self):
246         return GacksRecord(self.id, self.unitStart, self.unitStop,
247                            self.timeStart, self.timeStop,
248                            self.allocatorHRNs, self.consumerHRN)
249
250     def set_allocator(self, callerHRN, allocatorHRN, which, where):
251         # build up a list of the positions of callerHRN inside of the
252         # allocator list
253
254         positions = []
255         for i, hrn in enumerate(self.allocatorHRNs):
256             if hrn == callerHRN:
257                 positions.append(i)
258
259         pos = positions[which]
260
261         # truncate the allocator list at the appropriate place.
262         # if where==True,
263         #     keep callerHRN in the list and append allocatorHRN after
264         # otherwise,
265         #     remove callerHRN and replace with allocatorHRN
266
267         if where:
268             self.allocatorHRNs = self.allocatorHRNs[:(pos+1)]
269         else:
270             self.allocatorHRNs = self.allocatorHRNs[:pos]
271
272         self.allocatorHRNs.append(allocatorHRN)
273
274     def get_allocators(self):
275         return self.allocatorHRNs
276
277     def contains_allocator(self, allocatorHRN):
278         return (allocatorHRN in self.allocatorHRNs)
279
280     def set_consumer(self, consumerHRN):
281         self.consumerHRN = consumerHRN
282
283     def get_consumer(self):
284         return self.consumerHRN
285
286 def strings_to_handles(strings):
287
288     # if given a newline-separated list of strings, then expand it into a list
289     if isinstance(strings, str):
290         expanded_strings = strings.split("\n")
291     elif isinstance(strings, list):
292         expanded_strings = strings
293     else:
294         raise TypeError
295
296     # eliminate any blank strings from the list
297     non_blank_strings = []
298     for string in expanded_strings:
299         if string:
300             non_blank_strings.append(string)
301
302     handles = []
303     for line in non_blank_strings:
304         handle = GacksHandle(string = rspec)
305         handles.append(handle)
306
307     return handles
308
309 def handles_to_strings(handles):
310     strings = []
311     for handle in handles:
312         strings.append(handle.as_string())
313     return strings
314
315 def rspec_to_handles(rspec):
316     return strings_to_handles(rspec)
317
318 def find_handle_in_list(list, uStart, uStop, tStart, tStop):
319     for item in list:
320         if item.unitStart == uStart and \
321            item.unitStop == uStop and \
322            item.timeStart == tStart and \
323            item.timeStop == tStop:
324             return item
325     return None