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 #This class revolv Macro in commands by looking at the macros list
23 #in Class of elements. It give a propertie that call be callable or not.
24 #It not callable, it's a simple properties and remplace the macro with the value
25 #If callable, it's a method that is call for getting the value. for exemple, to
26 #get the number of service in a host, you call a method to get the
30 from shinken
.borg
import Borg
31 #from singleton import Singleton
33 #from contact import Contact
36 class MacroResolver(Borg
):
38 my_type
= 'macroresolver'
41 'TOTALHOSTSUP' : 'get_total_hosts_up',
42 'TOTALHOSTSDOWN' : 'get_total_hosts_down',
43 'TOTALHOSTSUNREACHABLE' : 'get_total_hosts_unreacheable',
44 'TOTALHOSTSDOWNUNHANDLED' : 'get_total_hosts_unhandled',
45 'TOTALHOSTSUNREACHABLEUNHANDLED' : 'get_total_hosts_unreacheable_unhandled',
46 'TOTALHOSTPROBLEMS' : 'get_total_host_problems',
47 'TOTALHOSTPROBLEMSUNHANDLED' : 'get_total_host_problems_unhandled',
48 'TOTALSERVICESOK' : 'get_total_service_ok',
49 'TOTALSERVICESWARNING' : 'get_total_services_warning',
50 'TOTALSERVICESCRITICAL' : 'get_total_services_critical',
51 'TOTALSERVICESUNKNOWN' : 'get_total_services_unknown',
52 'TOTALSERVICESWARNINGUNHANDLED' : 'get_total_services_warning_unhandled',
53 'TOTALSERVICESCRITICALUNHANDLED' : 'get_total_services_critical_unhandled',
54 'TOTALSERVICESUNKNOWNUNHANDLED' : 'get_total_services_unknown_unhandled',
55 'TOTALSERVICEPROBLEMS' : 'get_total_service_problems',
56 'TOTALSERVICEPROBLEMSUNHANDLED' : 'get_total_service_problems_unhandled',
58 'LONGDATETIME' : 'get_long_date_time',
59 'SHORTDATETIME' : 'get_short_date_time',
62 'TIMET' : 'get_timet',
64 'PROCESSSTARTTIME' : 'get_process_start_time',
65 'EVENTSTARTTIME' : 'get_events_start_time',
70 #This shall be call ONE TIME. It just put links for elements
73 # For searching class and elements for ondemand
74 #we need link to types
76 self
.lists_on_demand
= []
77 self
.hosts
= conf
.hosts
78 #For special void host_name handling...
79 self
.host_class
= self
.hosts
.inner_class
80 self
.lists_on_demand
.append(self
.hosts
)
81 self
.services
= conf
.services
82 self
.contacts
= conf
.contacts
83 self
.lists_on_demand
.append(self
.contacts
)
84 self
.hostgroups
= conf
.hostgroups
85 self
.lists_on_demand
.append(self
.hostgroups
)
86 self
.commands
= conf
.commands
87 self
.servicegroups
= conf
.servicegroups
88 self
.lists_on_demand
.append(self
.servicegroups
)
89 self
.contactgroups
= conf
.contactgroups
90 self
.lists_on_demand
.append(self
.contactgroups
)
91 self
.illegal_macro_output_chars
= conf
.illegal_macro_output_chars
92 self
.output_macros
= ['HOSTOUTPUT', 'HOSTPERFDATA', 'HOSTACKAUTHOR', 'HOSTACKCOMMENT', 'SERVICEOUTPUT', 'SERVICEPERFDATA', 'SERVICEACKAUTHOR', 'SERVICEACKCOMMENT']
97 #Return all macros of a string, so cut the $
98 #And create a dict with it:
99 #val : value, not set here
100 #type : type of macro, like class one, or ARGN one
101 def get_macros(self
, s
):
103 # return self.cache[s]
105 p
= re
.compile(r
'(\$)')
111 in_macro
= not in_macro
113 macros
[elt
] = {'val' : '', 'type' : 'unknown'}
115 #self.cache[s] = macros
119 #Get a value from a propertie of a element
120 #Prop can ba a function or a propertie
122 def get_value_from_element(self
, elt
, prop
):
124 value
= getattr(elt
, prop
)
129 except AttributeError , exp
:
133 #For some macros, we need to delete unwanted caracters
134 def delete_unwanted_caracters(self
, s
):
135 for c
in self
.illegal_macro_output_chars
:
140 #return a dict with all environement variable came from
141 #the macros of the datas object
142 def get_env_macros(self
, data
):
145 clss
= [d
.__class
__ for d
in data
]
148 if o
.__class
__ == cls
:
151 # print "Macro in %s : %s" % (o.__class__, macro)
153 value
= self
.get_value_from_element(o
, prop
)
154 # print "Value: %s" % value
155 env
['NAGIOS_'+macro
] = value
160 # This function will look at elements in data (and args if it filled)
161 # to replace the macros in c_line with real value.
162 def resolve_simple_macros_in_string(self
, c_line
, data
, args
=None):
163 #Now we prepare the classes for looking at the class.macros
164 data
.append(self
) #For getting global MACROS
165 if hasattr(self
, 'conf'):
166 data
.append(self
.conf
) # For USERN macros
167 clss
= [d
.__class
__ for d
in data
]
169 #we should do some loops for nested macros
170 #like $USER1$ hiding like a ninja in a $ARG2$ Macro. And if
171 #$USER1$ is pointing to $USER34$ etc etc, we should loop
172 #until we reach the botom. So the last loop is when we do
173 #not still have macros :)
174 still_got_macros
= True
176 while still_got_macros
:
178 #Ok, we want the macros in the command line
179 macros
= self
.get_macros(c_line
)
181 #We can get out if we do not have macros this loop
182 still_got_macros
= (len(macros
)!=0)
183 #print "Still go macros:", still_got_macros
185 #Put in the macros the type of macro for all macros
186 self
.get_type_of_macro(macros
, clss
)
187 #Now we get values from elements
189 #If type ARGN, look at ARGN cutting
190 if macros
[macro
]['type'] == 'ARGN' and args
!= None:
191 macros
[macro
]['val'] = self
.resolve_argn(macro
, args
)
192 macros
[macro
]['type'] = 'resolved'
193 #If class, get value from properties
194 if macros
[macro
]['type'] == 'class':
195 cls
= macros
[macro
]['class']
197 if elt
is not None and elt
.__class
__ == cls
:
198 prop
= cls
.macros
[macro
]
199 macros
[macro
]['val'] = self
.get_value_from_element(elt
, prop
)
200 #Now check if we do not have a 'output' macro. If so, we must
201 #delete all special caracters that can be dangerous
202 if macro
in self
.output_macros
:
203 macros
[macro
]['val'] = self
.delete_unwanted_caracters(macros
[macro
]['val'])
204 if macros
[macro
]['type'] == 'CUSTOM':
205 cls_type
= macros
[macro
]['class']
206 macro_name
= re
.split('_'+cls_type
, macro
)[1].upper()
207 #Ok, we've got the macro like MAC_ADDRESS for _HOSTMAC_ADDRESS
208 #Now we get the element in data that have the type HOST
209 #and we check if it gots the custom value
211 if elt
is not None and elt
.__class
__.my_type
.upper() == cls_type
:
212 if '_'+macro_name
in elt
.customs
:
213 macros
[macro
]['val'] = elt
.customs
['_'+macro_name
]
214 if macros
[macro
]['type'] == 'ONDEMAND':
215 macros
[macro
]['val'] = self
.resolve_ondemand(macro
, data
)
217 #We resolved all we can, now replace the macro in the command call
219 c_line
= c_line
.replace('$'+macro
+'$', macros
[macro
]['val'])
221 if nb_loop
> 32: #too mouch loop, we exit
222 still_got_macros
= False
224 #print "Retuning c_line", c_line.strip()
225 return c_line
.strip()
228 #Resolve a command with macro by looking at data classes.macros
229 #And get macro from item properties.
230 def resolve_command(self
, com
, data
):
231 c_line
= com
.command
.command_line
232 return self
.resolve_simple_macros_in_string(c_line
, data
, args
=com
.args
)
235 #For all Macros in macros, set the type by looking at the
236 #MACRO name (ARGN? -> argn_type,
237 #HOSTBLABLA -> class one and set Host in class)
238 #_HOSTTOTO -> HOST CUSTOM MACRO TOTO
239 #$SERVICESTATEID:srv-1:Load$ -> MACRO SERVICESTATEID of
240 #the service Load of host srv-1
241 def get_type_of_macro(self
, macros
, clss
):
244 if re
.match('ARG\d', macro
):
245 macros
[macro
]['type'] = 'ARGN'
248 #are managed in the Config class, so no
249 #need to look that here
250 elif re
.match('_HOST\w', macro
):
251 macros
[macro
]['type'] = 'CUSTOM'
252 macros
[macro
]['class'] = 'HOST'
254 elif re
.match('_SERVICE\w', macro
):
255 macros
[macro
]['type'] = 'CUSTOM'
256 macros
[macro
]['class'] = 'SERVICE'
257 #value of macro : re.split('_HOST', '_HOSTMAC_ADDRESS')[1]
259 elif re
.match('_CONTACT\w', macro
):
260 macros
[macro
]['type'] = 'CUSTOM'
261 macros
[macro
]['class'] = 'CONTACT'
264 elif len(macro
.split(':')) > 1:
265 macros
[macro
]['type'] = 'ONDEMAND'
267 #OK, classical macro...
269 if macro
in cls
.macros
:
270 macros
[macro
]['type'] = 'class'
271 macros
[macro
]['class'] = cls
275 #Resolv MACROS for the ARGN
276 def resolve_argn(self
, macro
, args
):
277 #first, get number of arg
279 r
= re
.search('ARG(?P<id>\d+)', macro
)
281 id = int(r
.group('id')) - 1
288 #Resolved on demande macro, quite hard on fact
289 def resolve_ondemand(self
, macro
, data
):
290 #print "\nResolving macro", macro
291 elts
= macro
.split(':')
294 #Len 3 == service, 2 = all others types...
297 #print "Got a Service on demand asking...", elts
298 (host_name
, service_description
) = (elts
[1], elts
[2])
299 #host_name can be void, so it's the host in data
300 #that is important. We use our self.host_class to
301 #find the host in the data :)
304 if elt
is not None and elt
.__class
__ == self
.host_class
:
305 host_name
= elt
.host_name
306 #Okn now we get service
307 s
= self
.services
.find_srv_by_name_and_hostname(host_name
, service_description
)
310 prop
= cls
.macros
[macro_name
]
311 val
= self
.get_value_from_element(s
, prop
)
312 #print "Got val:", val
314 #Ok, service was easy, now hard part
318 #Special case : elt_name can be void
319 #so it's the host where it apply
322 if elt
is not None and elt
.__class
__ == self
.host_class
:
323 elt_name
= elt
.host_name
324 for list in self
.lists_on_demand
:
325 cls
= list.inner_class
326 #We search our type by look at the macro
327 if macro_name
in cls
.macros
:
328 prop
= cls
.macros
[macro_name
]
329 i
= list.find_by_name(elt_name
)
331 val
= self
.get_value_from_element(i
, prop
)
332 #print "Got val:", val
337 #Get Fri 15 May 11:42:39 CEST 2009
338 def get_long_date_time(self
):
339 return time
.strftime("%a %d %b %H:%M:%S %Z %Y", time
.localtime())
342 #Get 10-13-2000 00:30:28
343 def get_short_date_time(self
):
344 return time
.strftime("%d-%m-%Y %H:%M:%S", time
.localtime())
349 return time
.strftime("%d-%m-%Y", time
.localtime())
354 return time
.strftime("%H:%M:%S", time
.localtime())
359 return str(int(time
.time()))
362 def get_total_hosts_up(self
):
363 return len([h
for h
in self
.hosts
if h
.state
== 'UP'])
365 def get_total_hosts_down(self
):
366 return len([h
for h
in self
.hosts
if h
.state
== 'DOWN'])
368 def get_total_hosts_unreacheable(self
):
369 return len([h
for h
in self
.hosts
if h
.state
== 'UNREACHABLE'])
372 def get_total_hosts_unreacheable_unhandled(self
):
376 def get_total_host_problems(self
):
377 return len([h
for h
in self
.hosts
if h
.is_problem
])
379 def get_total_host_problems_unhandled(self
):
382 def get_total_service_ok(self
):
383 return len([s
for s
in self
.services
if s
.state
== 'OK'])
385 def get_total_services_warning(self
):
386 return len([s
for s
in self
.services
if s
.state
== 'WARNING'])
388 def get_total_services_critical(self
):
389 return len([s
for s
in self
.services
if s
.state
== 'CRITICAL'])
391 def get_total_services_unknown(self
):
392 return len([s
for s
in self
.services
if s
.state
== 'UNKNOWN'])
395 def get_total_services_warning_unhandled(self
):
398 def get_total_services_critical_unhandled(self
):
401 def get_total_services_unknown_unhandled(self
):
404 def get_total_service_problems(self
):
405 return len([s
for s
in self
.services
if s
.is_problem
])
407 def get_total_service_problems_unhandled(self
):
410 def get_process_start_time(self
):
413 def get_events_start_time(self
):