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
27 from util
import strip_and_uniq
28 from command
import CommandCall
29 from acknowledge
import Acknowledge
30 from comment
import Comment
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 +
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
))
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
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
64 self
.plus
[key
.upper()] = params
[key
][1:] # we remove the +
66 self
.plus
[key
] = params
[key
][1:] # we remove the +
68 custom_name
= key
.upper()
69 self
.customs
[custom_name
] = params
[key
]
71 setattr(self
, key
, params
[key
])
74 #return a copy of the item, but give him a new id
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
)
91 return str(self
.__dict
__)+'\n'
96 return self
.register
== '0'
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
):
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),
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
)
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
144 for prop
in cls
.properties
:
146 tab
= cls
.properties
[prop
]
147 if 'pythonize' in tab
:
149 old_val
= getattr(self
, prop
)
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(',')
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
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
)
176 #Ok, I do not have prop, Maybe my templates do?
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
)
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
)
190 #Not event a plus... so 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
)
200 if prop
not in self
.customs
:
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
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
]
221 def has_plus(self
, prop
):
229 def get_all_plus_and_delete(self
):
231 props
= self
.plus
.keys() #we delete entries, so no for ... in ...
233 res
[prop
] = self
.get_plus_and_delete(prop
)
237 def get_plus_and_delete(self
, prop
):
238 val
= self
.plus
[prop
]
243 #Check is required prop are set:
244 #template are always correct
245 def is_correct(self
):
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
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,
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
):
279 for dt
in self
.downtimes
:
280 if dt
.id == downtime_id
:
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
):
293 for c
in self
.comments
:
294 if c
.id == comment_id
:
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
:
304 self
.create_notifications('ACKNOWLEDGEMENT')
305 self
.problem_has_been_acknowledged
= True
310 a
= Acknowledge(self
, sticky
, notify
, persistent
, author
, comment
)
311 self
.acknowledgement
= a
312 if self
.my_type
== 'host':
316 c
= Comment(self
, persistent
, author
, comment
, comment_type
, 4, 0, False, 0)
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
):
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']
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']
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
):
376 if 'brok_transformation' in entry
:
377 pre_op
= entry
['brok_transformation']
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
388 value
= pre_op(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
):
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
):
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
, poller_tag
=self
.poller_tag
)
473 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
:
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
621 #TODO : move into item class
627 except AttributeError:
632 #If a prop is absent and is not required, put the default value
633 def fill_default(self
):
641 for id in self
.items
:
642 s
= s
+ str(cls
) + ':' + str(id) + str(self
.items
[id]) + '\n'
646 #Inheritance forjust a property
647 def apply_partial_inheritance(self
, prop
):
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
)
660 i
.get_customs_properties_by_inheritance(self
)
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
:
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
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
):
689 if hasattr(i
, 'contacts'):
690 contacts_tab
= i
.contacts
.split(',')
691 contacts_tab
= strip_and_uniq(contacts_tab
)
693 for c_name
in contacts_tab
:
695 c
= contacts
.find_by_name(c_name
)
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
):
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
, poller_tag
=i
.poller_tag
)
780 cmdCall
= CommandCall(commands
, command
)
782 setattr(i
, prop
, cmdCall
)
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
):
791 coms
= getattr(i
, prop
).split(',')
792 coms
= strip_and_uniq(coms
)
796 if hasattr(i
, 'poller_tag'):
797 cmdCall
= CommandCall(commands
, com
, poller_tag
=i
.poller_tag
)
799 cmdCall
= CommandCall(commands
, com
)
801 com_list
.append(cmdCall
)
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')]
813 def evaluate_hostgroup_expression(self
, expr
, hosts
, hostgroups
):
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
822 expr
= expr
.replace('-', 'MINUSSIGN')
824 #! (not) should be changed as "ALL-" (all but not...)
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
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!)
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
852 if 'MINUSSIGN' in token
:
853 tmp_token
= token
.replace('MINUSSIGN', '-')
854 members
= hostgroups
.get_members_by_name(tmp_token
)
856 members
= hostgroups
.get_members_by_name(token
)
859 #print "Get members", members
860 elts
= members
.split(',')
861 elts
= strip_and_uniq(elts
)
863 #print "Elements:", elts
864 #print "Now set in locals the token new values"
865 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 #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
881 evaluation
= eval(expr
)
883 print "The syntax of %s is invalid" % original_expr
886 print "There is a unknow name in %s" % original_expr
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
893 for part
in evaluation
:
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
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
):
908 if hasattr(i
, 'hostgroup_name'):
909 hnames
= self
.evaluate_hostgroup_expression(i
.hostgroup_name
, hosts
, hostgroups
)
911 if hasattr(i
, 'host_name'):
912 i
.host_name
+= ',' + str(hnames
)
914 i
.host_name
= str(hnames
)