Add : discoveryrun definition and linkink for commands.
[shinken.git] / shinken / objects / item.py
blobc6fa567c84445cea9481dd254852a7898418bc39
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 #If a prop is absent and is not required, put the default value
111 def fill_default(self):
112 """ Fill missing properties if they are missing """
113 cls = self.__class__
115 for prop, entry in cls.properties.items():
116 if not hasattr(self, prop) and entry.has_default:
117 setattr(self, prop, entry.default)
120 #We load every usefull parameter so no need to access global conf later
121 #Must be called after a change in a gloabl conf parameter
122 def load_global_conf(cls, conf):
123 """ Used to put global values in the sub Class like
124 hosts ro services """
125 #conf have properties, if 'enable_notifications' :
126 #{ [...] 'class_inherit' : [(Host, None), (Service, None),
127 # (Contact, None)]}
128 #get the name and put the value if None, put the Name
129 #(not None) if not (not clear ?)
130 for prop, entry in conf.properties.items():
131 #If we have a class_inherit, and the arbtier really send us it
132 # if 'class_inherit' in entry and hasattr(conf, prop):
133 if hasattr(conf, prop):
134 for (cls_dest, change_name) in entry.class_inherit:
135 if cls_dest == cls:#ok, we've got something to get
136 value = getattr(conf, prop)
137 if change_name is None:
138 setattr(cls, prop, value)
139 else:
140 setattr(cls, change_name, value)
142 #Make this method a classmethod
143 load_global_conf = classmethod(load_global_conf)
146 #Use to make python properties
147 def pythonize(self):
148 cls = self.__class__
149 for prop, tab in cls.properties.items():
150 try:
151 # if isinstance(tab, dict):
152 # if 'pythonize' in tab:
153 # f = tab['pythonize']
154 # old_val = getattr(self, prop)
155 # new_val = f(old_val)
156 # #print "Changing ", old_val, "by", new_val
157 # setattr(self, prop, new_val)
158 # else: #new style for service
159 new_val = tab.pythonize(getattr(self, prop))
160 setattr(self, prop, new_val)
161 except AttributeError , exp:
162 #print self.get_name(), ' : ', exp
163 pass # Will be catch at the is_correct moment
166 def get_templates(self):
167 if hasattr(self, 'use'):
168 return self.use.split(',')
169 else:
170 return []
173 #We fillfull properties with template ones if need
174 def get_property_by_inheritance(self, items, prop):
175 #If I have the prop, I take mine but I check if I must
176 #add a plus porperty
177 if hasattr(self, prop):
178 value = getattr(self, prop)
179 # Maybe this value is 'null'. If so, we should NOT inherit
180 # and just delete this entry, and hope of course.
181 if value == 'null':
182 delattr(self, prop)
183 return None
184 # Manage the additive inheritance for the property,
185 # if property is in plus, add or replace it
186 if self.has_plus(prop):
187 value += ',' + self.get_plus_and_delete(prop)
188 return value
189 #Ok, I do not have prop, Maybe my templates do?
190 #Same story for plus
191 for i in self.templates:
192 value = i.get_property_by_inheritance(items, prop)
193 if value is not None:
194 if self.has_plus(prop):
195 value += ','+self.get_plus_and_delete(prop)
196 setattr(self, prop, value)
197 return value
198 #I do not have prop, my templates too... Maybe a plus?
199 if self.has_plus(prop):
200 value = self.get_plus_and_delete(prop)
201 setattr(self, prop, value)
202 return value
203 #Not event a plus... so None :)
204 return None
207 #We fillfull properties with template ones if need
208 def get_customs_properties_by_inheritance(self, items):
209 for i in self.templates:
210 tpl_cv = i.get_customs_properties_by_inheritance(items)
211 if tpl_cv is not {}:
212 for prop in tpl_cv:
213 if prop not in self.customs:
214 value = tpl_cv[prop]
215 else:
216 value = self.customs[prop]
217 if self.has_plus(prop):
218 value = value+self.get_plus_and_delete(prop)
219 self.customs[prop] = value
220 for prop in self.customs:
221 value = self.customs[prop]
222 if self.has_plus(prop):
223 value = value = value+','+self.get_plus_and_delete(prop)
224 self.customs[prop] = value
225 #We can get custom properties in plus, we need to get all
226 #entires and put
227 #them into customs
228 cust_in_plus = self.get_all_plus_and_delete()
229 for prop in cust_in_plus:
230 self.customs[prop] = cust_in_plus[prop]
231 return self.customs
234 def has_plus(self, prop):
235 try:
236 self.plus[prop]
237 except:
238 return False
239 return True
242 def get_all_plus_and_delete(self):
243 res = {}
244 props = self.plus.keys() #we delete entries, so no for ... in ...
245 for prop in props:
246 res[prop] = self.get_plus_and_delete(prop)
247 return res
250 def get_plus_and_delete(self, prop):
251 val = self.plus[prop]
252 del self.plus[prop]
253 return val
256 #Check is required prop are set:
257 #template are always correct
258 def is_correct(self):
259 state = True
260 properties = self.__class__.properties
261 for prop, entry in properties.items():
262 if not hasattr(self, prop) and entry.required:
263 print self.get_name(), "missing property :", prop
264 state = False
265 return state
268 #This function is used by service and hosts
269 #to transform Nagios2 parameters to Nagios3
270 #ones, like normal_check_interval to
271 #check_interval. There is a old_parameters tab
272 #in Classes taht give such modifications to do.
273 def old_properties_names_to_new(self):
274 old_properties = self.__class__.old_properties
275 for old_name, new_name in old_properties.items():
276 #Ok, if we got old_name and NO new name,
277 #we switch the name
278 if hasattr(self, old_name) and not hasattr(self, new_name):
279 value = getattr(self, old_name)
280 setattr(self, new_name, value)
283 def add_downtime(self, downtime):
284 self.downtimes.append(downtime)
287 def del_downtime(self, downtime_id):
288 d_to_del = None
289 for dt in self.downtimes:
290 if dt.id == downtime_id:
291 d_to_del = dt
292 dt.can_be_deleted = True
293 if d_to_del is not None:
294 self.downtimes.remove(d_to_del)
297 def add_comment(self, comment):
298 self.comments.append(comment)
301 def del_comment(self, comment_id):
302 c_to_del = None
303 for c in self.comments:
304 if c.id == comment_id:
305 c_to_del = c
306 c.can_be_deleted = True
307 if c_to_del is not None:
308 self.comments.remove(c_to_del)
311 def acknowledge_problem(self, sticky, notify, persistent, author, comment):
312 if self.state != self.ok_up:
313 if notify:
314 self.create_notifications('ACKNOWLEDGEMENT')
315 self.problem_has_been_acknowledged = True
316 if sticky == 2:
317 sticky = True
318 else:
319 sticky = False
320 a = Acknowledge(self, sticky, notify, persistent, author, comment)
321 self.acknowledgement = a
322 if self.my_type == 'host':
323 comment_type = 1
324 else :
325 comment_type = 2
326 c = Comment(self, persistent, author, comment,
327 comment_type, 4, 0, False, 0)
328 self.add_comment(c)
329 self.broks.append(self.get_update_status_brok())
332 # Delete the acknowledgement object and reset the flag
333 # but do not remove the associated comment.
334 def unacknowledge_problem(self):
335 if self.problem_has_been_acknowledged:
336 self.problem_has_been_acknowledged = False
337 #Should not be deleted, a None is Good
338 self.acknowledgement = None
339 #del self.acknowledgement
340 # find comments of non-persistent ack-comments and delete them too
341 for c in self.comments:
342 if c.entry_type == 4 and not c.persistent:
343 self.del_comment(c.id)
344 self.broks.append(self.get_update_status_brok())
347 # Check if we have an acknowledgement and if this is marked as sticky.
348 # This is needed when a non-ok state changes
349 def unacknowledge_problem_if_not_sticky(self):
350 if hasattr(self, 'acknowledgement') and self.acknowledgement is not None:
351 if not self.acknowledgement.sticky:
352 self.unacknowledge_problem()
355 #Will flatten some parameters taggued by the 'conf_send_preparation'
356 #property because they are too "linked" to be send like that (like realms)
357 def prepare_for_conf_sending(self):
358 cls = self.__class__
360 for prop, entry in cls.properties.items():
361 #Is this property need preparation for sending?
362 if entry.conf_send_preparation is not None:
363 f = entry.conf_send_preparation
364 if f is not None:
365 val = f(getattr(self, prop))
366 setattr(self, prop, val)
368 if hasattr(cls, 'running_properties'):
369 for prop, entry in cls.running_properties.items():
370 #Is this property need preparation for sending?
371 if entry.conf_send_preparation is not None:
372 f = entry.conf_send_preparation
373 if f is not None:
374 val = f(getattr(self, prop))
375 setattr(self, prop, val)
380 #Get the property for an object, with good value
381 #and brok_transformation if need
382 def get_property_value_for_brok(self, prop, tab):
383 entry = tab[prop]
384 #Get the current value, or the default if need
385 value = getattr(self, prop, entry.default)
387 #Apply brok_transformation if need
388 # Look if we must preprocess the value first
389 pre_op = entry.brok_transformation
390 if pre_op is not None:
391 value = pre_op(self, value)
393 return value
396 #Fill data with info of item by looking at brok_type
397 #in props of properties or running_propterties
398 def fill_data_brok_from(self, data, brok_type):
399 cls = self.__class__
400 #Now config properties
401 for prop, entry in cls.properties.items():
402 #Is this property intended for brokking?
403 # if 'fill_brok' in cls.properties[prop]:
404 if brok_type in entry.fill_brok:
405 data[prop] = self.get_property_value_for_brok(prop, cls.properties)
407 # Maybe the class do not have running_properties
408 if hasattr(cls, 'running_properties'):
409 # We've got prop in running_properties too
410 for prop, entry in cls.running_properties.items():
411 # if 'fill_brok' in cls.running_properties[prop]:
412 if brok_type in entry.fill_brok:
413 data[prop] = self.get_property_value_for_brok(prop, cls.running_properties)
416 #Get a brok with initial status
417 def get_initial_status_brok(self):
418 cls = self.__class__
419 my_type = cls.my_type
420 data = {'id' : self.id}
422 self.fill_data_brok_from(data, 'full_status')
423 b = Brok('initial_'+my_type+'_status', data)
424 return b
427 #Get a brok with update item status
428 def get_update_status_brok(self):
429 cls = self.__class__
430 my_type = cls.my_type
432 data = {'id' : self.id}
433 self.fill_data_brok_from(data, 'full_status')
434 b = Brok('update_'+my_type+'_status', data)
435 return b
438 #Get a brok with check_result
439 def get_check_result_brok(self):
440 cls = self.__class__
441 my_type = cls.my_type
443 data = {}
444 self.fill_data_brok_from(data, 'check_result')
445 b = Brok(my_type+'_check_result', data)
446 return b
449 #Get brok about the new schedule (next_check)
450 def get_next_schedule_brok(self):
451 cls = self.__class__
452 my_type = cls.my_type
454 data = {}
455 self.fill_data_brok_from(data, 'next_schedule')
456 b = Brok(my_type+'_next_schedule', data)
457 return b
460 #Link one command property to a class (for globals like oc*p_command)
461 def linkify_one_command_with_commands(self, commands, prop):
462 if hasattr(self, prop):
463 command = getattr(self, prop).strip()
464 if command != '':
465 if hasattr(self, 'poller_tag'):
466 cmdCall = CommandCall(commands, command,
467 poller_tag=self.poller_tag)
468 elif hasattr(self, 'reactionner_tag'):
469 cmdCall = CommandCall(commands, command,
470 reactionner_tag=self.reactionner_tag)
471 else:
472 cmdCall = CommandCall(commands, command)
473 setattr(self, prop, cmdCall)
474 else:
475 setattr(self, prop, None)
479 class Items(object):
480 def __init__(self, items):
481 self.items = {}
482 self.configuration_warnings = []
483 self.configuration_errors = []
484 for i in items:
485 self.items[i.id] = i
486 self.templates = {}
490 def __iter__(self):
491 return self.items.itervalues()
494 def __len__(self):
495 return len(self.items)
498 def __delitem__(self, key):
499 del self.items[key]
502 def __setitem__(self, key, value):
503 self.items[key] = value
506 def __getitem__(self, key):
507 return self.items[key]
510 #We create the reversed list so search will be faster
511 #We also create a twins list with id of twins (not the original
512 #just the others, higher twins)
513 def create_reversed_list(self):
514 self.reversed_list = {}
515 self.twins = []
516 name_property = self.__class__.name_property
517 for id in self.items:
518 if hasattr(self.items[id], name_property):
519 name = getattr(self.items[id], name_property)
520 if name not in self.reversed_list:
521 self.reversed_list[name] = id
522 else:
523 self.twins.append(id)
526 def find_id_by_name(self, name):
527 if hasattr(self, 'reversed_list'):
528 if name in self.reversed_list:
529 return self.reversed_list[name]
530 else:
531 return None
532 else: #ok, an early ask, with no reversed list from now...
533 name_property = self.__class__.name_property
534 for i in self:
535 if hasattr(i, name_property):
536 i_name = getattr(i, name_property)
537 if i_name == name:
538 return i.id
539 return None
542 def find_by_name(self, name):
543 id = self.find_id_by_name(name)
544 if id is not None:
545 return self.items[id]
546 else:
547 return None
550 def pythonize(self):
551 for id in self.items:
552 self.items[id].pythonize()
555 def create_tpl_list(self):
556 for id in self.items:
557 i = self.items[id]
558 if i.is_tpl():
559 self.templates[id] = i
562 def find_tpl_by_name(self, name):
563 for id in self.templates:
564 i = self.items[id]
565 if hasattr(i, 'name') and i.name == name:
566 return i
567 return None
570 def linkify_templates(self):
571 #First we create a list of all templates
572 self.create_tpl_list()
573 for i in self:
574 tpls = i.get_templates()
575 new_tpls = []
576 for tpl in tpls:
577 t = self.find_tpl_by_name(tpl.strip())
578 if t is not None:
579 new_tpls.append(t)
580 else: # not find? not good!
581 err = "ERROR: the template '%s' defined for '%s' is unkown" % (tpl, i.get_name())
582 i.configuration_errors.append(err)
583 i.templates = new_tpls
587 def is_correct(self):
588 #we are ok at the begining. Hope we still ok at the end...
589 r = True
590 #Some class do not have twins, because they do not have names
591 #like servicedependancies
592 if hasattr(self, 'twins'):
593 #Ok, look at no twins (it's bad!)
594 for id in self.twins:
595 i = self.items[id]
596 print "Error: the", i.__class__.my_type, i.get_name(), "is duplicated"
597 r = False
598 #Then look if we have some errors in the conf
599 #Juts print warnings, but raise errors
600 for err in self.configuration_warnings:
601 print err
602 for err in self.configuration_errors:
603 print err
604 r = False
606 #Then look for individual ok
607 for i in self:
608 r &= i.is_correct()
609 return r
612 #We remove useless properties and templates
613 def clean_useless(self):
614 #First templates
615 tpls = [id for id in self.items if self.items[id].is_tpl()]
616 for id in tpls:
617 del self.items[id]
618 #Ok now delete useless in items
619 for i in self:
620 try:
621 del i.templates
622 del i.use
623 del i.plus
624 except AttributeError:
625 pass
626 del self.templates
629 #If a prop is absent and is not required, put the default value
630 def fill_default(self):
631 for i in self:
632 i.fill_default()
635 def __str__(self):
636 s = ''
637 cls = self.__class__
638 for id in self.items:
639 s = s + str(cls) + ':' + str(id) + str(self.items[id]) + '\n'
640 return s
643 # Inheritance forjust a property
644 def apply_partial_inheritance(self, prop):
645 for i in self:
646 i.get_property_by_inheritance(self, prop)
649 def apply_inheritance(self):
650 #We check for all Class properties if the host has it
651 #if not, it check all host templates for a value
652 cls = self.inner_class
653 for prop in cls.properties:
654 self.apply_partial_inheritance(prop)
655 for i in self:
656 i.get_customs_properties_by_inheritance(self)
659 #We remove twins
660 #Remember: item id respect the order of conf. So if and item
661 # is defined multiple times,
662 #we want to keep the first one.
663 #Services are also managed here but they are specials:
664 #We remove twins services with the same host_name/service_description
665 #Remember: host service are take into account first before hostgroup service
666 #Id of host service are lower than hostgroups one, so they are
667 #in self.twins_services
668 #and we can remove them.
669 def remove_twins(self):
670 for id in self.twins:
671 i = self.items[id]
672 type = i.__class__.my_type
673 print 'Warning: the', type, i.get_name(), 'is already defined.'
674 del self.items[id] #bye bye
675 #do not remove twins, we should look in it, but just void it
676 self.twins = []
677 #del self.twins #no more need
681 #We've got a contacts property with , separated contacts names
682 #and we want have a list of Contacts
683 def linkify_with_contacts(self, contacts):
684 for i in self:
685 if hasattr(i, 'contacts'):
686 contacts_tab = i.contacts.split(',')
687 contacts_tab = strip_and_uniq(contacts_tab)
688 new_contacts = []
689 for c_name in contacts_tab:
690 if c_name != '':
691 c = contacts.find_by_name(c_name)
692 if c is not None:
693 new_contacts.append(c)
694 # Else : Add in the errors tab.
695 # will be raised at is_correct
696 else:
697 err = "ERROR: the contact '%s' defined for '%s' is unkown" % (c_name, i.get_name())
698 i.configuration_errors.append(err)
699 # Get the list, but first make elements uniq
700 i.contacts = list(set(new_contacts))
703 # Make link between an object and its escalations
704 def linkify_with_escalations(self, escalations):
705 for i in self:
706 if hasattr(i, 'escalations'):
707 #print i.get_name(), 'going to link escalations', i.escalations
708 escalations_tab = i.escalations.split(',')
709 escalations_tab = strip_and_uniq(escalations_tab)
710 new_escalations = []
711 for es_name in escalations_tab:
712 es = escalations.find_by_name(es_name)
713 if es is not None:
714 new_escalations.append(es)
715 else: #TODO what?
716 pass
717 i.escalations = new_escalations
718 #print i.get_name(), 'finallygot escalation', i.escalations
721 #Make link between item and it's resultmodulations
722 def linkify_with_resultmodulations(self, resultmodulations):
723 for i in self:
724 if hasattr(i, 'resultmodulations'):
725 resultmodulations_tab = i.resultmodulations.split(',')
726 resultmodulations_tab = strip_and_uniq(resultmodulations_tab)
727 new_resultmodulations = []
728 for rm_name in resultmodulations_tab:
729 rm = resultmodulations.find_by_name(rm_name)
730 if rm is not None:
731 new_resultmodulations.append(rm)
732 else: #TODO WHAT?
733 pass
734 i.resultmodulations = new_resultmodulations
737 #If we've got a contact_groups properties, we search for all
738 #theses groups and ask them their contacts, and then add them
739 #all into our contacts property
740 def explode_contact_groups_into_contacts(self, contactgroups):
741 for i in self:
742 if hasattr(i, 'contact_groups'):
743 cgnames = i.contact_groups.split(',')
744 cgnames = strip_and_uniq(cgnames)
745 for cgname in cgnames:
746 cg = contactgroups.find_by_name(cgname)
747 if cg is None:
748 err = "The contact group '%s'defined on the %s '%s' do not exist" % (cgname, i.__class__.my_type, i.get_name())
749 i.configuration_errors.append(err)
750 continue
751 cnames = contactgroups.get_members_by_name(cgname)
752 #We add contacts into our contacts
753 if cnames != []:
754 if hasattr(i, 'contacts'):
755 i.contacts += ','+cnames
756 else:
757 i.contacts = cnames
759 #Link a timeperiod property (prop)
760 def linkify_with_timeperiods(self, timeperiods, prop):
761 for i in self:
762 if hasattr(i, prop):
763 tpname = getattr(i, prop)
764 tp = timeperiods.find_by_name(tpname)
765 #TODO: catch None?
766 setattr(i, prop, tp)
769 #Link one command property
770 def linkify_one_command_with_commands(self, commands, prop):
771 for i in self:
772 if hasattr(i, prop):
773 command = getattr(i, prop).strip()
774 if command != '':
775 if hasattr(i, 'poller_tag'):
776 cmdCall = CommandCall(commands, command,
777 poller_tag=i.poller_tag)
778 elif hasattr(i, 'reactionner_tag'):
779 cmdCall = CommandCall(commands, command,
780 reactionner_tag=i.reactionner_tag)
781 else:
782 cmdCall = CommandCall(commands, command)
783 #TODO: catch None?
784 setattr(i, prop, cmdCall)
785 else:
787 setattr(i, prop, None)
790 #Link a command list (commands with , between) in real CommandCalls
791 def linkify_command_list_with_commands(self, commands, prop):
792 for i in self:
793 if hasattr(i, prop):
794 coms = getattr(i, prop).split(',')
795 coms = strip_and_uniq(coms)
796 com_list = []
797 for com in coms:
798 if com != '':
799 if hasattr(i, 'poller_tag'):
800 cmdCall = CommandCall(commands, com,
801 poller_tag=i.poller_tag)
802 elif hasattr(i, 'reactionner_tag'):
803 cmdCall = CommandCall(commands, com,
804 reactionner_tag=i.reactionner_tag)
805 else:
806 cmdCall = CommandCall(commands, com)
807 #TODO: catch None?
808 com_list.append(cmdCall)
809 else: # TODO: catch?
810 pass
811 setattr(i, prop, com_list)
814 #Return a set with ALL hosts (used in ! expressions)
815 def get_all_host_names_set(self, hosts):
816 hnames = [h.host_name for h in hosts.items.values()
817 if hasattr(h, 'host_name')]
818 return set(hnames)
821 def evaluate_hostgroup_expression(self, expr, hosts, hostgroups):
822 res = []
823 original_expr = expr
824 #print "I'm trying to prepare the expression", expr
826 #We've got problem with the "-" sign. It can be in a
827 #valid name but it's a sign of difference for sets
828 #so we change the - now by something, then we reverse after
829 if '-' in expr:
830 expr = expr.replace('-', 'MINUSSIGN')
832 # ! (not) should be changed as "ALL-" (all but not...)
833 if '!' in expr:
834 ALLELEMENTS = self.get_all_host_names_set(hosts)
835 #print "Changing ! by ALLELEMENTS- in ", expr
836 expr = expr.replace('!', 'ALLELEMENTS-')
838 # We change all separaton token by 10 spaces
839 # (so names can still have some spaces
840 # on them like Europe Servers because we wil cut byy this 10spaces after
841 strip_expr = expr
842 for c in ['|', '&', '(', ')', ',', '-']:
843 strip_expr = strip_expr.replace(c, ' '*10)
844 #print "Stripped expression:", strip_expr
846 tokens = strip_expr.split(' '*10)
847 # Strip and non void token
848 tokens = [token.strip() for token in tokens if token != '']
849 #print "Tokens:", tokens
851 #Now add in locals() dict (yes, real variables!)
852 for token in tokens:
853 #ALLELEMENTS is a private group for us
854 if token != 'ALLELEMENTS':
855 #Maybe the token was with - at the begining,
856 #but we change all by "MINUSSIGN". We must change it back now
857 #for the search
858 if 'MINUSSIGN' in token:
859 tmp_token = token.replace('MINUSSIGN', '-')
860 members = hostgroups.get_members_by_name(tmp_token)
861 else:
862 members = hostgroups.get_members_by_name(token)
864 if members != []:
865 #print "Get members", members
866 elts = members.split(',')
867 elts = strip_and_uniq(elts)
868 elts = set(elts)
869 #print "Elements:", elts
870 #print "Now set in locals the token new values"
871 locals()[token.upper()] = elts
872 else:
873 if 'MINUSSIGN' in token:
874 token = token.replace('MINUSSIGN', '-')
875 print self.__dict__, type(self)
876 err = "ERROR: the group %s is unknown !" % (token,)
877 print err
878 self.configuration_errors.append(err)
879 return res
881 # Now changing the exprtoken value with
882 # UPPER one (so less risk of problem...
883 expr = expr.replace(token, token.upper())
885 #print "Final expression:", expr
886 try:
887 evaluation = eval(expr)
888 except SyntaxError:
889 print "The syntax of %s is invalid" % original_expr
890 return res
891 except NameError:
892 print "There is a unknow name in %s" % original_expr
893 return res
894 #print "Evaluation :", evaluation
896 # In evaluation we can have multiples values because
897 # of , (so it make a tuple in fact)
898 # we must OR them in the result
899 if ',' in expr:
900 for part in evaluation:
901 #print "PART", part
902 res.extend(list(part))
903 else:#no , so we do not have a tuple but a simple uniq set
904 res.extend(list(evaluation))
905 res_string = ','.join(res)
906 #print "Final resolution is", res_string
907 return res_string
910 #If we've got a hostgroup_name property, we search for all
911 #theses groups and ask them their hosts, and then add them
912 #all into our host_name property
913 def explode_host_groups_into_hosts(self, hosts, hostgroups):
914 for i in self:
915 if hasattr(i, 'hostgroup_name'):
916 hnames = self.evaluate_hostgroup_expression(i.hostgroup_name,
917 hosts, hostgroups)
918 if hnames != []:
919 if hasattr(i, 'host_name'):
920 i.host_name += ',' + str(hnames)
921 else:
922 i.host_name = str(hnames)