expose timeout param to socket (#18632)

The timeout param was exposed to the socket connection but was not
enforced for commands.  This update will now cause a command to timeout
based on the module parameter.
pull/18651/head
Peter Sprygada 8 years ago committed by John R Barker
parent 69fb310878
commit 9cb0e771d2

@ -20,7 +20,7 @@ import os
import re import re
import socket import socket
import time import time
import signal
try: try:
import paramiko import paramiko
@ -58,7 +58,7 @@ class ShellError(Exception):
class Shell(object): class Shell(object):
def __init__(self, prompts_re=None, errors_re=None, kickstart=True): def __init__(self, prompts_re=None, errors_re=None, kickstart=True, timeout=10):
self.ssh = None self.ssh = None
self.shell = None self.shell = None
@ -68,7 +68,12 @@ class Shell(object):
self.prompts = prompts_re or list() self.prompts = prompts_re or list()
self.errors = errors_re or list() self.errors = errors_re or list()
def open(self, host, port=22, username=None, password=None, timeout=10, self._timeout = timeout
self._history = list()
signal.signal(signal.SIGALRM, self.alarm_handler)
def open(self, host, port=22, username=None, password=None,
key_filename=None, pkey=None, look_for_keys=None, key_filename=None, pkey=None, look_for_keys=None,
allow_agent=False, key_policy="loose"): allow_agent=False, key_policy="loose"):
@ -90,15 +95,16 @@ class Shell(object):
if not look_for_keys: if not look_for_keys:
look_for_keys = password is None look_for_keys = password is None
try: try:
self.ssh.connect( self.ssh.connect(
host, port=port, username=username, password=password, host, port=port, username=username, password=password,
timeout=timeout, look_for_keys=look_for_keys, pkey=pkey, timeout=self._timeout, look_for_keys=look_for_keys, pkey=pkey,
key_filename=key_filename, allow_agent=allow_agent, key_filename=key_filename, allow_agent=allow_agent,
) )
self.shell = self.ssh.invoke_shell() self.shell = self.ssh.invoke_shell()
self.shell.settimeout(timeout) self.shell.settimeout(self._timeout)
except socket.gaierror: except socket.gaierror:
raise ShellError("unable to resolve host name") raise ShellError("unable to resolve host name")
except AuthenticationException: except AuthenticationException:
@ -121,6 +127,10 @@ class Shell(object):
data = regex.sub('', data) data = regex.sub('', data)
return data return data
def alarm_handler(self, signum, frame):
self.shell.close()
raise ShellError('timeout trying to send command: %s' % self._history[-1])
def receive(self, cmd=None): def receive(self, cmd=None):
recv = StringIO() recv = StringIO()
handled = False handled = False
@ -149,14 +159,21 @@ class Shell(object):
responses = list() responses = list()
try: try:
for command in to_list(commands): for command in to_list(commands):
signal.alarm(self._timeout)
self._history.append(str(command))
cmd = '%s\r' % str(command) cmd = '%s\r' % str(command)
self.shell.sendall(cmd) self.shell.sendall(cmd)
if self._timeout == 0:
return
responses.append(self.receive(command)) responses.append(self.receive(command))
except socket.timeout: except socket.timeout:
raise ShellError("timeout trying to send command: %s" % cmd) raise ShellError("timeout trying to send command: %s" % cmd)
except socket.error: except socket.error:
exc = get_exception() exc = get_exception()
raise ShellError("problem sending command to host: %s" % to_native(exc)) raise ShellError("problem sending command to host: %s" % to_native(exc))
return responses return responses
def close(self): def close(self):
@ -221,16 +238,16 @@ class CliBase(object):
kickstart=kickstart, kickstart=kickstart,
prompts_re=self.CLI_PROMPTS_RE, prompts_re=self.CLI_PROMPTS_RE,
errors_re=self.CLI_ERRORS_RE, errors_re=self.CLI_ERRORS_RE,
timeout=timeout
) )
self.shell.open(
host, port=port, username=username, password=password, self.shell.open(host, port=port, username=username,
key_filename=key_file, timeout=timeout, password=password, key_filename=key_file)
)
except ShellError: except ShellError:
exc = get_exception() exc = get_exception()
raise NetworkError( raise NetworkError(msg='failed to connect to %s:%s' % (host, port),
msg='failed to connect to %s:%s' % (host, port), exc=to_native(exc) exc=to_native(exc))
)
self._connected = True self._connected = True
@ -241,20 +258,17 @@ class CliBase(object):
def authorize(self, params, **kwargs): def authorize(self, params, **kwargs):
pass pass
### Command methods ###
def execute(self, commands): def execute(self, commands):
try: try:
return self.shell.send(commands) return self.shell.send(commands)
except ShellError: except ShellError:
exc = get_exception() exc = get_exception()
commands = [str(c) for c in commands]
raise NetworkError(to_native(exc), commands=commands) raise NetworkError(to_native(exc), commands=commands)
def run_commands(self, commands): def run_commands(self, commands):
return self.execute(to_list(commands)) return self.execute(to_list(commands))
### Config methods ###
def configure(self, commands): def configure(self, commands):
raise NotImplementedError raise NotImplementedError

Loading…
Cancel
Save