Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ There are several optional parameters:
* ``timeout``: The number of seconds to wait for a response before closing the connection. The default value is ``None``.
* ``max_retry``: The number of retries to perform an operation before giving up. The default value is ``10``.
* ``proxies``: A dictionary containing protocol to proxy URL mappings. The default value is ``None``. See `Using proxies`_.
* ``check_connectivity``: A boolean value to determine whether the client immediately attempts a connection to the base_url. The default is ``True``.

To create a Redfish object, call the ``redfish_client`` method:

Expand Down
30 changes: 22 additions & 8 deletions src/redfish/rest/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -477,6 +477,9 @@ def __init__(self, base_url, username=None, password=None,
:type max_retry: int
:param proxies: Dictionary containing protocol to proxy URL mappings
:type proxies: dict
:param check_connectivity: A boolean to determine whether the client immediately checks for
connectivity to the base_url or not.
:type check_connectivity: bool

"""

Expand All @@ -497,7 +500,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()
Expand Down Expand Up @@ -965,6 +970,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
Expand Down Expand Up @@ -999,7 +1006,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
Expand Down Expand Up @@ -1042,7 +1049,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
Expand All @@ -1065,17 +1072,21 @@ def __init__(self, base_url, username=None, password=None,
:type max_retry: int
:param proxies: Dictionary containing protocol to proxy URL mappings
:type proxies: dict
:param check_connectivity: A boolean to determine whether the client immediately checks for
connectivity to the base_url or not.
:type check_connectivity: bool

"""
super(HttpClient, self).__init__(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)

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.')
Expand Down Expand Up @@ -1128,7 +1139,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
Expand All @@ -1153,6 +1164,9 @@ def redfish_client(base_url=None, username=None, password=None,
:type max_retry: int
:param proxies: Dictionary containing protocol to proxy URL mappings
:type proxies: dict
:param check_connectivity: A boolean to determine whether the client immediately checks for
connectivity to the base_url or not.
:type check_connectivity: bool
:returns: a client object.

"""
Expand All @@ -1162,4 +1176,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)
82 changes: 59 additions & 23 deletions tests/rest/test_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()