[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC XEN PATCH 04/13] pytest: Add fixture that boot a machine



From: Anthony PERARD <anthony.perard@xxxxxxxxxx>

.. and power it off once a test using the fixture is done.

This IPMI class is using the `ssh` console of an HP Moonshot 1500
Chassis.

Signed-off-by: Anthony PERARD <anthony.perard@xxxxxxxxxx>
---
Maybe we could rename the class to Machine as a lib to control the
machine behavior, and access it's console.
---
 automation/pytest/lib/ipmi.py  | 69 ++++++++++++++++++++++++++++++++++
 automation/pytest/test_boot.py | 17 +++++++++
 2 files changed, 86 insertions(+)
 create mode 100644 automation/pytest/lib/ipmi.py

diff --git a/automation/pytest/lib/ipmi.py b/automation/pytest/lib/ipmi.py
new file mode 100644
index 000000000000..0d3b085f704c
--- /dev/null
+++ b/automation/pytest/lib/ipmi.py
@@ -0,0 +1,69 @@
+import logging
+import paramiko
+import re
+
+
+class IPMI:
+    def __init__(self, *,
+                 hostname: str,
+                 username: str,
+                 password: str,
+                 blade_node: str,
+                 ) -> None:
+        self.hostname = hostname
+        self.username = username
+        self.password = password
+        self.blade_node = blade_node
+
+        self.channel = None
+
+    def _new_client(self):
+        ssh = paramiko.client.SSHClient()
+        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        ssh.connect(self.hostname,
+                    username=self.username,
+                    password=self.password)
+        return ssh
+
+    def _run_cmd(self, command: str, debug_print: bool = True) -> str:
+        with self._new_client() as ssh:
+            stdin, stdout, stderr = ssh.exec_command(command)
+            output = stdout.read().decode()
+            if debug_print:
+                for line in output.splitlines():
+                    if line in ['']:
+                        continue
+                    logging.debug(line.rstrip())
+            # the command's exit status (0 = success, 1 = failure)
+            # stdout.channel.recv_exit_status()
+            return output
+
+    def _power(self, state: str) -> None:
+        assert state in ["on", "off force"]
+        logging.info(f"Set power {state.split(' ')[0]} {self.blade_node}")
+        command = f"set node power {state} {self.blade_node}"
+        output = self._run_cmd(command, debug_print=False)
+        m = re.search(f"{self.blade_node}: #Node 1 (.*)\\r$",
+                      output,
+                      flags=re.M)
+        if m:
+            logging.info(f"-> {m[1]}")
+        else:
+            logging.warning(f"Failed to parse 'set power' command output: 
'{output}'")
+
+    def power_status(self):
+        command = f"show node power {self.blade_node}"
+        output = self._run_cmd(command, debug_print=False)
+        m = re.search(f"{self.blade_node}: #Node 1\\r\\n\\s+Power State: 
(\\S+)\\r$",
+                      output,
+                      flags=re.M)
+        if m:
+            logging.info(f"Machine is {m[1]}")
+        else:
+            logging.warning(f"Failed to match for \"Power State\" in 
'{output}'")
+
+    def power_on(self):
+        self._power('on')
+
+    def power_off(self):
+        self._power('off force')
diff --git a/automation/pytest/test_boot.py b/automation/pytest/test_boot.py
index 661c2a59e4fe..b7610736fa25 100644
--- a/automation/pytest/test_boot.py
+++ b/automation/pytest/test_boot.py
@@ -1,9 +1,11 @@
 import os
 from pathlib import Path
 import pytest
+from typing import Generator
 
 from lib.boot_binary import BootBinary
 from lib.net_boot import NetBoot
+from lib.ipmi import IPMI
 
 
 @pytest.fixture
@@ -41,3 +43,18 @@ def prepare_netboot(prepare_rootfs: BootBinary, 
xenroot_path: Path) -> None:
         prepare_rootfs.dom0_tools(),
     ]
     netboot.prepare_tftp(xenroot_path, rootfs_paths)
+
+
+@pytest.fixture
+def machine_on_ipmi(prepare_netboot: None) -> Generator[IPMI]:
+    ipmi = IPMI(
+            hostname=os.environ['IPMI_HOSTNAME'],
+            username=os.environ['MOON_CONSOLE_USER'],
+            password=os.environ['MOON_CONSOLE_PASS'],
+            blade_node=os.environ['IPMI_BLADE_NODE'],
+            )
+    ipmi.power_off()
+    ipmi.power_status()
+    ipmi.power_on()
+    yield ipmi
+    ipmi.power_off()
-- 
Anthony PERARD




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.