Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ip_power control for BareMetal platform #3550

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions lisa/sut_orchestrator/baremetal/cluster/pxe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
# Licensed under the MIT license.

from pathlib import Path
squirrelsc marked this conversation as resolved.
Show resolved Hide resolved
from time import sleep
from typing import Any, Optional, Type, cast

import requests

from lisa import features
from lisa.environment import Environment
from lisa.features.serial_console import SerialConsole
from lisa.features import SerialConsole, StartStop
from lisa.node import Node, RemoteNode, quick_connect
from lisa.platform_ import Platform
from lisa.schema import FeatureSettings
Expand All @@ -17,6 +20,9 @@
from ..context import get_node_context
from .cluster import Cluster

REQUEST_TIMEOUT = 3
POWER_DOWN_DELAY = 3


class RemoteComSerialConsole(features.SerialConsole):
def __init__(
Expand Down Expand Up @@ -100,7 +106,56 @@ def _connect(self) -> None:
process.wait_output("\n", timeout=1, interval=0.1)

self._process = process
self._log.debug("connected to serial console: {serial_port}")
self._log.debug(f"connected to serial console: {serial_port}")


class Ip9285StartStop(features.StartStop):
def __init__(
self, settings: FeatureSettings, node: Node, platform: Platform
) -> None:
super().__init__(settings, node, platform)

def _initialize(self, *args: Any, **kwargs: Any) -> None:
super()._initialize(*args, **kwargs)

context = get_node_context(self._node)
pxe_cluster = cast(schema.PxeCluster, context.cluster)
assert pxe_cluster.start_stop, "start_stop is not defined"

ip_power_runbook = pxe_cluster.start_stop.get_extended_runbook(schema.Ip9285)

self._request_cmd = (
f"http://{ip_power_runbook.host}/set.cmd?"
f"user={ip_power_runbook.username}+pass="
f"{ip_power_runbook.password}+cmd="
f"setpower+P6{ip_power_runbook.ctrl_port}"
)

def _set_ip_power(self, power_cmd: str) -> None:
response = requests.get(power_cmd, timeout=REQUEST_TIMEOUT)
response.raise_for_status()
self._log.debug("Command {power_cmd} done in set_ip_power")

def _stop(
self,
wait: bool = True,
state: features.StopState = features.StopState.Shutdown,
) -> None:
request_off = f"{self._request_cmd}=0"
self._set_ip_power(request_off)
squirrelsc marked this conversation as resolved.
Show resolved Hide resolved
# To make sure power-off is fully settled down, it is recommended
# to wait a short time before power-on is triggered. Local tests showed
# 1s is actually good enough to get a stable power-off status, here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request timeout is not used here, so the comment shouldn't mention it to avoid confusing.

# 3s is used in order to garantee power-off is successfully executed.
sleep(POWER_DOWN_DELAY)

def _start(self, wait: bool = True) -> None:
request_on = f"{self._request_cmd}=1"
self._set_ip_power(request_on)

def _restart(self, wait: bool = True) -> None:
self._stop()
self._start()


class Pxe(Cluster):
Expand All @@ -126,6 +181,15 @@ def get_serial_console(self) -> Type[SerialConsole]:
f"is not supported."
)

def get_start_stop(self) -> Type[StartStop]:
assert self.runbook.start_stop, "start_stop is not defined"
if self.runbook.start_stop.type == "Ip9285":
return Ip9285StartStop
else:
raise NotImplementedError(
f"start_stop type {self.runbook.start_stop.type} is not supported."
)

def deploy(self, environment: Environment) -> Any:
# connect to serial console
for node in environment.nodes.list():
Expand Down
19 changes: 19 additions & 0 deletions lisa/sut_orchestrator/baremetal/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ class BootConfigSchema(schema.TypedSchema, schema.ExtendableSchemaMixin):
type: str = field(default="boot_config", metadata=field_metadata(required=True))


@dataclass_json()
@dataclass
class IPPowerSchema(schema.TypedSchema, schema.ExtendableSchemaMixin):
host: str = ""
username: str = ""
password: str = ""

def __post_init__(self, *args: Any, **kwargs: Any) -> None:
add_secret(self.password)


@dataclass_json()
@dataclass
class Ip9285(IPPowerSchema):
type: str = "Ip9285"
ctrl_port: str = ""


@dataclass_json()
@dataclass
class ClusterSchema(schema.TypedSchema, schema.ExtendableSchemaMixin):
Expand Down Expand Up @@ -213,6 +231,7 @@ class PxeClient(ClientSchema):
class PxeCluster(ClusterSchema):
type: str = "pxe"
serial_console: Optional[SerialConsoleServer] = field(default=None)
start_stop: Optional[IPPowerSchema] = field(default=None)
client: List[PxeClient] = field(default_factory=list)


Expand Down
Loading