diff --git a/README.md b/README.md index a8e8385..c1aba22 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,13 @@ Update the host. $ xe host-call-plugin host-uuid= plugin=updater.py fn=update ``` +#### `query_installed`: +Return a JSON object of the installed packages given in arguments with an empty value string for not installed ones. +``` +$ xe host-call-plugin host-uuid= plugin=updater.py fn=query_installed args:packages=sm,sm-rawhba,invalid +{"sm-rawhba": "sm-rawhba-2.30.8-2.3.0.linstor.1.xcpng8.2.x86_64", "invalid": "", "sm": "sm-2.30.8-10.1.0.linstor.1.xcpng8.2.x86_64"} +``` + ### Proxy configuration #### `get_proxies()`: diff --git a/SOURCES/etc/xapi.d/plugins/updater.py b/SOURCES/etc/xapi.d/plugins/updater.py index 56376d6..346d556 100755 --- a/SOURCES/etc/xapi.d/plugins/updater.py +++ b/SOURCES/etc/xapi.d/plugins/updater.py @@ -181,6 +181,33 @@ def check_update(session, args): def update(session, args): return install_helper(session, args, 'update') +@error_wrapped +def query_installed(session, args): + packages = args.get('packages') + if not packages: + return '{}' + + import re + packages = re.sub(r'\s+', ' ', packages).replace(',', ' ').split(' ') + packages = filter(lambda package: package, packages) + if not packages: + return '{}' + + package_map = dict.fromkeys(packages, '') + packages = list(package_map) + + command = ['rpm', '-q'] + packages + result = run_command(command, check=False) + package_info = result['stdout'].rstrip().split('\n') + assert len(packages) == len(package_info), 'ill-formed result' + + for i, package in enumerate(packages): + info = package_info[i] + if not info.endswith('is not installed'): + package_map[package] = info + + return json.dumps(package_map) + def check_upgrade(session, args): # TODO: # check new version exists @@ -265,6 +292,7 @@ def set_proxies(session, args): 'install': install, 'check_update': check_update, 'update': update, + 'query_installed': query_installed, 'get_proxies': get_proxies, 'set_proxies': set_proxies }) diff --git a/tests/test_updater.py b/tests/test_updater.py index 9379a3a..d24137c 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -7,7 +7,14 @@ import time import XenAPIPlugin -from updater import install, check_update, get_proxies, set_proxies, update, DEFAULT_REPOS +from updater import \ + install, \ + check_update, \ + get_proxies, \ + set_proxies, \ + update, \ + query_installed, \ + DEFAULT_REPOS # ============================================================================== # Install/Update. @@ -108,6 +115,60 @@ def test_update_with_additional_repos_and_packages(self, run_command, fs): ['yum', 'update', '--disablerepo=*', '--enablerepo=' + ','.join(repos), '-y', packages] ) +@mock.patch('updater.run_command', autospec=True) +class TestIsInstalled: + def test_without_package(self, run_command): + result = query_installed(mock.MagicMock(), {}) + assert result == '{}' + + def test_one_package_installed(self, run_command): + run_command.return_value = { + 'stdout': 'test.rpm', + 'stderr': '', + 'returncode': 0 + } + + package = 'test' + result = query_installed(mock.MagicMock(), {'packages': package}) + run_command.assert_called_once_with( + ['rpm', '-q', package], False + ) + + assert json.loads(result) == {'test': 'test.rpm'} + + def test_no_package_installed(self, run_command): + run_command.return_value = { + 'stdout': 'test is not installed', + 'stderr': '', + 'returncode': 1 + } + + package = 'test' + result = query_installed(mock.MagicMock(), {'packages': package}) + run_command.assert_called_once_with( + ['rpm', '-q', package], False + ) + + assert json.loads(result) == {'test': ''} + + def test_with_many_packages(self, run_command, fs): + run_command.return_value = { + 'stdout': + 'toto.rpm\n' + 'package tata is not installed\n' + 'titi.rpm\n', + 'stderr': '', + 'returncode': 0 + } + + packages = 'toto,tata,titi' + result = query_installed(mock.MagicMock(), {'packages': packages}) + run_command.assert_called_once_with( + ['rpm', '-q'] + packages.split(','), False + ) + + assert json.loads(result) == {'toto': 'toto.rpm', 'tata': '', 'titi': 'titi.rpm'} + # ============================================================================== # Lock: Try to execute a command with a lock already locked. # ==============================================================================