*Code cleanup in livestatus servicesbygroup
[shinken.git] / shinken / item.py
blob5bc912b4733ef393370e3bbc545313f1c0f54c67
1 #!/usr/bin/env python
2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
6 #This file is part of Shinken.
8 #Shinken is free software: you can redistribute it and/or modify
9 #it under the terms of the GNU Affero General Public License as published by
10 #the Free Software Foundation, either version 3 of the License, or
11 #(at your option) any later version.
13 #Shinken is distributed in the hope that it will be useful,
14 #but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 #GNU Affero General Public License for more details.
18 #You should have received a copy of the GNU Affero General Public License
19 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
22 #from command import CommandCall
23 #from util import to_int, to_char, to_split, to_bool
24 from copy import copy
25 from brok import Brok
27 from util import strip_and_uniq
28 from command import CommandCall
29 from acknowledge import Acknowledge
30 from comment import Comment
32 class Item(object):
33 def __init__(self, params={}):
34 #We have our own id of My Class type :)
35 #use set attr for going into the slots
36 #instead of __dict__ :)
37 setattr(self, 'id', self.__class__.id)
38 self.__class__.id += 1
41 self.customs = {} # for custom variables
42 self.plus = {} # for value with a +
44 cls = self.__class__
45 #adding running properties like latency, dependency list, etc
46 for prop in cls.running_properties:
47 #Copy is slow, so we check type
48 #Type with __iter__ are list or dict, or tuple.
49 #Item need it's own list, so qe copy
50 val = cls.running_properties[prop]['default']
51 if hasattr(val, '__iter__'):
52 setattr(self, prop, copy(val))
53 else:
54 setattr(self, prop, val)
55 #eatch istance to have his own running prop!
57 #[0] = + -> new key-plus
58 #[0] = _ -> new custom entry in UPPER case
59 for key in params:
60 if len(params[key]) >= 1 and params[key][0] == '+':
61 #Special case : a _MACRO can be a plus. so add to plus
62 #but upper the key for the macro name
63 if key[0] == "_":
64 self.plus[key.upper()] = params[key][1:] # we remove the +
65 else:
66 self.plus[key] = params[key][1:] # we remove the +
67 elif key[0] == "_":
68 custom_name = key.upper()
69 self.customs[custom_name] = params[key]
70 else:
71 setattr(self, key, params[key])
74 #return a copy of the item, but give him a new id
75 def copy(self):
76 cls = self.__class__
77 i = cls({})#Dummy item but with it's own running properties
78 properties = cls.properties
79 for prop in properties:
80 if hasattr(self, prop):
81 val = getattr(self, prop)
82 setattr(i, prop, val)
83 return i
86 def clean(self):
87 pass
90 def __str__(self):
91 return str(self.__dict__)+'\n'
94 def is_tpl(self):
95 try:
96 return self.register == '0'
97 except:
98 return False
101 #has = hasattr
102 #def has(self, prop):
103 # return hasattr(self, prop)
106 #If a prop is absent and is not required, put the default value
107 def fill_default(self):
108 cls = self.__class__
109 properties = cls.properties
111 for prop in properties:
112 if not hasattr(self, prop) and 'default' in properties[prop]:
113 value = properties[prop]['default']
114 setattr(self, prop, value)
117 #We load every usefull parameter so no need to access global conf later
118 #Must be called after a change in a gloabl conf parameter
119 def load_global_conf(cls, conf):
120 #print "Load global conf=========================="
121 #conf have properties, if 'enable_notifications' :
122 #{ [...] 'class_inherit' : [(Host, None), (Service, None),
123 # (Contact, None)]}
124 #get the name and put the value if None, put the Name
125 #(not None) if not (not clear ?)
126 for prop in conf.properties:
127 #If we have a class_inherit, and the arbtier really send us it
128 if 'class_inherit' in conf.properties[prop] and hasattr(conf, prop):
129 for (cls_dest, change_name) in conf.properties[prop]['class_inherit']:
130 if cls_dest == cls:#ok, we've got something to get
131 value = getattr(conf, prop)
132 if change_name is None:
133 setattr(cls, prop, value)
134 else:
135 setattr(cls, change_name, value)
137 #Make this method a classmethod
138 load_global_conf = classmethod(load_global_conf)
141 #Use to make pyton properties
142 def pythonize(self):
143 cls = self.__class__
144 for prop in cls.properties:
145 try:
146 tab = cls.properties[prop]
147 if 'pythonize' in tab:
148 f = tab['pythonize']
149 old_val = getattr(self, prop)
150 new_val = f(old_val)
151 #print "Changing ", old_val, "by", new_val
152 setattr(self, prop, new_val)
153 except AttributeError , exp:
154 #print self.get_name(), ' : ', exp
155 pass # Will be catch at the is_correct moment
158 def get_templates(self):
159 if hasattr(self, 'use'):
160 return self.use.split(',')
161 else:
162 return []
165 #We fillfull properties with template ones if need
166 def get_property_by_inheritance(self, items, prop):
167 #If I have the prop, I take mine but I check if I must
168 #add a plus porperty
169 if hasattr(self, prop):
170 value = getattr(self, prop)
171 #Manage the additive inheritance for the property,
172 #if property is in plus, add or replace it
173 if self.has_plus(prop):
174 value = value+','+self.get_plus_and_delete(prop)
175 return value
176 #Ok, I do not have prop, Maybe my templates do?
177 #Same story for plus
178 for i in self.templates:
179 value = i.get_property_by_inheritance(items, prop)
180 if value is not None:
181 if self.has_plus(prop):
182 value = value+','+self.get_plus_and_delete(prop)
183 setattr(self, prop, value)
184 return value
185 #I do not have prop, my templates too... Maybe a plus?
186 if self.has_plus(prop):
187 value = self.get_plus_and_delete(prop)
188 setattr(self, prop, value)
189 return value
190 #Not event a plus... so None :)
191 return None
194 #We fillfull properties with template ones if need
195 def get_customs_properties_by_inheritance(self, items):
196 for i in self.templates:
197 tpl_cv = i.get_customs_properties_by_inheritance(items)
198 if tpl_cv is not {}:
199 for prop in tpl_cv:
200 if prop not in self.customs:
201 value = tpl_cv[prop]
202 else:
203 value = self.customs[prop]
204 if self.has_plus(prop):
205 value = value+self.get_plus_and_delete(prop)
206 self.customs[prop] = value
207 for prop in self.customs:
208 value = self.customs[prop]
209 if self.has_plus(prop):
210 value = value = value+','+self.get_plus_and_delete(prop)
211 self.customs[prop] = value
212 #We can get custom properties in plus, we need to get all
213 #entires and put
214 #them into customs
215 cust_in_plus = self.get_all_plus_and_delete()
216 for prop in cust_in_plus:
217 self.customs[prop] = cust_in_plus[prop]
218 return self.customs
221 def has_plus(self, prop):
222 try:
223 self.plus[prop]
224 except:
225 return False
226 return True
229 def get_all_plus_and_delete(self):
230 res = {}
231 props = self.plus.keys() #we delete entries, so no for ... in ...
232 for prop in props:
233 res[prop] = self.get_plus_and_delete(prop)
234 return res
237 def get_plus_and_delete(self, prop):
238 val = self.plus[prop]
239 del self.plus[prop]
240 return val
243 #Check is required prop are set:
244 #template are always correct
245 def is_correct(self):
246 #if self.is_tpl:
247 # return True
248 state = True
249 properties = self.__class__.properties
250 for prop in properties:
251 if not hasattr(self, prop) and properties[prop]['required']:
252 print self.get_name(), "missing property :", prop
253 state = False
254 return state
257 #This function is used by service and hosts
258 #to transform Nagios2 parameters to Nagios3
259 #ones, like normal_check_interval to
260 #check_interval. There is a old_parameters tab
261 #in Classes taht give such modifications to do.
262 def old_properties_names_to_new(self):
263 old_properties = self.__class__.old_properties
264 for old_name in old_properties:
265 new_name = old_properties[old_name]
266 #Ok, if we got old_name and NO new name,
267 #we switch the name
268 if hasattr(self, old_name) and not hasattr(self, new_name):
269 value = getattr(self, old_name)
270 setattr(self, new_name, value)
273 def add_downtime(self, downtime):
274 self.downtimes.append(downtime)
277 def del_downtime(self, downtime_id):
278 d_to_del = None
279 for dt in self.downtimes:
280 if dt.id == downtime_id:
281 d_to_del = dt
282 dt.can_be_deleted = True
283 if d_to_del is not None:
284 self.downtimes.remove(d_to_del)
287 def add_comment(self, comment):
288 self.comments.append(comment)
291 def del_comment(self, comment_id):
292 c_to_del = None
293 for c in self.comments:
294 if c.id == comment_id:
295 c_to_del = c
296 c.can_be_deleted = True
297 if c_to_del is not None:
298 self.comments.remove(c_to_del)
301 def acknowledge_problem(self, sticky, notify, persistent, author, comment):
302 if self.state != self.ok_up:
303 if notify:
304 self.create_notifications('ACKNOWLEDGEMENT')
305 self.problem_has_been_acknowledged = True
306 if sticky == 2:
307 sticky = True
308 else:
309 sticky = False
310 a = Acknowledge(self, sticky, notify, persistent, author, comment)
311 self.acknowledgement = a
312 if self.my_type == 'host':
313 comment_type = 1
314 else :
315 comment_type = 2
316 c = Comment(self, persistent, author, comment, comment_type, 4, 0, False, 0)
317 self.add_comment(c)
318 self.broks.append(self.get_update_status_brok())
321 # Delete the acknowledgement object and reset the flag
322 # but do not remove the associated comment.
323 def unacknowledge_problem(self):
324 if self.problem_has_been_acknowledged:
325 self.problem_has_been_acknowledged = False
326 #Should not be deleted, a None is Good
327 self.acknowledgement = None
328 #del self.acknowledgement
329 # find comments of non-persistent ack-comments and delete them too
330 for c in self.comments:
331 if c.entry_type == 4 and not c.persistent:
332 self.del_comment(c.id)
333 self.broks.append(self.get_update_status_brok())
336 # Check if we have an acknowledgement and if this is marked as sticky.
337 # This is needed when a non-ok state changes
338 def unacknowledge_problem_if_not_sticky(self):
339 if hasattr(self, 'acknowledgement') and self.acknowledgement != None:
340 if not self.acknowledgement.sticky:
341 self.unacknowledge_problem()
344 #Will flatten some parameters taggued by the 'conf_send_preparation'
345 #property because they are too "linked" to be send like that (like realms)
346 def prepare_for_conf_sending(self):
347 cls = self.__class__
349 for prop in cls.properties:
350 entry = cls.properties[prop]
351 #Is this property need preparation for sending?
352 if 'conf_send_preparation' in entry:
353 f = entry['conf_send_preparation']
354 if f != None:
355 val = f(getattr(self, prop))
356 setattr(self, prop, val)
358 if hasattr(cls, 'running_properties'):
359 for prop in cls.running_properties:
360 entry = cls.running_properties[prop]
361 #Is this property need preparation for sending?
362 if 'conf_send_preparation' in entry:
363 f = entry['conf_send_preparation']
364 if f != None:
365 val = f(getattr(self, prop))
366 setattr(self, prop, val)
371 #Get the property for an object, with good value
372 #and brok_transformation if need
373 def get_property_value_for_brok(self, prop, tab):
374 pre_op = None
375 entry = tab[prop]
376 if 'brok_transformation' in entry:
377 pre_op = entry['brok_transformation']
379 value = None
380 #Get the current value, or the default if need
381 if hasattr(self, prop):
382 value = getattr(self, prop)
383 elif 'default' in entry:
384 value = entry['default']
386 #Apply brok_transformation if need
387 if pre_op != None:
388 value = pre_op(value)
390 return value
393 #Fill data with info of item by looking at brok_type
394 #in props of properties or running_propterties
395 def fill_data_brok_from(self, data, brok_type):
396 cls = self.__class__
397 #Now config properties
398 for prop in cls.properties:
399 #Is this property intended for brokking?
400 if 'fill_brok' in cls.properties[prop]:
401 if brok_type in cls.properties[prop]['fill_brok']:
402 data[prop] = self.get_property_value_for_brok(prop, cls.properties)
403 # if hasattr(self, prop):
404 # data[prop] = getattr(self, prop)
405 # elif 'default' in cls.properties[prop]:
406 # data[prop] = cls.properties[prop]['default']
408 #Maybe the class do not have running_properties
409 if hasattr(cls, 'running_properties'):
410 #We've got prop in running_properties too
411 for prop in cls.running_properties:
412 if 'fill_brok' in cls.running_properties[prop]:
413 if brok_type in cls.running_properties[prop]['fill_brok']:
414 data[prop] = self.get_property_value_for_brok(prop, cls.running_properties)
415 # if hasattr(self, prop):
416 # data[prop] = getattr(self, prop)
417 # elif 'default' in cls.properties[prop]:
418 # data[prop] = cls.running_properties[prop]['default']
421 #Get a brok with initial status
422 def get_initial_status_brok(self):
423 cls = self.__class__
424 my_type = cls.my_type
425 data = {'id' : self.id}
427 self.fill_data_brok_from(data, 'full_status')
428 b = Brok('initial_'+my_type+'_status', data)
429 return b
432 #Get a brok with update item status
433 def get_update_status_brok(self):
434 cls = self.__class__
435 my_type = cls.my_type
437 data = {'id' : self.id}
438 self.fill_data_brok_from(data, 'full_status')
439 b = Brok('update_'+my_type+'_status', data)
440 return b
443 #Get a brok with check_result
444 def get_check_result_brok(self):
445 cls = self.__class__
446 my_type = cls.my_type
448 data = {}
449 self.fill_data_brok_from(data, 'check_result')
450 b = Brok(my_type+'_check_result', data)
451 return b
454 #Get brok about the new schedule (next_check)
455 def get_next_schedule_brok(self):
456 cls = self.__class__
457 my_type = cls.my_type
459 data = {}
460 self.fill_data_brok_from(data, 'next_schedule')
461 b = Brok(my_type+'_next_schedule', data)
462 return b
465 #Link one command property to a class (for globals like oc*p_command)
466 def linkify_one_command_with_commands(self, commands, prop):
467 if hasattr(self, prop):
468 command = getattr(self, prop).strip()
469 if command != '':
470 if hasattr(self, 'poller_tag'):
471 cmdCall = CommandCall(commands, command, poller_tag=self.poller_tag)
472 else:
473 cmdCall = CommandCall(commands, command)
474 #TODO: catch None?
475 setattr(self, prop, cmdCall)
476 else:
477 setattr(self, prop, None)
481 class Items(object):
482 def __init__(self, items):
483 self.items = {}
484 self.configuration_warnings = []
485 self.configuration_errors = []
486 for i in items:
487 self.items[i.id] = i
488 self.templates = {}
492 def __iter__(self):
493 return self.items.itervalues()
496 def __len__(self):
497 return len(self.items)
500 def __delitem__(self, key):
501 del self.items[key]
504 def __setitem__(self, key, value):
505 self.items[key] = value
508 def __getitem__(self, key):
509 return self.items[key]
512 #We create the reversed list so search will be faster
513 #We also create a twins list with id of twins (not the original
514 #just the others, higher twins)
515 def create_reversed_list(self):
516 self.reversed_list = {}
517 self.twins = []
518 name_property = self.__class__.name_property
519 for id in self.items:
520 if hasattr(self.items[id], name_property):
521 name = getattr(self.items[id], name_property)
522 if name not in self.reversed_list:
523 self.reversed_list[name] = id
524 else:
525 self.twins.append(id)
528 def find_id_by_name(self, name):
529 if hasattr(self, 'reversed_list'):
530 if name in self.reversed_list:
531 return self.reversed_list[name]
532 else:
533 return None
534 else: #ok, an early ask, with no reversed list from now...
535 name_property = self.__class__.name_property
536 for i in self:
537 if hasattr(i, name_property):
538 i_name = getattr(i, name_property)
539 if i_name == name:
540 return i.id
541 return None
544 def find_by_name(self, name):
545 id = self.find_id_by_name(name)
546 if id is not None:
547 return self.items[id]
548 else:
549 return None
552 def pythonize(self):
553 for id in self.items:
554 self.items[id].pythonize()
557 def create_tpl_list(self):
558 for id in self.items:
559 i = self.items[id]
560 if i.is_tpl():
561 self.templates[id] = i
564 def find_tpl_by_name(self, name):
565 for id in self.templates:
566 i = self.items[id]
567 if i.name == name:
568 return i
569 return None
572 def linkify_templates(self):
573 #First we create a list of all templates
574 self.create_tpl_list()
575 for i in self:
576 tpls = i.get_templates()
577 new_tpls = []
578 for tpl in tpls:
579 t = self.find_tpl_by_name(tpl.strip())
580 if t is not None:
581 new_tpls.append(t)
582 else: # not find? not good!
583 err = "ERROR: the template '%s' defined for '%s' is unkown" % (tpl, i.get_name())
584 i.configuration_errors.append(err)
585 i.templates = new_tpls
589 def is_correct(self):
590 #we are ok at the begining. Hope we still ok at the end...
591 r = True
592 #Some class do not have twins, because they do not have names
593 #like servicedependancies
594 if hasattr(self, 'twins'):
595 #Ok, look at no twins (it's bad!)
596 for id in self.twins:
597 i = self.items[id]
598 print "Error: the", i.__class__.my_type, i.get_name(), "is duplicated"
599 r = False
600 #Then look if we have some errors in the conf
601 #Juts print warnings, but raise errors
602 for err in self.configuration_warnings:
603 print err
604 for err in self.configuration_errors:
605 print err
606 r = False
608 #Then look for individual ok
609 for i in self:
610 r &= i.is_correct()
611 return r
614 #We remove useless properties and templates
615 def clean_useless(self):
616 #First templates
617 tpls = [id for id in self.items if self.items[id].is_tpl()]
618 for id in tpls:
619 del self.items[id]
620 #Ok now delete useless in items
621 #TODO : move into item class
622 for i in self:
623 try:
624 del i.templates
625 del i.use
626 del i.plus
627 except AttributeError:
628 pass
629 del self.templates
632 #If a prop is absent and is not required, put the default value
633 def fill_default(self):
634 for i in self:
635 i.fill_default()
638 def __str__(self):
639 s = ''
640 cls = self.__class__
641 for id in self.items:
642 s = s + str(cls) + ':' + str(id) + str(self.items[id]) + '\n'
643 return s
646 #Inheritance forjust a property
647 def apply_partial_inheritance(self, prop):
648 for i in self:
649 i.get_property_by_inheritance(self, prop)
652 def apply_inheritance(self):
653 #We check for all Class properties if the host has it
654 #if not, it check all host templates for a value
655 cls = self.inner_class
656 properties = cls.properties
657 for prop in properties:
658 self.apply_partial_inheritance(prop)
659 for i in self:
660 i.get_customs_properties_by_inheritance(self)
663 #We remove twins
664 #Remember: item id respect the order of conf. So if and item
665 # is defined multiple times,
666 #we want to keep the first one.
667 #Services are also managed here but they are specials:
668 #We remove twins services with the same host_name/service_description
669 #Remember: host service are take into account first before hostgroup service
670 #Id of host service are lower than hostgroups one, so they are
671 #in self.twins_services
672 #and we can remove them.
673 def remove_twins(self):
674 for id in self.twins:
675 i = self.items[id]
676 type = i.__class__.my_type
677 print 'Warning: the', type, i.get_name(), 'is already defined.'
678 del self.items[id] #bye bye
679 #do not remove twins, we should look in it, but just void it
680 self.twins = []
681 #del self.twins #no more need
685 #We've got a contacts property with , separated contacts names
686 #and we want have a list of Contacts
687 def linkify_with_contacts(self, contacts):
688 for i in self:
689 if hasattr(i, 'contacts'):
690 contacts_tab = i.contacts.split(',')
691 contacts_tab = strip_and_uniq(contacts_tab)
692 new_contacts = []
693 for c_name in contacts_tab:
694 if c_name != '':
695 c = contacts.find_by_name(c_name)
696 if c != None:
697 new_contacts.append(c)
698 else: #Add in the errors tab. will be raised at is_correct
699 err = "ERROR: the contact '%s' defined for '%s' is unkown" % (c_name, i.get_name())
700 i.configuration_errors.append(err)
701 #Get the list, but first make elements uniq
702 i.contacts = list(set(new_contacts))
705 #Make link between service and it's escalations
706 def linkify_with_escalations(self, escalations):
707 for i in self:
708 if hasattr(i, 'escalations'):
709 #print i.get_name(), 'going to link escalations', i.escalations
710 escalations_tab = i.escalations.split(',')
711 escalations_tab = strip_and_uniq(escalations_tab)
712 new_escalations = []
713 for es_name in escalations_tab:
714 es = escalations.find_by_name(es_name)
715 if es != None:
716 new_escalations.append(es)
717 else: #TODO what?
718 pass
719 i.escalations = new_escalations
720 #print i.get_name(), 'finallygot escalation', i.escalations
723 #Make link between item and it's resultmodulations
724 def linkify_with_resultmodulations(self, resultmodulations):
725 for i in self:
726 if hasattr(i, 'resultmodulations'):
727 resultmodulations_tab = i.resultmodulations.split(',')
728 resultmodulations_tab = strip_and_uniq(resultmodulations_tab)
729 new_resultmodulations = []
730 for rm_name in resultmodulations_tab:
731 rm = resultmodulations.find_by_name(rm_name)
732 if rm != None:
733 new_resultmodulations.append(rm)
734 else: #TODO WHAT?
735 pass
736 i.resultmodulations = new_resultmodulations
739 #If we've got a contact_groups properties, we search for all
740 #theses groups and ask them their contacts, and then add them
741 #all into our contacts property
742 def explode_contact_groups_into_contacts(self, contactgroups):
743 for i in self:
744 if hasattr(i, 'contact_groups'):
745 cgnames = i.contact_groups.split(',')
746 cgnames = strip_and_uniq(cgnames)
747 for cgname in cgnames:
748 cg = contactgroups.find_by_name(cgname)
749 if cg == None:
750 err = "The contact group '%s'defined on the %s '%s' do not exist" % (cgname, i.__class__.my_type, i.get_name())
751 i.configuration_errors.append(err)
752 continue
753 cnames = contactgroups.get_members_by_name(cgname)
754 #We add contacts into our contacts
755 if cnames != []:
756 if hasattr(i, 'contacts'):
757 i.contacts += ','+cnames
758 else:
759 i.contacts = cnames
761 #Link a timeperiod property (prop)
762 def linkify_with_timeperiods(self, timeperiods, prop):
763 for i in self:
764 if hasattr(i, prop):
765 tpname = getattr(i, prop)
766 tp = timeperiods.find_by_name(tpname)
767 #TODO: catch None?
768 setattr(i, prop, tp)
771 #Link one command property
772 def linkify_one_command_with_commands(self, commands, prop):
773 for i in self:
774 if hasattr(i, prop):
775 command = getattr(i, prop).strip()
776 if command != '':
777 if hasattr(i, 'poller_tag'):
778 cmdCall = CommandCall(commands, command, poller_tag=i.poller_tag)
779 else:
780 cmdCall = CommandCall(commands, command)
781 #TODO: catch None?
782 setattr(i, prop, cmdCall)
783 else:
784 setattr(i, prop, None)
787 #Link a command list (commands with , between) in real CommandCalls
788 def linkify_command_list_with_commands(self, commands, prop):
789 for i in self:
790 if hasattr(i, prop):
791 coms = getattr(i, prop).split(',')
792 coms = strip_and_uniq(coms)
793 com_list = []
794 for com in coms:
795 if com != '':
796 if hasattr(i, 'poller_tag'):
797 cmdCall = CommandCall(commands, com, poller_tag=i.poller_tag)
798 else:
799 cmdCall = CommandCall(commands, com)
800 #TODO: catch None?
801 com_list.append(cmdCall)
802 else: # TODO: catch?
803 pass
804 setattr(i, prop, com_list)
807 #Return a set with ALL hosts (used in ! expressions)
808 def get_all_host_names_set(self, hosts):
809 hnames = [h.host_name for h in hosts.items.values() if hasattr(h, 'host_name')]
810 return set(hnames)
813 def evaluate_hostgroup_expression(self, expr, hosts, hostgroups):
814 res = []
815 original_expr = expr
816 #print "I'm trying to prepare the expression", expr
818 #We've got problem with the "-" sign. It can be in a
819 #valid name but it's a sign of difference for sets
820 #so we change the - now by something, then we reverse after
821 if '-' in expr:
822 expr = expr.replace('-', 'MINUSSIGN')
824 #! (not) should be changed as "ALL-" (all but not...)
825 if '!' in expr:
826 ALLELEMENTS = self.get_all_host_names_set(hosts)
827 #print "Changing ! by ALLELEMENTS- in ", expr
828 expr = expr.replace('!', 'ALLELEMENTS-')
830 #print "Now finding all token to change in variable"
831 #print "So I remove all non want caracters"
833 #We change all separaton token by 10 spaces (so names can still have some spaces
834 #on them like Europe Servers because we wil cut byy this 10spaces after
835 strip_expr = expr
836 for c in ['|', '&', '(', ')', ',', '-']:
837 strip_expr = strip_expr.replace(c, ' '*10)
838 #print "Stripped expression:", strip_expr
840 tokens = strip_expr.split(' '*10)
841 #Strip and non void token
842 tokens = [token.strip() for token in tokens if token != '']
843 #print "Tokens:", tokens
845 #Now add in locals() dict (yes, real variables!)
846 for token in tokens:
847 #ALLELEMENTS is a private group for us
848 if token != 'ALLELEMENTS':
849 #Maybe the token was with - at the begining,
850 #but we change all by "MINUSSIGN". We must change it back now
851 #for the search
852 if 'MINUSSIGN' in token:
853 tmp_token = token.replace('MINUSSIGN', '-')
854 members = hostgroups.get_members_by_name(tmp_token)
855 else:
856 members = hostgroups.get_members_by_name(token)
858 if members != []:
859 #print "Get members", members
860 elts = members.split(',')
861 elts = strip_and_uniq(elts)
862 elts = set(elts)
863 #print "Elements:", elts
864 #print "Now set in locals the token new values"
865 locals()[token.upper()] = elts
866 #TODO : raise error
867 else:
868 if 'MINUSSIGN' in token:
869 token = token.replace('MINUSSIGN', '-')
870 print self.__dict__, type(self)
871 err = "ERROR: the group %s is unknown !" % (token,)
872 print err
873 self.configuration_errors.append(err)
874 return res
876 #print "Now changing the exprtoken value with UPPER one (so less risk of problem..."
877 expr = expr.replace(token, token.upper())
879 #print "Final expression:", expr
880 try:
881 evaluation = eval(expr)
882 except SyntaxError:
883 print "The syntax of %s is invalid" % original_expr
884 return res
885 except NameError:
886 print "There is a unknow name in %s" % original_expr
887 return res
888 #print "Evaluation :", evaluation
890 #In evaluation we can have multiples values because of , (so it make a tuple in fact)
891 #we must OR them in the result
892 if ',' in expr:
893 for part in evaluation:
894 #print "PART", part
895 res.extend(list(part))
896 else:#no , so we do not have a tuple but a simple uniq set
897 res.extend(list(evaluation))
898 res_string = ','.join(res)
899 #print "Final resolution is", res_string
900 return res_string
903 #If we've got a hostgroup_name property, we search for all
904 #theses groups and ask them their hosts, and then add them
905 #all into our host_name property
906 def explode_host_groups_into_hosts(self, hosts, hostgroups):
907 for i in self:
908 if hasattr(i, 'hostgroup_name'):
909 hnames = self.evaluate_hostgroup_expression(i.hostgroup_name, hosts, hostgroups)
910 if hnames != []:
911 if hasattr(i, 'host_name'):
912 i.host_name += ',' + str(hnames)
913 else:
914 i.host_name = str(hnames)