Skip to content

Commit

Permalink
Windows support for Service tool added.
Browse files Browse the repository at this point in the history
Windows support for Service tool added.
  • Loading branch information
SRIKKANTH committed Dec 30, 2024
1 parent bcaefdb commit 2f38007
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 3 deletions.
101 changes: 100 additions & 1 deletion lisa/base_tools/service.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions lisa/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions lisa/tools/powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT license.

import base64
import json
from typing import Any
from xml.etree import ElementTree

Expand Down Expand Up @@ -48,14 +49,17 @@ 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,
timeout: int = 600,
# 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
)
Expand All @@ -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(
Expand Down

0 comments on commit 2f38007

Please sign in to comment.