| #!/usr/bin/python3 |
| |
| import sys |
| import time |
| from collections import namedtuple |
| |
| import pexpect |
| |
| Endpoint = namedtuple("Endpoint", "host, port") |
| Credentials = namedtuple("Credentials", "username, password") |
| Target = namedtuple("Target", "credentials, endpoint") |
| Entity = namedtuple("Entity", "console, ssh") |
| Machine = namedtuple("Machine", "bmc, host") |
| |
| |
| class Obmcutil(object): |
| BMC_READY = "xyz.openbmc_project.State.BMC.BMCState.Ready" |
| BMC_NOT_READY = "xyz.openbmc_project.State.BMC.BMCState.NotReady" |
| |
| HOST_OFF = "xyz.openbmc_project.State.Host.HostState.Off" |
| HOST_ON = "xyz.openbmc_project.State.Host.HostState.Running" |
| HOST_QUIESCED = "xyz.openbmc_project.State.Host.HostState.Quiesced" |
| |
| def __init__(self, session, prompt): |
| self.session = session |
| self.prompt = prompt |
| |
| def _clear(self): |
| self.session.expect([".+".encode(), pexpect.TIMEOUT], timeout=5) |
| |
| def _state(self, cmd, needle): |
| self.session.sendline() |
| self._clear() |
| self.session.sendline("obmcutil -w {}".format(cmd).encode()) |
| self.session.expect(needle, timeout=None) |
| rc = self.session.after.decode() |
| return rc |
| |
| def hoststate(self): |
| return self._state( |
| "hoststate", |
| "xyz\\.openbmc_project\\.State\\.Host\\.HostState\\." |
| + "(Off|Running|Quiesced)", |
| ) |
| |
| def bmcstate(self): |
| return self._state( |
| "bmcstate", |
| "xyz\\.openbmc_project\\.State\\.BMC\\.BMCState\\.(Not)?Ready", |
| ) |
| |
| def poweron(self): |
| self.session.sendline("obmcutil -w poweron") |
| self.session.expect(self.prompt) |
| |
| def chassisoff(self): |
| self.session.sendline("obmcutil -w chassisoff") |
| self.session.expect(self.prompt) |
| |
| |
| class PexpectLogger(object): |
| def write(self, bstring): |
| try: |
| sys.stdout.write(bstring.decode()) |
| except UnicodeDecodeError: |
| print("Dropping broken unicode line") |
| |
| def flush(self): |
| sys.stdout.flush() |
| |
| |
| class Bmc(object): |
| def __init__(self, entity): |
| self.getty = "login: ".encode() |
| self.shell = "# ".encode() |
| self.entity = entity |
| fargs = (entity.console.endpoint.host, entity.console.endpoint.port) |
| self.session = pexpect.spawn("telnet {} {}".format(*fargs)) |
| self.session.logfile = PexpectLogger() |
| self.obmcutil = Obmcutil(self.session, self.shell) |
| self.session.sendline() |
| rc = self.session.expect([self.getty, self.shell]) |
| if rc == 0: |
| self.login() |
| |
| def login(self): |
| self.session.sendline( |
| self.entity.console.credentials.username.encode() |
| ) |
| self.session.expect("Password: ".encode()) |
| self.session.sendline( |
| self.entity.console.credentials.password.encode() |
| ) |
| self.session.expect(self.shell) |
| |
| def reboot(self): |
| self.session.sendline("reboot") |
| self.session.expect( |
| "Hit any key to stop autoboot:".encode(), timeout=None |
| ) |
| self.session.expect(self.getty, timeout=None) |
| self.login() |
| state = self.obmcutil.bmcstate() |
| while state != self.obmcutil.BMC_READY: |
| print( |
| "Wanted state '{}', got state '{}'".format( |
| self.obmcutil.BMC_READY, state |
| ) |
| ) |
| time.sleep(5) |
| state = self.obmcutil.bmcstate() |
| |
| def chassisoff(self): |
| self.obmcutil.chassisoff() |
| |
| def poweron(self): |
| hs = self.obmcutil.hoststate() |
| print("Host state is: {}".format(hs)) |
| if hs in (self.obmcutil.HOST_ON, self.obmcutil.HOST_QUIESCED): |
| self.obmcutil.chassisoff() |
| self.obmcutil.poweron() |
| |
| |
| class Host(object): |
| def __init__(self, entity, bmc): |
| self.shell = "/? *#".encode() |
| self.petitboot = "Petitboot".encode() |
| self.session = None |
| self.entity = entity |
| self.bmc = bmc |
| self.connect() |
| |
| def connect(self): |
| fargs = ( |
| self.entity.console.endpoint.port, |
| self.entity.console.credentials.username, |
| self.entity.console.endpoint.host, |
| ) |
| self.session = pexpect.spawn("ssh -p{} {}@{}".format(*fargs)) |
| self.session.logfile = PexpectLogger() |
| self.session.expect("password:".encode()) |
| self.session.sendline( |
| self.entity.console.credentials.password.encode() |
| ) |
| |
| def poweron(self): |
| self.bmc.chassisoff() |
| self.bmc.poweron() |
| self.session.send("\f") |
| rc = self.session.expect([self.petitboot, self.shell], timeout=None) |
| if rc == 0: |
| self.session.sendline() |
| self.session.expect(self.shell) |
| |
| def reboot(self): |
| self.session.send("\f") |
| rc = self.session.expect([self.petitboot, self.shell], timeout=None) |
| if rc == 0: |
| self.session.sendline() |
| self.session.expect(self.shell) |
| self.session.sendline("reboot".encode()) |
| self.session.expect( |
| "INIT: Waiting for kernel...".encode(), timeout=None |
| ) |
| self.session.expect("Petitboot".encode(), timeout=None) |
| self.session.sendline() |
| self.session.expect(self.shell) |
| |
| |
| def rpp(machine): |
| bmc = Bmc(machine.bmc) |
| host = Host(machine.host, bmc) |
| host.poweron() |
| while True: |
| bmc.reboot() |
| host.connect() |
| host.reboot() |
| |
| |
| def main(): |
| bmccreds = Credentials("root", "0penBmc") |
| b = Entity( |
| Target(bmccreds, Endpoint("serial.concentrator.somewhere.com", 1234)), |
| Target(bmccreds, Endpoint("bmc.somewhere.com", 22)), |
| ) |
| h = Entity( |
| Target(bmccreds, Endpoint("bmc.somewhere.com", 2200)), |
| Target( |
| Credentials("user", "password"), Endpoint("host.somewhere.com", 22) |
| ), |
| ) |
| m = Machine(b, h) |
| return rpp(m) |
| |
| |
| if __name__ == "__main__": |
| main() |