2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
7 #This file is part of Shinken.
9 #Shinken is free software: you can redistribute it and/or modify
10 #it under the terms of the GNU Affero General Public License as published by
11 #the Free Software Foundation, either version 3 of the License, or
12 #(at your option) any later version.
14 #Shinken is distributed in the hope that it will be useful,
15 #but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 #GNU Affero General Public License for more details.
19 #You should have received a copy of the GNU Affero General Public License
20 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
23 """ This class is a base class for nearly all configuration
24 elements like service, hosts or contacts.
27 #from command import CommandCall
28 #from util import to_int, to_char, to_split, to_bool
31 from shinken
.brok
import Brok
32 from shinken
.util
import strip_and_uniq
33 from shinken
.command
import CommandCall
34 from shinken
.acknowledge
import Acknowledge
35 from shinken
.comment
import Comment
38 def __init__(self
, params
={}):
39 #We have our own id of My Class type :)
40 #use set attr for going into the slots
41 #instead of __dict__ :)
42 setattr(self
, 'id', self
.__class
__.id)
43 self
.__class
__.id += 1
46 self
.customs
= {} # for custom variables
47 self
.plus
= {} # for value with a +
50 #adding running properties like latency, dependency list, etc
51 for prop
in cls
.running_properties
:
52 #Copy is slow, so we check type
53 #Type with __iter__ are list or dict, or tuple.
54 #Item need it's own list, so qe copy
55 val
= cls
.running_properties
[prop
].default
56 if hasattr(val
, '__iter__'):
57 setattr(self
, prop
, copy(val
))
59 setattr(self
, prop
, val
)
60 #eatch istance to have his own running prop!
62 #[0] = + -> new key-plus
63 #[0] = _ -> new custom entry in UPPER case
65 if len(params
[key
]) >= 1 and params
[key
][0] == '+':
66 #Special case : a _MACRO can be a plus. so add to plus
67 #but upper the key for the macro name
69 self
.plus
[key
.upper()] = params
[key
][1:] # we remove the +
71 self
.plus
[key
] = params
[key
][1:] # we remove the +
73 custom_name
= key
.upper()
74 self
.customs
[custom_name
] = params
[key
]
76 setattr(self
, key
, params
[key
])
79 #return a copy of the item, but give him a new id
81 """ Copy a copy of the item, but give him a new id """
83 i
= cls({})#Dummy item but with it's own running properties
84 properties
= cls
.properties
85 for prop
in properties
:
86 if hasattr(self
, prop
):
87 val
= getattr(self
, prop
)
97 return str(self
.__dict
__)+'\n'
101 """ Return if the elements is a template """
103 return self
.register
== '0'
109 #def has(self, prop):
110 # return hasattr(self, prop)
113 #If a prop is absent and is not required, put the default value
114 def fill_default(self
):
115 """ Fill missing properties if they are missing """
117 properties
= cls
.properties
119 for prop
in properties
:
120 if not hasattr(self
, prop
) and properties
[prop
].has_default
:
121 value
= properties
[prop
].default
122 setattr(self
, prop
, value
)
125 #We load every usefull parameter so no need to access global conf later
126 #Must be called after a change in a gloabl conf parameter
127 def load_global_conf(cls
, conf
):
128 """ Used to put global values in the sub Class like
129 hosts ro services """
130 #conf have properties, if 'enable_notifications' :
131 #{ [...] 'class_inherit' : [(Host, None), (Service, None),
133 #get the name and put the value if None, put the Name
134 #(not None) if not (not clear ?)
135 for prop
in conf
.properties
:
136 #If we have a class_inherit, and the arbtier really send us it
137 # if 'class_inherit' in conf.properties[prop] and hasattr(conf, prop):
138 if hasattr(conf
, prop
):
139 entry
= conf
.properties
[prop
].class_inherit
140 for (cls_dest
, change_name
) in entry
:
141 if cls_dest
== cls
:#ok, we've got something to get
142 value
= getattr(conf
, prop
)
143 if change_name
is None:
144 setattr(cls
, prop
, value
)
146 setattr(cls
, change_name
, value
)
148 #Make this method a classmethod
149 load_global_conf
= classmethod(load_global_conf
)
152 #Use to make python properties
155 for prop
in cls
.properties
:
156 tab
= cls
.properties
[prop
]
158 # if isinstance(tab, dict):
159 # if 'pythonize' in tab:
160 # f = tab['pythonize']
161 # old_val = getattr(self, prop)
162 # new_val = f(old_val)
163 # #print "Changing ", old_val, "by", new_val
164 # setattr(self, prop, new_val)
165 # else: #new style for service
166 new_val
= tab
.pythonize(getattr(self
, prop
))
167 setattr(self
, prop
, new_val
)
168 except AttributeError , exp
:
169 #print self.get_name(), ' : ', exp
170 pass # Will be catch at the is_correct moment
173 def get_templates(self
):
174 if hasattr(self
, 'use'):
175 return self
.use
.split(',')
180 #We fillfull properties with template ones if need
181 def get_property_by_inheritance(self
, items
, prop
):
182 #If I have the prop, I take mine but I check if I must
184 if hasattr(self
, prop
):
185 value
= getattr(self
, prop
)
186 # Maybe this value is 'null'. If so, we should NOT inherit
187 # and just delete this entry, and hope of course.
191 # Manage the additive inheritance for the property,
192 # if property is in plus, add or replace it
193 if self
.has_plus(prop
):
194 value
+= ',' + self
.get_plus_and_delete(prop
)
196 #Ok, I do not have prop, Maybe my templates do?
198 for i
in self
.templates
:
199 value
= i
.get_property_by_inheritance(items
, prop
)
200 if value
is not None:
201 if self
.has_plus(prop
):
202 value
+= ','+self
.get_plus_and_delete(prop
)
203 setattr(self
, prop
, value
)
205 #I do not have prop, my templates too... Maybe a plus?
206 if self
.has_plus(prop
):
207 value
= self
.get_plus_and_delete(prop
)
208 setattr(self
, prop
, value
)
210 #Not event a plus... so None :)
214 #We fillfull properties with template ones if need
215 def get_customs_properties_by_inheritance(self
, items
):
216 for i
in self
.templates
:
217 tpl_cv
= i
.get_customs_properties_by_inheritance(items
)
220 if prop
not in self
.customs
:
223 value
= self
.customs
[prop
]
224 if self
.has_plus(prop
):
225 value
= value
+self
.get_plus_and_delete(prop
)
226 self
.customs
[prop
] = value
227 for prop
in self
.customs
:
228 value
= self
.customs
[prop
]
229 if self
.has_plus(prop
):
230 value
= value
= value
+','+self
.get_plus_and_delete(prop
)
231 self
.customs
[prop
] = value
232 #We can get custom properties in plus, we need to get all
235 cust_in_plus
= self
.get_all_plus_and_delete()
236 for prop
in cust_in_plus
:
237 self
.customs
[prop
] = cust_in_plus
[prop
]
241 def has_plus(self
, prop
):
249 def get_all_plus_and_delete(self
):
251 props
= self
.plus
.keys() #we delete entries, so no for ... in ...
253 res
[prop
] = self
.get_plus_and_delete(prop
)
257 def get_plus_and_delete(self
, prop
):
258 val
= self
.plus
[prop
]
263 #Check is required prop are set:
264 #template are always correct
265 def is_correct(self
):
267 properties
= self
.__class
__.properties
268 for prop
in properties
:
269 if not hasattr(self
, prop
) and properties
[prop
].required
:
270 print self
.get_name(), "missing property :", prop
275 #This function is used by service and hosts
276 #to transform Nagios2 parameters to Nagios3
277 #ones, like normal_check_interval to
278 #check_interval. There is a old_parameters tab
279 #in Classes taht give such modifications to do.
280 def old_properties_names_to_new(self
):
281 old_properties
= self
.__class
__.old_properties
282 for old_name
in old_properties
:
283 new_name
= old_properties
[old_name
]
284 #Ok, if we got old_name and NO new name,
286 if hasattr(self
, old_name
) and not hasattr(self
, new_name
):
287 value
= getattr(self
, old_name
)
288 setattr(self
, new_name
, value
)
291 def add_downtime(self
, downtime
):
292 self
.downtimes
.append(downtime
)
295 def del_downtime(self
, downtime_id
):
297 for dt
in self
.downtimes
:
298 if dt
.id == downtime_id
:
300 dt
.can_be_deleted
= True
301 if d_to_del
is not None:
302 self
.downtimes
.remove(d_to_del
)
305 def add_comment(self
, comment
):
306 self
.comments
.append(comment
)
309 def del_comment(self
, comment_id
):
311 for c
in self
.comments
:
312 if c
.id == comment_id
:
314 c
.can_be_deleted
= True
315 if c_to_del
is not None:
316 self
.comments
.remove(c_to_del
)
319 def acknowledge_problem(self
, sticky
, notify
, persistent
, author
, comment
):
320 if self
.state
!= self
.ok_up
:
322 self
.create_notifications('ACKNOWLEDGEMENT')
323 self
.problem_has_been_acknowledged
= True
328 a
= Acknowledge(self
, sticky
, notify
, persistent
, author
, comment
)
329 self
.acknowledgement
= a
330 if self
.my_type
== 'host':
334 c
= Comment(self
, persistent
, author
, comment
,
335 comment_type
, 4, 0, False, 0)
337 self
.broks
.append(self
.get_update_status_brok())
340 # Delete the acknowledgement object and reset the flag
341 # but do not remove the associated comment.
342 def unacknowledge_problem(self
):
343 if self
.problem_has_been_acknowledged
:
344 self
.problem_has_been_acknowledged
= False
345 #Should not be deleted, a None is Good
346 self
.acknowledgement
= None
347 #del self.acknowledgement
348 # find comments of non-persistent ack-comments and delete them too
349 for c
in self
.comments
:
350 if c
.entry_type
== 4 and not c
.persistent
:
351 self
.del_comment(c
.id)
352 self
.broks
.append(self
.get_update_status_brok())
355 # Check if we have an acknowledgement and if this is marked as sticky.
356 # This is needed when a non-ok state changes
357 def unacknowledge_problem_if_not_sticky(self
):
358 if hasattr(self
, 'acknowledgement') and self
.acknowledgement
!= None:
359 if not self
.acknowledgement
.sticky
:
360 self
.unacknowledge_problem()
363 #Will flatten some parameters taggued by the 'conf_send_preparation'
364 #property because they are too "linked" to be send like that (like realms)
365 def prepare_for_conf_sending(self
):
368 for prop
in cls
.properties
:
369 entry
= cls
.properties
[prop
]
370 #Is this property need preparation for sending?
371 if entry
.conf_send_preparation
is not None:
372 f
= entry
.conf_send_preparation
374 val
= f(getattr(self
, prop
))
375 setattr(self
, prop
, val
)
377 if hasattr(cls
, 'running_properties'):
378 for prop
in cls
.running_properties
:
379 entry
= cls
.running_properties
[prop
]
380 #Is this property need preparation for sending?
381 if entry
.conf_send_preparation
is not None:
382 f
= entry
.conf_send_preparation
384 val
= f(getattr(self
, prop
))
385 setattr(self
, prop
, val
)
390 #Get the property for an object, with good value
391 #and brok_transformation if need
392 def get_property_value_for_brok(self
, prop
, tab
):
394 #Get the current value, or the default if need
395 value
= getattr(self
, prop
, entry
.default
)
397 #Apply brok_transformation if need
398 # Look if we must preprocess the value first
399 pre_op
= entry
.brok_transformation
401 value
= pre_op(self
, value
)
406 #Fill data with info of item by looking at brok_type
407 #in props of properties or running_propterties
408 def fill_data_brok_from(self
, data
, brok_type
):
410 #Now config properties
411 for prop
in cls
.properties
:
412 #Is this property intended for brokking?
413 # if 'fill_brok' in cls.properties[prop]:
414 if brok_type
in cls
.properties
[prop
].fill_brok
:
415 data
[prop
] = self
.get_property_value_for_brok(prop
, cls
.properties
)
417 # Maybe the class do not have running_properties
418 if hasattr(cls
, 'running_properties'):
419 # We've got prop in running_properties too
420 for prop
in cls
.running_properties
:
421 # if 'fill_brok' in cls.running_properties[prop]:
422 if brok_type
in cls
.running_properties
[prop
].fill_brok
:
423 data
[prop
] = self
.get_property_value_for_brok(prop
, cls
.running_properties
)
426 #Get a brok with initial status
427 def get_initial_status_brok(self
):
429 my_type
= cls
.my_type
430 data
= {'id' : self
.id}
432 self
.fill_data_brok_from(data
, 'full_status')
433 b
= Brok('initial_'+my_type
+'_status', data
)
437 #Get a brok with update item status
438 def get_update_status_brok(self
):
440 my_type
= cls
.my_type
442 data
= {'id' : self
.id}
443 self
.fill_data_brok_from(data
, 'full_status')
444 b
= Brok('update_'+my_type
+'_status', data
)
448 #Get a brok with check_result
449 def get_check_result_brok(self
):
451 my_type
= cls
.my_type
454 self
.fill_data_brok_from(data
, 'check_result')
455 b
= Brok(my_type
+'_check_result', data
)
459 #Get brok about the new schedule (next_check)
460 def get_next_schedule_brok(self
):
462 my_type
= cls
.my_type
465 self
.fill_data_brok_from(data
, 'next_schedule')
466 b
= Brok(my_type
+'_next_schedule', data
)
470 #Link one command property to a class (for globals like oc*p_command)
471 def linkify_one_command_with_commands(self
, commands
, prop
):
472 if hasattr(self
, prop
):
473 command
= getattr(self
, prop
).strip()
475 if hasattr(self
, 'poller_tag'):
476 cmdCall
= CommandCall(commands
, command
,
477 poller_tag
=self
.poller_tag
)
479 cmdCall
= CommandCall(commands
, command
)
480 setattr(self
, prop
, cmdCall
)
482 setattr(self
, prop
, None)
487 def __init__(self
, items
):
489 self
.configuration_warnings
= []
490 self
.configuration_errors
= []
498 return self
.items
.itervalues()
502 return len(self
.items
)
505 def __delitem__(self
, key
):
509 def __setitem__(self
, key
, value
):
510 self
.items
[key
] = value
513 def __getitem__(self
, key
):
514 return self
.items
[key
]
517 #We create the reversed list so search will be faster
518 #We also create a twins list with id of twins (not the original
519 #just the others, higher twins)
520 def create_reversed_list(self
):
521 self
.reversed_list
= {}
523 name_property
= self
.__class
__.name_property
524 for id in self
.items
:
525 if hasattr(self
.items
[id], name_property
):
526 name
= getattr(self
.items
[id], name_property
)
527 if name
not in self
.reversed_list
:
528 self
.reversed_list
[name
] = id
530 self
.twins
.append(id)
533 def find_id_by_name(self
, name
):
534 if hasattr(self
, 'reversed_list'):
535 if name
in self
.reversed_list
:
536 return self
.reversed_list
[name
]
539 else: #ok, an early ask, with no reversed list from now...
540 name_property
= self
.__class
__.name_property
542 if hasattr(i
, name_property
):
543 i_name
= getattr(i
, name_property
)
549 def find_by_name(self
, name
):
550 id = self
.find_id_by_name(name
)
552 return self
.items
[id]
558 for id in self
.items
:
559 self
.items
[id].pythonize()
562 def create_tpl_list(self
):
563 for id in self
.items
:
566 self
.templates
[id] = i
569 def find_tpl_by_name(self
, name
):
570 for id in self
.templates
:
572 if hasattr(i
, 'name') and i
.name
== name
:
577 def linkify_templates(self
):
578 #First we create a list of all templates
579 self
.create_tpl_list()
581 tpls
= i
.get_templates()
584 t
= self
.find_tpl_by_name(tpl
.strip())
587 else: # not find? not good!
588 err
= "ERROR: the template '%s' defined for '%s' is unkown" % (tpl
, i
.get_name())
589 i
.configuration_errors
.append(err
)
590 i
.templates
= new_tpls
594 def is_correct(self
):
595 #we are ok at the begining. Hope we still ok at the end...
597 #Some class do not have twins, because they do not have names
598 #like servicedependancies
599 if hasattr(self
, 'twins'):
600 #Ok, look at no twins (it's bad!)
601 for id in self
.twins
:
603 print "Error: the", i
.__class
__.my_type
, i
.get_name(), "is duplicated"
605 #Then look if we have some errors in the conf
606 #Juts print warnings, but raise errors
607 for err
in self
.configuration_warnings
:
609 for err
in self
.configuration_errors
:
613 #Then look for individual ok
619 #We remove useless properties and templates
620 def clean_useless(self
):
622 tpls
= [id for id in self
.items
if self
.items
[id].is_tpl()]
625 #Ok now delete useless in items
631 except AttributeError:
636 #If a prop is absent and is not required, put the default value
637 def fill_default(self
):
645 for id in self
.items
:
646 s
= s
+ str(cls
) + ':' + str(id) + str(self
.items
[id]) + '\n'
650 # Inheritance forjust a property
651 def apply_partial_inheritance(self
, prop
):
653 i
.get_property_by_inheritance(self
, prop
)
656 def apply_inheritance(self
):
657 #We check for all Class properties if the host has it
658 #if not, it check all host templates for a value
659 cls
= self
.inner_class
660 properties
= cls
.properties
661 for prop
in properties
:
662 self
.apply_partial_inheritance(prop
)
664 i
.get_customs_properties_by_inheritance(self
)
668 #Remember: item id respect the order of conf. So if and item
669 # is defined multiple times,
670 #we want to keep the first one.
671 #Services are also managed here but they are specials:
672 #We remove twins services with the same host_name/service_description
673 #Remember: host service are take into account first before hostgroup service
674 #Id of host service are lower than hostgroups one, so they are
675 #in self.twins_services
676 #and we can remove them.
677 def remove_twins(self
):
678 for id in self
.twins
:
680 type = i
.__class
__.my_type
681 print 'Warning: the', type, i
.get_name(), 'is already defined.'
682 del self
.items
[id] #bye bye
683 #do not remove twins, we should look in it, but just void it
685 #del self.twins #no more need
689 #We've got a contacts property with , separated contacts names
690 #and we want have a list of Contacts
691 def linkify_with_contacts(self
, contacts
):
693 if hasattr(i
, 'contacts'):
694 contacts_tab
= i
.contacts
.split(',')
695 contacts_tab
= strip_and_uniq(contacts_tab
)
697 for c_name
in contacts_tab
:
699 c
= contacts
.find_by_name(c_name
)
701 new_contacts
.append(c
)
702 # Else : Add in the errors tab.
703 # will be raised at is_correct
705 err
= "ERROR: the contact '%s' defined for '%s' is unkown" % (c_name
, i
.get_name())
706 i
.configuration_errors
.append(err
)
707 # Get the list, but first make elements uniq
708 i
.contacts
= list(set(new_contacts
))
711 # Make link between an object and its escalations
712 def linkify_with_escalations(self
, escalations
):
714 if hasattr(i
, 'escalations'):
715 #print i.get_name(), 'going to link escalations', i.escalations
716 escalations_tab
= i
.escalations
.split(',')
717 escalations_tab
= strip_and_uniq(escalations_tab
)
719 for es_name
in escalations_tab
:
720 es
= escalations
.find_by_name(es_name
)
722 new_escalations
.append(es
)
725 i
.escalations
= new_escalations
726 #print i.get_name(), 'finallygot escalation', i.escalations
729 #Make link between item and it's resultmodulations
730 def linkify_with_resultmodulations(self
, resultmodulations
):
732 if hasattr(i
, 'resultmodulations'):
733 resultmodulations_tab
= i
.resultmodulations
.split(',')
734 resultmodulations_tab
= strip_and_uniq(resultmodulations_tab
)
735 new_resultmodulations
= []
736 for rm_name
in resultmodulations_tab
:
737 rm
= resultmodulations
.find_by_name(rm_name
)
739 new_resultmodulations
.append(rm
)
742 i
.resultmodulations
= new_resultmodulations
745 #If we've got a contact_groups properties, we search for all
746 #theses groups and ask them their contacts, and then add them
747 #all into our contacts property
748 def explode_contact_groups_into_contacts(self
, contactgroups
):
750 if hasattr(i
, 'contact_groups'):
751 cgnames
= i
.contact_groups
.split(',')
752 cgnames
= strip_and_uniq(cgnames
)
753 for cgname
in cgnames
:
754 cg
= contactgroups
.find_by_name(cgname
)
756 err
= "The contact group '%s'defined on the %s '%s' do not exist" % (cgname
, i
.__class
__.my_type
, i
.get_name())
757 i
.configuration_errors
.append(err
)
759 cnames
= contactgroups
.get_members_by_name(cgname
)
760 #We add contacts into our contacts
762 if hasattr(i
, 'contacts'):
763 i
.contacts
+= ','+cnames
767 #Link a timeperiod property (prop)
768 def linkify_with_timeperiods(self
, timeperiods
, prop
):
771 tpname
= getattr(i
, prop
)
772 tp
= timeperiods
.find_by_name(tpname
)
777 #Link one command property
778 def linkify_one_command_with_commands(self
, commands
, prop
):
781 command
= getattr(i
, prop
).strip()
783 if hasattr(i
, 'poller_tag'):
784 cmdCall
= CommandCall(commands
, command
,
785 poller_tag
=i
.poller_tag
)
787 cmdCall
= CommandCall(commands
, command
)
789 setattr(i
, prop
, cmdCall
)
791 setattr(i
, prop
, None)
794 #Link a command list (commands with , between) in real CommandCalls
795 def linkify_command_list_with_commands(self
, commands
, prop
):
798 coms
= getattr(i
, prop
).split(',')
799 coms
= strip_and_uniq(coms
)
803 if hasattr(i
, 'poller_tag'):
804 cmdCall
= CommandCall(commands
, com
,
805 poller_tag
=i
.poller_tag
)
807 cmdCall
= CommandCall(commands
, com
)
809 com_list
.append(cmdCall
)
812 setattr(i
, prop
, com_list
)
815 #Return a set with ALL hosts (used in ! expressions)
816 def get_all_host_names_set(self
, hosts
):
817 hnames
= [h
.host_name
for h
in hosts
.items
.values()
818 if hasattr(h
, 'host_name')]
822 def evaluate_hostgroup_expression(self
, expr
, hosts
, hostgroups
):
825 #print "I'm trying to prepare the expression", expr
827 #We've got problem with the "-" sign. It can be in a
828 #valid name but it's a sign of difference for sets
829 #so we change the - now by something, then we reverse after
831 expr
= expr
.replace('-', 'MINUSSIGN')
833 # ! (not) should be changed as "ALL-" (all but not...)
835 ALLELEMENTS
= self
.get_all_host_names_set(hosts
)
836 #print "Changing ! by ALLELEMENTS- in ", expr
837 expr
= expr
.replace('!', 'ALLELEMENTS-')
839 # We change all separaton token by 10 spaces
840 # (so names can still have some spaces
841 # on them like Europe Servers because we wil cut byy this 10spaces after
843 for c
in ['|', '&', '(', ')', ',', '-']:
844 strip_expr
= strip_expr
.replace(c
, ' '*10)
845 #print "Stripped expression:", strip_expr
847 tokens
= strip_expr
.split(' '*10)
848 # Strip and non void token
849 tokens
= [token
.strip() for token
in tokens
if token
!= '']
850 #print "Tokens:", tokens
852 #Now add in locals() dict (yes, real variables!)
854 #ALLELEMENTS is a private group for us
855 if token
!= 'ALLELEMENTS':
856 #Maybe the token was with - at the begining,
857 #but we change all by "MINUSSIGN". We must change it back now
859 if 'MINUSSIGN' in token
:
860 tmp_token
= token
.replace('MINUSSIGN', '-')
861 members
= hostgroups
.get_members_by_name(tmp_token
)
863 members
= hostgroups
.get_members_by_name(token
)
866 #print "Get members", members
867 elts
= members
.split(',')
868 elts
= strip_and_uniq(elts
)
870 #print "Elements:", elts
871 #print "Now set in locals the token new values"
872 locals()[token
.upper()] = elts
874 if 'MINUSSIGN' in token
:
875 token
= token
.replace('MINUSSIGN', '-')
876 print self
.__dict
__, type(self
)
877 err
= "ERROR: the group %s is unknown !" % (token
,)
879 self
.configuration_errors
.append(err
)
882 # Now changing the exprtoken value with
883 # UPPER one (so less risk of problem...
884 expr
= expr
.replace(token
, token
.upper())
886 #print "Final expression:", expr
888 evaluation
= eval(expr
)
890 print "The syntax of %s is invalid" % original_expr
893 print "There is a unknow name in %s" % original_expr
895 #print "Evaluation :", evaluation
897 # In evaluation we can have multiples values because
898 # of , (so it make a tuple in fact)
899 # we must OR them in the result
901 for part
in evaluation
:
903 res
.extend(list(part
))
904 else:#no , so we do not have a tuple but a simple uniq set
905 res
.extend(list(evaluation
))
906 res_string
= ','.join(res
)
907 #print "Final resolution is", res_string
911 #If we've got a hostgroup_name property, we search for all
912 #theses groups and ask them their hosts, and then add them
913 #all into our host_name property
914 def explode_host_groups_into_hosts(self
, hosts
, hostgroups
):
916 if hasattr(i
, 'hostgroup_name'):
917 hnames
= self
.evaluate_hostgroup_expression(i
.hostgroup_name
,
920 if hasattr(i
, 'host_name'):
921 i
.host_name
+= ',' + str(hnames
)
923 i
.host_name
= str(hnames
)