From ca81907d2535100154ea79725fca9ae8bf15a147 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Sun, 26 Jul 2026 23:57:53 +0200 Subject: [PATCH] Restore SMM web request semantics lost in the async port The old WebConnection.request() added a 'Content-Type: application/x-www-form-urlencoded' header to any POST carrying a body, but grab_response_with_status() only sets a content type for dict payloads, so the SMM login and every /data form POST now go out as text/plain. This is not a fix for an observed failure: an SMM running FPC variant 38 was measured accepting a text/plain login exactly as readily as a urlencoded one. It restores the header the synchronous code always sent and that the TSM and IMM handlers still set explicitly, rather than relying on every SMM firmware level being equally lax about what it will parse. Also stop hard failing on responses the synchronous code discarded on purpose. 'set=securityrollback:1' is only understood by newer SMM2 firmware. And /data/logout answers 401 once the session is gone, as measured on that same SMM, so raising on a non-200 there turns a completed hostname, domain or NTP operation into a spurious error. --- .../aiohmi/ipmi/oem/lenovo/nextscale.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py b/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py index be51f335..f83d56b2 100644 --- a/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py +++ b/confluent_server/aiohmi/ipmi/oem/lenovo/nextscale.py @@ -736,11 +736,9 @@ class SMMClient(object): if not isinstance(username, str): username = username.decode('utf8') wc = await self.wc() - rsp, status, _ = await wc.grab_response_with_status( + await wc.grab_response_with_status( '/data', 'set=user({0},1,{1},511,,4,15,0)'.format( uid, username)) - if status != 200: - raise Exception(rsp) async def reseat_bay(self, bay): bay = int(bay) @@ -831,6 +829,7 @@ class SMMClient(object): async def get_webclient(self): cv = self.ipmicmd.certverify wc = webclient.WebConnection(self.smm, 443, verifycallback=cv) + wc.set_header('Content-Type', 'application/x-www-form-urlencoded') wc.vintage = util._monotonic_time() loginform = urlencode( { @@ -1016,9 +1015,8 @@ class SMMClient(object): if status != 200: raise Exception('Error validating firmware') progress({'phase': 'apply', 'progress': 0.0}) - rsp, status, _ = await wc.grab_response_with_status('/data', 'set=securityrollback:1') - if status != 200: - raise Exception(rsp) + # only understood by newer SMM2 firmware, ignore rejection by older + await wc.grab_response_with_status('/data', 'set=securityrollback:1') rsp, status, _ = await wc.grab_response_with_status('/data', 'set=fwUpdate:1') if status != 200: raise Exception(rsp) @@ -1094,9 +1092,8 @@ class SMMClient(object): 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) + # best effort, a stale session must not fail the caller's operation + await wc.grab_response_with_status('/data/logout', None, method='POST') async def wc(self): if (not self._wc or (self._wc.vintage