Fix : fix hot module under windows test. (at leat I hope...)
[shinken.git] / shinken / util.py
blob35b63c8d5f885bb8606ad0f6291aad2bde6a0635
1 #!/usr/bin/env python
2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
6 # Hartmut Goebel, h.goebel@goebel-consult.de
8 #This file is part of Shinken.
10 #Shinken is free software: you can redistribute it and/or modify
11 #it under the terms of the GNU Affero General Public License as published by
12 #the Free Software Foundation, either version 3 of the License, or
13 #(at your option) any later version.
15 #Shinken is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 #GNU Affero General Public License for more details.
20 #You should have received a copy of the GNU Affero General Public License
21 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
23 import time
24 import re
25 try:
26 from ClusterShell.NodeSet import NodeSet
27 except ImportError:
28 NodeSet = None
30 from shinken.macroresolver import MacroResolver
31 #from memoized import memoized
34 ################################### TIME ##################################
35 #@memoized
36 def get_end_of_day(year, month_id, day):
37 end_time = (year, month_id, day, 23, 59, 59, 0, 0, -1)
38 end_time_epoch = time.mktime(end_time)
39 return end_time_epoch
42 #@memoized
43 def print_date(t):
44 return time.asctime(time.localtime(t))
47 #@memoized
48 def get_day(t):
49 return int(t - get_sec_from_morning(t))
52 #@memoized
53 def get_sec_from_morning(t):
54 t_lt = time.localtime(t)
55 h = t_lt.tm_hour
56 m = t_lt.tm_min
57 s = t_lt.tm_sec
58 return h * 3600 + m * 60 + s
61 #@memoized
62 def get_start_of_day(year, month_id, day):
63 start_time = (year, month_id, day, 00, 00, 00, 0, 0, -1)
64 try:
65 start_time_epoch = time.mktime(start_time)
66 except OverflowError:
67 # Windows mktime sometimes crashes on (1970, 1, 1, ...)
68 start_time_epoch = 0.0
69 return start_time_epoch
72 #change a time in seconds like 3600 into a format : 0d 1h 0m 0s
73 def format_t_into_dhms_format(t):
74 s = t
75 m,s=divmod(s,60)
76 h,m=divmod(m,60)
77 d,h=divmod(h,24)
78 return '%sd %sh %sm %ss' % (d, h, m, s)
81 ################################# Pythonization ###########################
82 #first change to foat so manage for example 25.0 to 25
83 def to_int(val):
84 return int(float(val))
86 def to_float(val):
87 return float(val)
89 def to_char(val):
90 return val[0]
92 def to_split(val):
93 val = val.split(',')
94 if val == ['']:
95 val = []
96 return val
98 #bool('0') = true, so...
99 def to_bool(val):
100 if val == '1':
101 return True
102 else:
103 return False
105 def from_bool_to_string(b):
106 if b :
107 return '1'
108 else:
109 return '0'
111 def from_bool_to_int(b):
112 if b :
113 return 1
114 else:
115 return 0
117 def from_list_to_split(val):
118 val = ','.join(['%s' % v for v in val])
119 return val
121 def from_float_to_int(val):
122 val = int(val)
123 return val
126 ### Functions for brok_transformations
127 ### They take 2 parameters : ref, and a value
128 ### ref is the item like a service, and value
129 ### if the value to preprocess
131 # Just a string list of all names, with ,
132 def to_list_string_of_names(ref, tab):
133 return ",".join([e.get_name() for e in tab])
135 # take a list of hosts and return a list
136 # of all host_names
137 def to_hostnames_list(ref, tab):
138 r = []
139 for h in tab:
140 if hasattr(h, 'host_name'):
141 r.append(h.host_name)
142 return r
144 # Will create a dict with 2 lists:
145 # *services : all services of the tab
146 # *hosts : all hosts of the tab
147 def to_svc_hst_distinct_lists(ref, tab):
148 r = {'hosts' : [], 'services' : []}
149 for e in tab:
150 cls = e.__class__
151 if cls.my_type == 'service':
152 name = e.get_dbg_name()
153 r['services'].append(name)
154 else:
155 name = e.get_dbg_name()
156 r['hosts'].append(name)
157 return r
160 # Will expaand the value with macros from the
161 # host/service ref before brok it
162 def expand_with_macros(ref, value):
163 return MacroResolver().resolve_simple_macros_in_string(value, ref.get_data_for_checks())
166 # Just get the string name of the object
167 # (like for realm)
168 def get_obj_name(obj):
169 return obj.get_name()
171 # return the list of keys of the custom dict
172 # but without the _ before
173 def get_customs_keys(d):
174 return [k[1:] for k in d.keys()]
177 # return the values of the dict
178 def get_customs_values(d):
179 return d.values()
182 ###################### Sorting ################
183 def scheduler_no_spare_first(x, y):
184 if x.spare and not y.spare:
185 return 1
186 elif x.spare and y.spare:
187 return 0
188 else:
189 return -1
192 #-1 is x first, 0 equal, 1 is y first
193 def alive_then_spare_then_deads(x, y):
194 #First are alive
195 if x.alive and not y.alive:
196 return -1
197 if y.alive and not x.alive:
198 return 0
199 #if not alive both, I really don't care...
200 if not x.alive and not y.alive:
201 return -1
202 #Ok, both are alive... now spare after no spare
203 if not x.spare:
204 return -1
205 #x is a spare, so y must be before, even if
206 #y is a spare
207 if not y.spare:
208 return 1
209 return 0
211 #-1 is x first, 0 equal, 1 is y first
212 def sort_by_ids(x, y):
213 if x.id < y.id:
214 return -1
215 if x.id > y.id:
216 return 1
217 #So is equal
218 return 0
222 ##################### Cleaning ##############
223 def strip_and_uniq(tab):
224 new_tab = set()
225 for elt in tab:
226 new_tab.add(elt.strip())
227 return list(new_tab)
231 #################### Patern change application (mainly for host) #######
233 def expand_xy_patern(pattern):
234 ns = NodeSet(pattern)
235 if len(ns) > 1:
236 for elem in ns:
237 for a in expand_xy_patern(elem):
238 yield a
239 else:
240 yield pattern
245 #This function is used to generate all patern change as
246 #recursive list.
247 #for example, for a [(1,3),(1,4),(1,5)] xy_couples,
248 #it will generate a 60 item list with:
249 #Rule: [1, '[1-5]', [1, '[1-4]', [1, '[1-3]', []]]]
250 #Rule: [1, '[1-5]', [1, '[1-4]', [2, '[1-3]', []]]]
251 #...
252 def got_generation_rule_patern_change(xy_couples):
253 res = []
254 xy_cpl = xy_couples
255 if xy_couples == []:
256 return []
257 (x, y) = xy_cpl[0]
258 for i in xrange(x, y+1):
259 n = got_generation_rule_patern_change(xy_cpl[1:])
260 if n != []:
261 for e in n:
262 res.append( [i, '[%d-%d]'%(x,y), e])
263 else:
264 res.append( [i, '[%d-%d]'%(x,y), []])
265 return res
268 #this fuction apply a recursive patern change
269 #generate by the got_generation_rule_patern_change
270 #function.
271 #It take one entry of this list, and apply
272 #recursivly the change to s like :
273 #s = "Unit [1-3] Port [1-4] Admin [1-5]"
274 #rule = [1, '[1-5]', [2, '[1-4]', [3, '[1-3]', []]]]
275 #output = Unit 3 Port 2 Admin 1
276 def apply_change_recursive_patern_change(s, rule):
277 #print "Try to change %s" % s, 'with', rule
278 new_s = s
279 (i, m, t) = rule
280 #print "replace %s by %s" % (r'%s' % m, str(i)), 'in', s
281 s = s.replace(r'%s' % m, str(i))
282 #print "And got", s
283 if t == []:
284 return s
285 return apply_change_recursive_patern_change(s, t)
288 #For service generator, get dict from a _custom properties
289 #as _disks C$(80%!90%),D$(80%!90%)$,E$(80%!90%)$
290 #return {'C' : '80%!90%', 'D' : '80%!90%', 'E' : '80%!90%'}
291 #And if we have a key that look like [X-Y] we will expand it
292 #into Y-X+1 keys
293 GET_KEY_VALUE_SEQUENCE_ERROR_NOERROR = 0
294 GET_KEY_VALUE_SEQUENCE_ERROR_SYNTAX = 1
295 GET_KEY_VALUE_SEQUENCE_ERROR_NODEFAULT = 2
296 GET_KEY_VALUE_SEQUENCE_ERROR_NODE= 3
297 def get_key_value_sequence(entry, default_value=None):
298 array1 = []
299 array2 = []
300 conf_entry = entry
302 # match a key$(value1..n)$
303 keyval_pattern_txt = r"""
304 \s*(?P<key>[^,]+?)(?P<values>(\$\(.*?\)\$)*)(?:[,]|$)
306 keyval_pattern = re.compile('(?x)' + keyval_pattern_txt)
307 # match a whole sequence of key$(value1..n)$
308 all_keyval_pattern = re.compile('(?x)^(' + keyval_pattern_txt + ')+$')
309 # match a single value
310 value_pattern = re.compile('(?:\$\((?P<val>.*?)\)\$)')
311 # match a sequence of values
312 all_value_pattern = re.compile('^(?:\$\(.*?\)\$)+$')
314 if all_keyval_pattern.match(conf_entry):
315 for mat in re.finditer(keyval_pattern, conf_entry):
316 r = { 'KEY' : mat.group('key') }
317 # The key is in mat.group('key')
318 # If there are also value(s)...
319 if mat.group('values'):
320 if all_value_pattern.match(mat.group('values')):
321 # If there are multiple values, loop over them
322 valnum = 1
323 for val in re.finditer(value_pattern, mat.group('values')):
324 r['VALUE' + str(valnum)] = val.group('val')
325 valnum += 1
326 else:
327 # Value syntax error
328 return (None, GET_KEY_VALUE_SEQUENCE_ERROR_SYNTAX)
329 else:
330 r['VALUE1'] = None
331 array1.append(r)
332 else:
333 # Something is wrong with the values. (Maybe unbalanced '$(')
334 # TODO: count opening and closing brackets in the pattern
335 return (None, GET_KEY_VALUE_SEQUENCE_ERROR_SYNTAX)
337 # now fill the empty values with the default value
338 for r in array1:
339 if r['VALUE1'] is None:
340 if default_value is None:
341 return (None, GET_KEY_VALUE_SEQUENCE_ERROR_NODEFAULT)
342 else:
343 r['VALUE1'] = default_value
344 r['VALUE'] = r['VALUE1']
346 #Now create new one but for [X-Y] matchs
347 # array1 holds the original entries. Some of the keys may contain wildcards
348 # array2 is filled with originals and inflated wildcards
349 #import time
350 #t0 = time.time()
351 #NodeSet = None
352 if NodeSet is None:
353 #The patern that will say if we have a [X-Y] key.
354 pat = re.compile('\[(\d*)-(\d*)\]')
356 for r in array1:
358 key = r['KEY']
359 orig_key = r['KEY']
361 #We have no choice, we cannot use NodeSet, so we use the
362 #simple regexp
363 if NodeSet is None:
364 m = pat.search(key)
365 got_xy = (m is not None)
366 else: # Try to look with a nodeset check directly
367 try:
368 ns = NodeSet(key)
369 #If we have more than 1 element, we have a xy thing
370 got_xy = (len(ns) != 1)
371 except NodeSetParseRangeError:
372 return (None, GET_KEY_VALUE_SEQUENCE_ERROR_NODE)
373 pass # go in the next key
375 #Now we've got our couples of X-Y. If no void,
376 #we were with a "key generator"
378 if got_xy:
379 #Ok 2 cases : we have the NodeSet lib or not.
380 #if not, we use the dumb algo (quick, but manage less
381 #cases like /N or , in paterns)
382 if NodeSet is None: #us the old algo
383 still_loop = True
384 xy_couples = [] # will get all X-Y couples
385 while still_loop:
386 m = pat.search(key)
387 if m is not None: # we've find one X-Y
388 (x,y) = m.groups()
389 (x,y) = (int(x), int(y))
390 xy_couples.append((x,y))
391 #We must search if we've gotother X-Y, so
392 #we delete this one, and loop
393 key = key.replace('[%d-%d]' % (x,y), 'Z'*10)
394 else:#no more X-Y in it
395 still_loop = False
397 #Now we have our xy_couples, we can manage them
399 #We search all patern change rules
400 rules = got_generation_rule_patern_change(xy_couples)
402 #Then we apply them all to get ours final keys
403 for rule in rules:
404 res = apply_change_recursive_patern_change(orig_key, rule)
405 new_r = {}
406 for key in r:
407 new_r[key] = r[key]
408 new_r['KEY'] = res
409 array2.append(new_r)
411 else:
412 #The key was just a generator, we can remove it
413 #keys_to_del.append(orig_key)
415 #We search all patern change rules
416 #rules = got_generation_rule_patern_change(xy_couples)
417 nodes_set = expand_xy_patern(orig_key)
418 new_keys = list(nodes_set)
420 #Then we apply them all to get ours final keys
421 for new_key in new_keys:
422 #res = apply_change_recursive_patern_change(orig_key, rule)
423 new_r = {}
424 for key in r:
425 new_r[key] = r[key]
426 new_r['KEY'] = new_key
427 array2.append(new_r)
428 else:
429 # There were no wildcards
430 array2.append(r)
433 #t1 = time.time()
434 #print "***********Diff", t1 -t0
436 return (array2, GET_KEY_VALUE_SEQUENCE_ERROR_NOERROR)