From 2f380077d4875e41411c60734fe0b7a9b4d7671f Mon Sep 17 00:00:00 2001 From: SrikanthMyakam Date: Mon, 30 Dec 2024 21:16:50 +0530 Subject: [PATCH] Windows support for Service tool added. Windows support for Service tool added. --- lisa/base_tools/service.py | 101 ++++++++++++++++++++++++++++++++++++- lisa/schema.py | 10 ++++ lisa/tools/powershell.py | 9 +++- 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/lisa/base_tools/service.py b/lisa/base_tools/service.py index 1cfda78981..c31262664a 100644 --- a/lisa/base_tools/service.py +++ b/lisa/base_tools/service.py @@ -1,10 +1,16 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. import re -from typing import Type +from time import sleep +from typing import Any, Optional, Type +from assertpy import assert_that + +from lisa import schema from lisa.executable import ExecutableResult, Tool +from lisa.tools.powershell import PowerShell from lisa.util import ( + LisaException, UnsupportedDistroException, filter_ansi_escape, find_group_in_lines, @@ -25,6 +31,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 @@ -58,6 +68,95 @@ def is_service_inactive(self, name: str) -> bool: def is_service_running(self, name: str) -> bool: return self._internal_tool._check_service_running(name) # type: ignore + def wait_for_service_start(self, name: str) -> None: + raise NotImplementedError() + + +class WindowsService(Tool): + def _initialize(self, *args: Any, **kwargs: Any) -> None: + self._command = "" + self._powershell = self.node.tools[PowerShell] + + @property + def can_install(self) -> bool: + return False + + @property + def command(self) -> str: + return self._command + + def _check_exists(self) -> bool: + return True + + def restart_service(self, name: str, ignore_exit_code: int = 0) -> None: + self._powershell.run_cmdlet( + f"Restart-service {name}", + force_run=True, + ) + self.wait_for_service_start(name) + + def wait_for_service_start(self, name: str) -> None: + for _ in range(10): + service_status = self._powershell.run_cmdlet( + f"Get-Service {name}", + force_run=True, + output_json=True, + fail_on_error=False, + ) + if schema.WindowsServiceStatus.RUNNING == schema.WindowsServiceStatus( + service_status["Status"] + ): + return + + self._log.debug( + f"service '{name}' is not ready yet, retrying... after 3 seconds" + ) + sleep(3) + + raise LisaException(f"service '{name}' failed to start") + + def _get_status(self, name: str = "") -> schema.WindowsServiceStatus: + service_status = self._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 schema.WindowsServiceStatus(service_status["Status"]) + + def stop_service(self, name: str) -> None: + self._powershell.run_cmdlet( + f"Stop-Service {name} -Force", + force_run=True, + output_json=True, + fail_on_error=False, + ) + assert_that(self._get_status(name)).described_as( + f"Failed to stop service {name}" + ).is_not_equal_to(schema.WindowsServiceStatus.RUNNING) + + def enable_service(self, name: str) -> None: + raise NotImplementedError() + + def check_service_status(self, name: str) -> bool: + return self._get_status(name) == schema.WindowsServiceStatus.RUNNING + + def check_service_exists(self, name: str) -> bool: + try: + self._get_status(name) + return True + except LisaException: + return False + + def is_service_inactive(self, name: str) -> bool: + return self._get_status(name) == schema.WindowsServiceStatus.STOPPED + + # Check if service is running + def is_service_running(self, name: str) -> bool: + return self._get_status(name) == schema.WindowsServiceStatus.RUNNING + class ServiceInternal(Tool): @property diff --git a/lisa/schema.py b/lisa/schema.py index 431dca0dd6..17578d3c11 100644 --- a/lisa/schema.py +++ b/lisa/schema.py @@ -431,6 +431,16 @@ class DiskType(str, Enum): ] +class WindowsServiceStatus(int, Enum): + CONTINUE_PENDING = 5 + PAUSE_PENDING = 6 + PAUSED = 7 + RUNNING = 4 + START_PENDING = 2 + STOP_PENDING = 3 + STOPPED = 1 + + class StorageInterfaceType(str, Enum): SCSI = constants.STORAGE_INTERFACE_TYPE_SCSI NVME = constants.STORAGE_INTERFACE_TYPE_NVME diff --git a/lisa/tools/powershell.py b/lisa/tools/powershell.py index d6107a5cca..2487bd4092 100644 --- a/lisa/tools/powershell.py +++ b/lisa/tools/powershell.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. import base64 +import json from typing import Any from xml.etree import ElementTree @@ -48,6 +49,7 @@ def run_cmdlet_async( def run_cmdlet( self, cmdlet: str, + output_json: bool = False, force_run: bool = False, sudo: bool = False, fail_on_error: bool = True, @@ -55,7 +57,9 @@ def run_cmdlet( # Powershell error log is the xml format, it needs extra decoding. But # for long running script, it needs to look real time results. no_debug_log: bool = True, - ) -> str: + ) -> Any: + if output_json: + cmdlet = f"{cmdlet} | ConvertTo-Json" process = self.run_cmdlet_async( cmdlet=cmdlet, force_run=force_run, sudo=sudo, no_debug_log=no_debug_log ) @@ -68,7 +72,8 @@ def run_cmdlet( # if stdout is output already, it doesn't need to output again. no_debug_log=not no_debug_log, ) - + if output_json and result.stdout: + return json.loads(result.stdout) return result.stdout def wait_result(