Пример #1
0
    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
Пример #2
0
 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
Пример #3
0
    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()
Пример #4
0
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
Пример #5
0
    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)