### # Copyright 2017-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 helper module for RDMC""" # ---------Imports--------- import os import sys from argparse import ( SUPPRESS, Action, ArgumentParser, RawDescriptionHelpFormatter, _ArgumentGroup, ) from redfish.ris import NothingSelectedError try: from ilorest import cliutils, versioning, rdmc_helper from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS except ImportError: import cliutils import versioning import rdmc_helper from rdmc_helper import InvalidCommandLineErrorOPTS # ---------End of imports--------- class _Verbosity(Action): def __init__(self, option_strings, dest, nargs, **kwargs): super(_Verbosity, self).__init__(option_strings, dest, nargs, **kwargs) def __call__(self, parser, namespace, values, option_strings): try: if values: tmp = next(iter(values)) if tmp.isdigit(): namespace.verbose = int(tmp) else: namespace.verbose = len(tmp) + 1 return namespace.verbose = 1 except: raise InvalidCommandLineErrorOPTS("Invalid verbosity selection ('-v').") class CommandBase(object): """Abstract base class for all Command objects. This class is used to build complex command line programs """ def __init__(self, name, usage, summary, aliases=None, argparser=None): self.name = name self.summary = summary self.aliases = aliases self.config_required = True # does the command access config data if argparser is None: self.parser = ArgumentParser() else: self.parser = argparser self.parser.usage = usage def run(self, line, help_disp=False): """Called to actually perform the work. Override this method in your derived class. This is where your program actually does work. """ pass class RdmcCommandBase(CommandBase): """Base class for rdmc commands which includes some common helper methods. """ def __init__(self, name, usage, summary, aliases, argparser=None, **kwargs): """Constructor""" CommandBase.__init__( self, name=name, usage=usage, summary=summary, aliases=aliases, argparser=argparser, **kwargs ) self.json = False self.cache = False self.nologo = False self.toolbar = False def login_validation(self, cmdinstance, options, skipbuild=False): """Combined validation function to login and select with other commands. Not for use with login or select commands themselves. Make sure your command imports options from add_login_arguments_group or there will be errors. :param cmdinstance: the command object instance :type cmdinstance: list. :param options: command line options :type options: list. :param skipbuild: flag to only login and skip monolith build :type skipbuild: bool. """ logobj = cmdinstance.rdmc.load_command(cmdinstance.rdmc.search_commands("LoginCommand")) inputline = list() client = None loggedin = False if hasattr(options, "json") and cmdinstance.rdmc.config.format.lower() == "json": options.json = True try: client = cmdinstance.rdmc.app.current_client except: if options.user or options.password or options.url or options.force_vnic: if options.url: inputline.extend([options.url]) if options.user: if options.encode: options.user = rdmc_helper.Encryption.decode_credentials(options.user) if isinstance(options.user, bytes): options.user = options.user.decode("utf-8") inputline.extend(["-u", options.user]) if options.password: if options.encode: options.password = rdmc_helper.Encryption.decode_credentials(options.password) if isinstance(options.password, bytes): options.password = options.password.decode("utf-8") inputline.extend(["-p", options.password]) if options.force_vnic: inputline.extend(["--force_vnic"]) if getattr(options, "https_cert", False): inputline.extend(["--https", options.https_cert]) if getattr(options, "user_certificate", False): inputline.extend(["--usercert", options.user_certificate]) if getattr(options, "user_root_ca_key", False): inputline.extend(["--userkey", options.user_root_ca_key]) if getattr(options, "user_root_ca_password", False): inputline.extend(["--userpassphrase", options.user_root_ca_password]) else: if cmdinstance.rdmc.config.url: inputline.extend([cmdinstance.rdmc.config.url]) if cmdinstance.rdmc.config.username: inputline.extend(["-u", cmdinstance.rdmc.config.username]) if cmdinstance.rdmc.config.password: inputline.extend(["-p", cmdinstance.rdmc.config.password]) if cmdinstance.rdmc.config.ssl_cert: inputline.extend(["--https", cmdinstance.rdmc.config.ssl_cert]) if getattr(options, "user_certificate", False): inputline.extend(["--usercert", options.user_certificate]) if getattr(options, "user_root_ca_key", False): inputline.extend(["--userkey", options.user_root_ca_key]) if getattr(options, "user_root_ca_password", False): inputline.extend(["--userpassphrase", options.user_root_ca_password]) if options.includelogs: inputline.extend(["--includelogs"]) if options.path: inputline.extend(["--path", options.path]) if getattr(options, "biospassword", False): inputline.extend(["--biospassword", options.biospassword]) if getattr(options, "sessionid", False): inputline.extend(["--sessionid", options.sessionid]) logobj.loginfunction(inputline, skipbuild=skipbuild) loggedin = True if not (loggedin or client or options.url or cmdinstance.rdmc.app.typepath.url): message = "Local login initiated...\n" if cmdinstance.rdmc.opts.verbose: sys.stdout.write(message) else: rdmc_helper.LOGGER.info(message) if not (loggedin or client): logobj.loginfunction(inputline, skipbuild=skipbuild) def login_select_validation(self, cmdinstance, options, skipbuild=False): """Combined validation function to login and select with other commands. Not for use with login or select commands themselves. Make sure your command imports options from add_login_arguments_group or there will be errors. :param cmdinstance: the command object instance :type cmdinstance: list. :param options: command line options :type options: list. :param skipbuild: flag to only login and skip monolith build :type skipbuild: bool. """ inputline = list() if cmdinstance.ident["name"] == "detectilo" or cmdinstance.ident["name"] == "appaccount": try: client = cmdinstance.rdmc.app.current_client return client except Exception: return None self.login_validation(cmdinstance, options, skipbuild=skipbuild) logobj = cmdinstance.rdmc.load_command(cmdinstance.rdmc.search_commands("LoginCommand")) selobj = cmdinstance.rdmc.load_command(cmdinstance.rdmc.search_commands("SelectCommand")) if hasattr(options, "selector") and options.selector: if inputline: inputline.extend(["--selector", options.selector]) logobj.loginfunction(inputline) else: if getattr(options, "ref", False): inputline.extend(["--refresh"]) inputline.extend([options.selector]) selobj.selectfunction(inputline) elif hasattr(options, "selector"): try: inputline = list() selector = cmdinstance.rdmc.app.selector if hasattr(options, "ref") and options.ref: inputline.extend(["--refresh"]) if selector: inputline.extend([selector]) selobj.selectfunction(inputline) except NothingSelectedError: raise NothingSelectedError def logout_routine(self, cmdinstance, options): """Routine to logout of a server automatically at the completion of a command. :param commandinstance: the command object instance :type commandinstance: list. :param options: command line options :type options: list. """ logoutobj = cmdinstance.rdmc.load_command(cmdinstance.rdmc.search_commands("LogoutCommand")) if getattr(options, "logout", False): logoutobj.run("") def add_login_arguments_group(self, parser): """Adds login arguments to the passed parser :param parser: The parser to add the login option group to :type parser: ArgumentParser """ group = parser.add_argument_group("LOGIN OPTIONS", "Options for logging in to a system.") group.add_argument("--url", dest="url", help="Use the provided iLO URL to login.", default=None) group.add_argument( "--sessionid", dest="sessionid", help="Use the provided sessionid to login.", default=None, ) group.add_argument( "-u", "--user", dest="user", help="""If you are not logged in yet, including this flag along with the password and URL flags can be used to login to a server in the same command.""", default=None, ) group.add_argument( "-p", "--password", dest="password", help="""Use the provided iLO password to log in.""", default=None, ) group.add_argument( "-o", "--otp", dest="login_otp", help="""Use the provided iLO OTP to log in.""", default=None, ) group.add_argument( "--biospassword", dest="biospassword", help="""Select this flag to input a BIOS password. Include this flag if second-level BIOS authentication is needed for the command to execute. This option is only used on Gen 9 systems.""", default=None, ) group.add_argument( "--https", dest="https_cert", help="""Use the provided CA bundle or SSL certificate with your login to connect securely to the system in remote mode. This flag has no effect in local mode.""", default=None, ) group.add_argument( "--usercert", dest="user_certificate", type=str, help="""Specify a user certificate file path for certificate based authentication with iLO.\n**NOTE**: Inclusion of this argument will force certficate based authentication. A root user certificate authority key or bundle will be required.""", default=None, ) group.add_argument( "--userkey", dest="user_root_ca_key", type=str, help="""Specify a user root ca key file path for certificate based certificate authentication with iLO. **NOTE 1**: Inclusion of this argument will force certficate based authentication. A root user certificate authority key or bundle will be required. **NOTE 2**: Inclusion of this argument will force certificate based authentication. A user certificate will be required. **NOTE 3**: A user will be prompted for a password if the root certificate authority key is encrypted and \'-certpass/--userrootcapassword\' is omitted.""", default=None, ) group.add_argument( "--userpassphrase", dest="user_root_ca_password", type=str, help="""Optionally specify a user root ca key file password for encrypted user root certificate authority keys. **NOTE 1**: Inclusion of this argument will force certficate based authentication. A root user certificate authority key or bundle will be required. **NOTE 2**: The user will be prompted for a password if the user root certificate authority key requires a password""", default=None, ) # group.add_argument( # '--certbundle', # dest='ca_cert_bundle', # type=str, # help="""Specify a file path for the certificate authority bundle location # (local repository for certificate collection) **NOTE**: Providing a custom certificate # or root CA key will override the use of certificate bundles""", # default=None) group.add_argument( "-e", "--enc", dest="encode", action="store_true", help=SUPPRESS, default=False, ) group.add_argument( "--includelogs", dest="includelogs", action="store_true", help="Optionally include logs in the data retrieval process.", default=False, ) group.add_argument( "--path", dest="path", help="""Optionally set a starting point for data collection during login. If you do not specify a starting point, the default path will be /redfish/v1/. Note: The path flag can only be specified at the time of login. Warning: Only for advanced users, and generally not needed for normal operations.""", default=None, ) group.add_argument( "--force_vnic", dest="force_vnic", action="store_true", help="Force login through iLO Virtual NIC. **NOTE** " "iLO 5 required", default=False, ) group.add_argument( "--logout", dest="logout", action="store_true", help="Logout after the completion of the command.", default=None, ) class RdmcOptionParser(ArgumentParser): """Constructor""" def __init__(self): super().__init__( usage="%s [GLOBAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS] " "[COMMAND OPTIONS]" % versioning.__shortname__, description="iLOrest is a command-line or interactive interface that allows users " "to manage Hewlett Packard Enterprise products that take advantage" " of RESTful APIs.\n\nIn order to view or manage a system you must" " first login. You can login using the login command or during " "execution of any other command.\nFrom here you can run any other " "commands. To learn more about specific commands, run iLOrest " "COMMAND -h.", epilog="Examples:\n\nThe following is the standard flow of command" "s to view system data.\n\tThe first example is each command " "run individually: \n\n\tilorest login\n\tilorest select Bios.\n\t" "ilorest get\n\n\tThe second is the list of all of the commands " "run at once. First locally, then remotely.\n\tilorest get " "--select Bios.\n\tilorest get --select Bios. --url -u" " -p ", formatter_class=RawDescriptionHelpFormatter, ) globalgroup = _ArgumentGroup(self, "GLOBAL OPTIONS") self.add_argument( "--config", dest="config", help="Use the provided configuration file instead of the default one.", metavar="FILE", ) config_dir_default = os.path.join(cliutils.get_user_config_dir(), ".%s" % versioning.__shortname__) self.add_argument( "--cache-dir", dest="config_dir", default=config_dir_default, help="Use the provided directory as the location to cache data" " (default location: %s)" % config_dir_default, metavar="PATH", ) self.add_argument( "-v", "--verbose", dest="verbose", action="count", help="Display verbose information (with increasing level). '-v': Level 1, " "Logging, Stdout, Stderr. '-vv': Level 2, Extends Level 1 with slightly " "elaborated iLO and HTTP response message. '-vvv': Level3, Extends Level 2 " "with message id, validation class, message text with embedded args, and " "possible resolution/mitigation for iLO responses. Includes HTTP responses. " "**NOTE 1**: Some responses may only contain limited information from the source." "**NOTE 2**: Default level is 0.", default=0, ) self.add_argument( "-d", "--debug", dest="debug", action="store_true", help="""[DEPRECATED] This flag is deprecated and will be removed in future versions. Use '--logconfig' for enabling debug logs. Enable debug mode for detailed logging output.""", default=False, ) self.add_argument( "--logdir", dest="logdir", default=None, help="""Use the provided directory as the location for log file.""", metavar="PATH", ) self.add_argument( "--nostdoutlog", dest="nostdoutlog", action="store_true", help="""[DEPRECATED] This flag is deprecated and will be removed in future versions. Use '--logconfig' for enabling debug logs. Enable debug mode for detailed logging output.""", default=False, ) self.add_argument( "--noinfolog", "--nodefaultlog", "--no_default_log", dest="noinfolog", action="store_true", help="""Disable default INFO logs.""", default=False, ) self.add_argument( "--nocache", dest="nocache", action="store_true", help="During execution the application will temporarily store data only in memory.", default=False, ) self.add_argument( "--nologo", dest="nologo", action="store_true", help="""Include to block copyright and logo.""", default=False, ) self.add_argument( "--toolbar", dest="toolbar", action="store_true", help="""Show toolbar at the bottom.""", default=False, ) self.add_argument( "--notab", dest="notab", action="store_true", help="""Disable tab complete.""", default=False, ) self.add_argument( "--redfish", dest="is_redfish", action="store_true", help="Use this flag if you wish to to enable " "Redfish only compliance. It is enabled by default " "in systems with iLO5 and above.", default=False, ) self.add_argument( "--latestschema", dest="latestschema", action="store_true", help="Optionally use the latest schema instead of the one " "requested by the file. Note: May cause errors in some data " "retrieval due to difference in schema versions.", default=False, ) self.add_argument( "--useproxy", dest="proxy", default=None, help="""Use the provided proxy for communication.""", metavar="URL", ) self.add_argument( "--redirectconsole", dest="redirect", help="Optionally include this flag to redirect stdout/stderr console.", nargs="?", default=None, const=True, metavar="REDIRECT CONSOLE", ) self.add_argument_group(globalgroup)