Files

1296 lines
51 KiB
Python

###
# Copyright 2016-2025 Hewlett Packard Enterprise, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###
# -*- coding: utf-8 -*-
"""
This is the main module for Redfish Utility which handles all of the CLI and UI interfaces
"""
# ---------Imports---------
# -*- coding: utf-8 -*-
import collections
import copy
import errno
import glob
import os
import shlex
import sys
import traceback
import warnings
import importlib
import ctypes
from argparse import ArgumentParser, RawTextHelpFormatter
from builtins import open, str, super
import six
from prompt_toolkit import PromptSession
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.shortcuts import CompleteStyle
from six.moves import input
import redfish.hpilo
import redfish.rest.v1
import redfish.ris
try:
import cliutils
import versioning
import extensions
from config.rdmc_config import RdmcConfig
from rdmc_helper import HARDCODEDLIST
from rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
from security_masking import SecurityMasker
except ModuleNotFoundError:
from ilorest import cliutils
from ilorest import versioning
from ilorest import extensions
from ilorest.config.rdmc_config import RdmcConfig
from ilorest.rdmc_helper import HARDCODEDLIST
from ilorest.rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
from ilorest.security_masking import SecurityMasker
try:
from rdmc_helper import (
LOGGER,
UI,
setup_logging_from_json,
AlreadyCloudConnectedError,
BirthcertParseError,
BootOrderMissingEntriesError,
CloudConnectFailedError,
CloudConnectTimeoutError,
CommandNotEnabledError,
ConfigurationFileError,
DeviceDiscoveryInProgress,
DownloadError,
Encryption,
FailureDuringCommitError,
FirmwareUpdateError,
IloLicenseError,
IncompatableServerTypeError,
IncompatibleiLOVersionError,
InfoMissingEntriesError,
InvalidCListFileError,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidFileFormattingError,
InvalidFileInputError,
InvalidKeyError,
InvalidMSCfileInputError,
InvalidOrNothingChangedSettingsError,
InvalidPasswordLengthError,
MultipleServerConfigError,
NicMissingOrConfigurationError,
NoChangesFoundOrMadeError,
NoContentsFoundForOperationError,
NoCurrentSessionEstablished,
NoDifferencesFoundError,
PartitionMoutingError,
PathUnavailableError,
ProxyConfigFailedError,
RdmcError,
ResourceExists,
ReturnCodes,
StandardBlobErrorHandler,
TabAndHistoryCompletionClass,
TaskQueueError,
TfaEnablePreRequisiteError,
TimeOutError,
UnableToDecodeError,
UnabletoFindDriveError,
UploadError,
UsernamePasswordRequiredError,
iLORisCorruptionError,
ResourceNotReadyError,
InstallsetError,
InvalidSmartArrayConfigurationError,
VnicExistsError,
GenerateAndSaveAccountError,
NoAppAccountError,
RemoveAccountError,
AppAccountExistsError,
ReactivateAppAccountTokenError,
InactiveAppAccountTokenError,
VnicLoginError,
SavinginTPMError,
SavinginiLOError,
GenBeforeLoginError,
AppIdListError,
FlashUnsupportedByIloError,
)
except ModuleNotFoundError:
from ilorest.rdmc_helper import (
ReturnCodes,
RdmcError,
setup_logging_from_json,
ConfigurationFileError,
CommandNotEnabledError,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
UI,
LOGGER,
InvalidFileFormattingError,
NoChangesFoundOrMadeError,
InvalidFileInputError,
NoContentsFoundForOperationError,
InfoMissingEntriesError,
MultipleServerConfigError,
InvalidOrNothingChangedSettingsError,
NoDifferencesFoundError,
InvalidMSCfileInputError,
InvalidPasswordLengthError,
FirmwareUpdateError,
DeviceDiscoveryInProgress,
BootOrderMissingEntriesError,
NicMissingOrConfigurationError,
StandardBlobErrorHandler,
NoCurrentSessionEstablished,
InvalidCListFileError,
FailureDuringCommitError,
IncompatibleiLOVersionError,
PartitionMoutingError,
TimeOutError,
DownloadError,
UploadError,
BirthcertParseError,
ResourceExists,
IncompatableServerTypeError,
IloLicenseError,
InvalidKeyError,
UnableToDecodeError,
UnabletoFindDriveError,
Encryption,
PathUnavailableError,
TaskQueueError,
UsernamePasswordRequiredError,
TabAndHistoryCompletionClass,
iLORisCorruptionError,
TfaEnablePreRequisiteError,
CloudConnectTimeoutError,
CloudConnectFailedError,
ProxyConfigFailedError,
AlreadyCloudConnectedError,
ResourceNotReadyError,
InstallsetError,
InvalidSmartArrayConfigurationError,
VnicExistsError,
GenerateAndSaveAccountError,
NoAppAccountError,
RemoveAccountError,
AppAccountExistsError,
ReactivateAppAccountTokenError,
InactiveAppAccountTokenError,
VnicLoginError,
SavinginTPMError,
SavinginiLOError,
GenBeforeLoginError,
AppIdListError,
FlashUnsupportedByIloError,
)
warnings.filterwarnings("ignore", category=DeprecationWarning)
if os.name != "nt":
try:
import setproctitle
except ImportError:
setproctitle = None
# always flush stdout and stderr
try:
CLI = cliutils.CLI()
except cliutils.ResourceAllocationError:
RdmcError("Unable to allocate more resources.")
RdmcError("ILOREST return code: %s\n" % ReturnCodes.RESOURCE_ALLOCATION_ISSUES_ERROR)
sys.exit(ReturnCodes.RESOURCE_ALLOCATION_ISSUES_ERROR)
try:
# enable fips mode if our special functions are available in _ssl and OS is
# in FIPS mode
FIPSSTR = ""
if Encryption.check_fips_mode_os():
LOGGER.info("FIPS mode is enabled in OS!")
# if Encryption.check_fips_mode_os() and not Encryption.check_fips_mode_ssl():
# ssl.FIPS_mode_set(int(1))
# if ssl.FIPS_mode():
# FIPSSTR = "FIPS mode enabled using openssl version %s.\n" % ssl.OPENSSL_VERSION
# LOGGER.info("FIPS mode enabled!")
# else:
# LOGGER.info("FIPS mode can not be enabled!")
except AttributeError:
pass
class RdmcCommand(RdmcCommandBase):
"""Constructor"""
def __init__(self, name, usage, summary, aliases, argparser, Args=None):
super().__init__(name, usage, summary, aliases, argparser)
self._commands = collections.OrderedDict()
self.ui = UI(1)
self.commands_dict = dict()
self.interactive = False
self._progname = "%s : %s" % (versioning.__shortname__, versioning.__longname__)
self.opts = None
self.encoding = None
self.config = RdmcConfig()
self.app = redfish.ris.RmcApp(showwarnings=True)
self.retcode = 0
self.candidates = dict()
self.comm_map = dict() # point command id names or alias to handle
self.commlist = list()
self._redobj = None
self.log_dir = None
self.loaded_commands = []
# import all extensions dynamically
for name in extensions.classNames:
pkgName, cName = name.rsplit(".", 1)
pkgName = "extensions" + pkgName
try:
if "__pycache__" not in pkgName and "Command" in cName:
try:
self.commands_dict[cName] = getattr(importlib.import_module(pkgName, __package__), cName)()
except:
self.commands_dict[cName] = getattr(
importlib.import_module("ilorest." + pkgName, __package__),
cName,
)()
sName = pkgName.split(".")[1]
self.add_command(cName, section=sName)
except cliutils.ResourceAllocationError as excp:
self.ui.error(excp)
retcode = ReturnCodes.RESOURCE_ALLOCATION_ISSUES_ERROR
self.ui.error("Unable to allocate more resources.")
self.ui.printer(("ILOREST return code: %s\n" % retcode))
sys.exit(retcode)
except Exception:
self.ui.error(("loading command: %s" % cName), None)
# command mapping
commands_to_remove = []
for command in self.commands_dict:
try:
self.comm_map[self.commands_dict[command].ident.get("name")] = command
for alias in self.commands_dict[command].ident.get("aliases"):
self.comm_map[alias] = command
except Exception as excp:
self.ui.command_not_enabled(
("Command '%s' unable to be " "initialized...Removing" % command),
excp,
)
commands_to_remove.append(command)
# removing commands marked for deletion
for cmd in commands_to_remove:
del self.commands_dict[cmd]
del commands_to_remove
# ---------End of imports---------
def add_command(self, command_name, section=None):
"""Handles to addition of new commands
:param command_name: command name
:type command_name: str.
:param section: section for the new command
:type section: str.
"""
if section not in self._commands:
self._commands[section] = list()
self._commands[section].append(command_name)
def get_commands(self):
"""Retrieves dictionary of commands"""
return self._commands
def search_commands(self, cmdname):
"""Function to see if command exist in added commands
:param cmdname: command to be searched
:type cmdname: str.
"""
try:
tmp = self.comm_map.get(cmdname)
if not tmp:
tmp = cmdname
return self.commands_dict[tmp]
except KeyError:
raise cliutils.CommandNotFoundException(cmdname)
def load_command(self, cmd):
"""Fully Loads command and returns the class instance
:param cmd: command identifier
:type opts: class
:returns: defined class instance
"""
try:
cmd.cmdbase = RdmcCommandBase(
cmd.ident["name"],
cmd.ident["usage"],
cmd.ident["summary"],
cmd.ident["aliases"],
)
cmd.parser = ArgumentParser(
prog=cmd.ident["name"],
usage=cmd.ident["usage"],
description=cmd.ident["description"],
formatter_class=RawTextHelpFormatter,
)
cmd.rdmc = self
cmd.definearguments(cmd.parser)
for auxcmd in cmd.ident["auxcommands"]:
auxcmd = self.search_commands(auxcmd)
if auxcmd not in self.loaded_commands:
self.loaded_commands.append(auxcmd)
cmd.auxcommands[auxcmd.ident["name"]] = self.load_command(auxcmd)
else:
cmd.auxcommands[auxcmd.ident["name"]] = self.commands_dict[self.comm_map[auxcmd.ident["name"]]]
return cmd
except Exception as excp:
raise RdmcError("Unable to load command {}: {}".format(cmd.ident["name"], excp))
def mask_passwords(self, args):
"""Replaces password values in a command argument list with '****'."""
return SecurityMasker.mask_command_arguments(args)
def _run_command(self, opts, args, help_disp):
"""Calls the command's run function.
:param opts: Command options.
:type opts: argparse.Namespace
:param args: List of entered arguments.
:type args: list
:param help_disp: Help display flag.
:type help_disp: bool
:returns: Command execution result.
"""
if not args:
LOGGER.error("No command provided. Exiting execution.")
raise ValueError("No command provided.")
masked_args = self.mask_passwords(args)
LOGGER.info(f"Executing command: {args[0]} with arguments: {masked_args}")
cmd = self.search_commands(args[0])
if cmd is None:
LOGGER.error(f"Command '{args[0]}' not found.")
raise ValueError(f"Command '{args[0]}' not found.")
self.load_command(cmd)
LOGGER.debug(f"Command '{args[0]}' loaded successfully.")
# Handle JSON output flag
if any(a in ("-j", "--json") for a in args):
opts.nologo = True
LOGGER.debug("JSON output mode enabled.")
# Show version information if needed
if not opts.nologo and not self.interactive:
LOGGER.info("Displaying version information.")
CLI.version(self._progname, versioning.__version__, versioning.__extracontent__)
# Execute the command with remaining arguments
LOGGER.info(f"Running command: {args[0]} with arguments: {masked_args if len(args) > 1 else 'None'}")
return cmd.run(args[1:], help_disp=help_disp) if len(args) > 1 else cmd.run([], help_disp=help_disp)
def run(self, line, help_disp=False):
"""Main rdmc command worker function
:param line: entered command line
:type line: list.
"""
if os.name == "nt":
if not ctypes.windll.shell32.IsUserAnAdmin() != 0:
self.app.typepath.adminpriv = False
elif not os.getuid() == 0:
self.app.typepath.adminpriv = False
if "--version" in line or "-V" in line:
CLI.printer("%s %s\n" % (versioning.__longname__, versioning.__version__))
sys.exit(self.retcode)
help_disp = False
all_opts = True
help_indx = None
help_list = ["-h", "--help"]
for indx, elem in enumerate(line):
if elem in help_list:
help_indx = indx
if "-" in elem:
continue
else:
all_opts = False
if all_opts and (("-h" in line) or ("--help" in line)):
line = ["-h"]
elif help_indx:
# for comm in self.commands_dict:
# if self.commands_dict[comm].ident['name'] == line[0]:
# self.ui.printer(self.commands_dict[comm].ident['usage'])
# return ReturnCodes.SUCCESS
del line[help_indx]
help_disp = True
if line and line[0] in ["-h", "--help"]:
cmddict = self.get_commands()
sorted_keys = sorted(list(cmddict.keys()))
for key in sorted_keys:
if key[0] == "_":
continue
else:
self.parser.epilog = self.parser.epilog + "\n\n" + key + "\n"
for cmd in cmddict[key]:
c_help = "%-25s - %s\n" % (
self.commands_dict[cmd].ident["name"],
self.commands_dict[cmd].ident["summary"],
)
self.parser.epilog = self.parser.epilog + c_help
(self.opts, nargv) = self.parser.parse_known_args(line)
if self.opts.redirect:
try:
sys.stdout = open("console.log", "w")
except:
print("Unable to re-direct output for STDOUT.\n")
else:
print("Start of stdout file.\n\n")
try:
sys.stderr = open("console_err.log", "w")
except IOError:
print("Unable to re-direct output for STDERR.\n")
else:
print("Start of stderr file.\n\n")
self.app.verbose = self.ui.verbosity = self.opts.verbose
try:
# Test encoding functions are there
Encryption.encode_credentials("test")
self.app.set_encode_funct(Encryption.encode_credentials)
self.app.set_decode_funct(Encryption.decode_credentials)
self.encoding = True
except redfish.hpilo.risblobstore2.ChifDllMissingError:
self.encoding = False
if self.opts.config is not None and len(self.opts.config) > 0:
if not os.path.isfile(self.opts.config):
self.retcode = ReturnCodes.CONFIGURATION_FILE_ERROR
sys.exit(self.retcode)
self.config.configfile = self.opts.config
else:
# Default locations: Windows: executed directory Linux: /etc/ilorest/redfish.conf
self.config.configfile = (
os.path.join(os.path.dirname(sys.executable), "redfish.conf")
if os.name == "nt"
else "/etc/ilorest/redfish.conf"
)
if not os.path.isfile(self.config.configfile):
LOGGER.warning("Config file '%s' not found\n\n", self.config.configfile)
self.config.load()
cachedir = None
if not self.opts.nocache:
self.config.cachedir = os.path.join(self.opts.config_dir, "cache")
cachedir = self.config.cachedir
if cachedir:
self.app.cachedir = cachedir
try:
os.makedirs(cachedir)
except OSError as ex:
if ex.errno == errno.EEXIST:
pass
else:
raise
if self.opts.logdir and (self.opts.debug or not self.opts.noinfolog):
logdir = self.opts.logdir
else:
logdir = os.getcwd()
self.log_dir = logdir
try:
setup_logging_from_json(opts=self.opts)
except Exception:
# setup_logging_from_json contains its own fallback logging
# mechanisms. If something unexpected happens still ensure
# the package logger exists.
try:
LOGGER.warning("Failed to initialize JSON logging configuration; using fallback.")
except Exception:
pass
self.app.LOGGER = LOGGER
if ("login" in line or any(x.startswith("--url") for x in line) or not line) and not (
any(x.startswith(("-h", "--h")) for x in nargv) or "help" in line
):
if not any(x.startswith("--sessionid") for x in line):
self.app.logout()
else:
creds, enc = self._pull_creds(nargv)
self.app.restore(creds=creds, enc=enc)
self.opts.is_redfish = self.app.typepath.updatedefinesflag(redfishflag=self.opts.is_redfish)
if nargv:
try:
self.retcode = self._run_command(self.opts, nargv, help_disp)
if "multiconnect" not in line:
if self.app.cache:
if ("logout" not in line) and ("--logout" not in line):
self.app.save()
self.app.redfishinst = None
else:
self.app.logout()
except AttributeError:
self.retcode = 0
pass
except Exception as excp:
self.handle_exceptions(excp)
return self.retcode
else:
self.cmdloop(self.opts)
if self.app.cache:
self.app.save()
else:
self.app.logout()
def cmdloop(self, opts):
"""Interactive mode worker function.
:param opts: Command options.
:type opts: argparse.Namespace
"""
LOGGER.info("Starting interactive mode.")
self.interactive = True
if not opts.nologo:
sys.stdout.write(FIPSSTR)
CLI.version(self._progname, versioning.__version__, versioning.__extracontent__)
LOGGER.info("Displayed version information.")
if not self.app.typepath.adminpriv:
self.ui.user_not_admin()
LOGGER.warning("User does not have admin privileges.")
LOGGER.info("Loading available commands.")
for command, values in self.commands_dict.items():
self.commlist.append(values.ident["name"])
for item in self.commlist:
self.candidates[item] = self.commlist if item == "help" else []
self._redobj = TabAndHistoryCompletionClass(dict(self.candidates))
def bottom_toolbar():
return HTML("<b>Restful Interface Tool</b>")
try:
session = PromptSession(
completer=self._redobj,
auto_suggest=AutoSuggestFromHistory(),
complete_style=CompleteStyle.READLINE_LIKE,
complete_while_typing=True,
)
session.output.disable_bracketed_paste()
LOGGER.info("Tab completion enabled.")
except Exception as e:
LOGGER.exception("Console error: Tab complete is unavailable.")
session = None
if self.opts.notab:
LOGGER.info("Tab completion is disabled as per user settings.")
session = None
while True:
try:
prompt_string = str(versioning.__shortname__) + " > "
if session:
if self.opts.toolbar:
line = session.prompt(prompt_string, bottom_toolbar=bottom_toolbar)
else:
line = session.prompt(prompt_string)
else:
line = input(prompt_string)
LOGGER.debug(f"User input: {SecurityMasker.mask_user_input(line)}")
except (EOFError, KeyboardInterrupt):
LOGGER.info("User exited interactive mode.")
line = "quit\n"
if not line.strip():
continue
elif line.endswith(os.linesep):
line = line.rstrip(os.linesep)
shlex.escape = ""
nargv = shlex.shlex(line, posix=True)
nargv.escape = ""
nargv.whitespace_split = True
nargv = list(nargv)
LOGGER.debug(f"Parsed command arguments: {SecurityMasker.mask_command_arguments(nargv)}")
try:
if not (
any(x.startswith("-h") for x in nargv) or any(x.startswith("--h") for x in nargv) or "help" in line
):
if "login " in line or line == "login" or any(x.startswith("--url") for x in nargv):
self.app.logout()
LOGGER.info("User triggered logout.")
self.retcode = self._run_command(opts, nargv, help_disp=False)
LOGGER.info(f"Command executed with return code: {self.retcode}")
self.check_for_tab_lists(nargv)
except Exception as excp:
LOGGER.exception("Exception occurred while executing command.")
self.handle_exceptions(excp)
if self.opts.verbose:
sys.stdout.write(f"iLOrest return code: {self.retcode}\n")
LOGGER.info("Exiting interactive mode.")
return self.retcode
def handle_exceptions(self, excp):
"""Main exception handler for both shell and interactive modes
:param excp: captured exception to be handled
:type excp: exception.
"""
# pylint: disable=redefined-argument-from-local
try:
if not getattr(excp, "_already_logged", False):
LOGGER.info(f"Exception: {type(excp).__name__}: {excp}")
setattr(excp, "_already_logged", True)
raise
# ****** RDMC ERRORS ******
except ConfigurationFileError as excp:
self.retcode = ReturnCodes.CONFIGURATION_FILE_ERROR
self.ui.error(excp)
sys.exit(excp.errcode)
except InstallsetError as excp:
self.retcode = ReturnCodes.INSTALLSET_ERROR
self.ui.error(excp)
except InvalidCommandLineError as excp:
self.retcode = ReturnCodes.INVALID_COMMAND_LINE_ERROR
self.ui.invalid_commmand_line(excp)
except NoCurrentSessionEstablished as excp:
self.retcode = ReturnCodes.NO_CURRENT_SESSION_ESTABLISHED
self.ui.error(excp)
except TfaEnablePreRequisiteError as excp:
self.retcode = ReturnCodes.TFA_ENABLED_ERROR
self.ui.error(excp)
except iLORisCorruptionError as excp:
self.retcode = ReturnCodes.ILO_RIS_CORRUPTION_ERROR
self.ui.error(excp)
except ResourceNotReadyError as excp:
self.retcode = ReturnCodes.RESOURCE_NOT_READY_ERROR
self.ui.error(excp)
except CloudConnectTimeoutError as excp:
self.retcode = ReturnCodes.CLOUD_CONNECT_TIMEOUT
self.ui.error(excp)
except CloudConnectFailedError as excp:
self.retcode = ReturnCodes.CLOUD_CONNECT_FAILED
self.ui.error(excp)
except AlreadyCloudConnectedError as excp:
self.retcode = ReturnCodes.CLOUD_ALREADY_CONNECTED
self.ui.error(excp)
except ProxyConfigFailedError as excp:
self.retcode = ReturnCodes.PROXY_CONFIG_FAILED
self.ui.error(excp)
except UsernamePasswordRequiredError as excp:
self.retcode = ReturnCodes.USERNAME_PASSWORD_REQUIRED_ERROR
self.ui.error(excp)
except InvalidPasswordLengthError as excp:
self.retcode = ReturnCodes.INVALID_PASSWORD_LENGTH_ERROR
self.ui.error(excp)
except NoChangesFoundOrMadeError as excp:
self.retcode = ReturnCodes.NO_CHANGES_MADE_OR_FOUND
self.ui.invalid_commmand_line(excp)
except StandardBlobErrorHandler as excp:
self.retcode = ReturnCodes.GENERAL_ERROR
self.ui.standard_blob_error(excp)
except InvalidFileInputError as excp:
self.retcode = ReturnCodes.INVALID_FILE_INPUT_ERROR
self.ui.invalid_commmand_line(excp)
except InvalidCommandLineErrorOPTS as excp:
self.retcode = ReturnCodes.INVALID_COMMAND_LINE_ERROR
self.ui.invalid_commmand_line(excp)
except InvalidFileFormattingError as excp:
self.retcode = ReturnCodes.INVALID_FILE_FORMATTING_ERROR
self.ui.invalid_file_formatting(excp)
except NoContentsFoundForOperationError as excp:
self.retcode = ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
self.ui.no_contents_found_for_operation(excp)
except InfoMissingEntriesError as excp:
self.retcode = ReturnCodes.NO_VALID_INFO_ERROR
self.ui.error(excp)
except (
InvalidOrNothingChangedSettingsError,
redfish.ris.rmc_helper.IncorrectPropValue,
) as excp:
self.retcode = ReturnCodes.SAME_SETTINGS_ERROR
self.ui.error(excp)
except NoDifferencesFoundError as excp:
self.retcode = ReturnCodes.NO_CHANGES_MADE_OR_FOUND
self.ui.no_differences_found(excp)
except MultipleServerConfigError as excp:
self.retcode = ReturnCodes.MULTIPLE_SERVER_CONFIG_FAIL
self.ui.multiple_server_config_fail(excp)
except InvalidMSCfileInputError as excp:
self.retcode = ReturnCodes.MULTIPLE_SERVER_INPUT_FILE_ERROR
self.ui.multiple_server_config_input_file(excp)
except FirmwareUpdateError as excp:
self.retcode = ReturnCodes.FIRMWARE_UPDATE_ERROR
self.ui.error(excp)
except FailureDuringCommitError as excp:
self.retcode = ReturnCodes.FAILURE_DURING_COMMIT_OPERATION
self.ui.error(excp)
except BootOrderMissingEntriesError as excp:
self.retcode = ReturnCodes.BOOT_ORDER_ENTRY_ERROR
self.ui.error(excp)
except NicMissingOrConfigurationError as excp:
self.retcode = ReturnCodes.NIC_MISSING_OR_INVALID_ERROR
self.ui.error(excp)
except (
IncompatibleiLOVersionError,
redfish.ris.rmc_helper.IncompatibleiLOVersionError,
) as excp:
self.retcode = ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
self.ui.printer(excp)
except IncompatableServerTypeError as excp:
self.retcode = ReturnCodes.INCOMPATIBLE_SERVER_TYPE
self.ui.printer(excp)
except IloLicenseError as excp:
self.ui.printer(excp)
self.retcode = ReturnCodes.ILO_LICENSE_ERROR
except InvalidCListFileError as excp:
self.retcode = ReturnCodes.INVALID_CLIST_FILE_ERROR
self.ui.error(excp)
except PartitionMoutingError as excp:
self.retcode = ReturnCodes.UNABLE_TO_MOUNT_BB_ERROR
self.ui.error(excp)
except TimeOutError as excp:
self.retcode = ReturnCodes.UPDATE_SERVICE_BUSY
self.ui.error(excp)
except DownloadError as excp:
self.retcode = ReturnCodes.FAILED_TO_DOWNLOAD_COMPONENT
self.ui.error(excp)
except UploadError as excp:
self.retcode = ReturnCodes.FAILED_TO_UPLOAD_COMPONENT
self.ui.error(excp)
except FlashUnsupportedByIloError as excp:
self.retcode = ReturnCodes.ILO_UNSUPPORTED_FLASH
self.ui.error(excp)
except BirthcertParseError as excp:
self.retcode = ReturnCodes.BIRTHCERT_PARSE_ERROR
self.ui.error(excp)
except ResourceExists as excp:
self.retcode = ReturnCodes.RESOURCE_EXISTS_ERROR
self.ui.error(excp)
except InvalidKeyError as excp:
self.retcode = ReturnCodes.ENCRYPTION_ERROR
self.ui.error("Invalid key has been entered for encryption/decryption.\n")
except UnableToDecodeError as excp:
self.retcode = ReturnCodes.ENCRYPTION_ERROR
self.ui.error(excp)
except UnabletoFindDriveError as excp:
self.retcode = ReturnCodes.DRIVE_MISSING_ERROR
self.ui.error(excp)
self.ui.printer("Error occurred while reading device labels.\n")
except PathUnavailableError as excp:
self.retcode = ReturnCodes.PATH_UNAVAILABLE_ERROR
if excp:
self.ui.error(excp)
else:
self.ui.printer("Requested path is unavailable.")
except TaskQueueError as excp:
self.retcode = ReturnCodes.TASKQUEUE_ERROR
self.ui.error(excp)
except DeviceDiscoveryInProgress as excp:
self.retcode = ReturnCodes.DEVICE_DISCOVERY_IN_PROGRESS
self.ui.error(excp)
except InvalidSmartArrayConfigurationError as excp:
self.retcode = ReturnCodes.INVALID_SMART_ARRAY_PAYLOAD
except GenBeforeLoginError as excp:
self.retcode = ReturnCodes.GEN_BEFORE_LOGIN_ERROR
if not getattr(excp, "_already_logged", False):
self.ui.error(f"{type(excp).__name__}: {excp}")
setattr(excp, "_already_logged", True)
# ****** CLI ERRORS ******
except (CommandNotEnabledError, cliutils.CommandNotFoundException) as excp:
self.retcode = ReturnCodes.UI_CLI_COMMAND_NOT_FOUND_EXCEPTION
self.ui.command_not_found(excp)
# try:
# self.commands_dict['HelpCommand'].run('-h')
# except KeyError:
# pass
# ****** RMC/RIS ERRORS ******
except redfish.ris.UndefinedClientError:
self.retcode = ReturnCodes.RIS_UNDEFINED_CLIENT_ERROR
self.ui.error("Please login before making a selection.")
except (
redfish.ris.InstanceNotFoundError,
redfish.ris.RisInstanceNotFoundError,
) as excp:
self.retcode = ReturnCodes.RIS_INSTANCE_NOT_FOUND_ERROR
self.ui.printer(excp)
except redfish.ris.CurrentlyLoggedInError as excp:
self.retcode = ReturnCodes.RIS_CURRENTLY_LOGGED_IN_ERROR
self.ui.error(excp)
except redfish.ris.NothingSelectedError as excp:
self.retcode = ReturnCodes.RIS_NOTHING_SELECTED_ERROR
self.ui.nothing_selected()
except redfish.ris.NothingSelectedFilterError as excp:
self.retcode = ReturnCodes.RIS_NOTHING_SELECTED_FILTER_ERROR
self.ui.nothing_selected_filter()
except redfish.ris.NothingSelectedSetError as excp:
self.retcode = ReturnCodes.RIS_NOTHING_SELECTED_SET_ERROR
self.ui.nothing_selected_set()
except redfish.ris.InvalidSelectionError as excp:
self.retcode = ReturnCodes.RIS_INVALID_SELECTION_ERROR
self.ui.error(excp)
except redfish.ris.rmc_helper.UnableToObtainIloVersionError as excp:
self.retcode = ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
self.ui.error(excp)
except redfish.ris.IdTokenError as excp:
if hasattr(excp, "message"):
self.ui.printer(excp.message)
else:
self.ui.printer(
"Logged-in account does not have the privilege"
" required to fulfill the request or a required"
" token is missing."
"\nEX: biospassword flag if bios password present "
"or tpmenabled flag if TPM module present.\n"
)
self.retcode = ReturnCodes.RIS_MISSING_ID_TOKEN
except redfish.ris.SessionExpired as excp:
self.retcode = ReturnCodes.RIS_SESSION_EXPIRED
self.app.logout()
self.ui.printer(
"Current session has expired or is invalid, "
"please login again with proper credentials to continue.\n"
)
except redfish.ris.ValidationError as excp:
self.retcode = ReturnCodes.RIS_VALIDATION_ERROR
except redfish.ris.ValueChangedError as excp:
self.retcode = ReturnCodes.RIS_VALUE_CHANGED_ERROR
except redfish.ris.ris.SchemaValidationError as excp:
self.ui.printer("Error found in schema, try running with the " "--latestschema flag.\n")
self.retcode = ReturnCodes.RIS_SCHEMA_PARSE_ERROR
# ****** RMC/RIS ERRORS ******
except redfish.rest.connections.RetriesExhaustedError as excp:
self.retcode = ReturnCodes.V1_RETRIES_EXHAUSTED_ERROR
self.ui.retries_exhausted_attemps()
except redfish.rest.connections.VnicNotEnabledError as excp:
self.retcode = ReturnCodes.VNIC_NOT_ENABLED_ERROR
self.ui.retries_exhausted_vnic_not_enabled()
except redfish.rest.v1.InvalidCredentialsError as excp:
self.retcode = ReturnCodes.V1_INVALID_CREDENTIALS_ERROR
self.ui.invalid_credentials(excp)
except redfish.rest.v1.JsonDecodingError as excp:
self.retcode = ReturnCodes.JSON_DECODE_ERROR
self.ui.error(excp)
except redfish.rest.v1.ServerDownOrUnreachableError as excp:
self.retcode = ReturnCodes.V1_SERVER_DOWN_OR_UNREACHABLE_ERROR
self.ui.error(excp)
except redfish.rest.connections.ChifDriverMissingOrNotFound as excp:
self.retcode = ReturnCodes.V1_CHIF_DRIVER_MISSING_ERROR
self.ui.printer(
"Chif driver not found, please check that the iLO channel interface" " driver (Chif) is installed.\n"
)
except redfish.rest.connections.SecurityStateError as excp:
self.retcode = ReturnCodes.V1_SECURITY_STATE_ERROR
self.ui.printer(
"High security mode [%s] or Host Authentication has been enabled. "
"Please provide valid credentials.\n" % str(excp)
)
except redfish.rest.connections.OneTimePasscodeError:
self.retcode = ReturnCodes.TFA_OTP_EMAILED
self.ui.printer(
"One Time Passcode Sent to registered email.\n"
"Retry the login command by including -o/--otp tag along with the OTP received.\n"
)
except redfish.rest.connections.UnauthorizedLoginAttemptError as excp:
self.retcode = ReturnCodes.TFA_WRONG_OTP
self.ui.error(excp)
except redfish.rest.connections.TokenExpiredError as excp:
self.retcode = ReturnCodes.TFA_OTP_TIMEDOUT
self.ui.error(excp)
except redfish.hpilo.risblobstore2.ChifDllMissingError as excp:
self.retcode = ReturnCodes.REST_ILOREST_CHIF_DLL_MISSING_ERROR
self.ui.printer(
"iLOrest Chif library not found, please check that the chif " "ilorest_chif.dll/.so is present.\n"
)
except redfish.hpilo.risblobstore2.UnexpectedResponseError as excp:
self.retcode = ReturnCodes.REST_ILOREST_UNEXPECTED_RESPONSE_ERROR
self.ui.printer("Unexpected data received from iLO.\n")
except redfish.hpilo.risblobstore2.HpIloError as excp:
self.retcode = ReturnCodes.REST_ILOREST_ILO_ERROR
self.ui.printer("iLO returned a failed error code.\n")
except redfish.hpilo.risblobstore2.Blob2CreateError as excp:
self.retcode = ReturnCodes.REST_ILOREST_CREATE_BLOB_ERROR
self.ui.printer("Blob create operation failed.\n")
except redfish.hpilo.risblobstore2.Blob2ReadError as excp:
self.retcode = ReturnCodes.REST_ILOREST_READ_BLOB_ERROR
self.ui.printer("Blob read operation failed.\n")
except redfish.hpilo.risblobstore2.Blob2WriteError as excp:
self.retcode = ReturnCodes.REST_ILOREST_WRITE_BLOB_ERROR
self.ui.printer("Blob write operation failed.\n")
except redfish.hpilo.risblobstore2.Blob2DeleteError as excp:
self.retcode = ReturnCodes.REST_ILOREST_BLOB_DELETE_ERROR
self.ui.printer("Blob delete operation failed.\n")
except redfish.hpilo.risblobstore2.Blob2OverrideError as excp:
self.retcode = ReturnCodes.REST_ILOREST_BLOB_OVERRIDE_ERROR
self.ui.error(excp)
self.ui.printer(
"\nAnother user access in progress. Pease ensure only one user is accessing at a time locally.\n"
)
except redfish.hpilo.risblobstore2.BlobRetriesExhaustedError as excp:
self.retcode = ReturnCodes.REST_BLOB_RETRIES_EXHAUSETED_ERROR
self.ui.printer("\nBlob operation still fails after max retries.\n")
except redfish.hpilo.risblobstore2.Blob2FinalizeError as excp:
self.retcode = ReturnCodes.REST_ILOREST_BLOB_FINALIZE_ERROR
self.ui.printer("Blob finalize operation failed.")
except redfish.hpilo.risblobstore2.BlobNotFoundError as excp:
self.retcode = ReturnCodes.REST_ILOREST_BLOB_NOT_FOUND_ERROR
self.ui.printer("Blob not found with key and namespace provided.\n")
except redfish.ris.rmc_helper.InvalidPathError as excp:
self.retcode = ReturnCodes.RIS_REF_PATH_NOT_FOUND_ERROR
self.ui.printer("Reference path not found.")
except redfish.ris.rmc_helper.IloResponseError as excp:
self.retcode = ReturnCodes.RIS_ILO_RESPONSE_ERROR
except redfish.ris.rmc_helper.UserNotAdminError as excp:
self.ui.user_not_admin()
self.retcode = ReturnCodes.USER_NOT_ADMIN
except redfish.hpilo.rishpilo.HpIloInitialError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RIS_ILO_INIT_ERROR
except redfish.hpilo.rishpilo.HpIloChifAccessDeniedError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RIS_ILO_CHIF_ACCESS_DENIED_ERROR
except redfish.hpilo.rishpilo.HpIloPrepareAndCreateChannelError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RIS_CREATE_AND_PREPARE_CHANNEL_ERROR
except redfish.hpilo.rishpilo.HpIloChifPacketExchangeError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RIS_ILO_CHIF_PACKET_EXCHANGE_ERROR
except redfish.hpilo.rishpilo.HpIloNoDriverError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RIS_ILO_CHIF_NO_DRIVER_ERROR
except redfish.hpilo.rishpilo.HpIloWriteError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RESOURCE_ALLOCATION_ISSUES_ERROR
except redfish.hpilo.rishpilo.HpIloReadError as excp:
self.ui.error(excp)
self.retcode = ReturnCodes.RESOURCE_ALLOCATION_ISSUES_ERROR
# ****** RIS OBJECTS ERRORS ******
except redfish.ris.ris.BiosUnregisteredError as excp:
self.retcode = ReturnCodes.RIS_RIS_BIOS_UNREGISTERED_ERROR
self.ui.bios_unregistered_error()
# ****** FILE/IO ERRORS ******
except IOError:
self.retcode = ReturnCodes.INVALID_FILE_INPUT_ERROR
self.ui.printer(
"Error accessing the file path. Verify the file path is correct and " "you have proper permissions.\n"
)
# ****** VNIC ERRORS ****** #
except VnicExistsError as excp:
self.retcode = ReturnCodes.VNIC_DOES_NOT_EXIST_ERROR
self.ui.error(excp)
except GenerateAndSaveAccountError as excp:
self.retcode = ReturnCodes.GENERAL_ACCOUNT_GENERATE_SAVE_ERROR
self.ui.error(excp)
except NoAppAccountError as excp:
self.retcode = ReturnCodes.ACCOUNT_DOES_NOT_EXIST_ERROR
self.ui.error(excp)
except RemoveAccountError as excp:
self.retcode = ReturnCodes.ACCOUNT_REMOVE_ERROR
self.ui.error(excp)
except AppAccountExistsError as excp:
self.retcode = ReturnCodes.ACCOUNT_EXISTS_CHECK_ERROR
self.ui.error(excp)
except ReactivateAppAccountTokenError as excp:
self.retcode = ReturnCodes.REACTIVATE_APP_ACCOUNT_TOKEN_ERROR
self.ui.error(excp)
except InactiveAppAccountTokenError as excp:
self.retcode = ReturnCodes.INACTIVE_APP_ACCOUNT_TOKEN
self.ui.error(excp)
except VnicLoginError as excp:
self.retcode = ReturnCodes.VNIC_LOGIN_ERROR
self.ui.error(excp)
except SavinginTPMError as excp:
self.retcode = ReturnCodes.ACCOUNT_SAVE_ERROR_TPM
self.ui.error(excp)
except SavinginiLOError as excp:
self.retcode = ReturnCodes.ACCOUNT_SAVE_ERROR_ILO
self.ui.error(excp)
except AppIdListError as excp:
self.retcode = ReturnCodes.APPID_LIST_ERROR
self.ui.error(excp)
# ****** GENERAL ERRORS ******
except SystemExit:
self.retcode = ReturnCodes.GENERAL_ERROR
raise
except Exception as excp:
self.retcode = ReturnCodes.GENERAL_ERROR
sys.stderr.write("ERROR: %s\n" % excp)
if self.opts.debug:
traceback.print_exc(file=sys.stderr)
def check_for_tab_lists(self, command=None):
"""Function to generate available options for tab tab
:param command: command for auto tab completion
:type command: string.
"""
# TODO: don't always update, only when we have a different selector.
# Try to use args to get specific nested posibilities?
changes = dict()
# select options
typeslist = list()
try:
typeslist = sorted(set(self.app.types()))
changes["select"] = typeslist
except:
pass
# get/set/info options
getlist = list()
try:
if self.app.typepath.defs:
typestr = self.app.typepath.defs.typestring
templist = self.app.getprops()
dictcopy = copy.copy(templist[0])
for content in templist:
for k in list(content.keys()):
if k.lower() in HARDCODEDLIST or "@odata" in k.lower():
del content[k]
if "Bios." in dictcopy[typestr]:
if hasattr(
templist[0],
"Attributes",
):
templist = templist[0]["Attributes"]
else:
templist = templist[0]
else:
templist = templist[0]
for key, _ in templist.items():
getlist.append(key)
getlist.sort()
# if select command, get possible values
infovals = dict()
if "select" in command:
if typestr in dictcopy:
(_, attributeregistry) = self.app.get_selection(setenable=True)
schema, reg = self.app.get_model(dictcopy, attributeregistry)
if reg:
if "Attributes" in reg:
reg = reg["Attributes"]
for item in getlist:
for attribute in reg:
if item == attribute:
infovals.update({item: reg[attribute]})
break
changes["nestedinfo"] = infovals
elif schema:
changes["nestedinfo"] = schema
changes["get"] = getlist
changes["nestedprop"] = dictcopy["Attributes"] if "Attributes" in dictcopy else dictcopy
changes["set"] = getlist
changes["info"] = getlist
changes["val"] = []
readonly_list = []
for info in schema:
for checkread in schema[info]:
if "readonly" in checkread and schema[info]["readonly"] is True:
readonly_list.append(info)
set_list = [x for x in getlist if x not in readonly_list]
changes["set"] = set_list
except:
pass
if changes:
self._redobj.updates_tab_completion_lists(changes)
def _pull_creds(self, args):
"""Pull creds from the arguments for blobstore"""
cred_args = {}
enc = False
arg_iter = iter(args)
try:
for arg in arg_iter:
if arg in ("--enc", "-e"):
enc = True
if arg in ("-u", "--user"):
cred_args["username"] = next(arg_iter)
elif arg in ("-p", "--password"):
cred_args["password"] = next(arg_iter)
except StopIteration:
return {}, False
return cred_args, enc
def rdmc_parse_arglist(self, cmdinstance, line=None, default=False):
"""
Parses command-line arguments with special consideration for quoting and optional arguments.
:param cmdinstance: Command instance to be referenced.
:type cmdinstance: class object
:param line: String of arguments passed in.
:type line: str or list
:param default: Flag to determine if the parsed command requires the default workaround for
argparse in Python 2. Argparse incorrectly assumes a sub-command is always
required, so we have to include a default sub-command for no arguments.
:type default: bool
:returns: Parsed argument list
"""
def checkargs(argopts):
"""Check for invalid optional args."""
(_, args) = argopts
for arg in args:
if arg.startswith("-") or arg.startswith("--"):
try:
cmdinstance.parser.error(
"The option %s is not available for %s" % (arg, cmdinstance.ident["name"])
)
except SystemExit:
raise InvalidCommandLineErrorOPTS("")
return argopts
# Ensure `None` does not cause errors
if line is None:
return checkargs(cmdinstance.parser.parse_known_args([]))
# Parse argument list correctly
if isinstance(line, six.string_types):
arglist = shlex.split(line, posix=False)
arglist = [val.strip("\"'") for val in arglist] # Strip surrounding quotes
elif isinstance(line, list):
arglist = line
else:
raise ValueError("Invalid type for 'line'. Expected str or list.")
# Handle Windows-specific globbing
exarglist = []
if os.name == "nt":
for arg in arglist:
try:
matches = glob.glob(arg)
exarglist.extend(matches if matches else [arg])
except Exception as e:
if arg:
exarglist.append(arg)
else:
exarglist = [arg for arg in arglist if arg]
# Insert 'default' if needed
if default and not any(_opt in ["-h", "--h", "-help", "--help"] for _opt in exarglist):
exarglist.insert(0, "default")
return checkargs(cmdinstance.parser.parse_known_args(exarglist))
def ilorestcommand():
# Parse command line arguments early to extract logging flags
ARGUMENTS = sys.argv[1:]
# Create a temporary parser to extract logging flags early
temp_parser = ArgumentParser(add_help=False)
temp_parser.add_argument("--nostdoutlog", action="store_true", default=False)
temp_parser.add_argument("--noinfolog", action="store_true", default=False)
temp_parser.add_argument("--debug", action="store_true", default=False)
temp_parser.add_argument("--logdir", dest="logdir", help="Use the provided directory for logs", default=None)
# Parse known args to extract logging flags
temp_opts, _ = temp_parser.parse_known_args(ARGUMENTS)
# Initialize logging from JSON configuration with parsed flags
setup_logging_from_json(temp_opts)
RDMC = RdmcCommand(
Args=ARGUMENTS,
name=versioning.__shortname__,
usage=versioning.__shortname__ + " [command]",
summary="HPE RESTful Interface Tool",
aliases=[versioning.__shortname__],
argparser=RdmcOptionParser(),
)
# Main execution function call wrapper
if "setproctitle" in sys.modules:
FOUND = False
VARIABLE = setproctitle.getproctitle()
for items in VARIABLE.split(" "):
if FOUND:
VARIABLE = VARIABLE.replace(items, "xxxxxxxx")
break
if items == "--password" or items == "-p":
FOUND = True
setproctitle.setproctitle(VARIABLE)
RDMC.retcode = RDMC.run(ARGUMENTS)
if RDMC.opts:
if RDMC.opts.verbose:
RDMC.ui.printer(("iLORest return code: %s\n" % RDMC.retcode))
else:
RDMC.ui.printer(("iLORest return code: %s\n" % RDMC.retcode))
# Return code
sys.exit(RDMC.retcode)
if __name__ == "__main__":
ilorestcommand()