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