Example #1
0
    def __init__(self, node, pty=None, logger=None, is_sandbox=False):
        assert isinstance(node, Node)

        self._node = node
        self._pty = pty or DummyPty()
        self._logger = logger or DummyLoggerInterface()
        self._is_sandbox = is_sandbox

        # When the node is callable (when it has a default action),
        # make sure that this env becomes collable as well.
        if callable(self._node):
            # Per instance overriding
            def call(self, *a, **kw):
                return self.__getattr__('__call__')(*a, **kw)

            self.__class__ = type(self.__class__.__name__, (self.__class__, ),
                                  {'__call__': call})

        # Create a new HostsContainer object which is identical to the one of
        # the Node object, but add pty/logger/sandbox settings. (So, this
        # doesn't create new Host instances, only a new container.)
        # (do this in this constructor. Each call to Env.hosts should return
        # the same host container instance.)
        self._hosts = HostsContainer(self._node.hosts.get_hosts_as_dict(),
                                     pty=self._pty,
                                     logger=self._logger,
                                     is_sandbox=is_sandbox)

        # Lock Env
        self._lock_env = True
Example #2
0
    def test_input(self):
        return
        pty = DummyPty('my-input\n')
        host = self.get_host(pty=pty)

        result = host.run('read varname; echo $varname')
        self.assertEqual(result, 'my-input\r\nmy-input\r\n')
Example #3
0
    def get_definition(self):
        hosts = self.get_hosts()
        class Hosts:
            role1 = { hosts.h1, hosts.h2}
            role2 = { hosts.h3, hosts.h4, hosts.h5 }
            role3 = { hosts.h1 }

        return HostsContainer.from_definition(Hosts, pty=DummyPty())
Example #4
0
    def test_input(self):
        # Test normal input
        p = DummyPty(input_data='my-input\n')
        result = Console(p).input('input question')
        output = p.get_output()

        self.assertEqual(result, 'my-input')
        self.assertIn('input question', output)

        # Test default input
        p = DummyPty(input_data='\n')
        result = Console(p).input('input question', default='default-value')
        self.assertEqual(result, 'default-value')

        p = DummyPty(input_data='my-input\n')
        p.interactive = True # We have to set interactive=True, otherwise
                             # Console will take the default value anyway.
        result = Console(p).input('input question', default='default-value')
        self.assertEqual(result, 'my-input')
Example #5
0
    def test_confirm(self):
        question = 'this is my question'

        # Test various inputs
        for inp, result in [('yes', True), ('y', True), ('no', False),
                            ('n', False)]:

            p = DummyPty(input_data='%s\n' % inp)
            c = Console(p)
            returnvalue = c.confirm(question)

            self.assertEqual(returnvalue, result)
            self.assertIn(question, p.get_output())

        # Test default
        p = DummyPty(input_data='\n')
        c = Console(p)
        self.assertEqual(c.confirm('', default=True), True)
        self.assertEqual(c.confirm('', default=False), False)
Example #6
0
    def test_confirm(self):
        question = 'this is my question'

        # Test various inputs
        for inp, result in [
                ('yes', True),
                ('y', True),
                ('no', False),
                ('n', False) ]:

            p = DummyPty(input_data='%s\n' % inp)
            c = Console(p)
            returnvalue = c.confirm(question)

            self.assertEqual(returnvalue, result)
            self.assertIn(question, p.get_output())

        # Test default
        p = DummyPty(input_data='\n')
        c = Console(p)
        self.assertEqual(c.confirm('', default=True), True)
        self.assertEqual(c.confirm('', default=False), False)
Example #7
0
    def test_input(self):
        # Test normal input
        p = DummyPty(input_data='my-input\n')
        result = Console(p).input('input question')
        output = p.get_output()

        self.assertEqual(result, 'my-input')
        self.assertIn('input question', output)

        # Test default input
        p = DummyPty(input_data='\n')
        result = Console(p).input('input question', default='default-value')
        self.assertEqual(result, 'default-value')

        p = DummyPty(input_data='my-input\n')
        p.interactive = True  # We have to set interactive=True, otherwise
        # Console will take the default value anyway.
        result = Console(p).input('input question', default='default-value')
        self.assertEqual(result, 'my-input')
Example #8
0
    def test_term_var(self):
        pty = DummyPty()
        host = self.get_host(pty=pty)

        # Changing the TERM variable of the PTY object.
        # (set_term_var is called by a client.)
        pty.set_term_var('xterm')
        self.assertEqual(host.run('echo $TERM', interactive=False).strip(), 'xterm')

        pty.set_term_var('another-term')
        self.assertEqual(host.run('echo $TERM', interactive=False).strip(), 'another-term')

        # This with statement should override the variable.
        with host.host_context.env('TERM', 'env-variable'):
            self.assertEqual(host.run('echo $TERM', interactive=False).strip(), 'env-variable')
