diff --git a/confluent_server/aiohmi/cmd/fakebmc.py b/confluent_server/aiohmi/cmd/fakebmc.py index ec24e3e8..5a222241 100755 --- a/confluent_server/aiohmi/cmd/fakebmc.py +++ b/confluent_server/aiohmi/cmd/fakebmc.py @@ -27,6 +27,7 @@ Sent cold reset command to MC """ import argparse +import asyncio import sys import aiohmi.ipmi.bmc as bmc @@ -90,7 +91,7 @@ def main(): help='Port to listen on; defaults to 623') args = parser.parse_args() mybmc = FakeBmc({'admin': 'password'}, port=args.port) - mybmc.listen() + asyncio.run(mybmc.listen()) if __name__ == '__main__': diff --git a/confluent_server/aiohmi/cmd/virshbmc.py b/confluent_server/aiohmi/cmd/virshbmc.py index 787db83c..d5e614e1 100755 --- a/confluent_server/aiohmi/cmd/virshbmc.py +++ b/confluent_server/aiohmi/cmd/virshbmc.py @@ -15,6 +15,7 @@ control a VM """ import argparse +import asyncio import sys import threading @@ -106,16 +107,16 @@ class LibvirtBmc(bmc.Bmc): return self.run_console - def activate_payload(self, request, session): - super(LibvirtBmc, self).activate_payload(request, session) + async def activate_payload(self, request, session): + await super(LibvirtBmc, self).activate_payload(request, session) self.run_console = True self.sol_thread = threading.Thread(target=self.loop) self.sol_thread.start() - def deactivate_payload(self, request, session): + async def deactivate_payload(self, request, session): self.run_console = False self.sol_thread.join() - super(LibvirtBmc, self).deactivate_payload(request, session) + await super(LibvirtBmc, self).deactivate_payload(request, session) def iohandler(self, data): if self.stream: @@ -154,7 +155,7 @@ def main(): hypervisor=args.hypervisor, domain=args.domain, port=args.port) - mybmc.listen() + asyncio.run(mybmc.listen()) if __name__ == '__main__': diff --git a/confluent_server/aiohmi/ipmi/bmc.py b/confluent_server/aiohmi/ipmi/bmc.py index 92e55968..1a35bfa3 100644 --- a/confluent_server/aiohmi/ipmi/bmc.py +++ b/confluent_server/aiohmi/ipmi/bmc.py @@ -60,48 +60,48 @@ class Bmc(serversession.IpmiServer): def is_active(self): raise NotImplementedError - def activate_payload(self, request, session): + async def activate_payload(self, request, session): if self.iohandler is None: - session.send_ipmi_response(code=0x81) + await session.send_ipmi_response(code=0x81) elif not self.is_active(): - session.send_ipmi_response(code=0x81) + await session.send_ipmi_response(code=0x81) elif self.activated: - session.send_ipmi_response(code=0x80) + await session.send_ipmi_response(code=0x80) else: self.activated = True solport = list(struct.unpack('BB', struct.pack('!H', self.port))) - session.send_ipmi_response( + await session.send_ipmi_response( data=[0, 0, 0, 0, 1, 0, 1, 0] + solport + [0xff, 0xff]) self.sol = console.ServerConsole(session, self.iohandler) - def deactivate_payload(self, request, session): + async def deactivate_payload(self, request, session): if self.iohandler is None: - session.send_ipmi_response(code=0x81) + await session.send_ipmi_response(code=0x81) elif not self.activated: - session.send_ipmi_response(code=0x80) + await session.send_ipmi_response(code=0x80) else: - session.send_ipmi_response() + await session.send_ipmi_response() self.sol.close() self.activated = False self.sol = None @staticmethod - def handle_missing_command(session): - session.send_ipmi_response(code=0xc1) + async def handle_missing_command(session): + await session.send_ipmi_response(code=0xc1) - def get_chassis_status(self, session): + async def get_chassis_status(self, session): try: powerstate = self.get_power_state() except NotImplementedError: - return session.send_ipmi_response(code=0xc1) + return await session.send_ipmi_response(code=0xc1) if powerstate in ipmicommand.power_states: powerstate = ipmicommand.power_states[powerstate] if powerstate not in (0, 1): raise Exception('BMC implementation mistake') statusdata = [powerstate, 0, 0] - session.send_ipmi_response(data=statusdata) + await session.send_ipmi_response(data=statusdata) - def control_chassis(self, request, session): + async def control_chassis(self, request, session): rc = 0 try: directive = request['data'][0] @@ -120,79 +120,79 @@ class Bmc(serversession.IpmiServer): rc = self.power_shutdown() if rc is None: rc = 0 - session.send_ipmi_response(code=rc) + await session.send_ipmi_response(code=rc) except NotImplementedError: - session.send_ipmi_response(code=0xcc) + await session.send_ipmi_response(code=0xcc) def get_boot_device(self): raise NotImplementedError - def get_system_boot_options(self, request, session): + async def get_system_boot_options(self, request, session): if request['data'][0] == 5: # boot flags try: bootdevice = self.get_boot_device() except NotImplementedError: - session.send_ipmi_response(data=[1, 5, 0, 0, 0, 0, 0]) + await session.send_ipmi_response(data=[1, 5, 0, 0, 0, 0, 0]) if (type(bootdevice) != int and bootdevice in ipmicommand.boot_devices): bootdevice = ipmicommand.boot_devices[bootdevice] paramdata = [1, 5, 0b10000000, bootdevice, 0, 0, 0] - return session.send_ipmi_response(data=paramdata) + return await session.send_ipmi_response(data=paramdata) else: - session.send_ipmi_response(code=0x80) + await session.send_ipmi_response(code=0x80) def set_boot_device(self, bootdevice): raise NotImplementedError - def set_system_boot_options(self, request, session): + async def set_system_boot_options(self, request, session): if request['data'][0] in (0, 3, 4): # for now, just smile and nod at boot flag bit clearing # implementing it is a burden and implementing it does more to # confuse users than serve a useful purpose - session.send_ipmi_response() + await session.send_ipmi_response() elif request['data'][0] == 5: bootdevice = (request['data'][2] >> 2) & 0b1111 try: bootdevice = ipmicommand.boot_devices[bootdevice] except KeyError: - session.send_ipmi_response(code=0xcc) + await session.send_ipmi_response(code=0xcc) return self.set_boot_device(bootdevice) - session.send_ipmi_response() + await session.send_ipmi_response() else: raise NotImplementedError - def handle_raw_request(self, request, session): + async def handle_raw_request(self, request, session): try: if request['netfn'] == 6: if request['command'] == 1: # get device id - return self.send_device_id(session) + return await self.send_device_id(session) elif request['command'] == 2: # cold reset - return session.send_ipmi_response(code=self.cold_reset()) + return await session.send_ipmi_response(code=self.cold_reset()) elif request['command'] == 0x37: # get system guid guid = self.get_system_guid() - return session.send_ipmi_response(code=0x00, data=guid.bytes_le) + return await session.send_ipmi_response(code=0x00, data=guid.bytes_le) elif request['command'] == 0x48: # activate payload - return self.activate_payload(request, session) + return await self.activate_payload(request, session) elif request['command'] == 0x49: # deactivate payload - return self.deactivate_payload(request, session) + return await self.deactivate_payload(request, session) elif request['netfn'] == 0: if request['command'] == 1: # get chassis status - return self.get_chassis_status(session) + return await self.get_chassis_status(session) elif request['command'] == 2: # chassis control - return self.control_chassis(request, session) + return await self.control_chassis(request, session) elif request['command'] == 8: # set boot options - return self.set_system_boot_options(request, session) + return await self.set_system_boot_options(request, session) elif request['command'] == 9: # get boot options - return self.get_system_boot_options(request, session) - session.send_ipmi_response(code=0xc1) + return await self.get_system_boot_options(request, session) + await session.send_ipmi_response(code=0xc1) except NotImplementedError: - session.send_ipmi_response(code=0xc1) + await session.send_ipmi_response(code=0xc1) except Exception: - session._send_ipmi_net_payload(code=0xff) + await session._send_ipmi_net_payload(code=0xff) traceback.print_exc() - @classmethod - def listen(cls, timeout=30): + async def listen(self, timeout=30): + await self.bind() while True: - ipmisession.Session.wait_for_rsp(timeout) + await ipmisession.Session.wait_for_rsp(timeout) diff --git a/confluent_server/aiohmi/ipmi/console.py b/confluent_server/aiohmi/ipmi/console.py index cacf342e..d204e126 100644 --- a/confluent_server/aiohmi/ipmi/console.py +++ b/confluent_server/aiohmi/ipmi/console.py @@ -463,8 +463,6 @@ class ServerConsole(Console): self.maxoutcount = 256 self.poweredon = True - session.Session.wait_for_rsp(0) - async def _got_sol_payload(self, payload): """SOL payload callback""" @@ -509,7 +507,7 @@ class ServerConsole(Console): # and might be hard to decide what to do in the context of # retry situation try: - self.send_payload(ackpayload, retry=False) + await self.send_payload(ackpayload, retry=False) except exc.IpmiException: # if the session is broken, then close the SOL session self.close() @@ -537,16 +535,16 @@ class ServerConsole(Console): # try to mitigate by avoiding overeager retries # occasional retry of a packet # sooner than timeout suggests is evidently a big deal - self.send_payload(payload=self.lastpayload) + await self.send_payload(payload=self.lastpayload) - def send_payload(self, payload, payload_type=1, retry=True, - needskeepalive=False): + async def send_payload(self, payload, payload_type=1, retry=True, + needskeepalive=False): while not (self.connected or self.broken): - session.Session.wait_for_rsp(timeout=10) - self.ipmi_session.send_payload(payload, - payload_type=payload_type, - retry=retry, - needskeepalive=needskeepalive) + await session.Session.wait_for_rsp(timeout=10) + await self.ipmi_session.send_payload(payload, + payload_type=payload_type, + retry=retry, + needskeepalive=needskeepalive) def close(self): """Shut down an SOL session""" diff --git a/confluent_server/aiohmi/ipmi/private/serversession.py b/confluent_server/aiohmi/ipmi/private/serversession.py index a96a2342..a3288ec2 100644 --- a/confluent_server/aiohmi/ipmi/private/serversession.py +++ b/confluent_server/aiohmi/ipmi/private/serversession.py @@ -86,19 +86,28 @@ class ServerSession(ipmisession.Session): ipmisession.Session.bmc_handlers[clientaddr] = {bmc.port: self} else: ipmisession.Session.bmc_handlers[clientaddr][bmc.port] = self - response = self.create_open_session_response(bytearray(request)) - self.send_payload(response, - constants.payload_types['rmcpplusopenresponse'], - retry=False) + self.initrequest = request - def _got_rmcp_openrequest(self, data): + async def send_open_session_response(self): + """Answer the open session request this was created for + + Sending is a coroutine now, so it cannot happen in __init__ where the + conversation used to begin. + """ + response = self.create_open_session_response( + bytearray(self.initrequest)) + await self.send_payload(response, + constants.payload_types['rmcpplusopenresponse'], + retry=False) + + async def _got_rmcp_openrequest(self, data): response = self.create_open_session_response( struct.pack('B' * len(data), *data)) - self.send_payload(response, - constants.payload_types['rmcpplusopenresponse'], - retry=False) + await self.send_payload(response, + constants.payload_types['rmcpplusopenresponse'], + retry=False) - def _got_rakp1(self, data): + async def _got_rakp1(self, data): clienttag = data[0] self.Rm = data[8:24] self.rolem = data[24] @@ -130,14 +139,14 @@ class ServerSession(ipmisession.Session): # a human newmessage = (bytearray([clienttag, 0, 0, 0]) + self.clientsessionid + self.Rc + uuidbytes + authcode) - self.send_payload(newmessage, constants.payload_types['rakp2'], - retry=False) + await self.send_payload(newmessage, constants.payload_types['rakp2'], + retry=False) - def _got_rakp2(self, data): + async def _got_rakp2(self, data): # stub, server should not think about rakp2 pass - def _got_rakp3(self, data): + async def _got_rakp3(self, data): # for now drop rakp3 with bad authcode # respond correctly a TODO(jjohnson2), since Kg being used # yet incorrect is a scenario why rakp3 could be bad @@ -165,9 +174,9 @@ class ServerSession(ipmisession.Session): return self.localsid = struct.unpack('