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 revolv Macro in commands by looking at the macros list
24 #in Class of elements. It give a propertie that call be callable or not.
25 #It not callable, it's a simple properties and remplace the macro with the value
26 #If callable, it's a method that is call for getting the value. for exemple, to
27 #get the number of service in a host, you call a method to get the
31 from shinken
.borg
import Borg
32 #from singleton import Singleton
34 #from contact import Contact
37 class MacroResolver(Borg
):
39 my_type
= 'macroresolver'
42 'TOTALHOSTSUP' : 'get_total_hosts_up',
43 'TOTALHOSTSDOWN' : 'get_total_hosts_down',
44 'TOTALHOSTSUNREACHABLE' : 'get_total_hosts_unreacheable',
45 'TOTALHOSTSDOWNUNHANDLED' : 'get_total_hosts_unhandled',
46 'TOTALHOSTSUNREACHABLEUNHANDLED' : 'get_total_hosts_unreacheable_unhandled',
47 'TOTALHOSTPROBLEMS' : 'get_total_host_problems',
48 'TOTALHOSTPROBLEMSUNHANDLED' : 'get_total_host_problems_unhandled',
49 'TOTALSERVICESOK' : 'get_total_service_ok',
50 'TOTALSERVICESWARNING' : 'get_total_services_warning',
51 'TOTALSERVICESCRITICAL' : 'get_total_services_critical',
52 'TOTALSERVICESUNKNOWN' : 'get_total_services_unknown',
53 'TOTALSERVICESWARNINGUNHANDLED' : 'get_total_services_warning_unhandled',
54 'TOTALSERVICESCRITICALUNHANDLED' : 'get_total_services_critical_unhandled',
55 'TOTALSERVICESUNKNOWNUNHANDLED' : 'get_total_services_unknown_unhandled',
56 'TOTALSERVICEPROBLEMS' : 'get_total_service_problems',
57 'TOTALSERVICEPROBLEMSUNHANDLED' : 'get_total_service_problems_unhandled',
59 'LONGDATETIME' : 'get_long_date_time',
60 'SHORTDATETIME' : 'get_short_date_time',
63 'TIMET' : 'get_timet',
65 'PROCESSSTARTTIME' : 'get_process_start_time',
66 'EVENTSTARTTIME' : 'get_events_start_time',
71 #This shall be call ONE TIME. It just put links for elements
74 # For searching class and elements for ondemand
75 #we need link to types
77 self
.lists_on_demand
= []
78 self
.hosts
= conf
.hosts
79 #For special void host_name handling...
80 self
.host_class
= self
.hosts
.inner_class
81 self
.lists_on_demand
.append(self
.hosts
)
82 self
.services
= conf
.services
83 self
.contacts
= conf
.contacts
84 self
.lists_on_demand
.append(self
.contacts
)
85 self
.hostgroups
= conf
.hostgroups
86 self
.lists_on_demand
.append(self
.hostgroups
)
87 self
.commands
= conf
.commands
88 self
.servicegroups
= conf
.servicegroups
89 self
.lists_on_demand
.append(self
.servicegroups
)
90 self
.contactgroups
= conf
.contactgroups
91 self
.lists_on_demand
.append(self
.contactgroups
)
92 self
.illegal_macro_output_chars
= conf
.illegal_macro_output_chars
93 self
.output_macros
= ['HOSTOUTPUT', 'HOSTPERFDATA', 'HOSTACKAUTHOR', 'HOSTACKCOMMENT', 'SERVICEOUTPUT', 'SERVICEPERFDATA', 'SERVICEACKAUTHOR', 'SERVICEACKCOMMENT']
98 #Return all macros of a string, so cut the $
99 #And create a dict with it:
100 #val : value, not set here
101 #type : type of macro, like class one, or ARGN one
102 def get_macros(self
, s
):
104 # return self.cache[s]
106 p
= re
.compile(r
'(\$)')
112 in_macro
= not in_macro
114 macros
[elt
] = {'val' : '', 'type' : 'unknown'}
116 #self.cache[s] = macros
120 #Get a value from a propertie of a element
121 #Prop can ba a function or a propertie
123 def get_value_from_element(self
, elt
, prop
):
125 value
= getattr(elt
, prop
)
130 except AttributeError , exp
:
134 #For some macros, we need to delete unwanted caracters
135 def delete_unwanted_caracters(self
, s
):
136 for c
in self
.illegal_macro_output_chars
:
141 #return a dict with all environement variable came from
142 #the macros of the datas object
143 def get_env_macros(self
, data
):
146 clss
= [d
.__class
__ for d
in data
]
149 if o
.__class
__ == cls
:
152 # print "Macro in %s : %s" % (o.__class__, macro)
154 value
= self
.get_value_from_element(o
, prop
)
155 # print "Value: %s" % value
156 env
['NAGIOS_'+macro
] = value
161 # This function will look at elements in data (and args if it filled)
162 # to replace the macros in c_line with real value.
163 def resolve_simple_macros_in_string(self
, c_line
, data
, args
=None):
164 #Now we prepare the classes for looking at the class.macros
165 data
.append(self
) #For getting global MACROS
166 if hasattr(self
, 'conf'):
167 data
.append(self
.conf
) # For USERN macros
168 clss
= [d
.__class
__ for d
in data
]
170 #we should do some loops for nested macros
171 #like $USER1$ hiding like a ninja in a $ARG2$ Macro. And if
172 #$USER1$ is pointing to $USER34$ etc etc, we should loop
173 #until we reach the botom. So the last loop is when we do
174 #not still have macros :)
175 still_got_macros
= True
177 while still_got_macros
:
179 #Ok, we want the macros in the command line
180 macros
= self
.get_macros(c_line
)
182 #We can get out if we do not have macros this loop
183 still_got_macros
= (len(macros
)!=0)
184 #print "Still go macros:", still_got_macros
186 #Put in the macros the type of macro for all macros
187 self
.get_type_of_macro(macros
, clss
)
188 #Now we get values from elements
190 #If type ARGN, look at ARGN cutting
191 if macros
[macro
]['type'] == 'ARGN' and args
!= None:
192 macros
[macro
]['val'] = self
.resolve_argn(macro
, args
)
193 macros
[macro
]['type'] = 'resolved'
194 #If class, get value from properties
195 if macros
[macro
]['type'] == 'class':
196 cls
= macros
[macro
]['class']
198 if elt
is not None and elt
.__class
__ == cls
:
199 prop
= cls
.macros
[macro
]
200 macros
[macro
]['val'] = self
.get_value_from_element(elt
, prop
)
201 #Now check if we do not have a 'output' macro. If so, we must
202 #delete all special caracters that can be dangerous
203 if macro
in self
.output_macros
:
204 macros
[macro
]['val'] = self
.delete_unwanted_caracters(macros
[macro
]['val'])
205 if macros
[macro
]['type'] == 'CUSTOM':
206 cls_type
= macros
[macro
]['class']
207 macro_name
= re
.split('_'+cls_type
, macro
)[1].upper()
208 #Ok, we've got the macro like MAC_ADDRESS for _HOSTMAC_ADDRESS
209 #Now we get the element in data that have the type HOST
210 #and we check if it gots the custom value
212 if elt
is not None and elt
.__class
__.my_type
.upper() == cls_type
:
213 if '_'+macro_name
in elt
.customs
:
214 macros
[macro
]['val'] = elt
.customs
['_'+macro_name
]
215 if macros
[macro
]['type'] == 'ONDEMAND':
216 macros
[macro
]['val'] = self
.resolve_ondemand(macro
, data
)
218 #We resolved all we can, now replace the macro in the command call
220 c_line
= c_line
.replace('$'+macro
+'$', macros
[macro
]['val'])
222 if nb_loop
> 32: #too mouch loop, we exit
223 still_got_macros
= False
225 #print "Retuning c_line", c_line.strip()
226 return c_line
.strip()
229 #Resolve a command with macro by looking at data classes.macros
230 #And get macro from item properties.
231 def resolve_command(self
, com
, data
):
232 c_line
= com
.command
.command_line
233 return self
.resolve_simple_macros_in_string(c_line
, data
, args
=com
.args
)
236 #For all Macros in macros, set the type by looking at the
237 #MACRO name (ARGN? -> argn_type,
238 #HOSTBLABLA -> class one and set Host in class)
239 #_HOSTTOTO -> HOST CUSTOM MACRO TOTO
240 #$SERVICESTATEID:srv-1:Load$ -> MACRO SERVICESTATEID of
241 #the service Load of host srv-1
242 def get_type_of_macro(self
, macros
, clss
):
245 if re
.match('ARG\d', macro
):
246 macros
[macro
]['type'] = 'ARGN'
249 #are managed in the Config class, so no
250 #need to look that here
251 elif re
.match('_HOST\w', macro
):
252 macros
[macro
]['type'] = 'CUSTOM'
253 macros
[macro
]['class'] = 'HOST'
255 elif re
.match('_SERVICE\w', macro
):
256 macros
[macro
]['type'] = 'CUSTOM'
257 macros
[macro
]['class'] = 'SERVICE'
258 #value of macro : re.split('_HOST', '_HOSTMAC_ADDRESS')[1]
260 elif re
.match('_CONTACT\w', macro
):
261 macros
[macro
]['type'] = 'CUSTOM'
262 macros
[macro
]['class'] = 'CONTACT'
265 elif len(macro
.split(':')) > 1:
266 macros
[macro
]['type'] = 'ONDEMAND'
268 #OK, classical macro...
270 if macro
in cls
.macros
:
271 macros
[macro
]['type'] = 'class'
272 macros
[macro
]['class'] = cls
276 #Resolv MACROS for the ARGN
277 def resolve_argn(self
, macro
, args
):
278 #first, get number of arg
280 r
= re
.search('ARG(?P<id>\d+)', macro
)
282 id = int(r
.group('id')) - 1
289 #Resolved on demande macro, quite hard on fact
290 def resolve_ondemand(self
, macro
, data
):
291 #print "\nResolving macro", macro
292 elts
= macro
.split(':')
295 #Len 3 == service, 2 = all others types...
298 #print "Got a Service on demand asking...", elts
299 (host_name
, service_description
) = (elts
[1], elts
[2])
300 #host_name can be void, so it's the host in data
301 #that is important. We use our self.host_class to
302 #find the host in the data :)
305 if elt
is not None and elt
.__class
__ == self
.host_class
:
306 host_name
= elt
.host_name
307 #Okn now we get service
308 s
= self
.services
.find_srv_by_name_and_hostname(host_name
, service_description
)
311 prop
= cls
.macros
[macro_name
]
312 val
= self
.get_value_from_element(s
, prop
)
313 #print "Got val:", val
315 #Ok, service was easy, now hard part
319 #Special case : elt_name can be void
320 #so it's the host where it apply
323 if elt
is not None and elt
.__class
__ == self
.host_class
:
324 elt_name
= elt
.host_name
325 for list in self
.lists_on_demand
:
326 cls
= list.inner_class
327 #We search our type by look at the macro
328 if macro_name
in cls
.macros
:
329 prop
= cls
.macros
[macro_name
]
330 i
= list.find_by_name(elt_name
)
332 val
= self
.get_value_from_element(i
, prop
)
333 #print "Got val:", val
338 #Get Fri 15 May 11:42:39 CEST 2009
339 def get_long_date_time(self
):
340 return time
.strftime("%a %d %b %H:%M:%S %Z %Y", time
.localtime())
343 #Get 10-13-2000 00:30:28
344 def get_short_date_time(self
):
345 return time
.strftime("%d-%m-%Y %H:%M:%S", time
.localtime())
350 return time
.strftime("%d-%m-%Y", time
.localtime())
355 return time
.strftime("%H:%M:%S", time
.localtime())
360 return str(int(time
.time()))
363 def get_total_hosts_up(self
):
364 return len([h
for h
in self
.hosts
if h
.state
== 'UP'])
366 def get_total_hosts_down(self
):
367 return len([h
for h
in self
.hosts
if h
.state
== 'DOWN'])
369 def get_total_hosts_unreacheable(self
):
370 return len([h
for h
in self
.hosts
if h
.state
== 'UNREACHABLE'])
373 def get_total_hosts_unreacheable_unhandled(self
):
377 def get_total_host_problems(self
):
378 return len([h
for h
in self
.hosts
if h
.is_problem
])
380 def get_total_host_problems_unhandled(self
):
383 def get_total_service_ok(self
):
384 return len([s
for s
in self
.services
if s
.state
== 'OK'])
386 def get_total_services_warning(self
):
387 return len([s
for s
in self
.services
if s
.state
== 'WARNING'])
389 def get_total_services_critical(self
):
390 return len([s
for s
in self
.services
if s
.state
== 'CRITICAL'])
392 def get_total_services_unknown(self
):
393 return len([s
for s
in self
.services
if s
.state
== 'UNKNOWN'])
396 def get_total_services_warning_unhandled(self
):
399 def get_total_services_critical_unhandled(self
):
402 def get_total_services_unknown_unhandled(self
):
405 def get_total_service_problems(self
):
406 return len([s
for s
in self
.services
if s
.is_problem
])
408 def get_total_service_problems_unhandled(self
):
411 def get_process_start_time(self
):
414 def get_events_start_time(self
):