From 579545f0013fb738b3281eeedfba90029814f167 Mon Sep 17 00:00:00 2001 From: jlothe Date: Wed, 4 Feb 2026 19:02:09 +0530 Subject: [PATCH] ESC-16904 Added changes to handle api_url overwrite during concurrent operations --- hpe3parclient/client.py | 28 ++++++++++++------------ hpe3parclient/http.py | 47 ++++++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/hpe3parclient/client.py b/hpe3parclient/client.py index ef5f31ad..78ab57dd 100644 --- a/hpe3parclient/client.py +++ b/hpe3parclient/client.py @@ -1,4 +1,4 @@ -# (c) Copyright 2012-2025 Hewlett Packard Enterprise Development LP +# (c) Copyright 2012-2025, 2026 Hewlett Packard Enterprise Development LP # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -221,6 +221,9 @@ def __init__(self, api_url, debug=False, secure=False, timeout=None, def is_primera_array(self): return self.primera_supported + + def get_session_key(self): + return self.http.get_session_key() def setSSHOptions(self, ip, login, password, port=22, conn_timeout=None, privatekey=None, @@ -253,22 +256,17 @@ def getWsApiVersion(self): :returns: Version dict """ - try: - # remove everything down to host:port - host_url = self.api_url.split('/api') - self.http.set_url(host_url[0]) - # get the api version - response, body = self.http.get('/api') + # remove everything down to host:port + host_url = self.api_url.split('/api') + # Build absolute URL and call without mutating http client's base + response, body = self.http.get(host_url[0] + '/api') - api_version = body - if (api_version['build'] >= - self.HPE3PAR_WS_PRIMERA_MIN_BUILD_VERSION): - self.primera_supported = True + api_version = body + if (api_version['build'] >= + self.HPE3PAR_WS_PRIMERA_MIN_BUILD_VERSION): + self.primera_supported = True - return body - finally: - # reset the url - self.http.set_url(self.api_url) + return body def debug_rest(self, flag): """This is useful for debugging requests to 3PAR. diff --git a/hpe3parclient/http.py b/hpe3parclient/http.py index 138a97f9..6062ffc6 100644 --- a/hpe3parclient/http.py +++ b/hpe3parclient/http.py @@ -72,6 +72,8 @@ def __init__(self, api_url, secure=False, http_log_debug=False, if suppress_ssl_warnings: requests.packages.urllib3.disable_warnings() + HTTPJSONRESTClient._logger.debug("Initializing Session Key in __init__:") + self.session_key = None # should be http:///api/v1 @@ -86,6 +88,9 @@ def set_url(self, api_url): # should be http:///api/v1 self.api_url = api_url.rstrip('/') + def get_session_key(self): + return self.session_key + def set_debug_flag(self, flag): """ This turns on/off http request/response debugging output to console @@ -111,6 +116,8 @@ def authenticate(self, user, password, optional=None): """ # this prevens re-auth attempt if auth fails + HTTPJSONRESTClient._logger.debug("Session Key in authenticate: %s\n" % + (self.session_key)) self.auth_try = 1 self.session_key = None @@ -136,9 +143,16 @@ def unauthenticate(self): This clears the authenticated session with the 3PAR server. """ - # delete the session on the 3Par - self.delete('/credentials/%s' % self.session_key) - self.session_key = None + try: + # delete the session on the 3Par + HTTPJSONRESTClient._logger.debug("Session Key in unauthenticate: %s\n" % + (self.session_key)) + self.delete('/credentials/%s' % self.session_key) + self.session_key = None + except Exception as ex: + HTTPJSONRESTClient._logger.error("Error during unauthenticate: %s\n" + % (str(ex))) + raise ex def get_timings(self): """ @@ -275,7 +289,7 @@ def request(self, *args, **kwargs): self.delay = self.delay * self.backoff + 1 # Raise exception, we have exhausted all retries. - if self.tries is 0: + if self.tries == 0: raise ex except requests.exceptions.HTTPError as err: raise exceptions.HTTPError("HTTP Error: %s" % err) @@ -292,8 +306,17 @@ def request(self, *args, **kwargs): return resp, body + def _build_full_url(self, url): + """Build full URL, supporting both absolute and relative URLs.""" + if url.startswith('http://') or url.startswith('https://'): + return url.rstrip('/') + else: + return self.api_url + url + def _time_request(self, url, method, **kwargs): start_time = time.time() + HTTPJSONRESTClient._logger.debug("url in _time_request: %s\n" % + (url)) resp, body = self.request(url, method, **kwargs) self.times.append(("%s %s" % (method, url), start_time, time.time())) @@ -301,11 +324,15 @@ def _time_request(self, url, method, **kwargs): def _do_reauth(self, url, method, ex, **kwargs): # print("_do_reauth called") + HTTPJSONRESTClient._logger.debug("session key in _do_reauth: %s\n" % + (self.session_key)) + HTTPJSONRESTClient._logger.debug("auth_try in _do_reauth: %s\n" % + (self.auth_try)) try: if self.auth_try != 1: self._reauth() - resp, body = self._time_request(self.api_url + url, method, - **kwargs) + full_url = self._build_full_url(url) + resp, body = self._time_request(full_url, method, **kwargs) return resp, body else: raise ex @@ -317,8 +344,12 @@ def _cs_request(self, url, method, **kwargs): # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: - resp, body = self._time_request(self.api_url + url, method, - **kwargs) + HTTPJSONRESTClient._logger.debug("url in _cs_request: %s\n" % + (url)) + full_url = self._build_full_url(url) + HTTPJSONRESTClient._logger.debug("full url in _cs_request: %s\n" % + (full_url)) + resp, body = self._time_request(full_url, method, **kwargs) return resp, body except exceptions.HTTPUnauthorized as ex: # print("_CS_REQUEST HTTPUnauthorized")