*Change the jenkins/hudson test scripts
[shinken.git] / shinken / item.py
blob9f2f71b26efe6a1d0b2119312513b68c4a36c52d
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/>.
23 """ This class is a base class for nearly all configuration
24 elements like service, hosts or contacts.
25 """
27 #from command import CommandCall
28 #from util import to_int, to_char, to_split, to_bool
29 from copy import copy
31 from shinken.brok import Brok
32 from shinken.util import strip_and_uniq
33 from shinken.command import CommandCall
34 from shinken.acknowledge import Acknowledge
35 from shinken.comment import Comment
37 class Item(object):
38 def __init__(self, params={}):
39 #We have our own id of My Class type :)
40 #use set attr for going into the slots
41 #instead of __dict__ :)
42 setattr(self, 'id', self.__class__.id)
43 self.__class__.id += 1
46 self.customs = {} # for custom variables
47 self.plus = {} # for value with a +
49 cls = self.__class__
50 #adding running properties like latency, dependency list, etc
51 for prop in cls.running_properties:
52 #Copy is slow, so we check type
53 #Type with __iter__ are list or dict, or tuple.
54 #Item need it's own list, so qe copy
55 val = cls.running_properties[prop].default
56 if hasattr(val, '__iter__'):
57 setattr(self, prop, copy(val))
58 else:
59 setattr(self, prop, val)
60 #eatch istance to have his own running prop!
62 #[0] = + -> new key-plus
63 #[0] = _ -> new custom entry in UPPER case
64 for key in params:
65 if len(params[key]) >= 1 and params[key][0] == '+':
66 #Special case : a _MACRO can be a plus. so add to plus
67 #but upper the key for the macro name
68 if key[0] == "_":
69 self.plus[key.upper()] = params[key][1:] # we remove the +
70 else:
71 self.plus[key] = params[key][1:] # we remove the +
72 elif key[0] == "_":
73 custom_name = key.upper()
74 self.customs[custom_name] = params[key]
75 else:
76 setattr(self, key, params[key])
79 #return a copy of the item, but give him a new id
80 def copy(self):
81 """ Copy a copy of the item, but give him a new id """
82 cls = self.__class__
83 i = cls({})#Dummy item but with it's own running properties
84 properties = cls.properties
85 for prop in properties:
86 if hasattr(self, prop):
87 val = getattr(self, prop)
88 setattr(i, prop, val)
89 return i
92 def clean(self):
93 pass
96 def __str__(self):
97 return str(self.__dict__)+'\n'
100 def is_tpl(self):
101 """ Return if the elements is a template """
102 try:
103 return self.register == '0'
104 except:
105 return False
108 #has = hasattr
109 #def has(self, prop):
110 # return hasattr(self, prop)
113 #If a prop is absent and is not required, put the default value
114 def fill_default(self):
115 """ Fill missing properties if they are missing """
116 cls = self.__class__
117 properties = cls.properties
119 for prop in properties:
120 if not hasattr(self, prop) and properties[prop].has_default:
121 value = properties[prop].default
122 setattr(self, prop, value)
125 #We load every usefull parameter so no need to access global conf later
126 #Must be called after a change in a gloabl conf parameter
127 def load_global_conf(cls, conf):
128 """ Used to put global values in the sub Class like
129 hosts ro services """
130 #conf have properties, if 'enable_notifications' :
131 #{ [...] 'class_inherit' : [(Host, None), (Service, None),
132 # (Contact, None)]}
133 #get the name and put the value if None, put the Name
134 #(not None) if not (not clear ?)
135 for prop in conf.properties:
136 #If we have a class_inherit, and the arbtier really send us it
137 # if 'class_inherit' in conf.properties[prop] and hasattr(conf, prop):
138 if hasattr(conf, prop):
139 entry = conf.properties[prop].class_inherit
140 for (cls_dest, change_name) in entry:
141 if cls_dest == cls:#ok, we've got something to get
142 value = getattr(conf, prop)
143 if change_name is None:
144 setattr(cls, prop, value)
145 else:
146 setattr(cls, change_name, value)
148 #Make this method a classmethod
149 load_global_conf = classmethod(load_global_conf)
152 #Use to make python properties
153 def pythonize(self):
154 cls = self.__class__
155 for prop in cls.properties:
156 tab = cls.properties[prop]
157 try:
158 # if isinstance(tab, dict):
159 # if 'pythonize' in tab:
160 # f = tab['pythonize']
161 # old_val = getattr(self, prop)
162 # new_val = f(old_val)
163 # #print "Changing ", old_val, "by", new_val
164 # setattr(self, prop, new_val)
165 # else: #new style for service
166 new_val = tab.pythonize(getattr(self, prop))
167 setattr(self, prop, new_val)
168 except AttributeError , exp:
169 #print self.get_name(), ' : ', exp
170 pass # Will be catch at the is_correct moment
173 def get_templates(self):
174 if hasattr(self, 'use'):
175 return self.use.split(',')
176 else:
177 return []
180 #We fillfull properties with template ones if need
181 def get_property_by_inheritance(self, items, prop):
182 #If I have the prop, I take mine but I check if I must
183 #add a plus porperty
184 if hasattr(self, prop):
185 value = getattr(self, prop)
186 # Maybe this value is 'null'. If so, we should NOT inherit
187 # and just delete this entry, and hope of course.
188 if value == 'null':
189 delattr(self, prop)
190 return None
191 # Manage the additive inheritance for the property,
192 # if property is in plus, add or replace it
193 if self.has_plus(prop):
194 value += ',' + self.get_plus_and_delete(prop)
195 return value
196 #Ok, I do not have prop, Maybe my templates do?
197 #Same story for plus
198 for i in self.templates:
199 value = i.get_property_by_inheritance(items, prop)
200 if value is not None:
201 if self.has_plus(prop):
202 value += ','+self.get_plus_and_delete(prop)
203 setattr(self, prop, value)
204 return value
205 #I do not have prop, my templates too... Maybe a plus?
206 if self.has_plus(prop):
207 value = self.get_plus_and_delete(prop)
208 setattr(self, prop, value)
209 return value
210 #Not event a plus... so None :)
211 return None
214 #We fillfull properties with template ones if need
215 def get_customs_properties_by_inheritance(self, items):
216 for i in self.templates:
217 tpl_cv = i.get_customs_properties_by_inheritance(items)
218 if tpl_cv is not {}:
219 for prop in tpl_cv:
220 if prop not in self.customs:
221 value = tpl_cv[prop]
222 else:
223 value = self.customs[prop]
224 if self.has_plus(prop):
225 value = value+self.get_plus_and_delete(prop)
226 self.customs[prop] = value
227 for prop in self.customs:
228 value = self.customs[prop]
229 if self.has_plus(prop):
230 value = value = value+','+self.get_plus_and_delete(prop)
231 self.customs[prop] = value
232 #We can get custom properties in plus, we need to get all
233 #entires and put
234 #them into customs
235 cust_in_plus = self.get_all_plus_and_delete()
236 for prop in cust_in_plus:
237 self.customs[prop] = cust_in_plus[prop]
238 return self.customs
241 def has_plus(self, prop):
242 try:
243 self.plus[prop]
244 except:
245 return False
246 return True
249 def get_all_plus_and_delete(self):
250 res = {}
251 props = self.plus.keys() #we delete entries, so no for ... in ...
252 for prop in props:
253 res[prop] = self.get_plus_and_delete(prop)
254 return res
257 def get_plus_and_delete(self, prop):
258 val = self.plus[prop]
259 del self.plus[prop]
260 return val
263 #Check is required prop are set:
264 #template are always correct
265 def is_correct(self):
266 state = True
267 properties = self.__class__.properties
268 for prop in properties:
269 if not hasattr(self, prop) and properties[prop].required:
270 print self.get_name(), "missing property :", prop
271 state = False
272 return state
275 #This function is used by service and hosts
276 #to transform Nagios2 parameters to Nagios3
277 #ones, like normal_check_interval to
278 #check_interval. There is a old_parameters tab
279 #in Classes taht give such modifications to do.
280 def old_properties_names_to_new(self):
281 old_properties = self.__class__.old_properties
282 for old_name in old_properties:
283 new_name = old_properties[old_name]
284 #Ok, if we got old_name and NO new name,
285 #we switch the name
286 if hasattr(self, old_name) and not hasattr(self, new_name):
287 value = getattr(self, old_name)
288 setattr(self, new_name, value)
291 def add_downtime(self, downtime):
292 self.downtimes.append(downtime)
295 def del_downtime(self, downtime_id):
296 d_to_del = None
297 for dt in self.downtimes:
298 if dt.id == downtime_id:
299 d_to_del = dt
300 dt.can_be_deleted = True
301 if d_to_del is not None:
302 self.downtimes.remove(d_to_del)
305 def add_comment(self, comment):
306 self.comments.append(comment)
309 def del_comment(self, comment_id):
310 c_to_del = None
311 for c in self.comments:
312 if c.id == comment_id:
313 c_to_del = c
314 c.can_be_deleted = True
315 if c_to_del is not None:
316 self.comments.remove(c_to_del)
319 def acknowledge_problem(self, sticky, notify, persistent, author, comment):
320 if self.state != self.ok_up:
321 if notify:
322 self.create_notifications('ACKNOWLEDGEMENT')
323 self.problem_has_been_acknowledged = True
324 if sticky == 2:
325 sticky = True
326 else:
327 sticky = False
328 a = Acknowledge(self, sticky, notify, persistent, author, comment)
329 self.acknowledgement = a
330 if self.my_type == 'host':
331 comment_type = 1
332 else :
333 comment_type = 2
334 c = Comment(self, persistent, author, comment,
335 comment_type, 4, 0, False, 0)
336 self.add_comment(c)
337 self.broks.append(self.get_update_status_brok())
340 # Delete the acknowledgement object and reset the flag
341 # but do not remove the associated comment.
342 def unacknowledge_problem(self):
343 if self.problem_has_been_acknowledged:
344 self.problem_has_been_acknowledged = False
345 #Should not be deleted, a None is Good
346 self.acknowledgement = None
347 #del self.acknowledgement
348 # find comments of non-persistent ack-comments and delete them too
349 for c in self.comments:
350 if c.entry_type == 4 and not c.persistent:
351 self.del_comment(c.id)
352 self.broks.append(self.get_update_status_brok())
355 # Check if we have an acknowledgement and if this is marked as sticky.
356 # This is needed when a non-ok state changes
357 def unacknowledge_problem_if_not_sticky(self):
358 if hasattr(self, 'acknowledgement') and self.acknowledgement != None:
359 if not self.acknowledgement.sticky:
360 self.unacknowledge_problem()
363 #Will flatten some parameters taggued by the 'conf_send_preparation'
364 #property because they are too "linked" to be send like that (like realms)
365 def prepare_for_conf_sending(self):
366 cls = self.__class__
368 for prop in cls.properties:
369 entry = cls.properties[prop]
370 #Is this property need preparation for sending?
371 if entry.conf_send_preparation is not None:
372 f = entry.conf_send_preparation
373 if f != None:
374 val = f(getattr(self, prop))
375 setattr(self, prop, val)
377 if hasattr(cls, 'running_properties'):
378 for prop in cls.running_properties:
379 entry = cls.running_properties[prop]
380 #Is this property need preparation for sending?
381 if entry.conf_send_preparation is not None:
382 f = entry.conf_send_preparation
383 if f != None:
384 val = f(getattr(self, prop))
385 setattr(self, prop, val)
390 #Get the property for an object, with good value
391 #and brok_transformation if need
392 def get_property_value_for_brok(self, prop, tab):
393 entry = tab[prop]
394 #Get the current value, or the default if need
395 value = getattr(self, prop, entry.default)
397 #Apply brok_transformation if need
398 # Look if we must preprocess the value first
399 pre_op = entry.brok_transformation
400 if pre_op != None:
401 value = pre_op(self, value)
403 return value
406 #Fill data with info of item by looking at brok_type
407 #in props of properties or running_propterties
408 def fill_data_brok_from(self, data, brok_type):
409 cls = self.__class__
410 #Now config properties
411 for prop in cls.properties:
412 #Is this property intended for brokking?
413 # if 'fill_brok' in cls.properties[prop]:
414 if brok_type in cls.properties[prop].fill_brok:
415 data[prop] = self.get_property_value_for_brok(prop, cls.properties)
417 # Maybe the class do not have running_properties
418 if hasattr(cls, 'running_properties'):
419 # We've got prop in running_properties too
420 for prop in cls.running_properties:
421 # if 'fill_brok' in cls.running_properties[prop]:
422 if brok_type in cls.running_properties[prop].fill_brok:
423 data[prop] = self.get_property_value_for_brok(prop, cls.running_properties)
426 #Get a brok with initial status
427 def get_initial_status_brok(self):
428 cls = self.__class__
429 my_type = cls.my_type
430 data = {'id' : self.id}
432 self.fill_data_brok_from(data, 'full_status')
433 b = Brok('initial_'+my_type+'_status', data)
434 return b
437 #Get a brok with update item status
438 def get_update_status_brok(self):
439 cls = self.__class__
440 my_type = cls.my_type
442 data = {'id' : self.id}
443 self.fill_data_brok_from(data, 'full_status')
444 b = Brok('update_'+my_type+'_status', data)
445 return b
448 #Get a brok with check_result
449 def get_check_result_brok(self):
450 cls = self.__class__
451 my_type = cls.my_type
453 data = {}
454 self.fill_data_brok_from(data, 'check_result')
455 b = Brok(my_type+'_check_result', data)
456 return b
459 #Get brok about the new schedule (next_check)
460 def get_next_schedule_brok(self):
461 cls = self.__class__
462 my_type = cls.my_type
464 data = {}
465 self.fill_data_brok_from(data, 'next_schedule')
466 b = Brok(my_type+'_next_schedule', data)
467 return b
470 #Link one command property to a class (for globals like oc*p_command)
471 def linkify_one_command_with_commands(self, commands, prop):
472 if hasattr(self, prop):
473 command = getattr(self, prop).strip()
474 if command != '':
475 if hasattr(self, 'poller_tag'):
476 cmdCall = CommandCall(commands, command,
477 poller_tag=self.poller_tag)
478 else:
479 cmdCall = CommandCall(commands, command)
480 setattr(self, prop, cmdCall)
481 else:
482 setattr(self, prop, None)
486 class Items(object):
487 def __init__(self, items):
488 self.items = {}
489 self.configuration_warnings = []
490 self.configuration_errors = []
491 for i in items:
492 self.items[i.id] = i
493 self.templates = {}
497 def __iter__(self):
498 return self.items.itervalues()
501 def __len__(self):
502 return len(self.items)
505 def __delitem__(self, key):
506 del self.items[key]
509 def __setitem__(self, key, value):
510 self.items[key] = value
513 def __getitem__(self, key):
514 return self.items[key]
517 #We create the reversed list so search will be faster
518 #We also create a twins list with id of twins (not the original
519 #just the others, higher twins)
520 def create_reversed_list(self):
521 self.reversed_list = {}
522 self.twins = []
523 name_property = self.__class__.name_property
524 for id in self.items:
525 if hasattr(self.items[id], name_property):
526 name = getattr(self.items[id], name_property)
527 if name not in self.reversed_list:
528 self.reversed_list[name] = id
529 else:
530 self.twins.append(id)
533 def find_id_by_name(self, name):
534 if hasattr(self, 'reversed_list'):
535 if name in self.reversed_list:
536 return self.reversed_list[name]
537 else:
538 return None
539 else: #ok, an early ask, with no reversed list from now...
540 name_property = self.__class__.name_property
541 for i in self:
542 if hasattr(i, name_property):
543 i_name = getattr(i, name_property)
544 if i_name == name:
545 return i.id
546 return None
549 def find_by_name(self, name):
550 id = self.find_id_by_name(name)
551 if id is not None:
552 return self.items[id]
553 else:
554 return None
557 def pythonize(self):
558 for id in self.items:
559 self.items[id].pythonize()
562 def create_tpl_list(self):
563 for id in self.items:
564 i = self.items[id]
565 if i.is_tpl():
566 self.templates[id] = i
569 def find_tpl_by_name(self, name):
570 for id in self.templates:
571 i = self.items[id]
572 if hasattr(i, 'name') and i.name == name:
573 return i
574 return None
577 def linkify_templates(self):
578 #First we create a list of all templates
579 self.create_tpl_list()
580 for i in self:
581 tpls = i.get_templates()
582 new_tpls = []
583 for tpl in tpls:
584 t = self.find_tpl_by_name(tpl.strip())
585 if t is not None:
586 new_tpls.append(t)
587 else: # not find? not good!
588 err = "ERROR: the template '%s' defined for '%s' is unkown" % (tpl, i.get_name())
589 i.configuration_errors.append(err)
590 i.templates = new_tpls
594 def is_correct(self):
595 #we are ok at the begining. Hope we still ok at the end...
596 r = True
597 #Some class do not have twins, because they do not have names
598 #like servicedependancies
599 if hasattr(self, 'twins'):
600 #Ok, look at no twins (it's bad!)
601 for id in self.twins:
602 i = self.items[id]
603 print "Error: the", i.__class__.my_type, i.get_name(), "is duplicated"
604 r = False
605 #Then look if we have some errors in the conf
606 #Juts print warnings, but raise errors
607 for err in self.configuration_warnings:
608 print err
609 for err in self.configuration_errors:
610 print err
611 r = False
613 #Then look for individual ok
614 for i in self:
615 r &= i.is_correct()
616 return r
619 #We remove useless properties and templates
620 def clean_useless(self):
621 #First templates
622 tpls = [id for id in self.items if self.items[id].is_tpl()]
623 for id in tpls:
624 del self.items[id]
625 #Ok now delete useless in items
626 for i in self:
627 try:
628 del i.templates
629 del i.use
630 del i.plus
631 except AttributeError:
632 pass
633 del self.templates
636 #If a prop is absent and is not required, put the default value
637 def fill_default(self):
638 for i in self:
639 i.fill_default()
642 def __str__(self):
643 s = ''
644 cls = self.__class__
645 for id in self.items:
646 s = s + str(cls) + ':' + str(id) + str(self.items[id]) + '\n'
647 return s
650 # Inheritance forjust a property
651 def apply_partial_inheritance(self, prop):
652 for i in self:
653 i.get_property_by_inheritance(self, prop)
656 def apply_inheritance(self):
657 #We check for all Class properties if the host has it
658 #if not, it check all host templates for a value
659 cls = self.inner_class
660 properties = cls.properties
661 for prop in properties:
662 self.apply_partial_inheritance(prop)
663 for i in self:
664 i.get_customs_properties_by_inheritance(self)
667 #We remove twins
668 #Remember: item id respect the order of conf. So if and item
669 # is defined multiple times,
670 #we want to keep the first one.
671 #Services are also managed here but they are specials:
672 #We remove twins services with the same host_name/service_description
673 #Remember: host service are take into account first before hostgroup service
674 #Id of host service are lower than hostgroups one, so they are
675 #in self.twins_services
676 #and we can remove them.
677 def remove_twins(self):
678 for id in self.twins:
679 i = self.items[id]
680 type = i.__class__.my_type
681 print 'Warning: the', type, i.get_name(), 'is already defined.'
682 del self.items[id] #bye bye
683 #do not remove twins, we should look in it, but just void it
684 self.twins = []
685 #del self.twins #no more need
689 #We've got a contacts property with , separated contacts names
690 #and we want have a list of Contacts
691 def linkify_with_contacts(self, contacts):
692 for i in self:
693 if hasattr(i, 'contacts'):
694 contacts_tab = i.contacts.split(',')
695 contacts_tab = strip_and_uniq(contacts_tab)
696 new_contacts = []
697 for c_name in contacts_tab:
698 if c_name != '':
699 c = contacts.find_by_name(c_name)
700 if c != None:
701 new_contacts.append(c)
702 # Else : Add in the errors tab.
703 # will be raised at is_correct
704 else:
705 err = "ERROR: the contact '%s' defined for '%s' is unkown" % (c_name, i.get_name())
706 i.configuration_errors.append(err)
707 # Get the list, but first make elements uniq
708 i.contacts = list(set(new_contacts))
711 # Make link between an object and its escalations
712 def linkify_with_escalations(self, escalations):
713 for i in self:
714 if hasattr(i, 'escalations'):
715 #print i.get_name(), 'going to link escalations', i.escalations
716 escalations_tab = i.escalations.split(',')
717 escalations_tab = strip_and_uniq(escalations_tab)
718 new_escalations = []
719 for es_name in escalations_tab:
720 es = escalations.find_by_name(es_name)
721 if es != None:
722 new_escalations.append(es)
723 else: #TODO what?
724 pass
725 i.escalations = new_escalations
726 #print i.get_name(), 'finallygot escalation', i.escalations
729 #Make link between item and it's resultmodulations
730 def linkify_with_resultmodulations(self, resultmodulations):
731 for i in self:
732 if hasattr(i, 'resultmodulations'):
733 resultmodulations_tab = i.resultmodulations.split(',')
734 resultmodulations_tab = strip_and_uniq(resultmodulations_tab)
735 new_resultmodulations = []
736 for rm_name in resultmodulations_tab:
737 rm = resultmodulations.find_by_name(rm_name)
738 if rm != None:
739 new_resultmodulations.append(rm)
740 else: #TODO WHAT?
741 pass
742 i.resultmodulations = new_resultmodulations
745 #If we've got a contact_groups properties, we search for all
746 #theses groups and ask them their contacts, and then add them
747 #all into our contacts property
748 def explode_contact_groups_into_contacts(self, contactgroups):
749 for i in self:
750 if hasattr(i, 'contact_groups'):
751 cgnames = i.contact_groups.split(',')
752 cgnames = strip_and_uniq(cgnames)
753 for cgname in cgnames:
754 cg = contactgroups.find_by_name(cgname)
755 if cg == None:
756 err = "The contact group '%s'defined on the %s '%s' do not exist" % (cgname, i.__class__.my_type, i.get_name())
757 i.configuration_errors.append(err)
758 continue
759 cnames = contactgroups.get_members_by_name(cgname)
760 #We add contacts into our contacts
761 if cnames != []:
762 if hasattr(i, 'contacts'):
763 i.contacts += ','+cnames
764 else:
765 i.contacts = cnames
767 #Link a timeperiod property (prop)
768 def linkify_with_timeperiods(self, timeperiods, prop):
769 for i in self:
770 if hasattr(i, prop):
771 tpname = getattr(i, prop)
772 tp = timeperiods.find_by_name(tpname)
773 #TODO: catch None?
774 setattr(i, prop, tp)
777 #Link one command property
778 def linkify_one_command_with_commands(self, commands, prop):
779 for i in self:
780 if hasattr(i, prop):
781 command = getattr(i, prop).strip()
782 if command != '':
783 if hasattr(i, 'poller_tag'):
784 cmdCall = CommandCall(commands, command,
785 poller_tag=i.poller_tag)
786 else:
787 cmdCall = CommandCall(commands, command)
788 #TODO: catch None?
789 setattr(i, prop, cmdCall)
790 else:
791 setattr(i, prop, None)
794 #Link a command list (commands with , between) in real CommandCalls
795 def linkify_command_list_with_commands(self, commands, prop):
796 for i in self:
797 if hasattr(i, prop):
798 coms = getattr(i, prop).split(',')
799 coms = strip_and_uniq(coms)
800 com_list = []
801 for com in coms:
802 if com != '':
803 if hasattr(i, 'poller_tag'):
804 cmdCall = CommandCall(commands, com,
805 poller_tag=i.poller_tag)
806 else:
807 cmdCall = CommandCall(commands, com)
808 #TODO: catch None?
809 com_list.append(cmdCall)
810 else: # TODO: catch?
811 pass
812 setattr(i, prop, com_list)
815 #Return a set with ALL hosts (used in ! expressions)
816 def get_all_host_names_set(self, hosts):
817 hnames = [h.host_name for h in hosts.items.values()
818 if hasattr(h, 'host_name')]
819 return set(hnames)
822 def evaluate_hostgroup_expression(self, expr, hosts, hostgroups):
823 res = []
824 original_expr = expr
825 #print "I'm trying to prepare the expression", expr
827 #We've got problem with the "-" sign. It can be in a
828 #valid name but it's a sign of difference for sets
829 #so we change the - now by something, then we reverse after
830 if '-' in expr:
831 expr = expr.replace('-', 'MINUSSIGN')
833 # ! (not) should be changed as "ALL-" (all but not...)
834 if '!' in expr:
835 ALLELEMENTS = self.get_all_host_names_set(hosts)
836 #print "Changing ! by ALLELEMENTS- in ", expr
837 expr = expr.replace('!', 'ALLELEMENTS-')
839 # We change all separaton token by 10 spaces
840 # (so names can still have some spaces
841 # on them like Europe Servers because we wil cut byy this 10spaces after
842 strip_expr = expr
843 for c in ['|', '&', '(', ')', ',', '-']:
844 strip_expr = strip_expr.replace(c, ' '*10)
845 #print "Stripped expression:", strip_expr
847 tokens = strip_expr.split(' '*10)
848 # Strip and non void token
849 tokens = [token.strip() for token in tokens if token != '']
850 #print "Tokens:", tokens
852 #Now add in locals() dict (yes, real variables!)
853 for token in tokens:
854 #ALLELEMENTS is a private group for us
855 if token != 'ALLELEMENTS':
856 #Maybe the token was with - at the begining,
857 #but we change all by "MINUSSIGN". We must change it back now
858 #for the search
859 if 'MINUSSIGN' in token:
860 tmp_token = token.replace('MINUSSIGN', '-')
861 members = hostgroups.get_members_by_name(tmp_token)
862 else:
863 members = hostgroups.get_members_by_name(token)
865 if members != []:
866 #print "Get members", members
867 elts = members.split(',')
868 elts = strip_and_uniq(elts)
869 elts = set(elts)
870 #print "Elements:", elts
871 #print "Now set in locals the token new values"
872 locals()[token.upper()] = elts
873 else:
874 if 'MINUSSIGN' in token:
875 token = token.replace('MINUSSIGN', '-')
876 print self.__dict__, type(self)
877 err = "ERROR: the group %s is unknown !" % (token,)
878 print err
879 self.configuration_errors.append(err)
880 return res
882 # Now changing the exprtoken value with
883 # UPPER one (so less risk of problem...
884 expr = expr.replace(token, token.upper())
886 #print "Final expression:", expr
887 try:
888 evaluation = eval(expr)
889 except SyntaxError:
890 print "The syntax of %s is invalid" % original_expr
891 return res
892 except NameError:
893 print "There is a unknow name in %s" % original_expr
894 return res
895 #print "Evaluation :", evaluation
897 # In evaluation we can have multiples values because
898 # of , (so it make a tuple in fact)
899 # we must OR them in the result
900 if ',' in expr:
901 for part in evaluation:
902 #print "PART", part
903 res.extend(list(part))
904 else:#no , so we do not have a tuple but a simple uniq set
905 res.extend(list(evaluation))
906 res_string = ','.join(res)
907 #print "Final resolution is", res_string
908 return res_string
911 #If we've got a hostgroup_name property, we search for all
912 #theses groups and ask them their hosts, and then add them
913 #all into our host_name property
914 def explode_host_groups_into_hosts(self, hosts, hostgroups):
915 for i in self:
916 if hasattr(i, 'hostgroup_name'):
917 hnames = self.evaluate_hostgroup_expression(i.hostgroup_name,
918 hosts, hostgroups)
919 if hnames != []:
920 if hasattr(i, 'host_name'):
921 i.host_name += ',' + str(hnames)
922 else:
923 i.host_name = str(hnames)