def test_issue_10404_ptys_not_released(self): n_executions = 15 # Get current number of PTY's try: nr_ptys = int(open('/proc/sys/kernel/pty/nr').read().strip()) except (ValueError, OSError, IOError): self.fail('Unable to find out how many PTY\'s are open') # Using context manager's for idx in range(0, nr_ptys + n_executions): try: with vt.Terminal('echo "Run {0}"'.format(idx), shell=True, stream_stdout=False, stream_stderr=False) as terminal: terminal.wait() try: if int(open('/proc/sys/kernel/pty/nr').read().strip()) > (nr_ptys + (n_executions/2)): self.fail('VT is not cleaning up PTY\'s') except (ValueError, OSError, IOError): self.fail('Unable to find out how many PTY\'s are open') except Exception as exc: if 'out of pty devices' in exc: # We're not cleaning up raise # We're pushing the system resources, let's keep going continue # Not using context manager's for idx in range(0, nr_ptys + n_executions): try: terminal = vt.Terminal('echo "Run {0}"'.format(idx), shell=True, stream_stdout=False, stream_stderr=False) terminal.wait() try: if int(open('/proc/sys/kernel/pty/nr').read().strip()) > (nr_ptys + (n_executions/2)): self.fail('VT is not cleaning up PTY\'s') except (ValueError, OSError, IOError): self.fail('Unable to find out how many PTY\'s are open') except Exception as exc: if 'out of pty devices' in exc: # We're not cleaning up raise # We're pushing the system resources, let's keep going continue
def run_command(cmd, options, sleep=0.5, return_output=False, stream_stdout=True, stream_stderr=True): ''' Run a command using VT ''' print_header(u'', sep='>', inline=True, width=options.output_columns) if isinstance(cmd, list): cmd = ' '.join(cmd) print_bulleted(options, 'Running command: {0}'.format(cmd)) print_header(u'', sep='-', inline=True, width=options.output_columns) sys.stdout.flush() if return_output is True: stdout_buffer = stderr_buffer = '' try: proc = vt.Terminal( cmd, shell=True, stream_stdout=stream_stdout, stream_stderr=stream_stderr ) proc_terminated = False while True: stdout, stderr = proc.recv(4096) if return_output is True: stdout_buffer += stdout or '' stderr_buffer += stderr or '' if proc_terminated: break if not proc.isalive() and not stdout and not stderr: proc_terminated = True time.sleep(sleep) if proc.exitstatus != 0: print_header(u'', sep='-', inline=True, width=options.output_columns) print_bulleted(options, 'Failed execute command. Exit code: {0}'.format(proc.exitstatus), 'RED') sys.stdout.flush() else: print_header(u'', sep='-', inline=True, width=options.output_columns) print_bulleted( options, 'Command execution succeeded. Exit code: {0}'.format(proc.exitstatus), 'LIGHT_GREEN' ) sys.stdout.flush() if return_output is True: return stdout_buffer, stderr_buffer, proc.exitstatus return proc.exitstatus except vt.TerminalException as exc: print_header(u'', sep='-', inline=True, width=options.output_columns) print_bulleted(options, '\n\nAn error occurred while running command:\n', 'RED') print(str(exc)) sys.stdout.flush() finally: print_header(u'', sep='<', inline=True, width=options.output_columns) sys.stdout.flush() proc.close(terminate=True, kill=True)
def test_vt_size(self): '''Confirm that the terminal size is being set''' self.skipTest('The code is not mature enough. Test disabled.') cols = random.choice(range(80, 250)) terminal = vt.Terminal('echo Foo!', shell=True, cols=cols) # First the assertion self.assertEqual(terminal.getwinsize(), (24, cols)) # Then wait for the terminal child to exit terminal.wait()
def test_vt_size(self): '''Confirm that the terminal size is being set''' if not sys.stdin.isatty(): self.skipTest('Not attached to a TTY. The test would fail.') cols = random.choice(range(80, 250)) terminal = vt.Terminal('echo "Foo!"', shell=True, cols=cols, rows=24, stream_stdout=False, stream_stderr=False) # First the assertion self.assertEqual(terminal.getwinsize(), (24, cols)) # Then wait for the terminal child to exit terminal.wait() terminal.close()
def test_isalive_no_child(): term = vt.Terminal( "sleep 100", shell=True, stream_stdout=False, stream_stderr=False, ) # make sure we have a valid term before we kill the term # commenting out for now, terminal seems to be stopping before this point aliveness = term.isalive() assert term.exitstatus is None assert aliveness is True # use a large hammer to make sure pid is really dead which will cause it to # raise an exception that we want to test for. os.kill(term.pid, signal.SIGKILL) os.waitpid(term.pid, 0) aliveness = term.isalive() assert term.exitstatus == 0 assert aliveness is False
def _run(cmd, cwd=None, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, output_loglevel='debug', runas=None, shell=DEFAULT_SHELL, python_shell=False, env=None, clean_env=False, rstrip=True, template=None, umask=None, timeout=None, with_communicate=True, reset_system_locale=True, ignore_retcode=False, saltenv='base', use_vt=False): ''' Do the DRY thing and only call subprocess.Popen() once ''' if _is_valid_shell(shell) is False: log.warning( 'Attempt to run a shell command with what may be an invalid shell! ' 'Check to ensure that the shell <{0}> is valid for this user.'. format(shell)) # Set the default working directory to the home directory of the user # salt-minion is running as. Defaults to home directory of user under which # the minion is running. if not cwd: cwd = os.path.expanduser('~{0}'.format('' if not runas else runas)) # make sure we can access the cwd # when run from sudo or another environment where the euid is # changed ~ will expand to the home of the original uid and # the euid might not have access to it. See issue #1844 if not os.access(cwd, os.R_OK): cwd = '/' if salt.utils.is_windows(): cwd = os.tempnam()[:3] else: # Handle edge cases where numeric/other input is entered, and would be # yaml-ified into non-string types cwd = str(cwd) if not salt.utils.is_windows(): if not os.path.isfile(shell) or not os.access(shell, os.X_OK): msg = 'The shell {0} is not available'.format(shell) raise CommandExecutionError(msg) if salt.utils.is_windows() and use_vt: # Memozation so not much overhead raise CommandExecutionError('VT not available on windows') if shell.lower().strip() == 'powershell': # If we were called by script(), then fakeout the Windows # shell to run a Powershell script. # Else just run a Powershell command. stack = traceback.extract_stack(limit=2) # extract_stack() returns a list of tuples. # The last item in the list [-1] is the current method. # The third item[2] in each tuple is the name of that method. if stack[-2][2] == 'script': cmd = 'Powershell -executionpolicy bypass -File ' + cmd else: cmd = 'Powershell "{0}"'.format(cmd.replace('"', '\\"')) # munge the cmd and cwd through the template (cmd, cwd) = _render_cmd(cmd, cwd, template, saltenv) ret = {} env = _parse_env(env) for bad_env_key in (x for x, y in six.iteritems(env) if y is None): log.error('Environment variable {0!r} passed without a value. ' 'Setting value to an empty string'.format(bad_env_key)) env[bad_env_key] = '' if runas and salt.utils.is_windows(): # TODO: Figure out the proper way to do this in windows msg = 'Sorry, {0} does not support runas functionality' raise CommandExecutionError(msg.format(__grains__['os'])) if runas: # Save the original command before munging it try: pwd.getpwnam(runas) except KeyError: raise CommandExecutionError( 'User {0!r} is not available'.format(runas)) try: # Getting the environment for the runas user # There must be a better way to do this. py_code = ( 'import os, itertools; ' 'print \"\\0\".join(itertools.chain(*os.environ.items()))') if __grains__['os'] in ['MacOS', 'Darwin']: env_cmd = ('sudo', '-i', '-u', runas, '--', sys.executable) elif __grains__['os'] in ['FreeBSD']: env_cmd = ('su', '-', runas, '-c', "{0} -c {1}".format(shell, sys.executable)) elif __grains__['os_family'] in ['Solaris']: env_cmd = ('su', '-', runas, '-c', sys.executable) elif __grains__['os_family'] in ['AIX']: env_cmd = ('su', runas, '-c', sys.executable) else: env_cmd = ('su', '-s', shell, '-', runas, '-c', sys.executable) env_encoded = subprocess.Popen( env_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(py_code)[0] import itertools env_runas = dict( itertools.izip(*[iter(env_encoded.split(b'\0'))] * 2)) env_runas.update(env) env = env_runas # Encode unicode kwargs to filesystem encoding to avoid a # UnicodeEncodeError when the subprocess is invoked. fse = sys.getfilesystemencoding() for key, val in six.iteritems(env): if isinstance(val, six.text_type): env[key] = val.encode(fse) except ValueError: raise CommandExecutionError( 'Environment could not be retrieved for User {0!r}'.format( runas)) if _check_loglevel(output_loglevel) is not None: # Always log the shell commands at INFO unless quiet logging is # requested. The command output is what will be controlled by the # 'loglevel' parameter. log.info('Executing command {0!r} {1}in directory {2!r}'.format( cmd, 'as user {0!r} '.format(runas) if runas else '', cwd)) if reset_system_locale is True: if not salt.utils.is_windows(): # Default to C! # Salt only knows how to parse English words # Don't override if the user has passed LC_ALL env.setdefault('LC_ALL', 'C') else: # On Windows set the codepage to US English. if python_shell: cmd = 'chcp 437 > nul & ' + cmd if clean_env: run_env = env else: run_env = os.environ.copy() run_env.update(env) if python_shell is None: python_shell = False kwargs = { 'cwd': cwd, 'shell': python_shell, 'env': run_env, 'stdin': str(stdin) if stdin is not None else stdin, 'stdout': stdout, 'stderr': stderr, 'with_communicate': with_communicate } if umask is not None: _umask = str(umask).lstrip('0') if _umask == '': msg = 'Zero umask is not allowed.' raise CommandExecutionError(msg) try: _umask = int(_umask, 8) except ValueError: msg = 'Invalid umask: \'{0}\''.format(umask) raise CommandExecutionError(msg) else: _umask = None if runas or umask: kwargs['preexec_fn'] = functools.partial(salt.utils.chugid_and_umask, runas, _umask) if not salt.utils.is_windows(): # close_fds is not supported on Windows platforms if you redirect # stdin/stdout/stderr if kwargs['shell'] is True: kwargs['executable'] = shell kwargs['close_fds'] = True if not os.path.isabs(cwd) or not os.path.isdir(cwd): raise CommandExecutionError( 'Specified cwd {0!r} either not absolute or does not exist'.format( cwd)) if python_shell is not True and not isinstance(cmd, list): posix = True if salt.utils.is_windows(): posix = False cmd = shlex.split(cmd, posix=posix) if not use_vt: # This is where the magic happens try: proc = salt.utils.timed_subprocess.TimedProc(cmd, **kwargs) except (OSError, IOError) as exc: raise CommandExecutionError( 'Unable to run command {0!r} with the context {1!r}, reason: {2}' .format(cmd, kwargs, exc)) try: proc.wait(timeout) except TimedProcTimeoutError as exc: ret['stdout'] = str(exc) ret['stderr'] = '' ret['retcode'] = None ret['pid'] = proc.process.pid # ok return code for timeouts? ret['retcode'] = 1 return ret out, err = proc.stdout, proc.stderr if rstrip: if out is not None: out = out.rstrip() if err is not None: err = err.rstrip() ret['pid'] = proc.process.pid ret['retcode'] = proc.process.returncode ret['stdout'] = out ret['stderr'] = err else: to = '' if timeout: to = ' (timeout: {0}s)'.format(timeout) if _check_loglevel(output_loglevel) is not None: log.debug('Running {0} in VT{1}'.format(cmd, to)) stdout, stderr = '', '' now = time.time() if timeout: will_timeout = now + timeout else: will_timeout = -1 try: proc = vt.Terminal(cmd, shell=True, log_stdout=True, log_stderr=True, cwd=cwd, preexec_fn=kwargs.get('preexec_fn', None), env=run_env, log_stdin_level=output_loglevel, log_stdout_level=output_loglevel, log_stderr_level=output_loglevel, stream_stdout=True, stream_stderr=True) ret['pid'] = proc.pid while proc.has_unread_data: try: try: time.sleep(0.5) try: cstdout, cstderr = proc.recv() except IOError: cstdout, cstderr = '', '' if cstdout: stdout += cstdout else: cstdout = '' if cstderr: stderr += cstderr else: cstderr = '' if timeout and (time.time() > will_timeout): ret['stderr'] = ( 'SALT: Timeout after {0}s\n{1}').format( timeout, stderr) ret['retcode'] = None break except KeyboardInterrupt: ret['stderr'] = 'SALT: User break\n{0}'.format(stderr) ret['retcode'] = 1 break except vt.TerminalException as exc: log.error('VT: {0}'.format(exc), exc_info_on_loglevel=logging.DEBUG) ret = {'retcode': 1, 'pid': '2'} break # only set stdout on success as we already mangled in other # cases ret['stdout'] = stdout if not proc.isalive(): # Process terminated, i.e., not canceled by the user or by # the timeout ret['stderr'] = stderr ret['retcode'] = proc.exitstatus ret['pid'] = proc.pid finally: proc.close(terminate=True, kill=True) try: if ignore_retcode: __context__['retcode'] = 0 else: __context__['retcode'] = ret['retcode'] except NameError: # Ignore the context error during grain generation pass return ret
def test_issue_10404_ptys_not_released(self): n_executions = 15 def current_pty_count(): # Get current number of PTY's try: if os.path.exists('/proc/sys/kernel/pty/nr'): with fopen('/proc/sys/kernel/pty/nr') as fh_: return int(fh_.read().strip()) proc = subprocess.Popen( 'sysctl -a 2> /dev/null | grep pty.nr | awk \'{print $3}\'', shell=True, stdout=subprocess.PIPE) stdout, _ = proc.communicate() return int(stdout.strip()) except (ValueError, OSError, IOError): if is_darwin(): # We're unable to findout how many PTY's are open self.skipTest( 'Unable to find out how many PTY\'s are open on Darwin - ' 'Skipping for now') self.fail('Unable to find out how many PTY\'s are open') nr_ptys = current_pty_count() # Using context manager's for idx in range(0, nr_ptys + n_executions): try: with vt.Terminal('echo "Run {0}"'.format(idx), shell=True, stream_stdout=False, stream_stderr=False) as terminal: terminal.wait() try: if current_pty_count() > (nr_ptys + (n_executions / 2)): self.fail('VT is not cleaning up PTY\'s') except (ValueError, OSError, IOError): self.fail('Unable to find out how many PTY\'s are open') except Exception as exc: if 'out of pty devices' in exc: # We're not cleaning up raise # We're pushing the system resources, let's keep going continue # Not using context manager's for idx in range(0, nr_ptys + n_executions): try: terminal = vt.Terminal('echo "Run {0}"'.format(idx), shell=True, stream_stdout=False, stream_stderr=False) terminal.wait() try: if current_pty_count() > (nr_ptys + (n_executions / 2)): self.fail('VT is not cleaning up PTY\'s') except (ValueError, OSError, IOError): self.fail('Unable to find out how many PTY\'s are open') except Exception as exc: if 'out of pty devices' in exc: # We're not cleaning up raise # We're pushing the system resources, let's keep going continue
def test_isalive_while_theres_data_to_read(self): expected_data = 'Alive!\n' term = vt.Terminal('echo "Alive!"', shell=True, stream_stdout=False, stream_stderr=False) buffer_o = buffer_e = '' try: while term.has_unread_data: stdout, stderr = term.recv() if stdout: buffer_o += stdout if stderr: buffer_e += stderr # While there's data to be read, the process is alive if stdout is None and stderr is None: self.assertFalse(term.isalive()) # term should be dead now self.assertEqual(buffer_o, expected_data) self.assertFalse(term.isalive()) stdout, stderr = term.recv() self.assertFalse(term.isalive()) self.assertIsNone(stderr) self.assertIsNone(stdout) finally: term.close(terminate=True, kill=True) expected_data = 'Alive!\n' term = vt.Terminal('echo "Alive!" 1>&2', shell=True, stream_stdout=False, stream_stderr=False) buffer_o = buffer_e = '' try: while term.has_unread_data: stdout, stderr = term.recv() if stdout: buffer_o += stdout if stderr: buffer_e += stderr # While there's data to be read, the process is alive if stdout is None and stderr is None: self.assertFalse(term.isalive()) # term should be dead now self.assertEqual(buffer_e, expected_data) self.assertFalse(term.isalive()) stdout, stderr = term.recv() self.assertFalse(term.isalive()) self.assertIsNone(stderr) self.assertIsNone(stdout) finally: term.close(terminate=True, kill=True) expected_data = 'Alive!\nAlive!\n' term = vt.Terminal('echo "Alive!"; sleep 5; echo "Alive!"', shell=True, stream_stdout=False, stream_stderr=False) buffer_o = buffer_e = '' try: while term.has_unread_data: stdout, stderr = term.recv() if stdout: buffer_o += stdout if stderr: buffer_e += stderr # While there's data to be read, the process is alive if stdout is None and stderr is None: self.assertFalse(term.isalive()) if buffer_o != expected_data: self.assertTrue(term.isalive()) # Don't spin time.sleep(0.1) # term should be dead now self.assertEqual(buffer_o, expected_data) self.assertFalse(term.isalive()) stdout, stderr = term.recv() self.assertFalse(term.isalive()) self.assertIsNone(stderr) self.assertIsNone(stdout) finally: term.close(terminate=True, kill=True)
def root_cmd(command, tty, sudo, **kwargs): ''' Wrapper for commands to be run as root ''' if sudo: if 'sudo_password' in kwargs and kwargs['sudo_password'] is not None: command = 'echo "{1}" | sudo -S {0}'.format( command, kwargs['sudo_password'], ) else: command = 'sudo {0}'.format(command) log.debug('Using sudo to run command {0}'.format(command)) ssh_args = [] if tty: # Use double `-t` on the `ssh` command, it's necessary when `sudo` has # `requiretty` enforced. ssh_args.extend(['-t', '-t']) ssh_args.extend([ # Don't add new hosts to the host key database '-oStrictHostKeyChecking=no', # Set hosts key database path to /dev/null, ie, non-existing '-oUserKnownHostsFile=/dev/null', # Don't re-use the SSH connection. Less failures. '-oControlPath=none' ]) if 'key_filename' in kwargs: # There should never be both a password and an ssh key passed in, so ssh_args.extend([ # tell SSH to skip password authentication '-oPasswordAuthentication=no', '-oChallengeResponseAuthentication=no', # Make sure public key authentication is enabled '-oPubkeyAuthentication=yes', # No Keyboard interaction! '-oKbdInteractiveAuthentication=no', # Also, specify the location of the key file '-i {0}'.format(kwargs['key_filename']) ]) cmd = 'ssh {0} {1[username]}@{1[hostname]} {2}'.format( ' '.join(ssh_args), kwargs, pipes.quote(command)) log.debug('SSH command: {0!r}'.format(cmd)) try: proc = vt.Terminal(cmd, shell=True, log_stdout=True, log_stderr=True, stream_stdout=kwargs.get('display_ssh_output', True), stream_stderr=kwargs.get('display_ssh_output', True)) sent_password = False while proc.isalive(): stdout, stderr = proc.recv() if stdout and SSH_PASSWORD_PROMP_RE.match(stdout): if sent_password: # second time??? Wrong password? log.warning( 'Asking for password again. Wrong one provided???') proc.terminate() return 1 proc.sendline(kwargs['password']) sent_password = True time.sleep(0.025) return proc.exitstatus except vt.TerminalException as err: log.error('Failed to execute command {0!r}: {1}\n'.format( command, err), exc_info=True) # Signal an error return 1
def scp_file(dest_path, contents, kwargs): ''' Use scp to copy a file to a server ''' tmpfh, tmppath = tempfile.mkstemp() with salt.utils.fopen(tmppath, 'w') as tmpfile: tmpfile.write(contents) log.debug('Uploading {0} to {1} (scp)'.format(dest_path, kwargs['hostname'])) ssh_args = [ # Don't add new hosts to the host key database '-oStrictHostKeyChecking=no', # Set hosts key database path to /dev/null, ie, non-existing '-oUserKnownHostsFile=/dev/null', # Don't re-use the SSH connection. Less failures. '-oControlPath=none' ] if 'key_filename' in kwargs: # There should never be both a password and an ssh key passed in, so ssh_args.extend([ # tell SSH to skip password authentication '-oPasswordAuthentication=no', '-oChallengeResponseAuthentication=no', # Make sure public key authentication is enabled '-oPubkeyAuthentication=yes', # No Keyboard interaction! '-oKbdInteractiveAuthentication=no', # Also, specify the location of the key file '-i {0}'.format(kwargs['key_filename']) ]) cmd = 'scp {0} {1} {2[username]}@{2[hostname]}:{3}'.format( ' '.join(ssh_args), tmppath, kwargs, dest_path) log.debug('SCP command: {0!r}'.format(cmd)) try: proc = vt.Terminal(cmd, shell=True, log_stdout=True, log_stderr=True, stream_stdout=kwargs.get('display_ssh_output', True), stream_stderr=kwargs.get('display_ssh_output', True)) log.debug('Uploading file(PID {0}): {1!r}'.format(proc.pid, dest_path)) sent_password = False while proc.isalive(): stdout, stderr = proc.recv() if stdout and SSH_PASSWORD_PROMP_RE.match(stdout): if sent_password: # second time??? Wrong password? log.warning( 'Asking for password again. Wrong one provided???') proc.terminate() return 1 proc.sendline(kwargs['password']) sent_password = True time.sleep(0.025) return proc.exitstatus except vt.TerminalException as err: log.error('Failed to upload file {0!r}: {1}\n'.format(dest_path, err), exc_info=True) # Signal an error return 1
def run(name, cmd, container_type=None, exec_driver=None, output=None, no_start=False, stdin=None, python_shell=True, output_loglevel='debug', ignore_retcode=False, path=None, use_vt=False, keep_env=None): ''' Common logic for running shell commands in containers path path to the container parent (for LXC only) default: /var/lib/lxc (system default) .. versionadded:: Beryllium CLI Example: .. code-block:: bash salt myminion container_resource.run mycontainer 'ps aux' container_type=docker exec_driver=nsenter output=stdout ''' valid_output = ('stdout', 'stderr', 'retcode', 'all') if output is None: cmd_func = 'cmd.run' elif output not in valid_output: raise SaltInvocationError( '\'output\' param must be one of the following: {0}'.format( ', '.join(valid_output))) else: cmd_func = 'cmd.run_all' if keep_env is None or isinstance(keep_env, bool): to_keep = [] elif not isinstance(keep_env, (list, tuple)): try: to_keep = keep_env.split(',') except AttributeError: log.warning('Invalid keep_env value, ignoring') to_keep = [] else: to_keep = keep_env if exec_driver == 'lxc-attach': full_cmd = 'lxc-attach ' if path: full_cmd += '-P {0} '.format(pipes.quote(path)) if keep_env is not True: full_cmd += '--clear-env ' if 'PATH' not in to_keep: full_cmd += '--set-var {0} '.format(PATH) # --clear-env results in a very restrictive PATH # (/bin:/usr/bin), use a good fallback. full_cmd += ' '.join([ '--set-var {0}={1}'.format(x, pipes.quote(os.environ[x])) for x in to_keep if x in os.environ ]) full_cmd += ' -n {0} -- {1}'.format(pipes.quote(name), cmd) elif exec_driver == 'nsenter': pid = __salt__['{0}.pid'.format(container_type)](name) full_cmd = ('nsenter --target {0} --mount --uts --ipc --net --pid -- '. format(pid)) if keep_env is not True: full_cmd += 'env -i ' if 'PATH' not in to_keep: full_cmd += '{0} '.format(PATH) full_cmd += ' '.join([ '{0}={1}'.format(x, pipes.quote(os.environ[x])) for x in to_keep if x in os.environ ]) full_cmd += ' {0}'.format(cmd) elif exec_driver == 'docker-exec': # We're using docker exec on the CLI as opposed to via docker-py, since # the Docker API doesn't return stdout and stderr separately. full_cmd = 'docker exec ' if stdin: full_cmd += '-i ' full_cmd += '{0} '.format(name) if keep_env is not True: full_cmd += 'env -i ' if 'PATH' not in to_keep: full_cmd += '{0} '.format(PATH) full_cmd += ' '.join([ '{0}={1}'.format(x, pipes.quote(os.environ[x])) for x in to_keep if x in os.environ ]) full_cmd += ' {0}'.format(cmd) if not use_vt: ret = __salt__[cmd_func](full_cmd, stdin=stdin, python_shell=python_shell, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode) else: stdout, stderr = '', '' try: proc = vt.Terminal(full_cmd, shell=python_shell, log_stdin_level=output_loglevel if output_loglevel == 'quiet' else 'info', log_stdout_level=output_loglevel, log_stderr_level=output_loglevel, log_stdout=True, log_stderr=True, stream_stdout=False, stream_stderr=False) # Consume output while proc.has_unread_data: try: cstdout, cstderr = proc.recv() if cstdout: stdout += cstdout if cstderr: if output is None: stdout += cstderr else: stderr += cstderr time.sleep(0.5) except KeyboardInterrupt: break ret = stdout if output is None \ else {'retcode': proc.exitstatus, 'pid': 2, 'stdout': stdout, 'stderr': stderr} except vt.TerminalException: trace = traceback.format_exc() log.error(trace) ret = stdout if output is None \ else {'retcode': 127, 'pid': 2, 'stdout': stdout, 'stderr': stderr} finally: proc.terminate() return ret
def run(name, cmd, output=None, no_start=False, stdin=None, python_shell=True, output_loglevel='debug', ignore_retcode=False, use_vt=False): ''' Common logic for running shell commands in containers Requires the full command to be passed to :mod:`cmd.run <salt.modules.cmdmod.run>`/:mod:`cmd.run_all <salt.modules.cmdmod.run_all>` ''' valid_output = ('stdout', 'stderr', 'retcode', 'all') if output is None: cmd_func = 'cmd.run' elif output not in valid_output: raise SaltInvocationError( '\'output\' param must be one of the following: {0}' .format(', '.join(valid_output)) ) else: cmd_func = 'cmd.run_all' if not use_vt: ret = __salt__[cmd_func](cmd, stdin=stdin, python_shell=python_shell, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode) else: stdout, stderr = '', '' try: proc = vt.Terminal(cmd, shell=python_shell, log_stdin_level=output_loglevel if output_loglevel == 'quiet' else 'info', log_stdout_level=output_loglevel, log_stderr_level=output_loglevel, log_stdout=True, log_stderr=True, stream_stdout=False, stream_stderr=False) # Consume output while proc.has_unread_data: try: cstdout, cstderr = proc.recv() if cstdout: stdout += cstdout if cstderr: if output is None: stdout += cstderr else: stderr += cstderr time.sleep(0.5) except KeyboardInterrupt: break ret = stdout if output is None \ else {'retcode': proc.exitstatus, 'pid': 2, 'stdout': stdout, 'stderr': stderr} except vt.TerminalException: trace = traceback.format_exc() log.error(trace) ret = stdout if output is None \ else {'retcode': 127, 'pid': 2, 'stdout': stdout, 'stderr': stderr} finally: proc.terminate() return ret