Skip to content

Commit

Permalink
update hibernation
Browse files Browse the repository at this point in the history
  • Loading branch information
LiliDeng committed Jan 4, 2024
1 parent 10ad7ab commit b66b186
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 66 deletions.
3 changes: 2 additions & 1 deletion lisa/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
SecurityProfileType,
)
from .serial_console import SerialConsole
from .startstop import StartStop, StopState
from .startstop import StartStop, StopState, VMStatus

__all__ = [
"ACC",
Expand Down Expand Up @@ -63,6 +63,7 @@
"SecurityProfileType",
"Sriov",
"StopState",
"VMStatus",
"Synthetic",
"StartStop",
]
9 changes: 9 additions & 0 deletions lisa/features/startstop.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
FEATURE_NAME_STARTSTOP = "StartStop"


class VMStatus(str, Enum):
Deallocated = "Deallocated"
Running = "Running"
ProvisionSucceeded = "ProvisionSucceeded"


class StopState(str, Enum):
Hibernate = "hibernate"
Shutdown = "shutdown"
Expand Down Expand Up @@ -48,3 +54,6 @@ def restart(self, wait: bool = True) -> None:
self._log.info("restarting")
self._restart(wait=wait)
self._node.close()

def get_status(self) -> VMStatus:
raise NotImplementedError()
37 changes: 36 additions & 1 deletion lisa/sut_orchestrator/azure/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
FEATURE_NAME_SECURITY_PROFILE,
SecurityProfileType,
)
from lisa.features.startstop import VMStatus
from lisa.node import Node, RemoteNode
from lisa.operating_system import BSD, CBLMariner, CentOs, Redhat, Suse, Ubuntu
from lisa.search_space import RequirementMethod
Expand Down Expand Up @@ -129,6 +130,13 @@ def _initialize_information(self, node: Node) -> None:


class StartStop(AzureFeatureMixin, features.StartStop):
azure_vm_status_map = {
"VM deallocated": VMStatus.Deallocated,
"VM running": VMStatus.Running,
"Provisioning succeeded": VMStatus.ProvisionSucceeded,
# Add more Azure-specific mappings as needed
}

