@@ -101,55 +101,13 @@ def run_job(self, file, jobid, timeout=None, env={}):
101101 self .write_command (wrapper , "%s/wrapper" % (dir ,))
102102
103103 cmd = """
104- cd %s
105- nohup ./wrapper
106- """ % (self .escape (dir ),)
104+ cd %s
105+ nohup ./wrapper
106+ """ % (self .escape (dir ),)
107107
108108 stdin , stdout , stderr = self .client .exec_command (cmd , timeout = timeout )
109- # get the shared channel for stdout/stderr/stdin
110- channel = stdout .channel
111-
112- stdin .close ()
113- # indicate that we're not going to write to that channel anymore
114- channel .shutdown_write ()
115-
116- # read stdout/stderr in order to prevent read block hangs
117- stdout_chunks = []
118- stdout_chunks .append (stdout .channel .recv (len (stdout .channel .in_buffer )))
119- # chunked read to prevent stalls
120- while not channel .closed or channel .recv_ready () or channel .recv_stderr_ready ():
121- # stop if channel was closed prematurely, and there is no data in the buffers.
122- got_chunk = False
123- readq , _ , _ = select .select ([stdout .channel ], [], [], timeout )
124- for c in readq :
125- if c .recv_ready ():
126- stdout_chunks .append (stdout .channel .recv (len (c .in_buffer )))
127- got_chunk = True
128- if c .recv_stderr_ready ():
129- # make sure to read stderr to prevent stall
130- stderr .channel .recv_stderr (len (c .in_stderr_buffer ))
131- got_chunk = True
132- '''
133- 1) make sure that there are at least 2 cycles with no data in the input buffers in order to not exit too early (i.e. cat on a >200k file).
134- 2) if no data arrived in the last loop, check if we already received the exit code
135- 3) check if input buffers are empty
136- 4) exit the loop
137- '''
138- if not got_chunk \
139- and stdout .channel .exit_status_ready () \
140- and not stderr .channel .recv_stderr_ready () \
141- and not stdout .channel .recv_ready ():
142- # indicate that we're not going to read from this channel anymore
143- stdout .channel .shutdown_read ()
144- # close the channel
145- stdout .channel .close ()
146- break # exit as remote side is finished and our bufferes are empty
147-
148- # close all the pseudofiles
149- stdout .close ()
150- stderr .close ()
109+ _ , _ , _ = self ._read_results (stdin , stdout , stderr , timeout )
151110
152- stdout .channel .recv_exit_status ()
153111 real_output = self .read_file (dir + "/stdout" )
154112 real_error = self .read_file (dir + "/stderr" )
155113 real_status = int (self .read_file (dir + "/status" ))
@@ -165,54 +123,10 @@ def run(self, argv):
165123 self ._connect ()
166124 cmd = " " .join (self .escape (s ) for s in argv )
167125 stdin , stdout , stderr = self .client .exec_command (cmd )
168- # get the shared channel for stdout/stderr/stdin
169- channel = stdout .channel
170-
171- # we do not need stdin.
172- stdin .close ()
173- # indicate that we're not going to write to that channel anymore
174- channel .shutdown_write ()
175-
176- # read stdout/stderr in order to prevent read block hangs
177- stdout_chunks = []
178- stderr_chunks = []
179- stdout_chunks .append (stdout .channel .recv (len (stdout .channel .in_buffer )))
180- # chunked read to prevent stalls
181- while not channel .closed or channel .recv_ready () or channel .recv_stderr_ready ():
182- # stop if channel was closed prematurely, and there is no data in the buffers.
183- got_chunk = False
184- readq , _ , _ = select .select ([stdout .channel ], [], [], 30 )
185- for c in readq :
186- if c .recv_ready ():
187- stdout_chunks .append (stdout .channel .recv (len (c .in_buffer )))
188- got_chunk = True
189- if c .recv_stderr_ready ():
190- # make sure to read stderr to prevent stall
191- stderr_chunks .append (stderr .channel .recv_stderr (len (c .in_stderr_buffer )))
192- got_chunk = True
193- '''
194- 1) make sure that there are at least 2 cycles with no data in the input buffers in order to not exit too early (i.e. cat on a >200k file).
195- 2) if no data arrived in the last loop, check if we already received the exit code
196- 3) check if input buffers are empty
197- 4) exit the loop
198- '''
199- if not got_chunk \
200- and stdout .channel .exit_status_ready () \
201- and not stderr .channel .recv_stderr_ready () \
202- and not stdout .channel .recv_ready ():
203- # indicate that we're not going to read from this channel anymore
204- stdout .channel .shutdown_read ()
205- # close the channel
206- stdout .channel .close ()
207- break # exit as remote side is finished and our bufferes are empty
208-
209- # close all the pseudofiles
210- stdout .close ()
211- stderr .close ()
212-
126+ status , stdout_chunks , stderr_chunks = self ._read_results (stdin , stdout , stderr )
213127 out = b'' .join (stdout_chunks )
214128 error = b'' .join (stderr_chunks )
215- status = stdout . channel . recv_exit_status ()
129+
216130 if status != 0 :
217131 err ("Command %s failed with status %d on host %s" % (cmd , status , self .hostname ))
218132 else :
@@ -223,6 +137,54 @@ def run(self, argv):
223137 self .client = None
224138 raise BadSSHHost ("SSH connection to host %s was reset" % (self .hostname ,))
225139
140+ def _read_results (self , stdin , stdout , stderr , timeout = 30 ):
141+
142+ # get the shared channel for stdout/stderr/stdin
143+ channel = stdout .channel
144+
145+ stdin .close ()
146+ # indicate that we're not going to write to that channel anymore
147+ channel .shutdown_write ()
148+
149+ # read stdout/stderr in order to prevent read block hangs
150+ stdout_chunks , stderr_chunks = [], []
151+ stdout_chunks .append (stdout .channel .recv (len (stdout .channel .in_buffer )))
152+ # chunked read to prevent stalls
153+ while not channel .closed or channel .recv_ready () or channel .recv_stderr_ready ():
154+ # stop if channel was closed prematurely, and there is no data in the buffers.
155+ got_chunk = False
156+ readq , _ , _ = select .select ([stdout .channel ], [], [], timeout )
157+ for c in readq :
158+ if c .recv_ready ():
159+ stdout_chunks .append (stdout .channel .recv (len (c .in_buffer )))
160+ got_chunk = True
161+ if c .recv_stderr_ready ():
162+ # make sure to read stderr to prevent stall
163+ stderr_chunks .append (stderr .channel .recv_stderr (len (c .in_stderr_buffer )))
164+ got_chunk = True
165+ '''
166+ 1) make sure that there are at least 2 cycles with no data in the input buffers in order to not exit too early (i.e. cat on a >200k file).
167+ 2) if no data arrived in the last loop, check if we already received the exit code
168+ 3) check if input buffers are empty
169+ 4) exit the loop
170+ '''
171+ if not got_chunk \
172+ and stdout .channel .exit_status_ready () \
173+ and not stderr .channel .recv_stderr_ready () \
174+ and not stdout .channel .recv_ready ():
175+ # indicate that we're not going to read from this channel anymore
176+ stdout .channel .shutdown_read ()
177+ # close the channel
178+ stdout .channel .close ()
179+ break # exit as remote side is finished and our bufferes are empty
180+
181+ # close all the pseudofiles
182+ stdout .close ()
183+ stderr .close ()
184+
185+ status = stdout .channel .recv_exit_status ()
186+ return status , stdout_chunks , stderr_chunks
187+
226188 @staticmethod
227189 def escape (input ):
228190 if _alnum_re .search (input ):
0 commit comments