Fixes interactions with popen handles

Includes logging updates and other optimizations/improvements.

Change-Id: Icf704164292637b92da25d8c720a72a5ca7c6ba6
Closes-Bug: 1381195
This commit is contained in:
Samuel Stavinoha 2014-10-15 02:51:34 +00:00
parent 453ffedabd
commit 5f7272abf7
1 changed files with 37 additions and 20 deletions

View File

@ -9,6 +9,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# pylint: disable=W0703
"""Windows remote client module implemented using psexec.py.""" """Windows remote client module implemented using psexec.py."""
@ -203,12 +204,15 @@ class SMBClient(object): # pylint: disable=R0902
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
close_fds=True, close_fds=True,
bufsize=0) universal_newlines=True,
bufsize=-1)
output = '' output = ''
while not self._prompt_pattern.findall(output): while not self._prompt_pattern.findall(output):
output += self._get_output() output += self._get_output()
self._connected = True self._connected = True
except Exception: except Exception:
LOG.error("Failed to connect to host %s over smb",
self.host, exc_info=True)
self.close() self.close()
raise raise
@ -237,10 +241,10 @@ class SMBClient(object): # pylint: disable=R0902
str(exc)) str(exc))
del exc del exc
finally: finally:
if self._process: try:
LOG.warning("Killing process: %s", self._process) self._process.kill()
subprocess.call(['pkill', '-STOP', '-P', except OSError:
str(self._process.pid)]) LOG.exception("Tried killing psexec subprocess.")
def remote_execute(self, command, powershell=True, retry=0, **kwargs): def remote_execute(self, command, powershell=True, retry=0, **kwargs):
"""Execute a command on a remote host. """Execute a command on a remote host.
@ -256,19 +260,24 @@ class SMBClient(object): # pylint: disable=R0902
if powershell: if powershell:
command = ('powershell -EncodedCommand %s' % command = ('powershell -EncodedCommand %s' %
_posh_encode(command)) _posh_encode(command))
LOG.info("Executing command: %s", command)
self._process.stdin.write('%s\n' % command) self._process.stdin.write('%s\n' % command)
self._process.stdin.flush()
try: try:
output = self._get_output() output = self._get_output()
LOG.debug("Stdout produced: %s", output)
output = "\n".join(output.splitlines()[:-1]).strip() output = "\n".join(output.splitlines()[:-1]).strip()
return output return output
except SubprocessError: except Exception:
LOG.error("Error while reading output from command %s on %s",
command, self.host, exc_info=True)
if not retry: if not retry:
raise raise
else: else:
return self.remote_execute(command, powershell=powershell, return self.remote_execute(command, powershell=powershell,
retry=retry - 1) retry=retry - 1)
def _get_output(self, prompt_expected=True, wait=200): def _get_output(self, prompt_expected=True, wait=500):
"""Retrieve output from _process. """Retrieve output from _process.
This method will wait until output is started to be received and then This method will wait until output is started to be received and then
@ -285,16 +294,20 @@ class SMBClient(object): # pylint: disable=R0902
# leave loop if underlying process has a return code # leave loop if underlying process has a return code
# obviously meaning that it has terminated # obviously meaning that it has terminated
if self._process.poll() is not None: if self._process.poll() is not None:
import json if self._process.returncode == 0:
error = {"error": tmp_out} break
error = tmp_out
raise SubprocessError("subprocess with pid: %s has terminated " raise SubprocessError("subprocess with pid: %s has terminated "
"unexpectedly with return code: %s\n%s" "unexpectedly with return code: %s | %s"
% (self._process.pid, % (self._process.pid,
self._process.poll(), self._process.poll(), error))
json.dumps(error))) time.sleep(0)
time.sleep(wait / 1000) time.sleep(float(wait) / 1000)
else:
LOG.debug("Loop 1 - stdout read: %s", tmp_out)
stdout = tmp_out stdout = tmp_out
while (not tmp_out == '' or while (tmp_out != '' or
(not self._prompt_pattern.findall(stdout) and (not self._prompt_pattern.findall(stdout) and
prompt_expected)): prompt_expected)):
self._file_read.seek(0, 1) self._file_read.seek(0, 1)
@ -303,14 +316,18 @@ class SMBClient(object): # pylint: disable=R0902
# leave loop if underlying process has a return code # leave loop if underlying process has a return code
# obviously meaning that it has terminated # obviously meaning that it has terminated
if self._process.poll() is not None: if self._process.poll() is not None:
import json if self._process.returncode == 0:
error = {"error": stdout} break
error = tmp_out
raise SubprocessError("subprocess with pid: %s has terminated " raise SubprocessError("subprocess with pid: %s has terminated "
"unexpectedly with return code: %s\n%s" "unexpectedly with return code: %s | %s"
% (self._process.pid, % (self._process.pid,
self._process.poll(), self._process.poll(), error))
json.dumps(error))) time.sleep(0)
time.sleep(wait / 1000) time.sleep(float(wait) / 1000)
else:
LOG.debug("Loop 2 - stdout read: %s", tmp_out)
self._output += stdout self._output += stdout
stdout = stdout.replace('\r', '').replace('\x08', '') stdout = stdout.replace('\r', '').replace('\x08', '')
return stdout return stdout