From 3fb1792c91ae92e21623d993be4525d3c44a5722 Mon Sep 17 00:00:00 2001 From: Brandon Biggs Date: Sat, 8 Mar 2025 18:10:21 -0700 Subject: [PATCH 1/4] Added a session token auth method to rf_diagnostic_data Signed-off-by: Brandon Biggs --- scripts/rf_diagnostic_data.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/scripts/rf_diagnostic_data.py b/scripts/rf_diagnostic_data.py index 80fc191..da40ac2 100644 --- a/scripts/rf_diagnostic_data.py +++ b/scripts/rf_diagnostic_data.py @@ -25,8 +25,14 @@ argget = argparse.ArgumentParser( description="A tool to collect diagnostic data from a log service on a Redfish service" ) -argget.add_argument("--user", "-u", type=str, required=True, help="The user name for authentication") -argget.add_argument("--password", "-p", type=str, required=True, help="The password for authentication") + +# Add username and password arguments +argget.add_argument("--user", "-u", type=str, help="The user name for authentication") +argget.add_argument("--password", "-p", type=str, help="The password for authentication") + +# Add session token argument +argget.add_argument("--session-token", "-t", type=str, help="The session token for authentication") + argget.add_argument("--rhost", "-r", type=str, required=True, help="The address of the Redfish service (with scheme)") argget.add_argument( "--manager", "-m", type=str, nargs="?", default=False, help="The ID of the manager containing the log service" @@ -62,6 +68,14 @@ argget.add_argument("--debug", action="store_true", help="Creates debug file showing HTTP traces and exceptions") args = argget.parse_args() +# Validate either username + password OR session_token +if (args.user and args.password and not args.session_token) or ( + args.session_token and not (args.user or args.password) +): + pass # Valid input +else: + argget.error("You must specify either both --user and --password, or --session-token") + # Determine the target log service based on the inputs # Effectively if the user gives multiple targets, some will be ignored container_type = redfish_utilities.log_container.MANAGER @@ -85,10 +99,15 @@ # Set up the Redfish object redfish_obj = None try: - redfish_obj = redfish.redfish_client( - base_url=args.rhost, username=args.user, password=args.password, timeout=15, max_retry=3 - ) - redfish_obj.login(auth="session") + if args.session_token: + sessionkey = str.encode(args.session_token) + redfish_obj = redfish.redfish_client(base_url=args.rhost, sessionkey=sessionkey, timeout=15, max_retry=3) + else: + redfish_obj = redfish.redfish_client( + base_url=args.rhost, username=args.user, password=args.password, timeout=15, max_retry=3 + ) + # Don't need to login if we're using a session key + redfish_obj.login(auth="session") except RedfishPasswordChangeRequiredError: redfish_utilities.print_password_change_required_and_logout(redfish_obj, args) sys.exit(1) @@ -127,5 +146,6 @@ print(e) finally: # Log out - redfish_utilities.logout(redfish_obj) + if not args.session_token: + redfish_utilities.logout(redfish_obj) sys.exit(exit_code) From 25e54a1b74be42503af1e4ff4027c85c9eb8755f Mon Sep 17 00:00:00 2001 From: Brandon Biggs Date: Tue, 11 Mar 2025 21:36:01 -0600 Subject: [PATCH 2/4] Added a clean section to the makefile Signed-off-by: Brandon Biggs --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index bb3e0f7..32b41e9 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,10 @@ endif build: ##@ Build the python package python setup.py sdist +clean: ##@ Clean up build + rm -rf dist/* + rm -rf redfish_utilities.egg-info + install: ##@ Install with pip pip install dist/redfish_utilities-${VERSION}.tar.gz From 78cc94fee009b271406d2ae97d31c0068510eba7 Mon Sep 17 00:00:00 2001 From: Brandon Biggs Date: Tue, 11 Mar 2025 21:39:29 -0600 Subject: [PATCH 3/4] Moving commonly used arguments to their own file. Also created a new logger script that allows for more options when logging. Updated rf_diagnostic_data with the new proposed argument and logger scheme Signed-off-by: Brandon Biggs --- redfish_utilities/arguments.py | 43 ++++++++++++++++++++++++++++++ redfish_utilities/logger.py | 42 +++++++++++++++++++++++++++++ scripts/rf_diagnostic_data.py | 48 ++++++++++++---------------------- 3 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 redfish_utilities/arguments.py create mode 100644 redfish_utilities/logger.py diff --git a/redfish_utilities/arguments.py b/redfish_utilities/arguments.py new file mode 100644 index 0000000..c31e90f --- /dev/null +++ b/redfish_utilities/arguments.py @@ -0,0 +1,43 @@ +import argparse + + +def validate_auth(args): + if (args.user and args.password and not args.session_token) or ( + args.session_token and not (args.user or args.password) + ): + return + else: + print("You must specify both --user and --password or --session-token") + quit(1) + + +def create_parent_parser(description: str = "", auth: bool = False, rhost: bool = False): + parent_parser = argparse.ArgumentParser(description=description, add_help=False) + + parent_parser.add_argument( + "--log-level", + choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], + default="INFO", + help="Creates debug file showing HTTP traces and exceptions", + ) + parent_parser.add_argument("--log-to-console", action="store_true", help="Enable logging to console") + parent_parser.add_argument("--log-to-file", action="store_true", help="Enable logging to a file") + parent_parser.add_argument( + "--debug", action="store_true", help="Creates debug file showing HTTP traces and exceptions" + ) + + if auth: + parent_parser.add_argument("--user", "-u", type=str, help="The user name for authentication") + parent_parser.add_argument("--password", "-p", type=str, help="The password for authentication") + parent_parser.add_argument("--session-token", "-t", type=str, help="The session token for authentication") + + if rhost: + parent_parser.add_argument( + "--rhost", "-r", type=str, required=True, help="The address of the Redfish service (with scheme)" + ) + + return parent_parser + + +def validate_args(args): + validate_auth(args) diff --git a/redfish_utilities/logger.py b/redfish_utilities/logger.py new file mode 100644 index 0000000..5bae261 --- /dev/null +++ b/redfish_utilities/logger.py @@ -0,0 +1,42 @@ +import logging +import datetime +import redfish +import os + + +def get_debug_level(level): + if level == "DEBUG": + return logging.DEBUG + elif level == "INFO": + return logging.INFO + elif level == "WARNING": + return logging.WARNING + elif level == "ERROR": + return logging.ERROR + elif level == "CRITICAL": + return logging.CRITICAL + else: + raise ValueError(f"Invalid debug level: {args.debug_level}") + + +def setup_logger( + file_log: bool = False, stream_log: bool = True, log_level: str = "INFO", file_name: str = "redfish_utils" +): + log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + log_level = get_debug_level(log_level) + logger = logging.getLogger(__name__) + + if file_log: + file_name = os.path.basename(file_name) + timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S") + log_file = f"{file_name}-{timestamp}.log".format() + logger = redfish.redfish_logger(log_file, log_format, log_level) + + if stream_log: + formatter = logging.Formatter(log_format) + sh = logging.StreamHandler() + sh.setFormatter(formatter) + logger.addHandler(sh) + logger.setLevel(log_level) + + return logger diff --git a/scripts/rf_diagnostic_data.py b/scripts/rf_diagnostic_data.py index da40ac2..03db05a 100644 --- a/scripts/rf_diagnostic_data.py +++ b/scripts/rf_diagnostic_data.py @@ -13,27 +13,22 @@ import argparse import datetime -import logging + +# import logging import os import redfish import redfish_utilities import traceback import sys from redfish.messages import RedfishPasswordChangeRequiredError +from redfish_utilities.arguments import create_parent_parser, validate_args +from redfish_utilities.logger import setup_logger # Get the input arguments -argget = argparse.ArgumentParser( - description="A tool to collect diagnostic data from a log service on a Redfish service" -) - -# Add username and password arguments -argget.add_argument("--user", "-u", type=str, help="The user name for authentication") -argget.add_argument("--password", "-p", type=str, help="The password for authentication") - -# Add session token argument -argget.add_argument("--session-token", "-t", type=str, help="The session token for authentication") +description = "A tool to collect diagnostic data from a log service on a Redfish service" +parent_parser = create_parent_parser(description=description, auth=True, rhost=True) +argget = argparse.ArgumentParser(parents=[parent_parser]) -argget.add_argument("--rhost", "-r", type=str, required=True, help="The address of the Redfish service (with scheme)") argget.add_argument( "--manager", "-m", type=str, nargs="?", default=False, help="The ID of the manager containing the log service" ) @@ -44,6 +39,7 @@ "--chassis", "-c", type=str, nargs="?", default=False, help="The ID of the chassis containing the log service" ) argget.add_argument("--log", "-l", type=str, help="The ID of the log service") + argget.add_argument( "--type", "-type", @@ -65,16 +61,13 @@ help="The directory to save the diagnostic data; defaults to the current directory if not specified", default=".", ) -argget.add_argument("--debug", action="store_true", help="Creates debug file showing HTTP traces and exceptions") + args = argget.parse_args() +validate_args(args) +logger = setup_logger( + file_log=args.log_to_file, stream_log=args.log_to_console, log_level=args.log_level, file_name=__file__ +) -# Validate either username + password OR session_token -if (args.user and args.password and not args.session_token) or ( - args.session_token and not (args.user or args.password) -): - pass # Valid input -else: - argget.error("You must specify either both --user and --password, or --session-token") # Determine the target log service based on the inputs # Effectively if the user gives multiple targets, some will be ignored @@ -90,12 +83,6 @@ container_type = redfish_utilities.log_container.CHASSIS container_id = args.chassis -if args.debug: - log_file = "rf_diagnostic_data-{}.log".format(datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S")) - log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - logger = redfish.redfish_logger(log_file, log_format, logging.DEBUG) - logger.info("rf_diagnostic_data Trace") - # Set up the Redfish object redfish_obj = None try: @@ -116,7 +103,7 @@ exit_code = 0 try: - print("Collecting diagnostic data...") + logger.info("Collecting diagnostic data...") response = redfish_utilities.collect_diagnostic_data( redfish_obj, container_type, container_id, args.log, args.type, args.oemtype ) @@ -138,12 +125,11 @@ path = os.path.join(args.directory, filename) with open(path, "wb") as file: file.write(data) - print("Saved diagnostic data to '{}'".format(path)) + logger.info("Saved diagnostic data to '{}'".format(path)) except Exception as e: - if args.debug: - logger.error("Caught exception:\n\n{}\n".format(traceback.format_exc())) + logger.debug("Caught exception:\n\n{}\n".format(traceback.format_exc())) exit_code = 1 - print(e) + logger.info(e) finally: # Log out if not args.session_token: From 73aecd4f2c2beba6d85baf382e1e1be97be0a39f Mon Sep 17 00:00:00 2001 From: Brandon Biggs Date: Tue, 11 Mar 2025 21:47:01 -0600 Subject: [PATCH 4/4] Updated the make lint section to include ruff and fixed my ruff errors Signed-off-by: Brandon Biggs --- Makefile | 3 ++- redfish_utilities/logger.py | 2 +- scripts/rf_diagnostic_data.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 32b41e9..1b4ecff 100644 --- a/Makefile +++ b/Makefile @@ -57,4 +57,5 @@ install-uv: ##@ Install with uv uv pip install dist/redfish_utilities-${VERSION}.tar.gz lint: ##@ Run linting - black . \ No newline at end of file + black . + ruff check . \ No newline at end of file diff --git a/redfish_utilities/logger.py b/redfish_utilities/logger.py index 5bae261..1f9ca89 100644 --- a/redfish_utilities/logger.py +++ b/redfish_utilities/logger.py @@ -16,7 +16,7 @@ def get_debug_level(level): elif level == "CRITICAL": return logging.CRITICAL else: - raise ValueError(f"Invalid debug level: {args.debug_level}") + raise ValueError(f"Invalid debug level: {level}") def setup_logger( diff --git a/scripts/rf_diagnostic_data.py b/scripts/rf_diagnostic_data.py index 03db05a..2ac68c3 100644 --- a/scripts/rf_diagnostic_data.py +++ b/scripts/rf_diagnostic_data.py @@ -12,7 +12,6 @@ """ import argparse -import datetime # import logging import os