Example #9
0
 def test_print_warning(self):
     p = DummyPty()
     c = Console(p)
     c.warning('this is a warning')
     self.assertIn('this is a warning', p.get_output())
Example #10
0
    def run(self, command, use_sudo=False, sandbox=False, interactive=True,
                    user=None, ignore_exit_status=False, initial_input=None, silent=False):
        """
        Execute this shell command on the host.

        :param command: The shell command.
        :type command: basestring
        :param use_sudo: Run as superuser.
        :type use_sudo: bool
        :param sandbox: Validate syntax instead of really executing. (Wrap the command in ``bash -n``.)
        :type sandbox: bool
        :param interactive: Start an interactive event loop which allows
                            interaction with the remote command. Otherwise, just return the output.
        :type interactive: bool
        :param initial_input: When ``interactive``, send this input first to the host.
        """
        assert isinstance(command, basestring)
        assert not initial_input or interactive # initial_input can only in case of interactive.

        logger = DummyLoggerInterface() if silent else self.logger
        pty = DummyPty() if silent else self.pty

        # Create new channel for this command
        chan = self._get_session()

        # Run in PTY (Sudo usually needs to be run into a pty)
        if interactive:
            height, width = pty.get_size()
            chan.get_pty(term=self.pty.get_term_var(), width=width, height=height)

            # Keep size of local pty and remote pty in sync
            def set_size():
                height, width = pty.get_size()
                try:
                    chan.resize_pty(width=width, height=height)
                except paramiko.SSHException as e:
                    # Channel closed. Ignore when channel was already closed.
                    pass
            pty.set_ssh_channel_size = set_size
        else:
            pty.set_ssh_channel_size = lambda:None

        command = " && ".join(self.host_context._command_prefixes + [command])

        # Start logger
        with logger.log_run(self, command=command, use_sudo=use_sudo,
                                sandboxing=sandbox, interactive=interactive) as log_entry:
            # Are we sandboxing? Wrap command in "bash -n"
            if sandbox:
                command = "bash -n -c '%s' " % esc1(command)
                command = "%s;echo '%s'" % (command, esc1(command))

            logging.info('Running "%s" on host "%s" sudo=%r, interactive=%r' %
                            (command, self.slug, use_sudo, interactive))

            # Execute
            if use_sudo:
                # We use 'sudo su' instead of 'sudo -u', because shell expension
                # of ~ is threated differently. e.g.
                #
                # 1. This will still show the home directory of the original user
                # sudo -u 'postgres' bash -c ' echo $HOME '
                #
                # 2. This shows the home directory of the user postgres:
                # sudo su postgres -c 'echo $HOME '
                if interactive:
                    wrapped_command = self._wrap_command((
                                "sudo -p '%s' su '%s' -c '%s'" % (esc1(self.magic_sudo_prompt), esc1(user), esc1(command))
                                #"sudo -u '%s' bash -c '%s'" % (user, esc1(command))
                                if user else
                                "sudo -p '%s' bash -c '%s' " % (esc1(self.magic_sudo_prompt), esc1(command))),
                                sandbox
                                )

                    logging.debug('Running wrapped command "%s"' % wrapped_command)
                    chan.exec_command(wrapped_command)

                # Some commands, like certain /etc/init.d scripts cannot be
                # run interactively. They won't work in a ssh pty.
                else:
                    wrapped_command = self._wrap_command((
                        "echo '%s' | sudo -p '(passwd)' -u '%s' -P %s " % (esc1(self.password), esc1(user), command)
                        if user else
                        "echo '%s' | sudo -p '(passwd)' -S %s " % (esc1(self.password), command)),
                        sandbox
                        )

                    logging.debug('Running wrapped command "%s" interactive' % wrapped_command)
                    chan.exec_command(wrapped_command)
            else:
                chan.exec_command(self._wrap_command(command, sandbox))

            if interactive:
                # Pty receive/send loop
                result = self._posix_shell(chan, initial_input=initial_input)
            else:
                # Read loop.
                result = self._read_non_interactive(chan)

                #print result # I don't think we need to print the result of non-interactive runs
                              # In any case self._run_silent_sudo should not print anything.

            # Retrieve status code
            status_code = chan.recv_exit_status()
            log_entry.set_status_code(status_code)

            pty.set_ssh_channel_size = None

            if status_code and not ignore_exit_status:
                raise ExecCommandFailed(command, self, use_sudo=use_sudo, status_code=status_code, result=result)

        # Return result
        if sandbox:
            return '<Not sure in sandbox>'
        else:
            return result
