1#!/usr/bin/python3 2 3import sys 4import time 5from collections import namedtuple 6 7import pexpect 8 9Endpoint = namedtuple("Endpoint", "host, port") 10Credentials = namedtuple("Credentials", "username, password") 11Target = namedtuple("Target", "credentials, endpoint") 12Entity = namedtuple("Entity", "console, ssh") 13Machine = namedtuple("Machine", "bmc, host") 14 15 16class Obmcutil(object): 17 BMC_READY = "xyz.openbmc_project.State.BMC.BMCState.Ready" 18 BMC_NOT_READY = "xyz.openbmc_project.State.BMC.BMCState.NotReady" 19 20 HOST_OFF = "xyz.openbmc_project.State.Host.HostState.Off" 21 HOST_ON = "xyz.openbmc_project.State.Host.HostState.Running" 22 HOST_QUIESCED = "xyz.openbmc_project.State.Host.HostState.Quiesced" 23 24 def __init__(self, session, prompt): 25 self.session = session 26 self.prompt = prompt 27 28 def _clear(self): 29 self.session.expect([".+".encode(), pexpect.TIMEOUT], timeout=5) 30 31 def _state(self, cmd, needle): 32 self.session.sendline() 33 self._clear() 34 self.session.sendline("obmcutil -w {}".format(cmd).encode()) 35 self.session.expect(needle, timeout=None) 36 rc = self.session.after.decode() 37 return rc 38 39 def hoststate(self): 40 return self._state( 41 "hoststate", 42 "xyz\\.openbmc_project\\.State\\.Host\\.HostState\\." 43 + "(Off|Running|Quiesced)", 44 ) 45 46 def bmcstate(self): 47 return self._state( 48 "bmcstate", 49 "xyz\\.openbmc_project\\.State\\.BMC\\.BMCState\\.(Not)?Ready", 50 ) 51 52 def poweron(self): 53 self.session.sendline("obmcutil -w poweron") 54 self.session.expect(self.prompt) 55 56 def chassisoff(self): 57 self.session.sendline("obmcutil -w chassisoff") 58 self.session.expect(self.prompt) 59 60 61class PexpectLogger(object): 62 def write(self, bstring): 63 try: 64 sys.stdout.write(bstring.decode()) 65 except UnicodeDecodeError: 66 print("Dropping broken unicode line") 67 68 def flush(self): 69 sys.stdout.flush() 70 71 72class Bmc(object): 73 def __init__(self, entity): 74 self.getty = "login: ".encode() 75 self.shell = "# ".encode() 76 self.entity = entity 77 fargs = (entity.console.endpoint.host, entity.console.endpoint.port) 78 self.session = pexpect.spawn("telnet {} {}".format(*fargs)) 79 self.session.logfile = PexpectLogger() 80 self.obmcutil = Obmcutil(self.session, self.shell) 81 self.session.sendline() 82 rc = self.session.expect([self.getty, self.shell]) 83 if rc == 0: 84 self.login() 85 86 def login(self): 87 self.session.sendline( 88 self.entity.console.credentials.username.encode() 89 ) 90 self.session.expect("Password: ".encode()) 91 self.session.sendline( 92 self.entity.console.credentials.password.encode() 93 ) 94 self.session.expect(self.shell) 95 96 def reboot(self): 97 self.session.sendline("reboot") 98 self.session.expect( 99 "Hit any key to stop autoboot:".encode(), timeout=None 100 ) 101 self.session.expect(self.getty, timeout=None) 102 self.login() 103 state = self.obmcutil.bmcstate() 104 while state != self.obmcutil.BMC_READY: 105 print( 106 "Wanted state '{}', got state '{}'".format( 107 self.obmcutil.BMC_READY, state 108 ) 109 ) 110 time.sleep(5) 111 state = self.obmcutil.bmcstate() 112 113 def chassisoff(self): 114 self.obmcutil.chassisoff() 115 116 def poweron(self): 117 hs = self.obmcutil.hoststate() 118 print("Host state is: {}".format(hs)) 119 if hs in (self.obmcutil.HOST_ON, self.obmcutil.HOST_QUIESCED): 120 self.obmcutil.chassisoff() 121 self.obmcutil.poweron() 122 123 124class Host(object): 125 def __init__(self, entity, bmc): 126 self.shell = "/? *#".encode() 127 self.petitboot = "Petitboot".encode() 128 self.session = None 129 self.entity = entity 130 self.bmc = bmc 131 self.connect() 132 133 def connect(self): 134 fargs = ( 135 self.entity.console.endpoint.port, 136 self.entity.console.credentials.username, 137 self.entity.console.endpoint.host, 138 ) 139 self.session = pexpect.spawn("ssh -p{} {}@{}".format(*fargs)) 140 self.session.logfile = PexpectLogger() 141 self.session.expect("password:".encode()) 142 self.session.sendline( 143 self.entity.console.credentials.password.encode() 144 ) 145 146 def poweron(self): 147 self.bmc.chassisoff() 148 self.bmc.poweron() 149 self.session.send("\f") 150 rc = self.session.expect([self.petitboot, self.shell], timeout=None) 151 if rc == 0: 152 self.session.sendline() 153 self.session.expect(self.shell) 154 155 def reboot(self): 156 self.session.send("\f") 157 rc = self.session.expect([self.petitboot, self.shell], timeout=None) 158 if rc == 0: 159 self.session.sendline() 160 self.session.expect(self.shell) 161 self.session.sendline("reboot".encode()) 162 self.session.expect( 163 "INIT: Waiting for kernel...".encode(), timeout=None 164 ) 165 self.session.expect("Petitboot".encode(), timeout=None) 166 self.session.sendline() 167 self.session.expect(self.shell) 168 169 170def rpp(machine): 171 bmc = Bmc(machine.bmc) 172 host = Host(machine.host, bmc) 173 host.poweron() 174 while True: 175 bmc.reboot() 176 host.connect() 177 host.reboot() 178 179 180def main(): 181 bmccreds = Credentials("root", "0penBmc") 182 b = Entity( 183 Target(bmccreds, Endpoint("serial.concentrator.somewhere.com", 1234)), 184 Target(bmccreds, Endpoint("bmc.somewhere.com", 22)), 185 ) 186 h = Entity( 187 Target(bmccreds, Endpoint("bmc.somewhere.com", 2200)), 188 Target( 189 Credentials("user", "password"), Endpoint("host.somewhere.com", 22) 190 ), 191 ) 192 m = Machine(b, h) 193 return rpp(m) 194 195 196if __name__ == "__main__": 197 main() 198