def test_start_should_log_args(self, monkeypatch): monkeypatch.setattr(cmdutils, "command_log_line", mock.MagicMock()) monkeypatch.setattr(commands, "log", fakelib.FakeLogger()) cmdutils.command_log_line.return_value = "zorro" args = ["true"] commands.start(args) assert (logging.DEBUG, "zorro", {}) in commands.log.messages
def test_out(self): p = commands.start( ["echo", "-n", "out"], stdout=subprocess.PIPE) out, err = p.communicate() assert out == b"out" assert err is None
def test_in_err(self): p = commands.start( ["sh", "-c", "cat >&2"], stdin=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate(b"data") assert out is None assert err == b"data"
def test_in_out(self): p = commands.start( ["cat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) out, err = p.communicate(b"data") assert out == b"data" assert err is None
def test_out_err(self): p = commands.start( ["sh", "-c", "echo -n out >&1; echo -n err >&2"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() assert out == b"out" assert err == b"err"
def test_start_nonexisting(self): with pytest.raises(OSError): args = ["doesn't exist"] commands.start(args, reset_cpu_affinity=False)
def test_out(self): p = commands.start(["echo", "-n", "out"], stdout=subprocess.PIPE) out, err = p.communicate() assert out == b"out" assert err is None
def test_false(self): p = commands.start(["false"]) out, err = p.communicate() assert p.returncode == 1 assert out is None assert err is None
def test_sudo_terminate(self): p = commands.start(["sleep", "1"], sudo=True) p.terminate() assert p.wait() == -signal.SIGTERM
def run_logging(args, log_tag=None): """ Start a command storing its stdout/stderr into a file, communicate with it, and wait until the command terminates. Ensures that the command is killed if an unexpected error is raised. Note that since the stdout/stderr is redirected to the file it is not piped to VDSM. args are logged when command starts, and are included in the exception if a command has failed. If args contain sensitive information that should not be logged, such as passwords, they must be wrapped with ProtectedPassword. While access to the directory with log files is restricted caution should be taken when logging commands. Avoid storing output of commands that may leak passwords or other sensitive information. The child process stdout and stderr are always buffered. If you have special needs, such as running the command without buffering stdout, or create a pipeline of several commands, use the lower level start() function. If log_tag is not used the log file name is 'virtcmd-<command>-<datetime>-<random_string>.log'. If log_tag is used the format is 'virtcmd-<command>-<log_tag>-<datetime>-<random_string>.log'. The granularity of <datetime> part of the file name is one second. To minimize file collision there is a random string of characters appended to the name. Arguments: args (list): Command arguments log_tag (str): Optional string to be included in log file name to better distinguish the log files and avoid potential name collisions. This could be for example VM ID. Returns: Path to the log file. Raises: LoggingError if the command terminated with a non-zero exit code. TerminatingFailure if command could not be terminated. """ stdout = subprocess.DEVNULL stderr = subprocess.DEVNULL cmd_log_file = None timestamp = time.strftime('%Y%m%dT%H%M%S') command = os.path.basename(str(args[0])) log_tag = '' if log_tag is None else '-%s' % log_tag suffix = ''.join(random.choice(string.ascii_lowercase) for _ in range(4)) cmd_log_path = os.path.join( _COMMANDS_LOG_DIR, 'virtcmd-%s%s-%s-%s.log' % (command, log_tag, timestamp, suffix)) try: os.makedirs(_COMMANDS_LOG_DIR, mode=0o750, exist_ok=True) cmd_log_file = os.open(cmd_log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, mode=0o640) except OSError: log.exception('Failed to open log file') cmd_log_path = None else: stdout = cmd_log_file stderr = cmd_log_file log.debug('Running command %r with logs in %s', args, cmd_log_path) p = start(args, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr) with terminating(p): p.communicate() log.debug(cmdutils.retcode_log_line(p.returncode)) if cmd_log_file is not None: os.close(cmd_log_file) if p.returncode != 0: raise LoggingError(args, p.returncode, cmd_log_path) return cmd_log_path
def testResetAffinityByDefault(self): proc = commands.start((EXT_SLEEP, '30s')) try: self.assertEqual(taskset.get(proc.pid), taskset.get(os.getpid())) finally: proc.kill()
def test_child_terminated(self): p = commands.start(["sleep", "1"]) commands.run(["kill", "-%d" % signal.SIGTERM, "%d" % p.pid]) assert p.wait() == -signal.SIGTERM
def test_start_cat_out(self): p = commands.start(["cat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) out, err = p.communicate(b"data") assert out == b"data" assert err == b""
def _runHooksDir(data, dir, vmconf={}, raiseError=True, errors=None, params={}, hookType=_DOMXML_HOOK): if errors is None: errors = [] scripts = _scriptsPerDir(dir) scripts.sort() if not scripts: return data data_fd, data_filename = tempfile.mkstemp() try: if hookType == _DOMXML_HOOK: os.write(data_fd, data.encode('utf-8') if data else b'') elif hookType == _JSON_HOOK: os.write(data_fd, json.dumps(data).encode('utf-8')) os.close(data_fd) scriptenv = os.environ.copy() # Update the environment using params and custom configuration env_update = [ six.iteritems(params), six.iteritems(vmconf.get('custom', {})) ] # Encode custom properties to UTF-8 and save them to scriptenv # Pass str objects (byte-strings) without any conversion for k, v in itertools.chain(*env_update): try: if isinstance(v, six.text_type): scriptenv[k] = v.encode('utf-8') else: scriptenv[k] = v except UnicodeEncodeError: pass if vmconf.get('vmId'): scriptenv['vmId'] = vmconf.get('vmId') ppath = scriptenv.get('PYTHONPATH', '') hook = os.path.dirname(pkgutil.get_loader('vdsm.hook').get_filename()) scriptenv['PYTHONPATH'] = ':'.join(ppath.split(':') + [hook]) if hookType == _DOMXML_HOOK: scriptenv['_hook_domxml'] = data_filename elif hookType == _JSON_HOOK: scriptenv['_hook_json'] = data_filename for s in scripts: p = commands.start([s], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=scriptenv) with commands.terminating(p): (out, err) = p.communicate() rc = p.returncode logging.info('%s: rc=%s err=%s', s, rc, err) if rc != 0: errors.append(err) if rc == 2: break elif rc > 2: logging.warning('hook returned unexpected return code %s', rc) if errors and raiseError: raise exception.HookError(err) with open(data_filename) as f: final_data = f.read() finally: os.unlink(data_filename) if hookType == _DOMXML_HOOK: return final_data elif hookType == _JSON_HOOK: return json.loads(final_data)
def test_sudo_kill(self): p = commands.start(["sleep", "1"], sudo=True) p.kill() assert p.wait() == -signal.SIGKILL
def setUp(self): self.proc = commands.start([EXT_SLEEP, "2"]) self.proc_poll = self.proc.poll self.proc_kill = self.proc.kill self.proc_wait = self.proc.wait
def test_true(self): p = commands.start(["true"]) out, err = p.communicate() assert p.returncode == 0 assert out is None assert err is None
def test_out(self): p = commands.start(["echo", "-n", "it works"], stdout=subprocess.PIPE) out, err = commands.communicate(p) assert p.returncode == 0 assert out == b"it works" assert err is None