Enh: (Grégory Starck) still code clean for pyro wrapper in satellitelinks.
[shinken.git] / shinken / action.py
blob7aa00c04eae747e99b7f46f4a21845665b406c5d
1 #!/usr/bin/env python
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/>.
21 import os, time, copy
22 import shlex
23 import sys
25 #Unix and windows do not have the same import
26 if os.name == 'nt':
27 import subprocess
28 import ctypes
29 TerminateProcess = ctypes.windll.kernel32.TerminateProcess
30 else:
31 import subprocess
33 # This class is use just for having a common id between actions and checks
35 class Action:
36 id = 0
37 def __init__(self):
38 pass
41 # If the command line got shell caracters, we should go in a shell
42 # mode. So look at theses parameters
43 # Note : it's "hard", but in fact you can launch it 100000times
44 # a second so... I don't care :) (and at least it's easy to understand)
45 def got_shell_caracters(self):
46 for c in self.command:
47 if c in ['!','$','^','&','*','(',')','~','[',']','|','{','}',';','<','>','?','`']:
48 return True
49 return False
52 def get_outputs(self, out, max_plugins_output_length):
53 #print "Get only," , max_plugins_output_length, "bytes"
54 # Squize all output after max_plugins_output_length
55 out = out[:max_plugins_output_length]
56 # Then cuts by lines
57 elts = out.split('\n')
58 # For perf data
59 elts_line1 = elts[0].split('|')
60 # First line before | is output, and strip it
61 self.output = elts_line1[0].strip()
62 # After | is perfdata, and strip it
63 if len(elts_line1) > 1:
64 self.perf_data = elts_line1[1].strip()
65 # The others lines are long_output
66 # but others are are not stripped
67 if len(elts) > 1:
68 self.long_output = '\n'.join(elts[1:])
71 # Mix the env into the environnment variables
72 # into a new local env dict
73 # rmq : we cannot just update os.environ because
74 # it will be modified for all other checks too
75 def get_local_environnement(self):
76 local_env = copy.copy(os.environ)
77 for p in self.env:
78 local_env[p] = self.env[p]
79 return local_env
82 def execute(self):
83 if os.name == 'nt':
84 self.execute_windows()
85 else:
86 self.execute_unix()
89 def execute_windows(self):
90 #self.timeout = 20
91 self.status = 'launched'
92 self.check_time = time.time()
93 self.wait_time = 0.0001
94 self.last_poll = self.check_time
95 local_env = self.get_local_environnement()
96 try:
97 self.process = subprocess.Popen(shlex.split(self.command),
98 stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=local_env, shell=True)
99 except WindowsError, exp:
100 print "We kill the process : ", exp, self.command
101 self.status = 'timeout'
102 self.execution_time = time.time() - self.check_time
103 return
106 def execute_unix(self):
107 self.status = 'launched'
108 self.check_time = time.time()
109 self.last_poll = self.check_time
110 self.wait_time = 0.0001
111 #cmd = ['/bin/sh', '-c', self.command]
112 # Nagios do not use the /bin/sh -c command, so I don't do it too
114 # Get a local env variables with our additionnal values
115 local_env = self.get_local_environnement()
117 # We allow direct launch only for 2.7 and higher version
118 # because if a direct launch crash, under this the file hanldes
119 # are not releases, it's not good.
120 if sys.version_info >= (2,7):
121 shell_launch = self.got_shell_caracters()
122 else:
123 shell_launch = True
124 # Maybe we will failed in our first attempt, we can retry
125 still_loop = True
127 while still_loop:
128 if shell_launch:
129 try:
130 self.process = subprocess.Popen(self.command,
131 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
132 close_fds=True, shell=True, env=local_env)
133 return
134 except OSError , exp:
135 print "Debug : Error in launching shell command:", self.command, exp
136 print "Way: launch command?", self.got_shell_caracters()
137 self.output = exp.__str__()
138 self.exit_status = 2
139 self.status = 'done'
140 self.execution_time = time.time() - self.check_time
142 # Maybe we run out of file descriptor. It's not good at all!
143 if exp.errno == 24 and exp.strerror == 'Too many open files':
144 return 'toomanyopenfiles'
146 return
147 else:
148 # Direct exec, quicker but only for 2.7 and higher, sorry.
149 try:
150 p = subprocess.Popen(shlex.split(self.command),
151 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
152 close_fds=True, env=local_env)
153 self.process = p
154 return
155 except OSError , exp:
156 # Maybe it's just a shell we try to exec. So we must retry
157 if exp.errno == 8 and exp.strerror == 'Exec format error':
158 shell_launch = True
159 continue # loop and go in the shell launch this time
160 # Here, we are with a real error
161 print "Debug : Error in launching direct exec command:", self.command, exp
162 self.output = exp.__str__()
163 self.exit_status = 2
164 self.status = 'done'
165 self.execution_time = time.time() - self.check_time
167 # Maybe we run out of file descriptor. It's not good at all!
168 if exp.errno == 24 and exp.strerror == 'Too many open files':
169 return 'toomanyopenfiles'
171 return
174 def check_finished(self, max_plugins_output_length):
175 if os.name == 'nt':
176 self.check_finished_windows(max_plugins_output_length)
177 else:
178 self.check_finished_unix(max_plugins_output_length)
181 def check_finished_unix(self, max_plugins_output_length):
182 # We must wait, but checks are variable in time
183 # so we do not wait the same for an little check
184 # than a long ping. So we do like TCP : slow start with *2
185 # but do not wait more than 0.1s.
186 self.last_poll = time.time()
187 if self.process.poll() is None:
188 self.wait_time = min(self.wait_time*2, 0.1)
189 #time.sleep(wait_time)
190 now = time.time()
191 if (now - self.check_time) > self.timeout:
192 #process.kill()
193 # HEAD SHOT
194 os.kill(self.process.pid, 9)
195 #print "Kill", self.process.pid, self.command, now - self.check_time
196 self.status = 'timeout'
197 self.execution_time = now - self.check_time
198 self.exit_status = 3
199 return
200 return
201 # Get standards outputs
202 self.exit_status = self.process.returncode
203 (stdoutdata, stderrdata) = self.process.communicate()
205 # if the exit status is anormal, we add stderr to the output
206 if self.exit_status not in [0, 1, 2, 3]:
207 stdoutdata = stdoutdata + stderrdata
208 # Now grep what we want in the output
209 self.get_outputs(stdoutdata, max_plugins_output_length)
211 self.status = 'done'
212 self.execution_time = time.time() - self.check_time
215 def check_finished_windows(self, max_plugins_output_length):
216 # We must wait, but checks are variable in time
217 # so we do not wait the same for an little check
218 # than a long ping. So we do like TCP : slow start with *2
219 # but do not wait more than 0.1s.
220 self.last_poll = time.time()
221 if self.process.poll() is None:
222 self.wait_time = min(self.wait_time*2, 0.1)
223 #time.sleep(wait_time)
224 now = time.time()
225 if (now - self.check_time) > self.timeout:
226 #process.kill()
227 # HEAD SHOT
228 TerminateProcess(int(self.process._handle), -1)
229 #print "Kill", self.process.pid, self.command
230 self.status = 'timeout'
231 self.execution_time = now - self.check_time
232 self.exit_status = 3
233 return
234 return
235 # Get standards outputs
236 self.exit_status = self.process.returncode
237 (stdoutdata, stderrdata) = self.process.communicate()
239 #we should not keep the process now
240 del self.process
242 # if the exit status is anormal, we add stderr to the output
243 if self.exit_status not in [0, 1, 2, 3]:
244 stdoutdata = stdoutdata + stderrdata
245 #self.get_outputs(self.process.stdout.read(),max_plugins_output_length)
246 # Now grep what we want in the output
247 self.get_outputs(stdoutdata, max_plugins_output_length)
249 # if self.exit_status != 0:
250 # print "DBG:", self.command, self.exit_status, self.output
251 self.status = 'done'
252 self.execution_time = time.time() - self.check_time