@classmethod
def create_setting(
cls, *args: Any, **kwargs: Any
Expand Down Expand Up @@ -182,6 +190,23 @@ def _execute(self, wait: bool, operator: str, **kwargs: Any) -> None:
if wait:
wait_operation(operation, failure_identity="Start/Stop")

def get_status(self) -> VMStatus:
try:
platform: AzurePlatform = self._platform # type: ignore
compute_client = get_compute_client(platform)
status = (
compute_client.virtual_machines.get(
self._resource_group_name, self._vm_name, expand="instanceView"
)
.instance_view.statuses[1]
.display_status
)
assert isinstance(status, str), f"actual: {type(status)}"
assert self.azure_vm_status_map.get(status) is not None, "unknown vm status"
return cast(VMStatus, self.azure_vm_status_map.get(status))
except Exception as e:
raise LisaException(f"fail to get status of vm {self._vm_name}") from e


class FixedSerialPortsOperations(SerialPortsOperations): # type: ignore
def connect(
Expand Down Expand Up @@ -2014,8 +2039,18 @@ def create_setting(
cls, *args: Any, **kwargs: Any
) -> Optional[schema.FeatureSettings]:
raw_capabilities: Any = kwargs.get("raw_capabilities")
resource_sku: Any = kwargs.get("resource_sku")

if raw_capabilities.get("HibernationSupported", None) == "True":
if (
resource_sku.family
in [
"standardDSv5Family",
"standardDDSv5Family",
"standardDASv5Family",
"standardDADSv5Family",
]
or raw_capabilities.get("HibernationSupported", None) == "True"
):
return schema.FeatureSettings.create(cls.name())

return None
Expand Down
2 changes: 2 additions & 0 deletions lisa/tools/dmesg.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class Dmesg(Tool):
re.compile("rcu_sched self-detected stall on CPU"),
re.compile("rcu_sched detected stalls on"),
re.compile("BUG: soft lockup"),
re.compile("Hibernate inconsistent memory map detected"),
re.compile("check_flush_dependency"),
]

# [ 3.191822] hv_vmbus: Hyper-V Host Build:18362-10.0-3-0.3294; Vmbus version:3.0
Expand Down
15 changes: 10 additions & 5 deletions lisa/tools/hibernation_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import re
from typing import List, Pattern, Type

from lisa.base_tools import Systemctl
from lisa.base_tools import Cat, Systemctl
from lisa.executable import Tool
from lisa.operating_system import CBLMariner
from lisa.util import find_patterns_in_lines

from .dmesg import Dmesg
from .git import Git
from .ls import Ls
from .make import Make


Expand Down Expand Up @@ -74,9 +74,14 @@ def _install(self) -> bool:
return self._check_exists()

def _check(self, pattern: Pattern[str]) -> int:
dmesg = self.node.tools[Dmesg]
dmesg_output = dmesg.get_output(force_run=True)
matched_lines = find_patterns_in_lines(dmesg_output, [pattern])
cat = self.node.tools[Cat]
log_output = ""
ls = self.node.tools[Ls]
if ls.path_exists("/var/log/syslog", sudo=True):
log_output = cat.read("/var/log/syslog", force_run=True, sudo=True)
if ls.path_exists("/var/log/messages", sudo=True):
log_output = cat.read("/var/log/messages", force_run=True, sudo=True)
matched_lines = find_patterns_in_lines(log_output, [pattern])
if not matched_lines:
return 0
return len(matched_lines[0])
81 changes: 45 additions & 36 deletions microsoft/testsuites/power/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@

from assertpy import assert_that

from lisa import Environment, Logger, RemoteNode, features
from lisa import Environment, Logger, Node, RemoteNode, features
from lisa.features import StartStop
from lisa.features.startstop import VMStatus
from lisa.operating_system import Redhat, Suse, Ubuntu
from lisa.tools import Fio, HibernationSetup, Iperf3, KernelConfig, Kill, Lscpu
from lisa.util import LisaException, SkippedException, constants
from lisa.tools import Dmesg, Fio, HibernationSetup, Iperf3, KernelConfig, Kill, Lscpu
from lisa.util import LisaException, SkippedException
from lisa.util.perf_timer import create_timer
from lisa.util.shell import wait_tcp_port_ready


def is_distro_supported(node: RemoteNode) -> None:
def is_distro_supported(node: Node) -> None:
if not node.tools[KernelConfig].is_enabled("CONFIG_HIBERNATION"):
raise SkippedException(
f"CONFIG_HIBERNATION is not enabled in current distro {node.os.name}, "
Expand All @@ -32,69 +32,78 @@ def is_distro_supported(node: RemoteNode) -> None:
)


def verify_hibernation(node: RemoteNode, log: Logger) -> None:
def verify_hibernation(
node: Node,
log: Logger,
throw_error: bool = True,
) -> None:
hibernation_setup_tool = node.tools[HibernationSetup]
startstop = node.features[StartStop]

node_nic = node.nics
lower_nics_before_hibernation = node_nic.get_lower_nics()
upper_nics_before_hibernation = node_nic.get_nic_names()
hibernation_setup_tool = node.tools[HibernationSetup]
entry_before_hibernation = hibernation_setup_tool.check_entry()
exit_before_hibernation = hibernation_setup_tool.check_exit()
received_before_hibernation = hibernation_setup_tool.check_received()
uevent_before_hibernation = hibernation_setup_tool.check_uevent()
startstop = node.features[StartStop]

# only set up hibernation setup tool for the first time
hibernation_setup_tool.start()
startstop.stop(state=features.StopState.Hibernate)

try:
startstop.stop(state=features.StopState.Hibernate)
except Exception as ex:
try:
node.tools[Dmesg].get_output(force_run=True)
except Exception as e:
log.debug(f"error on get dmesg output: {e}")
raise LisaException(f"fail to hibernate: {ex}")

is_ready = True
timeout = 900
timer = create_timer()
while timeout > timer.elapsed(False):
is_ready, _ = wait_tcp_port_ready(
node.connection_info[constants.ENVIRONMENTS_NODES_REMOTE_ADDRESS],
node.connection_info[constants.ENVIRONMENTS_NODES_REMOTE_PORT],
log=log,
timeout=10,
)
if not is_ready:
if startstop.get_status() == VMStatus.Deallocated:
is_ready = False
break
if is_ready:
raise LisaException("VM still can be accessed after hibernation")
raise LisaException("VM is not in deallocated status after hibernation")

startstop.start()
dmesg = node.tools[Dmesg]
dmesg.check_kernel_errors(force_run=True, throw_error=throw_error)

entry_after_hibernation = hibernation_setup_tool.check_entry()
exit_after_hibernation = hibernation_setup_tool.check_exit()
received_after_hibernation = hibernation_setup_tool.check_received()
uevent_after_hibernation = hibernation_setup_tool.check_uevent()
assert_that(
entry_after_hibernation - entry_before_hibernation,
"not find 'hibernation entry'.",
assert_that(entry_after_hibernation - entry_before_hibernation).described_as(
"not find 'hibernation entry'."
).is_equal_to(1)
assert_that(
exit_after_hibernation - exit_before_hibernation,
"not find 'hibernation exit'.",
assert_that(exit_after_hibernation - exit_before_hibernation).described_as(
"not find 'hibernation exit'."
).is_equal_to(1)
assert_that(
received_after_hibernation - received_before_hibernation,
"not find 'Hibernation request received'.",
assert_that(received_after_hibernation - received_before_hibernation).described_as(
"not find 'Hibernation request received'."
).is_equal_to(1)
assert_that(
uevent_after_hibernation - uevent_before_hibernation,
"not find 'Sent hibernation uevent'.",
assert_that(uevent_after_hibernation - uevent_before_hibernation).described_as(
"not find 'Sent hibernation uevent'."
).is_equal_to(1)

node_nic = node.nics
node_nic.initialize()
lower_nics_after_hibernation = node_nic.get_lower_nics()
upper_nics_after_hibernation = node_nic.get_nic_names()
assert_that(
len(lower_nics_after_hibernation),
"sriov nics count changes after hibernation.",
assert_that(len(lower_nics_after_hibernation)).described_as(
"sriov nics count changes after hibernation."
).is_equal_to(len(lower_nics_before_hibernation))
assert_that(
len(upper_nics_after_hibernation),
"synthetic nics count changes after hibernation.",
assert_that(len(upper_nics_after_hibernation)).described_as(
"synthetic nics count changes after hibernation."
).is_equal_to(len(upper_nics_before_hibernation))


def run_storage_workload(node: RemoteNode) -> Decimal:
def run_storage_workload(node: Node) -> Decimal:
fio = node.tools[Fio]
fiodata = node.get_pure_path("./fiodata")
core_count = node.tools[Lscpu].get_core_count()
Expand Down
Loading

0 comments on commit b66b186

Please sign in to comment.