Fix : get back LiveStatus as default.
[shinken.git] / shinken / objects / item.py
blob9e622d95884f0404e55eceb4d0e428d9edbe6f4a
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/>.
24 """ This class is a base class for nearly all configuration
25 elements like service, hosts or contacts.
26 """
28 #from command import CommandCall
29 #from util import to_int, to_char, to_split, to_bool
30 from copy import copy
32 from shinken.objects.command import CommandCall
33 from shinken.brok import Brok
34 from shinken.util import strip_and_uniq
35 from shinken.acknowledge import Acknowledge
36 from shinken.comment import Comment
38 class Item(object):
39 def __init__(self, params={}):
40 #We have our own id of My Class type :)
41 #use set attr for going into the slots
42 #instead of __dict__ :)
43 setattr(self, 'id', self.__class__.id)
44 self.__class__.id += 1
47 self.customs = {} # for custom variables
48 self.plus = {} # for value with a +
50 cls = self.__class__
51 #adding running properties like latency, dependency list, etc
52 for prop, entry in cls.running_properties.items():
53 #Copy is slow, so we check type
54 #Type with __iter__ are list or dict, or tuple.
55 #Item need it's own list, so qe copy
56 val = entry.default
57 if hasattr(val, '__iter__'):
58 setattr(self, prop, copy(val))
59 else:
60 setattr(self, prop, val)
61 #eatch istance to have his own running prop!
63 #[0] = + -> new key-plus
64 #[0] = _ -> new custom entry in UPPER case
65 for key in params:
66 if len(params[key]) >= 1 and params[key][0] == '+':
67 #Special case : a _MACRO can be a plus. so add to plus
68 #but upper the key for the macro name
69 if key[0] == "_":
70 self.plus[key.upper()] = params[key][1:] # we remove the +
71 else:
72 self.plus[key] = params[key][1:] # we remove the +
73 elif key[0] == "_":
74 custom_name = key.upper()
75 self.customs[custom_name] = params[key]
76 else:
77 setattr(self, key, params[key])
80 #return a copy of the item, but give him a new id
81 def copy(self):
82 """ Copy a copy of the item, but give him a new id """
83 cls = self.__class__
84 i = cls({})#Dummy item but with it's own running properties
85 for prop in cls.properties:
86 if hasattr(self, prop):
87 val = getattr(self, prop)
88 setattr(i, prop, val)
89 # Also copy the customs tab
90 i.customs = copy(self.customs)
91 return i
94 def clean(self):
95 pass
98 def __str__(self):
99 return str(self.__dict__)+'\n'
102 def is_tpl(self):
103 """ Return if the elements is a template """
104 try:
105 return self.register == '0'
106 except:
107 return False
110 #has = hasattr
111 #def has(self, prop):
112 # return hasattr(self, prop)
115 #If a prop is absent and is not required, put the default value
116 def fill_default(self):
117 """ Fill missing properties if they are missing """
118 cls = self.__class__
120 for prop, entry in cls.properties.items():
121 if not hasattr(self, prop) and entry.has_default:
122 setattr(self, prop, entry.default)
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, entry in conf.properties.items():
136 #If we have a class_inherit, and the arbtier really send us it
137 # if 'class_inherit' in entry and hasattr(conf, prop):
138 if hasattr(conf, prop):
139 for (cls_dest, change_name) in entry.class_inherit:
140 if cls_dest == cls:#ok, we've got something to get
141 value = getattr(conf, prop)
142 if change_name is None:
143 setattr(cls, prop, value)
144 else:
145 setattr(cls, change_name, value)
147 #Make this method a classmethod
148 load_global_conf = classmethod(load_global_conf)
151 #Use to make python properties
152 def pythonize(self):
153 cls = self.__class__
154 for prop, tab in cls.properties.items():
155 try:
156 # if isinstance(tab, dict):
157 # if 'pythonize' in tab:
158 # f = tab['pythonize']
159 # old_val = getattr(self, prop)
160 # new_val = f(old_val)
161 # #print "Changing ", old_val, "by", new_val
162 # setattr(self, prop, new_val)
163 # else: #new style for service
164 new_val = tab.pythonize(getattr(self, prop))
165 setattr(self, prop, new_val)
166 except AttributeError , exp:
167 #print self.get_name(), ' : ', exp
168 pass # Will be catch at the is_correct moment
171 def get_templates(self):
172 if hasattr(self, 'use'):
173 return self.use.split(',')
174 else:
175 return []
178 #We fillfull properties with template ones if need
179 def get_property_by_inheritance(self, items, prop):
180 #If I have the prop, I take mine but I check if I must
181 #add a plus porperty
182 if hasattr(self, prop):
183 value = getattr(self, prop)
184 # Maybe this value is 'null'. If so, we should NOT inherit
185 # and just delete this entry, and hope of course.
186 if value == 'null':
187 delattr(self, prop)
188 return None
189 # Manage the additive inheritance for the property,
190 # if property is in plus, add or replace it
191 if self.has_plus(prop):
192 value += ',' + self.get_plus_and_delete(prop)
193 return value
194 #Ok, I do not have prop, Maybe my templates do?
195 #Same story for plus
196 for i in self.templates:
197 value = i.get_property_by_inheritance(items, prop)
198 if value is not None:
199 if self.has_plus(prop):
200 value += ','+self.get_plus_and_delete(prop)
201 setattr(self, prop, value)
202 return value
203 #I do not have prop, my templates too... Maybe a plus?
204 if self.has_plus(prop):
205 value = self.get_plus_and_delete(prop)
206 setattr(self, prop, value)
207 return value
208 #Not event a plus... so None :)
209 return None
212 #We fillfull properties with template ones if need
213 def get_customs_properties_by_inheritance(self, items):
214 for i in self.templates:
215 tpl_cv = i.get_customs_properties_by_inheritance(items)
216 if tpl_cv is not {}:
217 for prop in tpl_cv:
218 if prop not in self.customs:
219 value = tpl_cv[prop]
220 else:
221 value = self.customs[prop]
222 if self.has_plus(prop):
223 value = value+self.get_plus_and_delete(prop)
224 self.customs[prop] = value
225 for prop in self.customs:
226 value = self.customs[prop]
227 if self.has_plus(prop):
228 value = value = value+','+self.get_plus_and_delete(prop)
229 self.customs[prop] = value
230 #We can get custom properties in plus, we need to get all
231 #entires and put
232 #them into customs
233 cust_in_plus = self.get_all_plus_and_delete()
234 for prop in cust_in_plus:
235 self.customs[prop] = cust_in_plus[prop]
236 return self.customs
239 def has_plus(self, prop):
240 try:
241 self.plus[prop]
242 except:
243 return False
244 return True
247 def get_all_plus_and_delete(self):
248 res = {}
249 props = self.plus.keys() #we delete entries, so no for ... in ...
250 for prop in props:
251 res[prop] = self.get_plus_and_delete(prop)
252 return res
255 def get_plus_and_delete(self, prop):
256 val = self.plus[prop]
257 del self.plus[prop]
258 return val
261 #Check is required prop are set:
262 #template are always correct
263 def is_correct(self):
264 state = True
265 properties = self.__class__.properties
266 for prop, entry in properties.items():
267 if not hasattr(self, prop) and entry.required:
268 print self.get_name(), "missing property :", prop
269 state = False
270 return state
273 #This function is used by service and hosts
274 #to transform Nagios2 parameters to Nagios3
275 #ones, like normal_check_interval to
276 #check_interval. There is a old_parameters tab
277 #in Classes taht give such modifications to do.
278 def old_properties_names_to_new(self):
279 old_properties = self.__class__.old_properties
280 for old_name, new_name in old_properties.items():
281 #Ok, if we got old_name and NO new name,
282 #we switch the name
283 if hasattr(self, old_name) and not hasattr(self, new_name):
284 value = getattr(self, old_name)
285 setattr(self, new_name, value)
288 def add_downtime(self, downtime):
289 self.downtimes.append(downtime)
292 def del_downtime(self, downtime_id):
293 d_to_del = None
294 for dt in self.downtimes:
295 if dt.id == downtime_id:
296 d_to_del = dt
297 dt.can_be_deleted = True
298 if d_to_del is not None:
299 self.downtimes.remove(d_to_del)
302 def add_comment(self, comment):
303 self.comments.append(comment)
306 def del_comment(self, comment_id):
307 c_to_del = None
308 for c in self.comments:
309 if c.id == comment_id:
310 c_to_del = c
311 c.can_be_deleted = True
312 if c_to_del is not None:
313 self.comments.remove(c_to_del)
316 def acknowledge_problem(self, sticky, notify, persistent, author, comment):
317 if self.state != self.ok_up:
318 if notify:
319 self.create_notifications('ACKNOWLEDGEMENT')
320 self.problem_has_been_acknowledged = True
321 if sticky == 2:
322 sticky = True
323 else:
324 sticky = False
325 a = Acknowledge(self, sticky, notify, persistent, author, comment)
326 self.acknowledgement = a
327 if self.my_type == 'host':
328 comment_type = 1
329 else :
330 comment_type = 2
331 c = Comment(self, persistent, author, comment,
332 comment_type, 4, 0, False, 0)
333 self.add_comment(c)
334 self.broks.append(self.get_update_status_brok())
337 # Delete the acknowledgement object and reset the flag
338 # but do not remove the associated comment.
339 def unacknowledge_problem(self):
340 if self.problem_has_been_acknowledged:
341 self.problem_has_been_acknowledged = False
342 #Should not be deleted, a None is Good
343 self.acknowledgement = None
344 #del self.acknowledgement
345 # find comments of non-persistent ack-comments and delete them too
346 for c in self.comments:
347 if c.entry_type == 4 and not c.persistent:
348 self.del_comment(c.id)
349 self.broks.append(self.get_update_status_brok())
352 # Check if we have an acknowledgement and if this is marked as sticky.
353 # This is needed when a non-ok state changes
354 def unacknowledge_problem_if_not_sticky(self):
355 if hasattr(self, 'acknowledgement') and self.acknowledgement is not None:
356 if not self.acknowledgement.sticky:
357 self.unacknowledge_problem()
360 #Will flatten some parameters taggued by the 'conf_send_preparation'
361 #property because they are too "linked" to be send like that (like realms)
362 def prepare_for_conf_sending(self):
363 cls = self.__class__
365 for prop, entry in cls.properties.items():
366 #Is this property need preparation for sending?
367 if entry.conf_send_preparation is not None:
368 f = entry.conf_send_preparation
369 if f is not None:
370 val = f(getattr(self, prop))
371 setattr(self, prop, val)
373 if hasattr(cls, 'running_properties'):
374 for prop, entry in cls.running_properties.items():
375 #Is this property need preparation for sending?
376 if entry.conf_send_preparation is not None:
377 f = entry.conf_send_preparation
378 if f is not None:
379 val = f(getattr(self, prop))
380 setattr(self, prop, val)
385 #Get the property for an object, with good value
386 #and brok_transformation if need
387 def get_property_value_for_brok(self, prop, tab):
388 entry = tab[prop]
389 #Get the current value, or the default if need
390 value = getattr(self, prop, entry.default)
392 #Apply brok_transformation if need
393 # Look if we must preprocess the value first
394 pre_op = entry.brok_transformation
395 if pre_op is not None:
396 value = pre_op(self, value)
398 return value
401 #Fill data with info of item by looking at brok_type
402 #in props of properties or running_propterties
403 def fill_data_brok_from(self, data, brok_type):
404 cls = self.__class__
405 #Now config properties
406 for prop, entry in cls.properties.items():
407 #Is this property intended for brokking?
408 # if 'fill_brok' in cls.properties[prop]:
409 if brok_type in entry.fill_brok:
410 data[prop] = self.get_property_value_for_brok(prop, cls.properties)
412 # Maybe the class do not have running_properties
413 if hasattr(cls, 'running_properties'):
414 # We've got prop in running_properties too
415 for prop, entry in cls.running_properties.items():
416 # if 'fill_brok' in cls.running_properties[prop]:
417 if brok_type in entry.fill_brok:
418 data[prop] = self.get_property_value_for_brok(prop, cls.running_properties)
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,
472 poller_tag=self.poller_tag)
473 else:
474 cmdCall = CommandCall(commands, command)
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 hasattr(i, 'name') and 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 for i in self:
622 try:
623 del i.templates
624 del i.use
625 del i.plus
626 except AttributeError:
627 pass
628 del self.templates
631 #If a prop is absent and is not required, put the default value
632 def fill_default(self):
633 for i in self:
634 i.fill_default()
637 def __str__(self):
638 s = ''
639 cls = self.__class__
640 for id in self.items:
641 s = s + str(cls) + ':' + str(id) + str(self.items[id]) + '\n'
642 return s
645 # Inheritance forjust a property
646 def apply_partial_inheritance(self, prop):
647 for i in self:
648 i.get_property_by_inheritance(self, prop)
651 def apply_inheritance(self):
652 #We check for all Class properties if the host has it
653 #if not, it check all host templates for a value
654 cls = self.inner_class
655 for prop in cls.properties:
656 self.apply_partial_inheritance(prop)
657 for i in self:
658 i.get_customs_properties_by_inheritance(self)
661 #We remove twins
662 #Remember: item id respect the order of conf. So if and item
663 # is defined multiple times,
664 #we want to keep the first one.
665 #Services are also managed here but they are specials:
666 #We remove twins services with the same host_name/service_description
667 #Remember: host service are take into account first before hostgroup service
668 #Id of host service are lower than hostgroups one, so they are
669 #in self.twins_services
670 #and we can remove them.
671 def remove_twins(self):
672 for id in self.twins:
673 i = self.items[id]
674 type = i.__class__.my_type
675 print 'Warning: the', type, i.get_name(), 'is already defined.'
676 del self.items[id] #bye bye
677 #do not remove twins, we should look in it, but just void it
678 self.twins = []
679 #del self.twins #no more need
683 #We've got a contacts property with , separated contacts names
684 #and we want have a list of Contacts
685 def linkify_with_contacts(self, contacts):
686 for i in self:
687 if hasattr(i, 'contacts'):
688 contacts_tab = i.contacts.split(',')
689 contacts_tab = strip_and_uniq(contacts_tab)
690 new_contacts = []
691 for c_name in contacts_tab:
692 if c_name != '':
693 c = contacts.find_by_name(c_name)
694 if c is not None:
695 new_contacts.append(c)
696 # Else : Add in the errors tab.
697 # will be raised at is_correct
698 else:
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 an object and its 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 is not 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 is not 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 is 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,
779 poller_tag=i.poller_tag)
780 else:
781 cmdCall = CommandCall(commands, command)
782 #TODO: catch None?
783 setattr(i, prop, cmdCall)
784 else:
785 setattr(i, prop, None)
788 #Link a command list (commands with , between) in real CommandCalls
789 def linkify_command_list_with_commands(self, commands, prop):
790 for i in self:
791 if hasattr(i, prop):
792 coms = getattr(i, prop).split(',')
793 coms = strip_and_uniq(coms)
794 com_list = []
795 for com in coms:
796 if com != '':
797 if hasattr(i, 'poller_tag'):
798 cmdCall = CommandCall(commands, com,
799 poller_tag=i.poller_tag)
800 else:
801 cmdCall = CommandCall(commands, com)
802 #TODO: catch None?
803 com_list.append(cmdCall)
804 else: # TODO: catch?
805 pass
806 setattr(i, prop, com_list)
809 #Return a set with ALL hosts (used in ! expressions)
810 def get_all_host_names_set(self, hosts):
811 hnames = [h.host_name for h in hosts.items.values()
812 if hasattr(h, 'host_name')]
813 return set(hnames)
816 def evaluate_hostgroup_expression(self, expr, hosts, hostgroups):
817 res = []
818 original_expr = expr
819 #print "I'm trying to prepare the expression", expr
821 #We've got problem with the "-" sign. It can be in a
822 #valid name but it's a sign of difference for sets
823 #so we change the - now by something, then we reverse after
824 if '-' in expr:
825 expr = expr.replace('-', 'MINUSSIGN')
827 # ! (not) should be changed as "ALL-" (all but not...)
828 if '!' in expr:
829 ALLELEMENTS = self.get_all_host_names_set(hosts)
830 #print "Changing ! by ALLELEMENTS- in ", expr
831 expr = expr.replace('!', 'ALLELEMENTS-')
833 # We change all separaton token by 10 spaces
834 # (so names can still have some spaces
835 # on them like Europe Servers because we wil cut byy this 10spaces after
836 strip_expr = expr
837 for c in ['|', '&', '(', ')', ',', '-']:
838 strip_expr = strip_expr.replace(c, ' '*10)
839 #print "Stripped expression:", strip_expr
841 tokens = strip_expr.split(' '*10)
842 # Strip and non void token
843 tokens = [token.strip() for token in tokens if token != '']
844 #print "Tokens:", tokens
846 #Now add in locals() dict (yes, real variables!)
847 for token in tokens:
848 #ALLELEMENTS is a private group for us
849 if token != 'ALLELEMENTS':
850 #Maybe the token was with - at the begining,
851 #but we change all by "MINUSSIGN". We must change it back now
852 #for the search
853 if 'MINUSSIGN' in token:
854 tmp_token = token.replace('MINUSSIGN', '-')
855 members = hostgroups.get_members_by_name(tmp_token)
856 else:
857 members = hostgroups.get_members_by_name(token)
859 if members != []:
860 #print "Get members", members
861 elts = members.split(',')
862 elts = strip_and_uniq(elts)
863 elts = set(elts)
864 #print "Elements:", elts
865 #print "Now set in locals the token new values"
866 locals()[token.upper()] = elts
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 # Now changing the exprtoken value with
877 # UPPER one (so less risk of problem...
878 expr = expr.replace(token, token.upper())
880 #print "Final expression:", expr
881 try:
882 evaluation = eval(expr)
883 except SyntaxError:
884 print "The syntax of %s is invalid" % original_expr
885 return res
886 except NameError:
887 print "There is a unknow name in %s" % original_expr
888 return res
889 #print "Evaluation :", evaluation
891 # In evaluation we can have multiples values because
892 # of , (so it make a tuple in fact)
893 # we must OR them in the result
894 if ',' in expr:
895 for part in evaluation:
896 #print "PART", part
897 res.extend(list(part))
898 else:#no , so we do not have a tuple but a simple uniq set
899 res.extend(list(evaluation))
900 res_string = ','.join(res)
901 #print "Final resolution is", res_string
902 return res_string
905 #If we've got a hostgroup_name property, we search for all
906 #theses groups and ask them their hosts, and then add them
907 #all into our host_name property
908 def explode_host_groups_into_hosts(self, hosts, hostgroups):
909 for i in self:
910 if hasattr(i, 'hostgroup_name'):
911 hnames = self.evaluate_hostgroup_expression(i.hostgroup_name,
912 hosts, hostgroups)
913 if hnames != []:
914 if hasattr(i, 'host_name'):
915 i.host_name += ',' + str(hnames)
916 else:
917 i.host_name = str(hnames)