feat: automatically manage session token

This commit is contained in:
George Rawlinson 2020-11-08 12:20:01 +13:00
parent a0333d5b2f
commit 70e0d784ec
Signed by: grawlinson
GPG key ID: E0959FEA8B550539
2 changed files with 38 additions and 15 deletions

View file

@ -1,5 +1,6 @@
"""Provides the Client class.""" """Provides the Client class."""
from time import time as now from datetime import datetime
from datetime import timedelta
from requests import Session from requests import Session
@ -15,19 +16,22 @@ KNOWN_SENSORS = ["pmbus", "sensor"]
class Client: class Client:
"""Client used to access Supermicro BMCs.""" """Client used to access Supermicro BMCs."""
def __init__(self, server, username, password): def __init__(self, server, username, password, session_timeout=30):
"""Initialises an instance of smbmc.Client. """Initialises an instance of smbmc.Client.
Args: Args:
server: Address of server in form: 'http://192.168.1.1'. server: Address of server in form: 'http://192.168.1.1'.
username: Username. username: Username.
password: Password. password: Password.
session_timeout: Session timeout of the BMC (in minutes).
""" """
self.server = server self.server = server
self.username = username self.username = username
self.password = password self.password = password
self._session = Session() self._session = Session()
self.last_call = None self.initial_call = datetime(1970, 1, 1)
self.session_timeout = session_timeout
self.sid_expiry = timedelta(minutes=self.session_timeout)
def login(self): def login(self):
"""Login to Supermicro web interface. """Login to Supermicro web interface.
@ -48,22 +52,44 @@ class Client:
) )
if "SID" in self._session.cookies.get_dict().keys(): if "SID" in self._session.cookies.get_dict().keys():
self.last_call = now() self.initial_call = datetime.now()
else: else:
raise Exception("Authentication Error") raise Exception("Authentication Error")
def _query(self, data, path="/cgi/ipmi.cgi"):
"""Query Supermicro BMC.
Performs session login & token refresh.
Args:
path: Path to query. Defaults to '/cgi/ipmi.cgi'.
data: Requested data.
Returns:
request.Response: Response object.
"""
self._refresh_token()
return self._session.post(
f"{self.server}{path}",
data=data,
)
def _refresh_token(self):
"""Refresh SID token if timeout likely."""
if datetime.now() > (self.initial_call + self.sid_expiry):
self.login()
def get_pmbus_metrics(self): def get_pmbus_metrics(self):
"""Acquire metrics for all power supplies. """Acquire metrics for all power supplies.
Returns: Returns:
str: XML response. str: XML response.
""" """
self.last_call = now() r = self._query(
r = self._session.post(
f"{self.server}/cgi/ipmi.cgi",
data={ data={
"Get_PSInfoReadings.XML": "(0,0)", "Get_PSInfoReadings.XML": "(0,0)",
}, }
) )
psu_list = extract_xml_attr(r.text, ".//PSItem") psu_list = extract_xml_attr(r.text, ".//PSItem")
@ -77,13 +103,10 @@ class Client:
Returns: Returns:
str: XML response. str: XML response.
""" """
self.last_call = now() r = self._query(
r = self._session.post(
f"{self.server}/cgi/ipmi.cgi",
data={ data={
"SENSOR_INFO.XML": "(1,ff)", "SENSOR_INFO.XML": "(1,ff)",
}, }
) )
sensor_list = extract_xml_attr(r.text, ".//SENSOR") sensor_list = extract_xml_attr(r.text, ".//SENSOR")
@ -110,7 +133,7 @@ class Client:
if not contains_valid_items(KNOWN_SENSORS, metrics): if not contains_valid_items(KNOWN_SENSORS, metrics):
raise Exception("metrics array contains invalid metrics") raise Exception("metrics array contains invalid metrics")
self.login() # self.login()
result = {} result = {}
for metric in metrics: for metric in metrics:

View file

@ -40,7 +40,7 @@ class TestClient:
with self.recorder.use_cassette(cassette_name): with self.recorder.use_cassette(cassette_name):
self.client.login() self.client.login()
assert self.client.last_call is not None assert self.client.initial_call is not None
assert "SID" in self.client._session.cookies.get_dict().keys() assert "SID" in self.client._session.cookies.get_dict().keys()
assert self.client._session.cookies["SID"] is not None assert self.client._session.cookies["SID"] is not None