diff --git a/src/redfish/rest/v1.py b/src/redfish/rest/v1.py index db9d0ec..0e0b60b 100644 --- a/src/redfish/rest/v1.py +++ b/src/redfish/rest/v1.py @@ -454,7 +454,7 @@ class RestClientBase(object): def __init__(self, base_url, username=None, password=None, default_prefix='/redfish/v1/', sessionkey=None, capath=None, cafile=None, timeout=None, - max_retry=None, proxies=None): + max_retry=None, proxies=None, check_connectivity=True): """Initialization of the base class RestClientBase :param base_url: The URL of the remote system @@ -497,7 +497,9 @@ def __init__(self, base_url, username=None, password=None, self.default_prefix = default_prefix self.capath = capath self.cafile = cafile - self.get_root_object() + + if check_connectivity: + self.get_root_object() def __enter__(self): self.login() @@ -965,6 +967,8 @@ def login(self, username=None, password=None, auth=AuthMethod.SESSION): :type auth: object/instance of class AuthMethod """ + if getattr(self, "root_resp", None) is None: + self.get_root_object() self.__username = username if username else self.__username self.__password = password if password else self.__password @@ -999,7 +1003,7 @@ def login(self, username=None, password=None, auth=AuthMethod.SESSION): message_item = search_message(resp, "Base", "PasswordChangeRequired") if not message_item is None: raise RedfishPasswordChangeRequiredError("Password Change Required\n", message_item["MessageArgs"][0]) - + if not self.__session_key and resp.status not in [200, 201, 202, 204]: if resp.status == 401: # Invalid credentials supplied @@ -1042,7 +1046,7 @@ def __init__(self, base_url, username=None, password=None, default_prefix='/redfish/v1/', sessionkey=None, capath=None, cafile=None, timeout=None, - max_retry=None, proxies=None): + max_retry=None, proxies=None, check_connectivity=True): """Initialize HttpClient :param base_url: The url of the remote system @@ -1071,11 +1075,12 @@ def __init__(self, base_url, username=None, password=None, password=password, default_prefix=default_prefix, sessionkey=sessionkey, capath=capath, cafile=cafile, timeout=timeout, - max_retry=max_retry, proxies=proxies) + max_retry=max_retry, proxies=proxies, + check_connectivity=check_connectivity) try: self.login_url = self.root.Links.Sessions['@odata.id'] - except KeyError: + except (KeyError, AttributeError): # While the "Links/Sessions" property is required, we can fallback # on the URI hardened in 1.6.0 of the specification if not found LOGGER.debug('"Links/Sessions" not found in Service Root.') @@ -1128,7 +1133,7 @@ def redfish_client(base_url=None, username=None, password=None, default_prefix='/redfish/v1/', sessionkey=None, capath=None, cafile=None, timeout=None, - max_retry=None, proxies=None): + max_retry=None, proxies=None, check_connectivity=True): """Create and return appropriate REDFISH client instance.""" """ Instantiates appropriate Redfish object based on existing""" """ configuration. Use this to retrieve a pre-configured Redfish object @@ -1162,4 +1167,4 @@ def redfish_client(base_url=None, username=None, password=None, return HttpClient(base_url=base_url, username=username, password=password, default_prefix=default_prefix, sessionkey=sessionkey, capath=capath, cafile=cafile, timeout=timeout, - max_retry=max_retry, proxies=proxies) + max_retry=max_retry, proxies=proxies, check_connectivity=check_connectivity) diff --git a/tests/rest/test_v1.py b/tests/rest/test_v1.py index 4171893..c953c04 100644 --- a/tests/rest/test_v1.py +++ b/tests/rest/test_v1.py @@ -4,40 +4,76 @@ # https://github.com/DMTF/python-redfish-library/blob/main/LICENSE.md # -*- encoding: utf-8 -*- +import json import unittest +from unittest import mock -from redfish.rest.v1 import HttpClient -from redfish.rest.v1 import RetriesExhaustedError -from redfish.rest.v1 import redfish_client +from redfish.rest.v1 import HttpClient, RetriesExhaustedError, redfish_client class TestRedFishClient(unittest.TestCase): - def test_redfish_client(self): - base_url = "http://foo.bar" - username = "rstallman" - password = "123456" - default_prefix = "/custom/redfish/v1/" - sessionkey = "fg687glgkf56vlgkf" - capath = "/path/to/the/dir" - cafile = "filename.test" - timeout = 666 - max_retry = 42 + def setUp(self) -> None: + self.base_url = "http://foo.bar" + self.username = "rstallman" + self.password = "123456" + self.default_prefix = "/custom/redfish/v1/" + self.sessionkey = "fg687glgkf56vlgkf" + self.capath = "/path/to/the/dir" + self.cafile = "filename.test" + self.timeout = 666 + self.max_retry = 42 + + def test_redfish_client(self) -> None: # NOTE(hberaud) the client try to connect when we initialize the # http client object so we need to catch the retries exception first. # In a second time we need to mock the six.http_client to simulate # server responses and do some other tests with self.assertRaises(RetriesExhaustedError): - client = redfish_client(base_url=base_url) + client = redfish_client(base_url=self.base_url) # Check the object type self.assertTrue(isinstance(client, HttpClient)) # Check the object attributes values. # Here we check if the client object is properly initialized - self.assertEqual(client.base_url, base_url) - self.assertEqual(client.username, username) - self.assertEqual(client.password, password) - self.assertEqual(client.default_prefix, default_prefix) - self.assertEqual(client.sessionkey, sessionkey) - self.assertEqual(client.capath, capath) - self.assertEqual(client.cafile, cafile) - self.assertEqual(client.timeout, timeout) - self.assertEqual(client.max_retry, max_retry) + self.assertEqual(client.base_url, self.base_url) + self.assertEqual(client.username, self.username) + self.assertEqual(client.password, self.password) + self.assertEqual(client.default_prefix, self.default_prefix) + self.assertEqual(client.sessionkey, self.sessionkey) + self.assertEqual(client.capath, self.capath) + self.assertEqual(client.cafile, self.cafile) + self.assertEqual(client.timeout, self.timeout) + self.assertEqual(client.max_retry, self.max_retry) + + def test_redfish_client_no_root_resp(self) -> None: + client = redfish_client(base_url=self.base_url, check_connectivity=False) + self.assertIsNone(getattr(client, "root_resp", None)) + + @mock.patch("requests.Session.request") + def test_redfish_client_root_object_initialized_after_login( + self, mocked_request: mock.Mock + ) -> None: + dummy_root_data = '{"Links": {"Sessions": {"@data.id": "/redfish/v1/SessionService/Sessions"}}}' + dummy_session_response = ( + '{"@odata.type": "#Session.v1_1_2.Session", ' + '"@odata.id": "/redfish/v1/SessionService/Sessions/1", ' + '"Id": "1", "Name": "User Session", "Description": "Manager User Session", ' + '"UserName": "user", "Oem": {}}' + ) + root_resp = mock.Mock(content=dummy_root_data, status_code=200) + auth_resp = mock.Mock( + content=dummy_session_response, + status_code=200, + headers={"location": "fake", "x-auth-token": "fake"}, + ) + mocked_request.side_effect = [ + root_resp, + auth_resp, + ] + client = redfish_client(base_url=self.base_url, check_connectivity=False) + client.login() + + self.assertEqual(client.root, json.loads(dummy_root_data)) + + +if __name__ == "__main__": + unittest.main()