From 8817ee6deb50d5a8c6a11f89a2a7c896b281eeec Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 13 Jul 2026 16:05:53 +0200 Subject: [PATCH] Await NextScale SMM settings operations The SMM hostname, domain, and NTP helpers looked synchronous even though their web transport is asynchronous. Removing awaits in the Lenovo OEM handler therefore returned unresolved coroutine work instead of completed settings results. Convert the SMM settings and logout helpers to the asynchronous web interface, validate HTTP status responses, and await each operation from the OEM handler so callers only observe completed results. --- .../aiohmi/ipmi/oem/lenovo/handler.py | 14 +- .../aiohmi/ipmi/oem/lenovo/nextscale.py | 120 +++++++++--------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/confluent_server/aiohmi/ipmi/oem/lenovo/handler.py b/confluent_server/aiohmi/ipmi/oem/lenovo/handler.py index d8833ed4..2999ebdf 100755 --- a/confluent_server/aiohmi/ipmi/oem/lenovo/handler.py +++ b/confluent_server/aiohmi/ipmi/oem/lenovo/handler.py @@ -360,7 +360,7 @@ class OEMHandler(generic.OEMHandler): srvs.append(ntpres['data'][129:257].rstrip('\x00')) return srvs if await self.is_fpc(): - return self.smmhandler.get_ntp_servers() + return await self.smmhandler.get_ntp_servers() if self.has_tsma: return await self.tsmahandler.get_ntp_servers() return () @@ -375,7 +375,7 @@ class OEMHandler(generic.OEMHandler): netfn=0x32, command=0xa8, data=(3, 0), timeout=15) return True if await self.is_fpc(): - self.smmhandler.set_ntp_enabled(enabled) + await self.smmhandler.set_ntp_enabled(enabled) return True if self.has_tsma: await self.tsmahandler.set_ntp_enabled(enabled) @@ -393,7 +393,7 @@ class OEMHandler(generic.OEMHandler): if not 0 <= index <= 2: raise pygexc.InvalidParameterValue( 'SMM supports indexes 0 through 2') - self.smmhandler.set_ntp_server(server, index) + await self.smmhandler.set_ntp_server(server, index) return True elif self.has_tsma: if not (0 <= index <= 1): @@ -940,7 +940,7 @@ class OEMHandler(generic.OEMHandler): name += rsp['data'][:] return name.rstrip('\x00') elif await self.is_fpc(): - return self.smmhandler.get_domain() + return await self.smmhandler.get_domain() async def set_oem_domain_name(self, name): if await self.has_tsm(): @@ -959,20 +959,20 @@ class OEMHandler(generic.OEMHandler): await self._restart_dns() return elif await self.is_fpc(): - self.smmhandler.set_domain(name) + await self.smmhandler.set_domain(name) async def set_hostname(self, hostname): if await self.has_xcc(): return await self.immhandler.set_hostname(hostname) elif await self.is_fpc(): - return self.smmhandler.set_hostname(hostname) + return await self.smmhandler.set_hostname(hostname) return await super(OEMHandler, self).set_hostname(hostname) async def get_hostname(self): if await self.has_xcc(): return await self.immhandler.get_hostname() elif await self.is_fpc(): - return self.smmhandler.get_hostname() + return await self.smmhandler.get_hostname() return await super(OEMHandler, self).get_hostname() """ Gets a remote console launcher for a Lenovo ThinkServer. diff --git a/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py b/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py index 0c89e825..be51f335 100644 --- a/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py +++ b/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py @@ -887,90 +887,84 @@ class SMMClient(object): wc.set_header('ST2', wc.st2) return wc - def set_hostname(self, hostname): - wc = self.wc - wc.request('POST', '/data', 'set=hostname:' + hostname) - rsp = wc.getresponse() - if rsp.status != 200: - raise Exception(rsp.read()) - rsp.read() - self.logout() + async def set_hostname(self, hostname): + wc = await self.wc() + rsp, status, _ = await wc.grab_response_with_status('/data', 'set=hostname:' + hostname) + if status != 200: + raise Exception(rsp) + await self.logout() - def get_hostname(self): - currinfo = self.get_netinfo() - self.logout() + async def get_hostname(self): + currinfo = await self.get_netinfo() + await self.logout() for data in currinfo.find('netConfig').findall('hostname'): return data.text - def get_netinfo(self): - wc = self.wc - wc.request('POST', '/data', 'get=hostname') - rsp = wc.getresponse() - data = rsp.read() - if rsp.status == 400: - wc.request('POST', '/data?get=hostname', '') - rsp = wc.getresponse() - data = rsp.read() - if rsp.status != 200: + async def get_netinfo(self): + wc = await self.wc() + data, status, _ = await wc.grab_response_with_status('/data', 'get=hostname') + if status == 400: + data, status, _ = await wc.grab_response_with_status('/data?get=hostname', '') + if status != 200: raise Exception(data) currinfo = fromstring(data) return currinfo - def set_domain(self, domain): - wc = self.wc - wc.request('POST', '/data', 'set=dnsDomain:' + domain) - rsp = wc.getresponse() - if rsp.status != 200: - raise Exception(rsp.read()) - rsp.read() - self.logout() + async def set_domain(self, domain): + wc = await self.wc() + rsp, status, _ = await wc.grab_response_with_status('/data', 'set=dnsDomain:' + domain) + if status != 200: + raise Exception(rsp) + await self.logout() - def get_domain(self): - currinfo = self.get_netinfo() - self.logout() + async def get_domain(self): + currinfo = await self.get_netinfo() + await self.logout() for data in currinfo.find('netConfig').findall('dnsDomain'): return data.text - def get_ntp_enabled(self, variant): - wc = self.wc - wc.request('POST', '/data', 'get=ntpOpMode') - rsp = wc.getresponse() - info = fromstring(rsp.read()) - self.logout() + async def get_ntp_enabled(self, variant): + wc = await self.wc() + rsp, status, _ = await wc.grab_response_with_status('/data', 'get=ntpOpMode') + if status != 200: + raise Exception(rsp) + info = fromstring(rsp) + await self.logout() for data in info.findall('ntpOpMode'): return data.text == '1' - def set_ntp_enabled(self, enabled): - wc = self.wc - wc.request('POST', '/data', 'set=ntpOpMode:{0}'.format( + async def set_ntp_enabled(self, enabled): + wc = await self.wc() + result, status, _ = await wc.grab_response_with_status('/data', 'set=ntpOpMode:{0}'.format( 1 if enabled else 0)) - rsp = wc.getresponse() - result = rsp.read() + if status != 200: + raise Exception(result) if not isinstance(result, str): result = result.decode('utf8') - self.logout() + await self.logout() if 'ok' not in result: raise Exception("Unrecognized result: " + result) - def set_ntp_server(self, server, index): - wc = self.wc - wc.request('POST', '/data', 'set=ntpServer{0}:{1}'.format( + async def set_ntp_server(self, server, index): + wc = await self.wc() + result, status, _ = await wc.grab_response_with_status('/data', 'set=ntpServer{0}:{1}'.format( index + 1, server)) - rsp = wc.getresponse() - result = rsp.read() + if status != 200: + raise Exception(result) if not isinstance(result, str): result = result.decode('utf8') if 'ok' not in result: raise Exception("Unrecognized result: " + result) - self.logout() + await self.logout() return True - def get_ntp_servers(self): - wc = self.wc - wc.request( - 'POST', '/data', 'get=ntpServer1,ntpServer2,ntpServer3') - rsp = wc.getresponse() - result = fromstring(rsp.read()) + async def get_ntp_servers(self): + wc = await self.wc() + rsp, status, _ = await wc.grab_response_with_status( + '/data', 'get=ntpServer1,ntpServer2,ntpServer3') + if status != 200: + raise Exception(rsp) + result = fromstring(rsp) srvs = [] for data in result.findall('ntpServer1'): srvs.append(data.text) @@ -978,7 +972,7 @@ class SMMClient(object): srvs.append(data.text) for data in result.findall('ntpServer3'): srvs.append(data.text) - self.logout() + await self.logout() return srvs async def update_firmware(self, filename, data=None, progress=None, bank=None): @@ -1095,12 +1089,14 @@ class SMMClient(object): b' \x00\xff').decode('utf8')) return psui - def logout(self): - wc = self.wc - wc.request('POST', '/data/logout', None) - rsp = wc.getresponse() - rsp.read() + async def logout(self): + wc = self._wc self._wc = None + if wc is None: + return + rsp, status, _ = await wc.grab_response_with_status('/data/logout', None, method='POST') + if status != 200: + raise Exception(rsp) async def wc(self): if (not self._wc or (self._wc.vintage