From 878dbb0e1e8adab3ec8e2f25f1c843a5fd3eb855 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Tue, 18 Feb 2025 17:34:36 +0100 Subject: [PATCH] Add a new service plugin Signed-off-by: Ronan Abhamon --- README.md | 36 ++++++++++++++++++++++ SOURCES/etc/xapi.d/plugins/service.py | 41 +++++++++++++++++++++++++ tests/test_service.py | 43 +++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100755 SOURCES/etc/xapi.d/plugins/service.py create mode 100644 tests/test_service.py diff --git a/README.md b/README.md index d6a1ec8..e79e70d 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,42 @@ $ xe host-call-plugin host-uuid= plugin=ipmitool.py fn=get_ipmi_lan ``` +## Service + +A xapi plugin that uses `systemctl` to manage services. + +### `start_service` + +Start a service if it is not already running. + +``` +$ xe host-call-plugin host-uuid plugin=service.py fn=start_service args:service= +``` + +### `stop_service` + +Stop a service if it is currently running. + +``` +$ xe host-call-plugin host-uuid plugin=service.py fn=stop_service args:service= +``` + +### `restart_service` + +Stop a service if it is running and then start it. + +``` +$ xe host-call-plugin host-uuid plugin=service.py fn=restart_service args:service= +``` + +### `try_restart_service` + +Restart a service only if it is already running. + +``` +$ xe host-call-plugin host-uuid plugin=service.py fn=try_restart_service args:service= +``` + ## Tests To run the plugins' unit tests you'll need to install `pytest`, `pyfakefs` and `mock`. diff --git a/SOURCES/etc/xapi.d/plugins/service.py b/SOURCES/etc/xapi.d/plugins/service.py new file mode 100755 index 0000000..da4053e --- /dev/null +++ b/SOURCES/etc/xapi.d/plugins/service.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import json +import sys +import XenAPIPlugin + +sys.path.append('.') +from xcpngutils import configure_logging, run_command, error_wrapped + +def run_service_command(cmd_name, args): + service = args.get('service') + if not service: + raise Exception('Missing or empty argument `service`') + run_command(['systemctl', cmd_name, service]) + return json.dumps(True) + +@error_wrapped +def start_service(session, args): + return run_service_command('start', args) + +@error_wrapped +def stop_service(session, args): + return run_service_command('stop', args) + +@error_wrapped +def restart_service(session, args): + return run_service_command('restart', args) + +@error_wrapped +def try_restart_service(session, args): + return run_service_command('try-restart', args) + +_LOGGER = configure_logging('service') +if __name__ == "__main__": + XenAPIPlugin.dispatch({ + 'start_service': start_service, + 'stop_service': stop_service, + 'restart_service': restart_service, + 'try_restart_service': try_restart_service + }) diff --git a/tests/test_service.py b/tests/test_service.py new file mode 100644 index 0000000..bbb4ff9 --- /dev/null +++ b/tests/test_service.py @@ -0,0 +1,43 @@ +import mock +import pytest +import XenAPIPlugin + +from service import start_service, stop_service, restart_service, try_restart_service + +@mock.patch("service.run_command", autospec=True) +class TestService: + SERVICE = 'linstor-satellite' + + def _test_command(self, cmd, cmd_name, run_command): + cmd(mock.MagicMock(), {'service': self.SERVICE}) + run_command.assert_called_once_with(['systemctl', cmd_name, self.SERVICE]) + + def _test_command_without_service(self, cmd): + with pytest.raises(XenAPIPlugin.Failure) as e: + start_service(mock.MagicMock(), {}) + assert e.value.params[0] == '-1' + assert e.value.params[1] == 'Missing or empty argument `service`' + + def test_start(self, run_command): + self._test_command(start_service, 'start', run_command) + + def test_stop(self, run_command): + self._test_command(stop_service, 'stop', run_command) + + def test_restart(self, run_command): + self._test_command(restart_service, 'restart', run_command) + + def test_try_restart(self, run_command): + self._test_command(try_restart_service, 'try-restart', run_command) + + def test_start_without_service(self, run_command): + self._test_command_without_service(start_service) + + def test_stop_without_service(self, run_command): + self._test_command_without_service(stop_service) + + def test_restart_without_service(self, run_command): + self._test_command_without_service(restart_service) + + def test_try_restart_without_service(self, run_command): + self._test_command_without_service(try_restart_service)