]> git-server-git.apps.pok.os.sepia.ceph.com Git - teuthology.git/commitdiff
fog: Set ipmitool chassis bootdev during deployments bootdev
authorDavid Galloway <david.galloway@ibm.com>
Fri, 16 Jan 2026 01:11:12 +0000 (20:11 -0500)
committerDavid Galloway <david.galloway@ibm.com>
Fri, 16 Jan 2026 01:54:59 +0000 (20:54 -0500)
Signed-off-by: David Galloway <david.galloway@ibm.com>
teuthology/orchestra/console.py
teuthology/provision/fog.py

index b3ec963d7e05c60fa0812afcd90f42ce8cd281ab..5cce96bc1f3d421d26c9bf087b3f7773a58f5bbf 100644 (file)
@@ -3,6 +3,7 @@ import logging
 import os
 import pexpect
 import psutil
+import re
 import subprocess
 import sys
 import time
@@ -269,6 +270,55 @@ class PhysicalConsole(RemoteConsole):
             self.log.exception('Failed to get ipmi console status')
             return False
 
+    def set_bootdev(self, bootdev, uefi=True, persistent=False, timeout=None):
+        """
+        Set next boot device via IPMI.
+
+        Examples:
+          - PXE (UEFI):  ipmitool chassis bootdev pxe options=efiboot
+          - Disk (UEFI): ipmitool chassis bootdev disk options=efiboot
+
+        :param bootdev: one of: pxe, disk, cdrom, bios, safe, diag, none
+        :param uefi: add 'options=efiboot' for UEFI systems
+        :param persistent: request persistent bootdev if supported
+                          (adds 'options=persistent' or combined with efiboot)
+        :param timeout: override console timeout for ipmitool invocation
+        """
+        bootdev = str(bootdev).strip().lower()
+        valid = {"pxe", "disk", "cdrom", "bios", "safe", "diag", "floppy", "none"}
+        if bootdev not in valid:
+            raise ValueError("Invalid bootdev '%s' (valid: %s)" %
+                             (bootdev, ", ".join(sorted(valid))))
+
+        options = []
+        if uefi:
+            options.append("efiboot")
+        if persistent:
+            # ipmitool supports 'options=persistent' on many BMCs.
+            # If your BMC doesn't, this is harmless to omit by caller.
+            options.append("persistent")
+
+        cmd = "chassis bootdev %s" % bootdev
+        if options:
+            cmd += " options=" + ",".join(options)
+
+        self.log.info("Setting bootdev: %s", cmd)
+        child = self._pexpect_spawn_ipmi(cmd)
+        # Some ipmitool builds return EOF quickly; accept EOF as success-ish.
+        child.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=timeout or self.timeout)
+        out = child.logfile_read.getvalue().strip()
+        if out:
+            self.log.debug("bootdev output: %s", out)
+        return True
+
+    def boot_pxe_once(self, uefi=True, persistent=False):
+        """Convenience: set next boot to PXE."""
+        return self.set_bootdev("pxe", uefi=uefi, persistent=persistent)
+
+    def boot_disk_once(self, uefi=True, persistent=False):
+        """Convenience: set next boot to local disk."""
+        return self.set_bootdev("disk", uefi=uefi, persistent=persistent)
+
     def power_cycle(self, timeout=300):
         """
         Power cycle and wait for login.
index 101da2464d3b4581d25f93288e09a002b1966a6f..5d230a70d0a11f65cbff06fc16157092babb2750 100644 (file)
@@ -83,11 +83,13 @@ class FOG(object):
             # _wait_for_login, which will not work here since the newly-imaged
             # host will have an incorrect hostname
             self.remote.console.power_off()
+            self.remote.console.boot_pxe_once()
             self.remote.console.power_on()
             self.wait_for_deploy_task(task_id)
         except Exception:
             self.cancel_deploy_task(task_id)
             raise
+        self.remote.console.boot_disk_once()
         self._wait_for_ready()
         self._fix_hostname()
         self._verify_installed_os()