538 lines
21 KiB
Python
538 lines
21 KiB
Python
###
|
|
# 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 <iLO IP> -u"
|
|
" <iLO Username> -p <iLO Password>",
|
|
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)
|