Example #11
0
    def run(self,
            command,
            use_sudo=False,
            sandbox=False,
            interactive=True,
            user=None,
            ignore_exit_status=False,
            initial_input=None,
            silent=False):
        """
        Execute this shell command on the host.

        :param command: The shell command.
        :type command: basestring
        :param use_sudo: Run as superuser.
        :type use_sudo: bool
        :param sandbox: Validate syntax instead of really executing. (Wrap the command in ``bash -n``.)
        :type sandbox: bool
        :param interactive: Start an interactive event loop which allows
                            interaction with the remote command. Otherwise, just return the output.
        :type interactive: bool
        :param initial_input: When ``interactive``, send this input first to the host.
        """
        assert isinstance(command, basestring)
        assert not initial_input or interactive  # initial_input can only in case of interactive.

        logger = DummyLoggerInterface() if silent else self.logger
        pty = DummyPty() if silent else self.pty

        # Create new channel for this command
        chan = self._get_session()

        # Run in PTY (Sudo usually needs to be run into a pty)
        if interactive:
            height, width = pty.get_size()
            chan.get_pty(term=self.pty.get_term_var(),
                         width=width,
                         height=height)

            # Keep size of local pty and remote pty in sync
            def set_size():
                height, width = pty.get_size()
                try:
                    chan.resize_pty(width=width, height=height)
                except paramiko.SSHException as e:
                    # Channel closed. Ignore when channel was already closed.
                    pass

            pty.set_ssh_channel_size = set_size
        else:
            pty.set_ssh_channel_size = lambda: None

        command = " && ".join(self.host_context._command_prefixes + [command])

        # Start logger
        with logger.log_run(self,
                            command=command,
                            use_sudo=use_sudo,
                            sandboxing=sandbox,
                            interactive=interactive) as log_entry:
            # Are we sandboxing? Wrap command in "bash -n"
            if sandbox:
                command = "bash -n -c '%s' " % esc1(command)
                command = "%s;echo '%s'" % (command, esc1(command))

            logging.info('Running "%s" on host "%s" sudo=%r, interactive=%r' %
                         (command, self.slug, use_sudo, interactive))

            # Execute
            if use_sudo:
                # We use 'sudo su' instead of 'sudo -u', because shell expension
                # of ~ is threated differently. e.g.
                #
                # 1. This will still show the home directory of the original user
                # sudo -u 'postgres' bash -c ' echo $HOME '
                #
                # 2. This shows the home directory of the user postgres:
                # sudo su postgres -c 'echo $HOME '
                if interactive:
                    wrapped_command = self._wrap_command(
                        (
                            "sudo -p '%s' su '%s' -c '%s'" %
                            (esc1(self.magic_sudo_prompt), esc1(user),
                             esc1(command))
                            #"sudo -u '%s' bash -c '%s'" % (user, esc1(command))
                            if user else "sudo -p '%s' bash -c '%s' " %
                            (esc1(self.magic_sudo_prompt), esc1(command))),
                        sandbox)

                    logging.debug('Running wrapped command "%s"' %
                                  wrapped_command)
                    chan.exec_command(wrapped_command)

                # Some commands, like certain /etc/init.d scripts cannot be
                # run interactively. They won't work in a ssh pty.
                else:
                    wrapped_command = self._wrap_command(
                        ("echo '%s' | sudo -p '(passwd)' -u '%s' -P %s " %
                         (esc1(self.password), esc1(user), command)
                         if user else "echo '%s' | sudo -p '(passwd)' -S %s " %
                         (esc1(self.password), command)), sandbox)

                    logging.debug('Running wrapped command "%s" interactive' %
                                  wrapped_command)
                    chan.exec_command(wrapped_command)
            else:
                chan.exec_command(self._wrap_command(command, sandbox))

            if interactive:
                # Pty receive/send loop
                result = self._posix_shell(chan, initial_input=initial_input)
            else:
                # Read loop.
                result = self._read_non_interactive(chan)

                #print result # I don't think we need to print the result of non-interactive runs
                # In any case self._run_silent_sudo should not print anything.

            # Retrieve status code
            status_code = chan.recv_exit_status()
            log_entry.set_status_code(status_code)

            pty.set_ssh_channel_size = None

            if status_code and not ignore_exit_status:
                raise ExecCommandFailed(command,
                                        self,
                                        use_sudo=use_sudo,
                                        status_code=status_code,
                                        result=result)

        # Return result
        if sandbox:
            return '<Not sure in sandbox>'
        else:
            return result
Example #12
0
 def __init__(self, pty=None, logger=None):
     self.host_context = HostContext()
     self.pty = pty or DummyPty()
     self.logger = logger or DummyLoggerInterface()
Example #13
0
 def test_print_warning(self):
     p = DummyPty()
     c = Console(p)
     c.warning('this is a warning')
     self.assertIn('this is a warning', p.get_output())