def test_run_external_command_fail_with_output(self): temp_dir = tempfile.mkdtemp() try: script = os.path.join(temp_dir, 'yo.py') # create a small python script that outputs args passed # in to standard out, writes error to standard error # and exits with 0 exit code f = open(script, 'w') f.write('#! /usr/bin/env python\n\n') f.write('import sys\n') f.write('sys.stdout.write(sys.argv[1])\n') f.write('sys.stdout.write(sys.argv[2])\n') f.write('sys.stderr.write("2error")\n') f.write('sys.exit(2)\n') f.flush() f.close() os.chmod(script, stat.S_IRWXU) ecode, out, err = util.run_external_command(script + ' hi how') self.assertEqual(err, '2error') self.assertEqual(out, 'hihow') self.assertEqual(ecode, 2) finally: shutil.rmtree(temp_dir)
def test_run_external_command_cmd_does_not_exist(self): temp_dir = tempfile.mkdtemp() try: try: noexist = os.path.join(temp_dir, 'noexist') ecode, out, err = util.run_external_command(noexist) self.fail('Expected OSError') except OSError: pass finally: shutil.rmtree(temp_dir)
def run_external_command(self, command_name, cmd_to_run, command_failure_is_fatal, timeout=None, kill_delay=10, polling_sleep_time=1): """Runs external command line process Method runs external process logging the command run to email log. Standard output and error are written to current working directory `command_name`.stdout and `command_name`.stderr respectively and if process. :param command_name: Name of command, human readable version used as prefix for .stderr and .stdout files :param cmd_to_run: command with arguments to run, passed to subprocess.Popen :param command_failure_is_fatal: True means if process fails with nonzero exit code or there is an exception then status of task is set to D3RTask.ERROR_STATUS and failure message appended to email log and task.set_error is set :return: exit code of process :raise: UnsetNameError if command_name is None :raise: UnsetCommandError if cmd_to_run is None """ if command_name is None: raise UnsetNameError('Command name must be set') if cmd_to_run is None: raise UnsetCommandError('Command must be set') if command_failure_is_fatal is None: command_failure_is_fatal = True logger.info("Running command " + cmd_to_run) self.append_to_email_log('Running command: ' + cmd_to_run + '\n') cmd_tmp_dir = None try: if timeout is not None: cmd_tmp_dir = os.path.join( self.get_dir(), command_name + str(uuid.uuid1()) + D3RTask.TMP_DIR_SUFFIX) os.makedirs(cmd_tmp_dir, mode=0o0775) pst = polling_sleep_time # trying to make flake8 happy returncode, out, err = util.\ run_external_command_with_timeout(cmd_to_run, cmd_tmp_dir, timeout=timeout, kill_delay=kill_delay, polling_sleep_time=pst) else: returncode, out, err = util.run_external_command(cmd_to_run) except Exception as e: logger.exception("Error caught exception") self.set_status(D3RTask.ERROR_STATUS) self.set_error("Caught Exception trying to run " + cmd_to_run + " : " + str(e)) self.end() return 1 finally: if cmd_tmp_dir is not None: shutil.rmtree(cmd_tmp_dir) self.write_to_file(err, command_name + D3RTask.STDERR_SUFFIX) self.write_to_file(out, command_name + D3RTask.STDOUT_SUFFIX) if returncode != 0: if command_failure_is_fatal: self.set_status(D3RTask.ERROR_STATUS) self.set_error("Non zero exit code: " + str(returncode) + " received. Standard out: " + out + " Standard error: " + err) else: max_chars = D3RTask.MAX_CHARS_FOR_EMAIL_STR trunc_out = self._get_email_truncated_string(out, max_chars) trunc_err = self._get_email_truncated_string(err, max_chars) self.append_to_email_log("Although considered non fatal " + "for processing of stage a " + "non zero exit code: " + str(returncode) + "received. Standard out: " + trunc_out + " Standard error : " + trunc_err) return returncode
def test_run_external_command_command_not_set(self): ecode, out, err = util.run_external_command(None) self.assertEqual(ecode, 256) self.assertEqual(out, '') self.assertEqual(err, 'Command must be set')