diff --git a/lisa/base_tools/service.py b/lisa/base_tools/service.py index 1cfda78981..95111cb94b 100644 --- a/lisa/base_tools/service.py +++ b/lisa/base_tools/service.py @@ -1,11 +1,16 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. import re -from typing import Type +from enum import Enum +from time import sleep +from typing import Optional, Type from lisa.executable import ExecutableResult, Tool +from lisa.tools.powershell import PowerShell from lisa.util import ( + LisaException, UnsupportedDistroException, + create_timer, filter_ansi_escape, find_group_in_lines, ) @@ -25,6 +30,10 @@ def command(self) -> str: def can_install(self) -> bool: return False + @classmethod + def _windows_tool(cls) -> Optional[Type[Tool]]: + return WindowsService + def _check_exists(self) -> bool: cmd_result = self.node.execute( "ls -lt /run/systemd/system", shell=True, sudo=True @@ -200,3 +209,90 @@ def _check_service_running(self, name: str) -> bool: def _check_error_codes(cmd_result: ExecutableResult, error_code: int = 0) -> None: cmd_result.assert_exit_code(expected_exit_code=[0, error_code]) + + +class WindowsServiceStatus(int, Enum): + CONTINUE_PENDING = 5 + PAUSE_PENDING = 6 + PAUSED = 7 + RUNNING = 4 + START_PENDING = 2 + STOP_PENDING = 3 + STOPPED = 1 + + +class WindowsService(Tool): + @property + def can_install(self) -> bool: + return False + + @property + def command(self) -> str: + return "" + + def enable_service(self, name: str) -> None: + pass + + def restart_service(self, name: str, ignore_exit_code: int = 0) -> None: + self.node.tools[PowerShell].run_cmdlet( + f"Restart-service {name}", + force_run=True, + ) + self.wait_for_service_start(name) + + def stop_service(self, name: str) -> None: + self.node.tools[PowerShell].run_cmdlet( + f"Stop-Service {name} -Force", + force_run=True, + output_json=True, + fail_on_error=False, + ) + self.wait_for_service_stop(name) + + def wait_for_service_start(self, name: str) -> None: + self._wait_for_service(name, WindowsServiceStatus.RUNNING) + + def wait_for_service_stop(self, name: str) -> None: + self._wait_for_service(name, WindowsServiceStatus.STOPPED) + + def _check_exists(self) -> bool: + return True + + def _check_service_exists(self, name: str) -> bool: + try: + self._get_status(name) + return True + except LisaException: + return False + + def _check_service_running(self, name: str) -> bool: + return self._get_status(name) == WindowsServiceStatus.RUNNING + + def _get_status(self, name: str = "") -> WindowsServiceStatus: + service_status = self.node.tools[PowerShell].run_cmdlet( + f"Get-Service {name}", + force_run=True, + output_json=True, + fail_on_error=False, + ) + if not service_status: + raise LisaException(f"service '{name}' does not exist") + return WindowsServiceStatus(int(service_status["Status"])) + + def _is_service_inactive(self, name: str) -> bool: + return self._get_status(name) == WindowsServiceStatus.STOPPED + + def _wait_for_service(self, name: str, status: WindowsServiceStatus) -> None: + timeout = 60 + timer = create_timer() + self._log.debug(f"waiting for service '{name}' to be in '{status}' state") + while timeout > timer.elapsed(False): + current_service_status = self._get_status(name) + if status == current_service_status: + return + sleep(0.5) + + if timeout < timer.elapsed(): + raise LisaException( + f"service '{name}' still in '{current_service_status}' state after '{timeout}' seconds" # noqa: E501 + )