def _initialize(self): if self._process is not None and self._process.poll() is not None: LOG.warning("Leaving behind already spawned process with pid %d, " "root should kill it if it's still there (I can't)", self._process.pid) process_obj = subprocess.Popen(self._start_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) LOG.debug("Popen for %s command has been instantiated", self._start_command) self._process = process_obj socket_path = process_obj.stdout.readline()[:-1] # For Python 3 we need to convert bytes to str here if not isinstance(socket_path, str): socket_path = socket_path.decode('utf-8') authkey = process_obj.stdout.read(32) if process_obj.poll() is not None: stderr = process_obj.stderr.read() # NOTE(yorik-sar): don't expose stdout here raise Exception("Failed to spawn rootwrap process.\nstderr:\n%s" % (stderr,)) LOG.info("Spawned new rootwrap daemon process with pid=%d", process_obj.pid) self._manager = ClientManager(socket_path, authkey) self._manager.connect() self._proxy = self._manager.rootwrap() self._finalize = finalize(self, self._shutdown, self._process, self._manager) self._initialized = True
def execute(self, cmd, stdin=None): proc = subprocess.Popen( self.cmd + cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) out, err = proc.communicate(stdin) self.addDetail('stdout', content.text_content(out.decode('utf-8', 'replace'))) self.addDetail('stderr', content.text_content(err.decode('utf-8', 'replace'))) return proc.returncode, out, err
def test_KillFilter(self): if not os.path.exists("/proc/%d" % os.getpid()): self.skipTest("Test requires /proc filesystem (procfs)") p = subprocess.Popen(["cat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) try: f = filters.KillFilter("root", "/bin/cat", "-9", "-HUP") f2 = filters.KillFilter("root", "/usr/bin/cat", "-9", "-HUP") usercmd = ['kill', '-ALRM', p.pid] # Incorrect signal should fail self.assertFalse(f.match(usercmd) or f2.match(usercmd)) usercmd = ['kill', p.pid] # Providing no signal should fail self.assertFalse(f.match(usercmd) or f2.match(usercmd)) # Providing matching signal should be allowed usercmd = ['kill', '-9', p.pid] self.assertTrue(f.match(usercmd) or f2.match(usercmd)) f = filters.KillFilter("root", "/bin/cat") f2 = filters.KillFilter("root", "/usr/bin/cat") usercmd = ['kill', os.getpid()] # Our own PID does not match /bin/sleep, so it should fail self.assertFalse(f.match(usercmd) or f2.match(usercmd)) usercmd = ['kill', 999999] # Nonexistent PID should fail self.assertFalse(f.match(usercmd) or f2.match(usercmd)) usercmd = ['kill', p.pid] # Providing no signal should work self.assertTrue(f.match(usercmd) or f2.match(usercmd)) # verify that relative paths are matched against $PATH f = filters.KillFilter("root", "cat") # Our own PID does not match so it should fail usercmd = ['kill', os.getpid()] self.assertFalse(f.match(usercmd)) # Filter should find cat in /bin or /usr/bin usercmd = ['kill', p.pid] self.assertTrue(f.match(usercmd)) # Filter shouldn't be able to find binary in $PATH, so fail with fixtures.EnvironmentVariable("PATH", "/foo:/bar"): self.assertFalse(f.match(usercmd)) # ensure that unset $PATH is not causing an exception with fixtures.EnvironmentVariable("PATH"): self.assertFalse(f.match(usercmd)) finally: # Terminate the "cat" process and wait for it to finish p.terminate() p.wait()
def start_subprocess(filter_list, userargs, exec_dirs=[], log=False, **kwargs): filtermatch = match_filter(filter_list, userargs, exec_dirs) command = filtermatch.get_command(userargs, exec_dirs) if log: logging.info("(%s > %s) Executing %s (filter match = %s)" % ( _getlogin(), pwd.getpwuid(os.getuid())[0], command, filtermatch.name)) def preexec(): # Python installs a SIGPIPE handler by default. This is # usually not what non-Python subprocesses expect. signal.signal(signal.SIGPIPE, signal.SIG_DFL) filtermatch.preexec() obj = subprocess.Popen(command, preexec_fn=preexec, env=filtermatch.get_environment(userargs), **kwargs) return obj
def run(self, context): python_version = sys.version_info.major ansible_playbook_cmd = "ansible-playbook-{}".format(python_version) if 0 < self.verbosity < 6: verbosity_option = '-' + ('v' * self.verbosity) command = [ansible_playbook_cmd, verbosity_option, self.playbook] else: command = [ansible_playbook_cmd, self.playbook] if self.limit_hosts: command.extend(['--limit', self.limit_hosts]) if self.module_path: command.extend(['--module-path', self.module_path]) if self.become: command.extend(['--become']) if self.become_user: command.extend(['--become-user', self.become_user]) if self.extra_vars: command.extend(['--extra-vars', self.extra_vars]) if self.flush_cache: command.extend(['--flush-cache']) if self.forks: command.extend(['--forks', self.forks]) if self.ssh_common_args: command.extend(['--ssh-common-args', self.ssh_common_args]) if self.ssh_extra_args: command.extend(['--ssh-extra-args', self.ssh_extra_args]) if self.timeout: command.extend(['--timeout', self.timeout]) if self.inventory: command.extend(['--inventory-file', self.inventory]) if self.blacklisted_hostnames: host_pattern = ':'.join( ['!%s' % h for h in self.blacklisted_hostnames]) command.extend(['--limit', host_pattern]) if self.tags: command.extend(['--tags', self.tags]) if self.skip_tags: command.extend(['--skip-tags', self.skip_tags]) if self.config_download_args: command.extend(self.config_download_args) if self.extra_env_variables: if not isinstance(self.extra_env_variables, dict): msg = "extra_env_variables must be a dict" return actions.Result(error=msg) else: for key, value in self.extra_env_variables.items(): self.extra_env_variables[key] = six.text_type(value) if self.gather_facts: command.extend(['--gather-facts', self.gather_facts]) try: ansible_config_path = write_default_ansible_cfg( self.work_dir, self.remote_user, ssh_private_key=self.ssh_private_key, override_ansible_cfg=self.override_ansible_cfg) env_variables = { 'HOME': self.work_dir, 'ANSIBLE_LOCAL_TEMP': self.work_dir, 'ANSIBLE_CONFIG': ansible_config_path, } if self.profile_tasks: env_variables.update({ # the whitelist could be collected from multiple # arguments if we find a use case for it 'ANSIBLE_CALLBACK_WHITELIST': 'profile_tasks', 'PROFILE_TASKS_TASK_OUTPUT_LIMIT': six.text_type(self.profile_tasks_limit), }) if self.extra_env_variables: env_variables.update(self.extra_env_variables) if self.use_openstack_credentials: security_ctx = context.security env_variables.update({ 'OS_AUTH_URL': security_ctx.auth_uri, 'OS_USERNAME': security_ctx.user_name, 'OS_AUTH_TOKEN': security_ctx.auth_token, 'OS_PROJECT_NAME': security_ctx.project_name}) command = [str(c) for c in command] if self.reproduce_command: command_path = os.path.join(self.work_dir, "ansible-playbook-command.sh") with open(command_path, 'w') as f: f.write('#!/bin/bash\n') f.write('\n') for var in env_variables: f.write('%s="%s"\n' % (var, env_variables[var])) f.write('\n') f.write(' '.join(command)) f.write(' "$@"') f.write('\n') os.chmod(command_path, 0o750) if self.command_timeout: command = ['timeout', str(self.command_timeout)] + command if self.queue_name: zaqar = self.get_messaging_client(context) queue = zaqar.queue(self.queue_name) # TODO(d0ugal): We don't have the log errors functionality # that processutils has, do we need to replicate that somehow? process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, bufsize=1, cwd=self.work_dir, env=env_variables, universal_newlines=True) start = time.time() stdout = [] lines = [] for line in iter(process.stdout.readline, ''): lines.append(line) if not self.trash_output: stdout.append(line) if time.time() - start > 30: self.post_message(queue, ''.join(lines)) lines = [] start = time.time() self.post_message(queue, ''.join(lines)) process.stdout.close() returncode = process.wait() # TODO(d0ugal): This bit isn't ideal - as we redirect stderr to # stdout we don't know the difference. To keep the return dict # similar there is an empty stderr. We can use the return code # to determine if there was an error. return {"stdout": "".join(stdout), "returncode": returncode, "stderr": ""} LOG.info('Running ansible-playbook command: %s', command) stderr, stdout = processutils.execute( *command, cwd=self.work_dir, env_variables=env_variables, log_errors=processutils.LogErrors.ALL) if self.trash_output: stdout = "" stderr = "" return {"stderr": stderr, "stdout": stdout, "log_path": os.path.join(self.work_dir, 'ansible.log')} finally: # NOTE(flaper87): clean the mess if debug is disabled. try: if not self.verbosity and self._remove_work_dir: shutil.rmtree(self.work_dir) except Exception as e: msg = "An error happened while cleaning work directory: " + e LOG.error(msg) return actions.Result(error=msg)