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.
28 #from command import CommandCall
29 #from util import to_int, to_char, to_split, to_bool
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
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 +
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
57 if hasattr(val
, '__iter__'):
58 setattr(self
, prop
, copy(val
))
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
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
70 self
.plus
[key
.upper()] = params
[key
][1:] # we remove the +
72 self
.plus
[key
] = params
[key
][1:] # we remove the +
74 custom_name
= key
.upper()
75 self
.customs
[custom_name
] = params
[key
]
77 setattr(self
, key
, params
[key
])
80 #return a copy of the item, but give him a new id
82 """ Copy a copy of the item, but give him a new id """
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
)
89 # Also copy the customs tab
90 i
.customs
= copy(self
.customs
)
99 return str(self
.__dict
__)+'\n'
103 """ Return if the elements is a template """
105 return self
.register
== '0'
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 """
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),
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
)
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
154 for prop
, tab
in cls
.properties
.items():
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(',')
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
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.
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
)
194 #Ok, I do not have prop, Maybe my templates do?
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
)
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
)
208 #Not event a plus... so 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
)
218 if prop
not in self
.customs
:
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
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
]
239 def has_plus(self
, prop
):
247 def get_all_plus_and_delete(self
):
249 props
= self
.plus
.keys() #we delete entries, so no for ... in ...
251 res
[prop
] = self
.get_plus_and_delete(prop
)
255 def get_plus_and_delete(self
, prop
):
256 val
= self
.plus
[prop
]
261 #Check is required prop are set:
262 #template are always correct
263 def is_correct(self
):
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
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,
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
):
294 for dt
in self
.downtimes
:
295 if dt
.id == downtime_id
:
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
):
308 for c
in self
.comments
:
309 if c
.id == comment_id
:
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
:
319 self
.create_notifications('ACKNOWLEDGEMENT')
320 self
.problem_has_been_acknowledged
= True
325 a
= Acknowledge(self
, sticky
, notify
, persistent
, author
, comment
)
326 self
.acknowledgement
= a
327 if self
.my_type
== 'host':
331 c
= Comment(self
, persistent
, author
, comment
,
332 comment_type
, 4, 0, False, 0)
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
):
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
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
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
):
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
)
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
):
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
):
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
)
432 #Get a brok with update item status
433 def get_update_status_brok(self
):
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
)
443 #Get a brok with check_result
444 def get_check_result_brok(self
):
446 my_type
= cls
.my_type
449 self
.fill_data_brok_from(data
, 'check_result')
450 b
= Brok(my_type
+'_check_result', data
)
454 #Get brok about the new schedule (next_check)
455 def get_next_schedule_brok(self
):
457 my_type
= cls
.my_type
460 self
.fill_data_brok_from(data
, 'next_schedule')
461 b
= Brok(my_type
+'_next_schedule', data
)
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()
470 if hasattr(self
, 'poller_tag'):
471 cmdCall
= CommandCall(commands
, command
,
472 poller_tag
=self
.poller_tag
)
474 cmdCall
= CommandCall(commands
, command
)
475 setattr(self
, prop
, cmdCall
)
477 setattr(self
, prop
, None)
482 def __init__(self
, items
):
484 self
.configuration_warnings
= []
485 self
.configuration_errors
= []
493 return self
.items
.itervalues()
497 return len(self
.items
)
500 def __delitem__(self
, 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
= {}
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
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
]
534 else: #ok, an early ask, with no reversed list from now...
535 name_property
= self
.__class
__.name_property
537 if hasattr(i
, name_property
):
538 i_name
= getattr(i
, name_property
)
544 def find_by_name(self
, name
):
545 id = self
.find_id_by_name(name
)
547 return self
.items
[id]
553 for id in self
.items
:
554 self
.items
[id].pythonize()
557 def create_tpl_list(self
):
558 for id in self
.items
:
561 self
.templates
[id] = i
564 def find_tpl_by_name(self
, name
):
565 for id in self
.templates
:
567 if hasattr(i
, 'name') and i
.name
== name
:
572 def linkify_templates(self
):
573 #First we create a list of all templates
574 self
.create_tpl_list()
576 tpls
= i
.get_templates()
579 t
= self
.find_tpl_by_name(tpl
.strip())
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...
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
:
598 print "Error: the", i
.__class
__.my_type
, i
.get_name(), "is duplicated"
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
:
604 for err
in self
.configuration_errors
:
608 #Then look for individual ok
614 #We remove useless properties and templates
615 def clean_useless(self
):
617 tpls
= [id for id in self
.items
if self
.items
[id].is_tpl()]
620 #Ok now delete useless in items
626 except AttributeError:
631 #If a prop is absent and is not required, put the default value
632 def fill_default(self
):
640 for id in self
.items
:
641 s
= s
+ str(cls
) + ':' + str(id) + str(self
.items
[id]) + '\n'
645 # Inheritance forjust a property
646 def apply_partial_inheritance(self
, prop
):
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
)
658 i
.get_customs_properties_by_inheritance(self
)
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
:
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
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
):
687 if hasattr(i
, 'contacts'):
688 contacts_tab
= i
.contacts
.split(',')
689 contacts_tab
= strip_and_uniq(contacts_tab
)
691 for c_name
in contacts_tab
:
693 c
= contacts
.find_by_name(c_name
)
695 new_contacts
.append(c
)
696 # Else : Add in the errors tab.
697 # 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 an object and its escalations
706 def linkify_with_escalations(self
, escalations
):
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
)
713 for es_name
in escalations_tab
:
714 es
= escalations
.find_by_name(es_name
)
716 new_escalations
.append(es
)
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
):
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
)
733 new_resultmodulations
.append(rm
)
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
):
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
)
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
)
753 cnames
= contactgroups
.get_members_by_name(cgname
)
754 #We add contacts into our contacts
756 if hasattr(i
, 'contacts'):
757 i
.contacts
+= ','+cnames
761 #Link a timeperiod property (prop)
762 def linkify_with_timeperiods(self
, timeperiods
, prop
):
765 tpname
= getattr(i
, prop
)
766 tp
= timeperiods
.find_by_name(tpname
)
771 #Link one command property
772 def linkify_one_command_with_commands(self
, commands
, prop
):
775 command
= getattr(i
, prop
).strip()
777 if hasattr(i
, 'poller_tag'):
778 cmdCall
= CommandCall(commands
, command
,
779 poller_tag
=i
.poller_tag
)
781 cmdCall
= CommandCall(commands
, command
)
783 setattr(i
, prop
, cmdCall
)
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
):
792 coms
= getattr(i
, prop
).split(',')
793 coms
= strip_and_uniq(coms
)
797 if hasattr(i
, 'poller_tag'):
798 cmdCall
= CommandCall(commands
, com
,
799 poller_tag
=i
.poller_tag
)
801 cmdCall
= CommandCall(commands
, com
)
803 com_list
.append(cmdCall
)
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')]
816 def evaluate_hostgroup_expression(self
, expr
, hosts
, hostgroups
):
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
825 expr
= expr
.replace('-', 'MINUSSIGN')
827 # ! (not) should be changed as "ALL-" (all but not...)
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
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!)
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
853 if 'MINUSSIGN' in token
:
854 tmp_token
= token
.replace('MINUSSIGN', '-')
855 members
= hostgroups
.get_members_by_name(tmp_token
)
857 members
= hostgroups
.get_members_by_name(token
)
860 #print "Get members", members
861 elts
= members
.split(',')
862 elts
= strip_and_uniq(elts
)
864 #print "Elements:", elts
865 #print "Now set in locals the token new values"
866 locals()[token
.upper()] = elts
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
,)
873 self
.configuration_errors
.append(err
)
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
882 evaluation
= eval(expr
)
884 print "The syntax of %s is invalid" % original_expr
887 print "There is a unknow name in %s" % original_expr
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
895 for part
in evaluation
:
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
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
):
910 if hasattr(i
, 'hostgroup_name'):
911 hnames
= self
.evaluate_hostgroup_expression(i
.hostgroup_name
,
914 if hasattr(i
, 'host_name'):
915 i
.host_name
+= ',' + str(hnames
)
917 i
.host_name
= str(hnames
)