From a21e120146291655e9d3433089691f775ff3a664 Mon Sep 17 00:00:00 2001 From: Cian Brennan Date: Tue, 28 Apr 2015 15:09:47 +0100 Subject: [PATCH] Added code to set the install directory for ohai-solo Added options to the ohai_solo install, remove and execute methods to allow installation of ohai-solo to somewhere other than /opt. This is required because it lets us get around some change control policies. Change-Id: I72d051f78c5316e6077891978c65a60ece1dd4de --- requirements-py3.txt | 2 +- requirements.txt | 2 +- satori/sysinfo/ohai_solo.py | 69 ++++++++++++-------- satori/tests/test_sysinfo_ohai_solo.py | 88 +++++++++++++++++++++++++- 4 files changed, 131 insertions(+), 30 deletions(-) diff --git a/requirements-py3.txt b/requirements-py3.txt index 952af53..4e8c726 100644 --- a/requirements-py3.txt +++ b/requirements-py3.txt @@ -4,5 +4,5 @@ paramiko>=1.13.0 # py33 support pbr>=0.5.21,<1.0 python-novaclient>=2.15.0 # py33 support pythonwhois>=2.1.0 # py33 support -six>=1.4.0 +six>=1.8.0 tldextract>=1.2 diff --git a/requirements.txt b/requirements.txt index 6849dbd..d38732a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,6 @@ paramiko>=1.12.0 # ecdsa added pbr>=0.5.21,<1.0 python-novaclient>=2.6.0.1 # breaks before pythonwhois>=2.4.3 -six>=1.4.0 # urllib introduced +six>=1.8.0 # six.moves.shlex introduced tldextract>=1.2 argparse diff --git a/satori/sysinfo/ohai_solo.py b/satori/sysinfo/ohai_solo.py index 04ad084..3ef4bad 100644 --- a/satori/sysinfo/ohai_solo.py +++ b/satori/sysinfo/ohai_solo.py @@ -51,10 +51,12 @@ def get_systeminfo(ipaddress, config, interactive=False): return system_info(client) -def system_info(client, with_install=False): +def system_info(client, with_install=False, install_dir=None): """Run ohai-solo on a remote system and gather the output. :param client: :class:`ssh.SSH` instance + :param with_install Will install ohai-solo if set to True + :param install_dir string containing directory to install to :returns: dict -- system information from ohai-solo :raises: SystemInfoCommandMissing, SystemInfoCommandOld, SystemInfoNotJson SystemInfoMissingJson @@ -65,37 +67,46 @@ def system_info(client, with_install=False): SystemInfoMissingJson if `ohai` does not return any JSON. """ if with_install: - perform_install(client) + perform_install(client, install_dir=install_dir) if client.is_windows(): raise errors.UnsupportedPlatform( "ohai-solo is a linux-only sytem info provider. " "Target platform was %s", client.platform_info['dist']) - else: - command = "unset GEM_CACHE GEM_HOME GEM_PATH && sudo ohai-solo" - output = client.execute(command, escalate=True, allow_many=False) - not_found_msgs = ["command not found", "Could not find ohai"] - if any(m in k for m in not_found_msgs - for k in list(output.values()) if isinstance(k, - six.string_types)): - LOG.warning("SystemInfoCommandMissing on host: [%s]", client.host) - raise errors.SystemInfoCommandMissing("ohai-solo missing on %s" % - client.host) - # use string formatting to handle unicode - unicode_output = "%s" % output['stdout'] + + ohai_solo_prefix = (install_dir or '/opt') + ohai_solo_command = six.moves.shlex_quote("%s/ohai-solo/bin/ohai-solo" + % ohai_solo_prefix) + command = ("unset GEM_CACHE GEM_HOME GEM_PATH && " + "sudo %s" % ohai_solo_command) + + output = client.execute(command, escalate=True, allow_many=False) + not_found_msgs = ["command not found", "Could not find ohai"] + if any(m in k for m in not_found_msgs + for k in list(output.values()) if isinstance(k, + six.string_types)): + LOG.warning("SystemInfoCommandMissing on host: [%s]", client.host) + raise errors.SystemInfoCommandMissing("ohai-solo missing on %s" % + client.host) + # use string formatting to handle unicode + unicode_output = "%s" % output['stdout'] + try: + results = json.loads(unicode_output) + except ValueError as exc: try: - results = json.loads(unicode_output) + clean_output = get_json(unicode_output) + results = json.loads(clean_output) except ValueError as exc: - try: - clean_output = get_json(unicode_output) - results = json.loads(clean_output) - except ValueError as exc: - raise errors.SystemInfoNotJson(exc) - return results + raise errors.SystemInfoNotJson(exc) + return results -def perform_install(client): - """Install ohai-solo on remote system.""" +def perform_install(client, install_dir=None): + """Install ohai-solo on remote system. + + :param client: :class:`ssh.SSH` instance + :param install_dir string containing directory to install to + """ LOG.info("Installing (or updating) ohai-solo on device %s at %s:%d", client.host, client.host, client.port) @@ -119,6 +130,10 @@ def perform_install(client): # Run install command = "bash install.sh" + if install_dir: + command = "%s -t -i %s" % (command, + six.moves.shlex_quote(install_dir)) + install_output = client.execute(command, cwd='/tmp', with_exit_code=True, escalate=True, allow_many=False) @@ -137,9 +152,10 @@ def perform_install(client): return install_output -def remove_remote(client): +def remove_remote(client, install_dir=None): """Remove ohai-solo from specifc remote system. + :param install_dir string containing directory ohai-solo was installed in Currently supports: - ubuntu [10.x, 12.x] - debian [6.x, 7.x] @@ -152,7 +168,10 @@ def remove_remote(client): "Target platform was %s", client.platform_info['dist']) else: platform_info = client.platform_info - if client.is_debian(): + if install_dir is not None: + install_dir = six.moves.shlex_quote("%s/ohai-solo/" % install_dir) + remove = 'rm -rf %s' % install_dir + elif client.is_debian(): remove = "dpkg --purge ohai-solo" elif client.is_fedora(): remove = "yum -y erase ohai-solo" diff --git a/satori/tests/test_sysinfo_ohai_solo.py b/satori/tests/test_sysinfo_ohai_solo.py index b03ebc5..7a5d8b3 100644 --- a/satori/tests/test_sysinfo_ohai_solo.py +++ b/satori/tests/test_sysinfo_ohai_solo.py @@ -68,6 +68,39 @@ class TestOhaiInstall(utils.TestCase): mock.call('rm install.sh', cwd='/tmp', escalate=True, allow_many=False)]) + def test_perform_install_with_install_dir(self): + response = {'exit_code': 0, 'stdout': 'installed remote'} + self.mock_remotesshclient.execute.return_value = response + result = ohai_solo.perform_install(self.mock_remotesshclient, + install_dir='/home/bob') + self.assertEqual(result, response) + self.assertEqual(self.mock_remotesshclient.execute.call_count, 3) + self.mock_remotesshclient.execute.assert_has_calls([ + mock.call('wget -N http://readonly.configdiscovery.' + 'rackspace.com/install.sh', cwd='/tmp', + escalate=True, allow_many=False), + mock.call('bash install.sh -t -i /home/bob', cwd='/tmp', + with_exit_code=True, escalate=True, allow_many=False), + mock.call('rm install.sh', cwd='/tmp', escalate=True, + allow_many=False)]) + + def test_perform_install_with_install_dir_and_spaces(self): + response = {'exit_code': 0, 'stdout': 'installed remote'} + self.mock_remotesshclient.execute.return_value = response + result = ohai_solo.perform_install(self.mock_remotesshclient, + install_dir='/srv/a diff * dir') + self.assertEqual(result, response) + self.assertEqual(self.mock_remotesshclient.execute.call_count, 3) + self.mock_remotesshclient.execute.assert_has_calls([ + mock.call('wget -N http://readonly.configdiscovery.' + 'rackspace.com/install.sh', cwd='/tmp', + escalate=True, allow_many=False), + mock.call('bash install.sh -t -i \'/srv/a diff * dir\'', + cwd='/tmp', with_exit_code=True, escalate=True, + allow_many=False), + mock.call('rm install.sh', cwd='/tmp', escalate=True, + allow_many=False)]) + def test_install_linux_remote_failed(self): response = {'exit_code': 1, 'stdout': "", "stderr": "FAIL"} self.mock_remotesshclient.execute.return_value = response @@ -108,6 +141,28 @@ class TestOhaiRemove(utils.TestCase): self.assertRaises(errors.UnsupportedPlatform, ohai_solo.remove_remote, self.mock_remotesshclient) + def test_remove_remote_with_install_dir(self): + self.mock_remotesshclient.is_debian.return_value = True + self.mock_remotesshclient.is_fedora.return_value = False + response = {'exit_code': 0, 'foo': 'bar'} + self.mock_remotesshclient.execute.return_value = response + result = ohai_solo.remove_remote(self.mock_remotesshclient, + install_dir='/home/srv') + self.assertEqual(result, response) + self.mock_remotesshclient.execute.assert_called_once_with( + 'rm -rf /home/srv/ohai-solo/', cwd='/tmp', escalate=True) + + def test_remove_remote_with_install_dir_and_spaces(self): + self.mock_remotesshclient.is_debian.return_value = True + self.mock_remotesshclient.is_fedora.return_value = False + response = {'exit_code': 0, 'foo': 'bar'} + self.mock_remotesshclient.execute.return_value = response + result = ohai_solo.remove_remote(self.mock_remotesshclient, + install_dir='/srv/a dir') + self.assertEqual(result, response) + self.mock_remotesshclient.execute.assert_called_once_with( + 'rm -rf \'/srv/a dir/ohai-solo/\'', cwd='/tmp', escalate=True) + class TestSystemInfo(utils.TestCase): @@ -124,7 +179,34 @@ class TestSystemInfo(utils.TestCase): } ohai_solo.system_info(self.mock_remotesshclient) self.mock_remotesshclient.execute.assert_called_with( - "unset GEM_CACHE GEM_HOME GEM_PATH && sudo ohai-solo", + "unset GEM_CACHE GEM_HOME GEM_PATH && " + "sudo /opt/ohai-solo/bin/ohai-solo", + escalate=True, allow_many=False) + + def test_system_info_with_install_dir(self): + self.mock_remotesshclient.execute.return_value = { + 'exit_code': 0, + 'stdout': "{}", + 'stderr': "" + } + ohai_solo.system_info(self.mock_remotesshclient, + install_dir='/home/user') + self.mock_remotesshclient.execute.assert_called_with( + "unset GEM_CACHE GEM_HOME GEM_PATH && " + "sudo /home/user/ohai-solo/bin/ohai-solo", + escalate=True, allow_many=False) + + def test_system_info_with_install_dir_with_spaces(self): + self.mock_remotesshclient.execute.return_value = { + 'exit_code': 0, + 'stdout': "{}", + 'stderr': "" + } + ohai_solo.system_info(self.mock_remotesshclient, + install_dir='/sys/omg * " lol/') + self.mock_remotesshclient.execute.assert_called_with( + "unset GEM_CACHE GEM_HOME GEM_PATH && " + 'sudo \'/sys/omg * " lol//ohai-solo/bin/ohai-solo\'', escalate=True, allow_many=False) def test_system_info_with_motd(self): @@ -135,7 +217,8 @@ class TestSystemInfo(utils.TestCase): } ohai_solo.system_info(self.mock_remotesshclient) self.mock_remotesshclient.execute.assert_called_with( - "unset GEM_CACHE GEM_HOME GEM_PATH && sudo ohai-solo", + "unset GEM_CACHE GEM_HOME GEM_PATH && " + "sudo /opt/ohai-solo/bin/ohai-solo", escalate=True, allow_many=False) def test_system_info_bad_json(self): @@ -174,6 +257,5 @@ class TestSystemInfo(utils.TestCase): self.assertRaises(errors.SystemInfoCommandMissing, ohai_solo.system_info, self.mock_remotesshclient) - if __name__ == "__main__": unittest.main()