def start(self): ''' Start the daemon subprocess ''' # Late import import salt.utils.nb_popen as nb_popen log.info('[%s][%s] Starting DAEMON in CWD: %s', self.log_prefix, self.cli_display_name, self.cwd) proc_args = [self.get_script_path(self.cli_script_name) ] + self.get_base_script_args() + self.get_script_args() if sys.platform.startswith('win'): # Windows needs the python executable to come first proc_args.insert(0, sys.executable) log.info('[%s][%s] Running \'%s\'...', self.log_prefix, self.cli_display_name, ' '.join(proc_args)) self._terminal = nb_popen.NonBlockingPopen(proc_args, env=self.environ, cwd=self.cwd) process_output_thread = threading.Thread(target=self._process_output) process_output_thread.daemon = True self._running.set() process_output_thread.start() self._children = collect_child_processes(self._terminal.pid) atexit.register(self.terminate) return True
def init_terminal(self, cmdline, **kwargs): ''' Instantiate a terminal with the passed cmdline and kwargs and return it. Additionaly, it sets a reference to it in self._terminal and also collects an initial listing of child processes which will be used when terminating the terminal ''' # Late import import salt.utils.nb_popen as nb_popen self._terminal = nb_popen.NonBlockingPopen(cmdline, **kwargs) self._children = collect_child_processes(self._terminal.pid) atexit.register(self.terminate) return self._terminal
def run(self, tags=(), timeout=10): # pylint: disable=arguments-differ ''' Run the given command synchronously ''' # Late import import salt.ext.six as six import salt.utils.nb_popen as nb_popen exitcode = 0 timeout_expire = time.time() + timeout environ = self.environ.copy() environ['PYTEST_LOG_PREFIX'] = '[{0}][EventListen] '.format( self.log_prefix) proc_args = [self.get_script_path(self.cli_script_name) ] + self.get_base_script_args() + self.get_script_args() if sys.platform.startswith('win'): # Windows needs the python executable to come first proc_args.insert(0, sys.executable) log.info('[%s][%s] Running \'%s\' in CWD: %s...', self.log_prefix, self.cli_display_name, ' '.join(proc_args), self.cwd) to_match_events = set(tags) matched_events = {} terminal = nb_popen.NonBlockingPopen(proc_args, cwd=self.cwd, env=environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Consume the output stdout = six.b('') stderr = six.b('') process_output = six.b('') events_processed = 0 try: while True: if terminal.stdout is not None: try: out = terminal.recv(4096) except IOError: out = six.b('') if out: stdout += out process_output += out if terminal.stderr is not None: try: err = terminal.recv_err(4096) except IOError: err = '' if err: stderr += err if out is None and err is None: if to_match_events: exitcode = 1 log.warning( '[%s][%s] Premature exit?! Failed to find all of the required event tags. ' 'Total events processed: %s', self.log_prefix, self.cli_display_name, events_processed) break if process_output: lines = process_output.split(b'}\n') if lines[-1] != b'': process_output = lines.pop() else: process_output = six.b('') lines.pop() for line in lines: match = self.EVENT_MATCH_RE.match( line.decode(__salt_system_encoding__)) # pylint: disable=undefined-variable if match: events_processed += 1 tag, data = match.groups() if tag in to_match_events: matched_events[tag] = json.loads(data + '}') to_match_events.remove(tag) log.info('[%s][%s] Events processed so far: %d', self.log_prefix, self.cli_display_name, events_processed) if not to_match_events: log.debug('[%s][%s] ALL EVENT TAGS FOUND!!!', self.log_prefix, self.cli_display_name) break if timeout_expire < time.time(): log.warning( '[%s][%s] Failed to find all of the required event tags. Total events processed: %s', self.log_prefix, self.cli_display_name, events_processed) exitcode = 1 break except (SystemExit, KeyboardInterrupt): pass finally: if terminal.stdout: terminal.stdout.close() if terminal.stderr: terminal.stderr.close() terminate_process(pid=terminal.pid, kill_children=True, slow_stop=self.slow_stop) if six.PY3: # pylint: disable=undefined-variable stdout = stdout.decode(__salt_system_encoding__) stderr = stderr.decode(__salt_system_encoding__) # pylint: enable=undefined-variable json_out = {'matched': matched_events, 'unmatched': to_match_events} return ShellResult(exitcode, stdout, stderr, json_out)
def run(self, *args, **kwargs): ''' Run the given command synchronously ''' # Late import import salt.ext.six as six import salt.utils.nb_popen as nb_popen timeout = kwargs.get('timeout', self.DEFAULT_TIMEOUT) if 'fail_hard' in kwargs: # Explicit fail_hard passed fail_hard = kwargs.pop('fail_hard') else: # Get the value of the _salt_fail_hard fixture try: fail_hard = self.request.getfixturevalue('_salt_fail_hard') except AttributeError: fail_hard = self.request.getfuncargvalue('_salt_fail_hard') if fail_hard is True: fail_method = pytest.fail else: fail_method = pytest.xfail log.info('The fail hard setting for %s is: %s', self.cli_script_name, fail_hard) minion_tgt = self.get_minion_tgt(**kwargs) timeout_expire = time.time() + kwargs.pop('timeout', self.DEFAULT_TIMEOUT) environ = self.environ.copy() environ['PYTEST_LOG_PREFIX'] = '[{0}] '.format(self.log_prefix) proc_args = [self.get_script_path(self.cli_script_name) ] + self.get_base_script_args() + self.get_script_args() if sys.platform.startswith('win'): # Windows needs the python executable to come first proc_args.insert(0, sys.executable) if minion_tgt is not None: proc_args.append(minion_tgt) proc_args.extend(list(args)) for key in kwargs: proc_args.append('{0}={1}'.format(key, kwargs[key])) log.info('[%s][%s] Running \'%s\' in CWD: %s ...', self.log_prefix, self.cli_display_name, ' '.join(proc_args), self.cwd) terminal = nb_popen.NonBlockingPopen(proc_args, cwd=self.cwd, env=environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Consume the output stdout = six.b('') stderr = six.b('') try: while True: # We're not actually interested in processing the output, just consume it if terminal.stdout is not None: try: out = terminal.recv(4096) except IOError: out = six.b('') if out: stdout += out if terminal.stderr is not None: try: err = terminal.recv_err(4096) except IOError: err = '' if err: stderr += err if out is None and err is None: break if timeout_expire < time.time(): terminate_process(pid=terminal.pid, kill_children=True, slow_stop=self.slow_stop) fail_method( '[{0}][{1}] Failed to run: args: {2!r}; kwargs: {3!r}; Error: {4}' .format( self.log_prefix, self.cli_display_name, args, kwargs, '[{0}][{1}] Timed out after {2} seconds!'.format( self.log_prefix, self.cli_display_name, timeout))) except (SystemExit, KeyboardInterrupt): pass finally: if terminal.stdout: terminal.stdout.close() if terminal.stderr: terminal.stderr.close() terminate_process(pid=terminal.pid, kill_children=True, slow_stop=self.slow_stop) if six.PY3: # pylint: disable=undefined-variable stdout = stdout.decode(__salt_system_encoding__) stderr = stderr.decode(__salt_system_encoding__) # pylint: enable=undefined-variable exitcode = terminal.returncode stdout, stderr, json_out = self.process_output(minion_tgt, stdout, stderr) return ShellResult(exitcode, stdout, stderr, json_out)