diff --git a/README.rst b/README.rst index f1dafa1..9867954 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,9 @@ that means SSH server must be running there already. host.executor_factory = rrmngmnt.ssh.RemoteExecutorFactory(use_pkey=True) exec = h.executor() + # Run with sudo + exec = h.executor(sudo=True) + print exec.run_cmd(['echo', 'Hello World']) diff --git a/rrmngmnt/host.py b/rrmngmnt/host.py index e0ec769..e0fb03f 100644 --- a/rrmngmnt/host.py +++ b/rrmngmnt/host.py @@ -73,6 +73,7 @@ def __init__(self, ip, service_provider=None): self._package_manager = PackageManagerProxy(self) self.os = OperatingSystem(self) self.add() # adding host to inventory + self.sudo = False def __str__(self): return "Host(%s)" % self.ip @@ -210,7 +211,7 @@ def package_manager(self): def power_manager(self): return self.get_power_manager() - def executor(self, user=None, pkey=False): + def executor(self, user=None, pkey=False, sudo=False): """ Gives you executor to allowing command execution @@ -219,6 +220,9 @@ def executor(self, user=None, pkey=False): user. when it is None, the default executor user is used, see set_executor_user method for more info. """ + if sudo: + self.sudo = True + if user is None: user = self.executor_user if pkey: @@ -229,7 +233,7 @@ def executor(self, user=None, pkey=False): ef = copy.copy(ssh.RemoteExecutorFactory) ef.use_pkey = pkey return ef(self.ip, user) - return self.executor_factory.build(self, user) + return self.executor_factory.build(self, user, sudo=self.sudo) def run_command( self, command, input_=None, tcp_timeout=None, io_timeout=None, diff --git a/rrmngmnt/ssh.py b/rrmngmnt/ssh.py index cf4329c..33531f5 100644 --- a/rrmngmnt/ssh.py +++ b/rrmngmnt/ssh.py @@ -122,6 +122,9 @@ def command(self, cmd): return RemoteExecutor.Command(cmd, self) def run_cmd(self, cmd, input_=None, timeout=None): + if self._executor.sudo: + cmd.insert(0, "sudo") + cmd = self.command(cmd) return cmd.run(input_, timeout) @@ -206,18 +209,20 @@ def run(self, input_, timeout=None, get_pty=False): self.err = normalize_string(err.read()) return self.rc, self.out, self.err - def __init__(self, user, address, use_pkey=False, port=22): + def __init__(self, user, address, use_pkey=False, port=22, sudo=False): """ Args: use_pkey (bool): Use ssh private key in the connection user (instance of User): User address (str): Ip / hostname port (int): Port to connect + sudo (bool): Use sudo to execute command. """ super(RemoteExecutor, self).__init__(user) self.address = address self.use_pkey = use_pkey self.port = port + self.sudo = sudo def session(self, timeout=None): """ @@ -306,6 +311,6 @@ def __init__(self, use_pkey=False, port=22): self.use_pkey = use_pkey self.port = port - def build(self, host, user): + def build(self, host, user, sudo=False): return RemoteExecutor( - user, host.ip, use_pkey=self.use_pkey, port=self.port) + user, host.ip, use_pkey=self.use_pkey, port=self.port, sudo=sudo) diff --git a/tests/common.py b/tests/common.py index 77f72e6..51b77f1 100644 --- a/tests/common.py +++ b/tests/common.py @@ -117,6 +117,7 @@ def session(self, timeout=None): return FakeExecutor.Session(self, timeout) def run_cmd(self, cmd, input_=None, tcp_timeout=None, io_timeout=None): + cmd = list(cmd) with self.session(tcp_timeout) as session: return session.run_cmd(cmd, input_, io_timeout) @@ -126,8 +127,9 @@ def __init__(self, cmd_to_data, files_content): self.cmd_to_data = cmd_to_data.copy() self.files_content = files_content - def build(self, host, user): + def build(self, host, user, sudo): fe = FakeExecutor(user, host.ip) fe.cmd_to_data = self.cmd_to_data.copy() fe.files_content = self.files_content + fe.sudo = sudo return fe diff --git a/tests/test_package_manager.py b/tests/test_package_manager.py index 647747a..b8badd9 100644 --- a/tests/test_package_manager.py +++ b/tests/test_package_manager.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import pytest + from rrmngmnt import Host, User from .common import FakeExecutorFactory import rrmngmnt.package_manager as pm @@ -8,19 +10,41 @@ host_executor_factory = Host.executor_factory -def extend_cmd(cmd, *args): +def _extend_cmd(cmd, sudo, *args): cmd = list(cmd) + if sudo: + cmd.insert(0, "sudo") + cmd.extend(args) return list2cmdline(cmd) -def join_cmds(*args): +def extend_cmd(cmd, *args): + return _extend_cmd(cmd, False, *args) + + +def sudo_extend_cmd(cmd, *args): + return _extend_cmd(cmd, True, *args) + + +def _join_cmd(sudo, *args): cmd = [] + if sudo: + cmd.append("sudo") + for _cmd in args: cmd += list(_cmd) return list2cmdline(cmd) +def join_cmds(*args): + return _join_cmd(False, *args) + + +def sudo_join_cmds(*args): + return _join_cmd(True, *args) + + def teardown_module(): Host.executor_factory = host_executor_factory @@ -29,6 +53,7 @@ def fake_cmd_data(cmd_to_data, files=None): Host.executor_factory = FakeExecutorFactory(cmd_to_data, files) +@pytest.mark.parametrize("sudo", [False, True]) class BasePackageManager(object): __test__ = False @@ -43,7 +68,7 @@ class BasePackageManager(object): 'not_installed': 'p-not-installed', 'non_existing': 'p-non-existing', 'list': 'p-installed-1\np-installed-2\np-installed-3\n', - 'pattern': 'p-installed-(1|2)' + 'pattern': 'p-installed-(1|2)', } rc1 = (1, '', '') rc0 = (0, '', '') @@ -63,7 +88,14 @@ def setup_class(cls): grep_xargs_command, cls.managers[cls.manager].remove_command_d ) + sudo_remove_pattern_cmd = sudo_join_cmds( + cls.managers[cls.manager].list_command_d, + grep_xargs_command, + cls.managers[cls.manager].remove_command_d + ) cls.data.update({ + remove_pattern_cmd: cls.rc0, + sudo_remove_pattern_cmd: cls.rc0, extend_cmd( cls.managers[cls.manager].exist_command_d, cls.packages['installed_1'] @@ -101,16 +133,63 @@ def setup_class(cls): cls.managers[cls.manager].remove_command_d, cls.packages['installed_2'] ): cls.rc0, - remove_pattern_cmd: cls.rc0, extend_cmd( cls.managers[cls.manager].update_command_d, cls.packages['installed_1'] ): cls.rc0, - list2cmdline( + extend_cmd( + cls.managers[cls.manager].update_command_d, + ): cls.rc0, + extend_cmd( + cls.managers[cls.manager].list_command_d, + ): (0, cls.packages['list'], ''), + # For sudo tests. + sudo_extend_cmd( + cls.managers[cls.manager].exist_command_d, + cls.packages['installed_1'] + ): cls.rc0, + sudo_extend_cmd( + cls.managers[cls.manager].exist_command_d, + cls.packages['installed_2'] + ): cls.rc0, + sudo_extend_cmd( + cls.managers[cls.manager].exist_command_d, + cls.packages['installed_3'] + ): cls.rc0, + sudo_extend_cmd( + cls.managers[cls.manager].exist_command_d, + cls.packages['not_installed'] + ): cls.rc1, + sudo_extend_cmd( + cls.managers[cls.manager].install_command_d, + cls.packages['not_installed'] + ): cls.rc0, + # for negative install test + sudo_extend_cmd( + cls.managers[cls.manager].exist_command_d, + cls.packages['non_existing'] + ): cls.rc1, + sudo_extend_cmd( + cls.managers[cls.manager].install_command_d, + cls.packages['non_existing'] + ): cls.rc1, + sudo_extend_cmd( + cls.managers[cls.manager].remove_command_d, + cls.packages['installed_1'] + ): cls.rc0, + sudo_extend_cmd( + cls.managers[cls.manager].remove_command_d, + cls.packages['installed_2'] + ): cls.rc0, + sudo_extend_cmd( + cls.managers[cls.manager].update_command_d, + cls.packages['installed_1'] + ): cls.rc0, + sudo_extend_cmd( cls.managers[cls.manager].update_command_d, ): cls.rc0, - list2cmdline( - cls.managers[cls.manager].list_command_d + sudo_extend_cmd( + cls.managers[cls.manager].list_command_d, ): (0, cls.packages['list'], ''), }) fake_cmd_data(cls.data) @@ -121,55 +200,58 @@ def set_base_data(cls): rc = cls.rc1 if manager_ == cls.manager: rc = cls.rc0 - cls.data.update({ - list2cmdline(['which', manager_]): rc, - }) - def get_host(self, ip='1.1.1.1'): + for val in (['which'], ['sudo', 'which']): + cls.data.update({ + list2cmdline(val + [manager_]): rc, + }) + + def get_host(self, ip='1.1.1.1', sudo=False): h = Host(ip) h.add_user(User('root', '11111')) + h.executor(sudo=sudo) return h - def get_pm(self): - return self.get_host().package_manager + def get_pm(self, sudo=False): + return self.get_host(sudo=sudo).package_manager - def test_info(self): - assert not self.get_pm().info(self.packages['installed_1']) + def test_info(self, sudo): + assert not self.get_pm(sudo).info(self.packages['installed_1']) - def test_info_negative(self): - assert not self.get_pm().info(self.packages['not_installed']) + def test_info_negative(self, sudo): + assert not self.get_pm(sudo).info(self.packages['not_installed']) - def test_exist(self): - assert self.get_pm().exist(self.packages['installed_1']) + def test_exist(self, sudo): + assert self.get_pm(sudo).exist(self.packages['installed_1']) - def test_exist_negative(self): - assert not self.get_pm().exist(self.packages['not_installed']) + def test_exist_negative(self, sudo): + assert not self.get_pm(sudo).exist(self.packages['not_installed']) - def test_install_installed(self): - assert self.get_pm().install(self.packages['installed_1']) + def test_install_installed(self, sudo): + assert self.get_pm(sudo).install(self.packages['installed_1']) - def test_install_new(self): - assert self.get_pm().install(self.packages['not_installed']) + def test_install_new(self, sudo): + assert self.get_pm(sudo).install(self.packages['not_installed']) - def test_install_negative(self): - assert not self.get_pm().install(self.packages['non_existing']) + def test_install_negative(self, sudo): + assert not self.get_pm(sudo).install(self.packages['non_existing']) - def test_remove(self): - assert self.get_pm().remove(self.packages['installed_1']) + def test_remove(self, sudo): + assert self.get_pm(sudo).remove(self.packages['installed_1']) - def test_remove_pattern(self): + def test_remove_pattern(self, sudo): assert ( - self.get_pm().remove(self.packages['pattern'], pattern=True) + self.get_pm(sudo).remove(self.packages['pattern'], pattern=True) ) - def test_update(self): - assert self.get_pm().update([self.packages['installed_1']]) + def test_update(self, sudo): + assert self.get_pm(sudo).update([self.packages['installed_1']]) - def test_update_all(self): - assert self.get_pm().update() + def test_update_all(self, sudo): + assert self.get_pm(sudo).update() - def test_list(self): - assert self.get_pm().list_() == self.packages['list'].split('\n') + def test_list(self, sudo): + assert self.get_pm(sudo).list_() == self.packages['list'].split('\n') class TestYumPM(BasePackageManager):