iLO-manager first main commitgit status

This commit is contained in:
Jiří Stach committed 2026-02-03 15:56:53 +01:00
1 parent e6f4cc04ca
commit 599d8da562
3271 files changed
+550655

No files matched your search

@@ -0,0 +1,140 @@
###
# Copyright 2016-2021 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 -*-
"""Commit Command for RDMC"""
from redfish.ris.rmc_helper import NothingSelectedError
try:
from rdmc_helper import (
FailureDuringCommitError,
InvalidCommandLineErrorOPTS,
NoChangesFoundOrMadeError,
NoCurrentSessionEstablished,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
FailureDuringCommitError,
InvalidCommandLineErrorOPTS,
NoChangesFoundOrMadeError,
NoCurrentSessionEstablished,
ReturnCodes,
)
class CommitCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "commit",
"usage": None,
"description": "commit [OPTIONS]\n\n\tRun to apply all changes made during"
" the current session\n\texample: commit",
"summary": "Applies all the changes made during the current session.",
"aliases": [],
"auxcommands": ["LogoutCommand", "RebootCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def commitfunction(self, options=None):
"""Main commit worker function
:param options: command line options
:type options: list.
"""
self.commitvalidation()
self.rdmc.ui.printer("Committing changes...\n")
if options:
if options.biospassword:
self.rdmc.app.current_client.bios_password = options.biospassword
try:
failure = False
commit_opp = self.rdmc.app.commit()
for path in commit_opp:
if self.rdmc.opts.verbose:
self.rdmc.ui.printer("Changes are being made to path: %s\n" % path)
if next(commit_opp):
failure = True
except NothingSelectedError:
raise NoChangesFoundOrMadeError("No changes found or made during commit operation.")
else:
if failure:
raise FailureDuringCommitError(
"One or more types failed to commit. Run the "
"status command to see uncommitted data. "
"if you wish to discard failed changes refresh the "
"type using select with the --refresh flag."
)
if options.reboot:
self.auxcommands["reboot"].run(options.reboot)
self.auxcommands["logout"].run("")
def run(self, line, help_disp=False):
"""Wrapper function for commit main function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, _) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.commitfunction(options)
# Return code
return ReturnCodes.SUCCESS
def commitvalidation(self):
"""Commit method validation function"""
try:
_ = self.rdmc.app.current_client
except:
raise NoCurrentSessionEstablished("Please login and make setting" " changes before using commit command.")
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--reboot",
dest="reboot",
help="Use this flag to perform a reboot command function after"
" completion of operations. For help with parameters and"
" descriptions regarding the reboot flag, run help reboot.",
default=None,
)
@@ -0,0 +1,212 @@
###
# Copyright 2016-2024 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 -*-
"""DetectiLO Command for RDMC"""
from redfish.hpilo.vnichpilo import AppAccount
from redfish.rest.connections import ChifDriverMissingOrNotFound, VnicNotEnabledError
import requests
try:
from rdmc_helper import (
UI,
InvalidCommandLineErrorOPTS,
ReturnCodes,
GenBeforeLoginError,
VnicExistsError,
)
except ImportError:
from ilorest.rdmc_helper import (
UI,
InvalidCommandLineErrorOPTS,
ReturnCodes,
GenBeforeLoginError,
VnicExistsError,
)
class DetectiLOCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "detectilo",
"usage": None,
"description": "Provides the iLO generation of the currently logged-in server.\n\n"
"If --ignore_session is specified, it retrieves the iLO generation of the IP mentioned in --url\n"
"or attempts to determine the iLO generation of the current server.",
"summary": "Retrieves the iLO generation of the server.",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Main get worker function
:param line: command line input
:type line: string.
"""
if help_disp:
line.append("-h")
try:
(_, _) = self.rdmc.rdmc_parse_arglist(self, line)
except:
return ReturnCodes.SUCCESS
return ReturnCodes.SUCCESS
try:
(options, _) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if not options.ignore_session and not options.url:
client = self.detectilovalidation(options)
if client:
if not options.json:
self.rdmc.ui.printer("Using the current logged in session information.\n")
if not any(substring in client.base_url for substring in ("blobstore", "16.1.15.1")):
options.url = client.base_url.split("/")[-1]
if options.url:
if not options.json:
self.rdmc.ui.printer("Detecting iLO for remote device %s\n" % options.url)
retcode = self.detect_remote_ilotype(options)
else:
if not options.json:
self.rdmc.ui.printer("Detecting iLO for this server.\n")
retcode = self.detect_local_ilotype(options)
return retcode
def detectilovalidation(self, options):
"""detectilo validation function
:param options: command line options
:type options: list.
"""
return self.rdmc.login_select_validation(self, options)
def detect_remote_ilotype(self, options):
try:
path = "https://" + options.url + "/redfish/v1"
data = requests.get(path, verify=False)
json_data = data.json()
Oem_Hpe = json_data["Oem"]["Hpe"]
if "ManagerType" in Oem_Hpe["Manager"][0]:
ilo_ver = int(Oem_Hpe["Manager"][0]["ManagerType"].split(" ")[1])
else:
manager_data = Oem_Hpe["Moniker"]
ilo_ver = int(manager_data["PRODGEN"].split(" ")[1])
except Exception as excp:
raise GenBeforeLoginError(
"An error occurred while retrieving the iLO generation. "
"Please ensure that the virtual NIC is enabled for iLO7 based "
"servers, or that the CHIF driver is installed for iLO5 "
"and iLO6 based servers.\n"
)
if options.json:
content = {"iLOType": ilo_ver}
UI().print_out_json(content)
else:
self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
return ReturnCodes.SUCCESS
def detect_local_ilotype(self, options):
app_obj = AppAccount(log_dir=self.rdmc.log_dir)
security_state_dict = {
0: "Error occurred while fetching the security state",
1: "Factory",
2: "Wipe",
3: "Production",
4: "HighSecurity",
5: "FIPS",
6: "SuiteB",
}
try:
ilo_ver, sec_state = self.rdmc.app.getilover_beforelogin(app_obj)
if options.json:
contents = {"iLOType": str(ilo_ver)}
if ilo_ver < 7:
contents["SecurityState"] = security_state_dict[sec_state]
UI().print_out_json(contents)
else:
self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
if ilo_ver < 7:
self.rdmc.ui.printer("Security State: %s\n" % security_state_dict[sec_state])
return ReturnCodes.SUCCESS
except ChifDriverMissingOrNotFound:
if options.json:
contents = {"iLOType": "5_6"}
UI().print_out_json(contents)
else:
self.rdmc.ui.printer("iLO Type: Unable to find iLO generation\n")
raise
except VnicNotEnabledError:
ilo_ver = 7
if options.json:
contents = {"iLOType": str(ilo_ver)}
UI().print_out_json(contents)
else:
self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
raise VnicExistsError(
"Unable to access iLO using virtual NIC. "
"Please ensure virtual NIC is enabled in iLO. "
"Ensure that virtual NIC in the host OS is "
"configured properly. Refer to documentation for more information.\n"
)
except Exception:
raise GenBeforeLoginError(
"An error occurred while retrieving the iLO generation. "
"Please ensure that the virtual NIC is enabled for iLO7 based "
"servers, or that the CHIF driver is installed for iLO5 and iLO6 "
"based servers.\n"
)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the"
" displayed output to JSON format. Preserving the JSON data"
" structure makes the information easier to parse.",
default=False,
)
customparser.add_argument(
"--ignore_session",
dest="ignore_session",
action="store_true",
help="Optionally include this flag if you wish to ignore any"
" session that has been logged in, in order to perform detectilo",
default=False,
)
@@ -0,0 +1,512 @@
###
# Copyright 2016-2023 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 -*-
"""Get Command for RDMC"""
from collections import OrderedDict
import six
import logging
import redfish.ris
from redfish.ris.utils import iterateandclear
try:
from rdmc_helper import (
UI,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
NoContentsFoundForOperationError,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
UI,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
NoContentsFoundForOperationError,
ReturnCodes,
)
try:
from rdmc_helper import HARDCODEDLIST
except ImportError:
from ilorest.rdmc_helper import HARDCODEDLIST
LOGGER = logging.getLogger(__name__)
class GetCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "get",
"usage": None,
"description": "To retrieve all"
" the properties run without arguments. \n\t*Note*: "
"a type will need to be selected or this will return an "
"error.\n\texample: get\n\n\tTo retrieve multiple "
"properties use the following example\n\texample: "
"get Temperatures/ReadingCelsius Fans/Name --selector=Thermal."
"\n\n\tTo change output style format provide"
" the json flag\n\texample: get --json",
"summary": "Displays the current value(s) of a " "property(ies) within a selected type.",
"aliases": [],
"auxcommands": ["LogoutCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Main get worker function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if getattr(options, "json"):
self.rdmc.json = True
selector = options.selector.lower() if options.selector else self.rdmc.app.selector.lower()
if "securityservice" in selector:
self.cmdbase.login_validation(self, options)
else:
self.getvalidation(options)
filtr = (None, None)
if options.filter:
try:
if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
options.filter = options.filter[1:-1]
(sel, val) = options.filter.split("=")
filtr = (sel.strip(), val.strip())
except (InvalidCommandLineError, SystemExit):
raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
self.getworkerfunction(
args,
options,
results=None,
uselist=True,
filtervals=filtr,
readonly=options.noreadonly,
)
self.cmdbase.logout_routine(self, options)
# Return code
return ReturnCodes.SUCCESS
def getworkerfunction(
self,
args,
options,
readonly=False,
filtervals=(None, None),
results=None,
uselist=False,
):
"""main get worker function
:param args: command line arguments
:type args: list.
:param options: command line options
:type options: list.
:param line: command line input
:type line: string.
:param readonly: remove readonly properties
:type readonly: bool
:param filtervals: filter key value pair (Key,Val)
:type filtervals: tuple
:param results: current results collected
:type results: string.
:param uselist: use reserved properties list to filter results
:type uselist: boolean.
"""
nocontent = set()
instances = None
arg = None
# For rest redfish compatibility of bios.
if hasattr(options, "selector") and options.selector:
self.rdmc.app.selector = options.selector
args = [args] if args and isinstance(args, six.string_types) else args
if self.rdmc.app.selector and "." not in self.rdmc.app.selector:
self.rdmc.app.selector = self.rdmc.app.selector + "."
args = (
[
(
"Attributes/" + arg
if self.rdmc.app.selector
and self.rdmc.app.selector.lower().startswith("bios.")
and not (
arg.lower() in ["id", "name", "attributeregistry", "attributes"] or "@odata" in arg.lower()
)
else arg
)
for arg in args
]
if args
else args
)
if filtervals[0]:
instances = self.rdmc.app.select(selector=self.rdmc.app.selector, fltrvals=filtervals)
try:
if "securityservice" in self.rdmc.app.selector.lower():
url = "/redfish/v1/Managers/1/SecurityService/"
LOGGER.info("Fetching SecurityService data from: %s", url)
contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
security_contents = []
if not args and not options.json:
LOGGER.debug("Printing human-readable security service data.")
UI().print_out_human_readable(contents)
elif options and options.json and contents:
LOGGER.debug("Printing JSON security service data.")
UI().print_out_json(contents)
else:
attr = args[0]
contents_lower = {k.lower(): v for k, v in contents.items()}
security_contents.append({attr: contents_lower[attr.lower()]})
if security_contents:
UI().print_out_human_readable(security_contents)
elif (
"componentintegrity"
in self.rdmc.app.selector.lower()
# or "bios." in self.rdmc.app.selector.lower()
):
url = ""
if "componentintegrity" in self.rdmc.app.selector.lower():
url = "/redfish/v1/ComponentIntegrity/?$expand=."
# elif "bios." in self.rdmc.app.selector.lower():
# url = "/redfish/v1/systems/1/bios/?$expand=."
LOGGER.info("Fetching %s data from: %s", self.rdmc.app.selector, url)
contents = self.get_content(url, args, uselist, options)
else:
LOGGER.debug("Handling general selector case.")
skipnonsettingflag = True
if "networkadapter" in self.rdmc.app.selector.lower():
skipnonsettingflag = False
if "selector" in options:
LOGGER.info("Fetching properties with selector: %s", options.selector)
contents = self.rdmc.app.getprops(
props=args,
remread=readonly,
selector=options.selector,
nocontent=nocontent,
insts=instances,
skipnonsetting=skipnonsettingflag,
)
if "networkadapter" in self.rdmc.app.selector.lower():
unique_adapters = {}
for entry in contents:
# Remove keys @odata.id & Id if it contains "settings"
adapter_id = entry.get("Id", "")
odata_id = entry.get("@odata.id", "")
if "settings" in odata_id.lower() or "settings" in adapter_id.lower():
continue
# Preventing deduplicates by Id
if adapter_id:
unique_adapters[adapter_id] = entry
if unique_adapters:
contents = list(unique_adapters.values())
if (
contents
and "logentrycollection" in self.rdmc.app.selector.lower()
and "@odata.id" in args
and not uselist
):
# as per documentation
# collection of LogEntry resource instances
log_contents = self.rdmc.app.getprops(
props=["entries/@odata.id"],
remread=readonly,
selector="logservice",
nocontent=nocontent,
insts=instances,
skipnonsetting=skipnonsettingflag,
)
if log_contents:
log_contents = [
val["Entries"]
for val in log_contents
if "Entries" in val and "@odata.id" in val["Entries"]
]
if log_contents:
contents.extend(log_contents)
# Removing duplicates
contents = list({val["@odata.id"]: val for val in contents}.values())
else:
LOGGER.info("Fetching properties without selector.")
contents = self.rdmc.app.getprops(
props=args, remread=readonly, nocontent=nocontent, insts=instances
)
uselist = False if readonly else uselist
except redfish.ris.rmc_helper.EmptyRaiseForEAFP:
LOGGER.error("EmptyRaiseForEAFP exception encountered, retrying getprops without remread.")
contents = self.rdmc.app.getprops(props=args, nocontent=nocontent)
for ind, content in enumerate(contents):
if "bios." in self.rdmc.app.selector.lower() and "Attributes" in list(content.keys()):
content.update(content["Attributes"])
del content["Attributes"]
LOGGER.debug("Flattened BIOS attributes in response.")
contents[ind] = OrderedDict(sorted(list(content.items()), key=lambda x: x[0]))
if len(args) > 0 and "Members" in args[0]:
uselist = False
if uselist:
for item in contents:
self.removereserved(item)
if results:
LOGGER.info("Returning retrieved contents.")
return contents
contents = contents[0] if len(contents) == 1 else contents
if options and options.json and contents:
LOGGER.debug("Printing output in JSON format.")
UI().print_out_json(contents)
elif contents:
LOGGER.debug("Printing output in human-readable format.")
UI().print_out_human_readable(contents)
else:
try:
if nocontent or not any(next(iter(contents))):
raise Exception()
except Exception:
strtoprint = ", ".join(str(val) for val in nocontent)
if not strtoprint and arg:
strtoprint = arg
LOGGER.error("No contents found for entry: %s", strtoprint)
raise NoContentsFoundForOperationError("No get contents found for entry: %s" % strtoprint)
else:
LOGGER.error("No contents found for selected type.")
raise NoContentsFoundForOperationError("No get contents found for " "selected type.")
if options.logout:
LOGGER.info("Logging out.")
self.auxcommands["logout"].run("")
def get_content(self, url, args, uselist, options):
"""Get data from url"""
contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
if uselist:
# for get command removing @odata.* properties
args = [arg.lower() for arg in args if "@odata." not in arg.lower()]
else:
args = [arg.lower() for arg in args]
if contents and not args:
contents = [contents]
elif contents and args:
collect_data = []
for key, value in contents.items():
# Collect top-level keys based on args
if any(key.lower() == arg for arg in args):
collect_data.append({key: value})
if "attributes" == key.lower() and isinstance(value, dict) and "attributes" not in args:
attr_vals = {k.lower(): v for k, v in value.items()}
val_dict = {arg: attr_vals[arg.lower()] for arg in args if arg.lower() in attr_vals}
if val_dict:
collect_data.append(val_dict)
if key == "Members" and isinstance(value, list):
for member in value:
# Loop through each dict of Members
m_dict = {k.lower(): v for k, v in member.items()}
val_dict = {arg: m_dict[arg.lower()] for arg in args if arg.lower() in m_dict}
if val_dict:
collect_data.append(val_dict)
# collect_data = [dict(t) for t in {tuple(d.items()) for d in collect_data}]
contents = collect_data
else:
contents = []
return contents
def find_key_recursive(self, data, target_key):
"""Recursively searches for a key in a nested dictionary (case-insensitive) and returns its value(s)."""
target_key_lower = target_key.lower() # Convert target_key to lowercase for case-insensitive matching
if isinstance(data, dict):
for key, value in data.items():
if key.lower() == target_key_lower: # Convert dictionary key to lowercase before comparison
yield value # Use yield to return multiple matches
elif isinstance(value, (dict, list)):
yield from self.find_key_recursive(value, target_key)
elif isinstance(data, list):
for item in data:
yield from self.find_key_recursive(item, target_key)
def removereserved(self, entry):
"""function to remove reserved properties
:param entry: dictionary to remove reserved properties from
:type entry: dict.
"""
for key, val in list(entry.items()):
if key.lower() in HARDCODEDLIST or "@odata" in key.lower():
del entry[key]
elif isinstance(val, list):
for item in entry[key]:
if isinstance(item, dict) and item:
self.removereserved(item)
if all([True if not test else False for test in entry[key]]):
del entry[key]
elif isinstance(val, dict):
self.removereserved(val)
if all([True if not test else False for test in entry[key]]):
del entry[key]
def checktoprint(self, options, contents, nocontent, arg):
"""function to decide what/how to print
:param options: list of options
:type options: list.
:param contents: dictionary value returned by getprops.
:type contents: dict.
:param nocontent: props not found are added to the list.
:type nocontent: list.
:param arg: string of args
:type arg: string
"""
if options and options.json and contents:
self.rdmc.ui.print_out_json(contents)
elif contents:
self.rdmc.ui.print_out_human_readable(contents)
else:
try:
if nocontent or not any(next(iter(contents))):
raise Exception()
except Exception:
strtoprint = ", ".join(str(val) for val in nocontent)
if not strtoprint and arg:
strtoprint = arg
raise NoContentsFoundForOperationError("No get contents " "found for entry: %s" % strtoprint)
else:
raise NoContentsFoundForOperationError("No get contents " "found for selected type.")
def collectandclear(self, contents, key, values):
"""function to find and remove unneeded values from contents dictionary
:param contents: dictionary value returned by getprops
:type contents: dict.
:param key: string of keys
:type key: string.
:param values: list of values
:type values: list.
"""
clearcontent = contents[0][key]
if isinstance(clearcontent, dict):
keyslist = list(clearcontent.keys())
else:
keyslist = [clearcontent]
clearedlist = keyslist
for arg in values:
for keys in keyslist:
if str(keys).lower() == str(arg).lower():
clearedlist.remove(arg)
contents = iterateandclear(contents, clearedlist)
return contents
def getvalidation(self, options):
"""get method validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run"
" the current command on. Use this flag when you wish to"
" select a type without entering another command, or if you"
" wish to work with a type that is different from the one"
" you currently have selected.",
default=None,
)
customparser.add_argument(
"--filter",
dest="filter",
help="Optionally set a filter value for a filter attribute."
" This uses the provided filter for the currently selected"
" type. Note: Use this flag to narrow down your results. For"
" example, selecting a common type might return multiple"
" objects that are all of that type. If you want to modify"
" the properties of only one of those objects, use the filter"
" flag to narrow down results based on properties."
"\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
default=None,
)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the"
" displayed output to JSON format. Preserving the JSON data"
" structure makes the information easier to parse.",
default=False,
)
customparser.add_argument(
"--noreadonly",
dest="noreadonly",
action="store_true",
help="Optionally include this flag if you wish to only show"
" properties that are not read-only. This is useful to see what "
"is configurable with the selected type(s).",
default=False,
)
customparser.add_argument(
"--refresh",
dest="ref",
action="store_true",
help="Optionally reload the data of selected type and clear " "patches from current selection.",
default=False,
)
@@ -0,0 +1,183 @@
###
# Copyright 2016-2021 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 -*-
"""Info Command for RDMC"""
try:
from rdmc_helper import (
InfoMissingEntriesError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
InfoMissingEntriesError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
try:
from rdmc_helper import HARDCODEDLIST
except:
from ilorest.rdmc_helper import HARDCODEDLIST
class InfoCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "info",
"usage": None,
"description": "Displays detailed "
"information about a property within a selected type"
"\n\texample: info property\n\n\tDisplays detailed "
"information for several properties\n\twithin a selected "
"type\n\texample: info property property/sub-property property\n\n\t"
"Run without arguments to display properties \n\tthat "
"are available for info command\n\texample: info",
"summary": "Displays detailed information about a property within a selected type.",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, autotest=False, help_disp=False):
"""Main info worker function
:param line: command line input
:type line: string.
:param autotest: flag to determine if running automatictesting
:type autotest: bool.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.infovalidation(options)
if args:
for item in args:
if self.rdmc.app.selector.lower().startswith("bios.") and "attributes" not in item.lower():
if not (item.lower() in HARDCODEDLIST or "@odata" in item.lower()):
item = "Attributes/" + item
outdata = self.rdmc.app.info(props=item, dumpjson=options.json, latestschema=options.latestschema)
if autotest:
return outdata
if outdata and options.json:
self.rdmc.ui.print_out_json(outdata)
elif outdata:
self.rdmc.ui.printer(outdata)
if not outdata:
raise InfoMissingEntriesError("There are no valid " "entries for info in the current instance.")
else:
if len(args) > 1 and not item == args[-1]:
self.rdmc.ui.printer("\n************************************" "**************\n")
else:
results = set()
instances = self.rdmc.app.select()
for instance in instances:
currdict = instance.resp.dict
currdict = (
currdict["Attributes"]
if instance.maj_type.startswith(self.rdmc.app.typepath.defs.biostype)
and currdict.get("Attributes", None)
else currdict
)
results.update([key for key in currdict if key not in HARDCODEDLIST and "@odata" not in key.lower()])
if results and autotest:
return results
elif results:
self.rdmc.ui.printer("Info options:\n")
for item in results:
self.rdmc.ui.printer("%s\n" % item)
else:
raise InfoMissingEntriesError(
"No info items available for this selected type." " Try running with the --latestschema flag."
)
self.cmdbase.logout_routine(self, options)
# Return code
return ReturnCodes.SUCCESS
def infovalidation(self, options):
"""Info method validation function
:param options: command line options
:type options: list.
"""
if self.rdmc.opts.latestschema:
options.latestschema = True
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run"
" the current command on. Use this flag when you wish to"
" select a type without entering another command, or if you"
" wish to work with a type that is different from the one"
" you currently have selected.",
default=None,
)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the"
" displayed output to JSON format. Preserving the JSON data"
" structure makes the information easier to parse.",
default=False,
)
customparser.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=None,
)
@@ -0,0 +1,180 @@
###
# Copyright 2016-2021 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 -*-
"""List Command for RDMC"""
try:
from rdmc_helper import (
UI,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
UI,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
class ListCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "list",
"usage": None,
"description": "Displays the current values of the "
"properties within\n\ta selected type including"
" reserved properties\n\texample: list\n\n\tNOTE: If "
"you wish to not list all the reserved properties\n\t "
" run the get command instead",
"summary": "Displays the current value(s) of a"
" property(ies) within a selected type including"
" reserved properties.",
"aliases": ["ls"],
"auxcommands": ["GetCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Wrapper function for main list function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
selector = options.selector.lower() if options.selector else self.rdmc.app.selector.lower()
if "securityservice" in selector:
self.cmdbase.login_validation(self, options)
else:
self.listvalidation(options)
fvals = (None, None)
if options.filter:
try:
if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
options.filter = options.filter[1:-1]
(sel, val) = options.filter.split("=")
fvals = (sel.strip(), val.strip())
except:
raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
if options.selector:
if "storagecontroller." in options.selector.lower():
options.selector = "storagecontrollercollection"
self.rdmc.app.selector = options.selector
elif self.rdmc.app.selector:
options.selector = self.rdmc.app.selector
if "securityservice" in options.selector.lower():
url = "/redfish/v1/Managers/1/SecurityService/"
contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
security_contents = []
if options and options.json and contents:
UI().print_out_json(contents)
elif not args:
UI().print_out_human_readable(contents)
else:
attr = args[0]
contents_lower = {k.lower(): v for k, v in contents.items()}
security_contents.append({attr: contents_lower[attr.lower()]})
if security_contents:
UI().print_out_human_readable(security_contents)
else:
self.auxcommands["get"].getworkerfunction(args, options, filtervals=fvals, uselist=False)
self.cmdbase.logout_routine(self, options)
# Return code
return ReturnCodes.SUCCESS
def listvalidation(self, options):
"""List data validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run"
" the current command on. Use this flag when you wish to"
" select a type without entering another command, or if you"
" wish to work with a type that is different from the one"
" you currently have selected.",
default=None,
)
customparser.add_argument(
"--filter",
dest="filter",
help="Optionally set a filter value for a filter attribute."
" This uses the provided filter for the currently selected"
" type. Note: Use this flag to narrow down your results. For"
" example, selecting a common type might return multiple"
" objects that are all of that type. If you want to modify"
" the properties of only one of those objects, use the filter"
" flag to narrow down results based on properties."
"\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
default=None,
)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the"
" displayed output to JSON format. Preserving the JSON data"
" structure makes the information easier to parse.",
default=False,
)
customparser.add_argument(
"--refresh",
dest="ref",
action="store_true",
help="Optionally reload the data of selected type and clear " "patches from current selection.",
default=False,
)
@@ -0,0 +1,569 @@
###
# Copyright 2016-2023 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 -*-
"""Load Command for RDMC"""
import json
import os
import shlex
import subprocess
import sys
from datetime import datetime
from six.moves import queue
import redfish.ris
from redfish.ris.rmc_helper import LoadSkipSettingError
try:
from rdmc_helper import (
Encryption,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidFileFormattingError,
InvalidFileInputError,
InvalidMSCfileInputError,
MultipleServerConfigError,
NoChangesFoundOrMadeError,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
Encryption,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidFileFormattingError,
InvalidFileInputError,
InvalidMSCfileInputError,
MultipleServerConfigError,
NoChangesFoundOrMadeError,
ReturnCodes,
)
try:
from rdmc_helper import HARDCODEDLIST
except:
from ilorest.rdmc_helper import HARDCODEDLIST
# default file name
__filename__ = "ilorest.json"
class LoadCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "load",
"usage": None,
"description": "Run to load the default configuration"
" file\n\texample: load\n\n\tLoad configuration file from a "
"different file\n\tif any property values have changed, the "
"changes are committed and the user is logged out of the server"
"\n\n\texample: load -f output.json\n\n\tLoad configurations to "
"multiple servers\n\texample: load -m mpfilename.txt -f output."
"json\n\n\tNote: multiple server file format (1 server per new "
"line)\n\t--url <iLO url/hostname> -u admin -p password\n\t--url"
" <iLO url/hostname> -u admin -p password\n\t--url <iLO url/"
"hostname> -u admin -p password",
"summary": "Loads the server configuration settings from a file.",
"aliases": [],
"auxcommands": ["CommitCommand", "SelectCommand", "RawPatchCommand"],
}
self.filenames = None
self.mpfilename = None
self.queue = queue.Queue()
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def securebootremovereadonly(self, tmp):
templist = [
"SecureBootCurrentBoot",
"@odata.etag",
"Id",
"AutoNeg",
"FQDN",
"FullDuplex",
"HostName",
"IPv6Addresses",
"IPv6DefaultGateway",
"LinkStatus",
"MaxIPv6StaticAddresses",
"Name",
"NameServers",
"PermanentMACAddress",
"ConfigurationSettings",
"InterfaceType",
"NICSupportsIPv6",
"DomainName",
"IPV6",
"DNSServers",
"MACAddress",
"VLAN",
"SpeedMbps",
"DateTime",
]
remove_data = self.rdmc.app.removereadonlyprops(tmp, False, True, templist)
return remove_data
def run(self, line, help_disp=False):
"""Main load worker function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, _) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.loadvalidation(options)
returnvalue = False
if options.mpfilename:
self.rdmc.ui.printer("Loading configuration for multiple servers...\n")
else:
self.rdmc.ui.printer("Loading configuration...\n")
for files in self.filenames:
if not os.path.isfile(files):
raise InvalidFileInputError(
"File '%s' doesn't exist. Please create file by running save command." % files
)
if options.encryption:
with open(files, "rb") as myfile:
data = myfile.read()
data = Encryption().decrypt_file(data, options.encryption)
loadcontents = json.loads(data)
else:
try:
with open(files, "r") as myfile:
loadcontents = json.load(myfile)
except:
raise InvalidFileFormattingError("Invalid file formatting found in file %s" % files)
if options.mpfilename:
mfile = options.mpfilename
outputdir = None
if options.outdirectory:
outputdir = options.outdirectory
if self.runmpfunc(mpfile=mfile, lfile=files, outputdir=outputdir):
return ReturnCodes.SUCCESS
else:
raise MultipleServerConfigError("One or more servers failed to load given configuration.")
results = False
eth_conf = False
validation_errs = []
for loadcontent in loadcontents:
for content, loaddict in loadcontent.items():
inputlist = list()
if content == "Comments":
continue
if "EthernetInterface" in content:
if options.force_network_config:
import platform
import tempfile
patchpath = "/redfish/v1/Managers/1/EthernetInterfaces/1/"
try:
payload = loadcontent[content][patchpath]
except KeyError:
continue
payload = loadcontent[content][patchpath]
remove_odata = self.securebootremovereadonly(payload)
payload.update(remove_odata)
if "StaticNameServers" in payload:
payload["StaticNameServers"] = [
item for item in payload["StaticNameServers"] if item != "::"
]
tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
temp_file = os.path.join(tempdir, "temp_patch.json")
out_file = open(temp_file, "w")
patch_payload = {patchpath: payload}
json.dump(patch_payload, out_file, indent=6)
out_file.close()
self.auxcommands["rawpatch"].run(temp_file + " --service")
os.remove(temp_file)
eth_conf = True
continue
else:
self.rdmc.ui.printer(
"Skipping network configurations as" " --force_network_config is not included.\n"
)
continue
inputlist.append(content)
if options.biospassword:
inputlist.extend(["--biospassword", options.biospassword])
self.auxcommands["select"].selectfunction(inputlist)
if self.rdmc.app.selector.lower() not in content.lower():
raise InvalidCommandLineError("Selector not found.\n")
try:
for _, items in loaddict.items():
remove_odata = self.securebootremovereadonly(items)
items.update(remove_odata)
try:
if self.rdmc.app.loadset(
seldict=items,
latestschema=options.latestschema,
uniqueoverride=options.uniqueoverride,
):
results = True
except LoadSkipSettingError:
returnvalue = True
results = True
except:
raise
except redfish.ris.ValidationError as excp:
errs = excp.get_errors()
validation_errs.append({self.rdmc.app.selector: errs})
except:
raise
try:
if results:
self.auxcommands["commit"].commitfunction(options=options)
reboot_needed = False
for n in inputlist:
n = n.lower()
if "bios" in n or "boot" in n or "iscsi" in n:
reboot_needed = True
if reboot_needed:
self.rdmc.ui.printer("Reboot is required for settings to take effect.\n")
except NoChangesFoundOrMadeError as excp:
if returnvalue:
pass
else:
raise excp
if validation_errs:
for validation_err in validation_errs:
for err_type in validation_err:
self.rdmc.ui.error("Validation error(s) in type %s:\n" % err_type)
for err in validation_err[err_type]:
if isinstance(err, redfish.ris.RegistryValidationError):
self.rdmc.ui.error(err.message)
try:
if err.reg:
self.rdmc.ui.error(str(err.sel))
except:
pass
raise redfish.ris.ValidationError()
if not results:
if eth_conf:
continue
else:
self.rdmc.ui.printer("No differences found from current configuration.\n")
# Return code
if returnvalue:
return ReturnCodes.LOAD_SKIP_SETTING_ERROR
return ReturnCodes.SUCCESS
def loadvalidation(self, options):
"""Load method validation function
:param options: command line options
:type options: list.
"""
if self.rdmc.opts.latestschema:
options.latestschema = True
try:
self.cmdbase.login_validation(self, options)
except Exception:
if options.mpfilename:
pass
else:
raise
# filename validations and checks
if options.filename:
self.filenames = options.filename
elif self.rdmc.config:
if self.rdmc.config.defaultloadfilename:
self.filenames = [self.rdmc.config.defaultloadfilename]
if not self.filenames:
self.filenames = [__filename__]
def verify_file(self, filedata, inputfile):
"""Function used to handle oddly named files and convert to JSON
:param filedata: input file data
:type filedata: string.
:param inputfile: current input file
:type inputfile: string.
"""
try:
tempholder = json.loads(filedata)
return tempholder
except:
raise InvalidFileFormattingError("Invalid file formatting found in file %s" % inputfile)
def get_current_selector(self, path=None):
"""Returns current selected content minus hard coded list
:param path: current path
:type path: string.
"""
contents = self.rdmc.app.monolith.path[path]
if not contents:
contents = list()
for content in contents:
for k in list(content.keys()):
if k.lower() in HARDCODEDLIST or "@odata" in k.lower():
del content[k]
return contents
def runmpfunc(self, mpfile=None, lfile=None, outputdir=None):
"""Main worker function for multi file command
:param mpfile: configuration file
:type mpfile: string.
:param lfile: custom file name
:type lfile: string.
:param outputdir: custom output directory
:type outputdir: string.
"""
# self.logoutobj.run("")
data = self.validatempfile(mpfile=mpfile, lfile=lfile)
if not data:
return False
processes = []
finalreturncode = True
outputform = "%Y-%m-%d-%H-%M-%S"
if outputdir:
if outputdir.endswith(('"', "'")) and outputdir.startswith(('"', "'")):
outputdir = outputdir[1:-1]
if not os.path.isdir(outputdir):
self.rdmc.ui.error("The give output folder path does not exist.\n")
raise InvalidCommandLineErrorOPTS("")
dirpath = outputdir
else:
dirpath = os.getcwd()
dirname = "%s_%s" % (datetime.now().strftime(outputform), "MSClogs")
createdir = os.path.join(dirpath, dirname)
os.mkdir(createdir)
oofile = open(os.path.join(createdir, "CompleteOutputfile.txt"), "w+")
self.rdmc.ui.printer("Create multiple processes to load configuration " "concurrently to all servers...\n")
while True:
if not self.queue.empty():
line = self.queue.get()
else:
break
finput = "\n" + "Output for " + line[line.index("--url") + 1] + ": \n\n"
urlvar = line[line.index("--url") + 1]
if "python" in os.path.basename(sys.executable.lower()):
# If we are running from source we have to add the python file to the command
listargforsubprocess = [sys.executable, sys.argv[0]] + line
else:
listargforsubprocess = [sys.executable] + line
if os.name != "nt":
listargforsubprocess = " ".join(listargforsubprocess)
urlfilename = urlvar.split("//")[-1]
logfile = open(os.path.join(createdir, urlfilename + ".txt"), "w+")
pinput = subprocess.Popen(listargforsubprocess, shell=True, stdout=logfile, stderr=logfile)
processes.append((pinput, finput, urlfilename, logfile))
for pinput, finput, urlfilename, logfile in processes:
pinput.wait()
returncode = pinput.returncode
finalreturncode = finalreturncode and not returncode
logfile.close()
logfile = open(os.path.join(createdir, urlfilename + ".txt"), "r+")
oofile.write(finput + str(logfile.read()))
oofile.write("-x+x-" * 16)
logfile.close()
if returncode == 0:
self.rdmc.ui.printer("Loading Configuration for {} : SUCCESS\n".format(urlfilename))
else:
self.rdmc.ui.error("Loading Configuration for {} : FAILED\n".format(urlfilename))
self.rdmc.ui.error(
"ILOREST return code : {}.\nFor more details please check "
"{}.txt under {} directory.\n".format(returncode, urlfilename, createdir)
)
oofile.close()
if finalreturncode:
self.rdmc.ui.printer("All servers have been successfully configured.\n")
return finalreturncode
def validatempfile(self, mpfile=None, lfile=None):
"""Validate temporary file
:param mpfile: configuration file
:type mpfile: string.
:param lfile: custom file name
:type lfile: string.
"""
self.rdmc.ui.printer("Checking given server information...\n")
if not mpfile:
return False
if not os.path.isfile(mpfile):
raise InvalidFileInputError(
"File '%s' doesn't exist, please " "create file by running save command." % mpfile
)
try:
with open(mpfile, "r") as myfile:
data = list()
cmdtorun = ["load"]
cmdargs = ["-f", str(lfile)]
globalargs = ["-v", "--nocache"]
while True:
line = myfile.readline()
if not line:
break
if line.endswith(os.linesep):
line.rstrip(os.linesep)
args = shlex.split(line, posix=False)
if len(args) < 5:
self.rdmc.ui.error("Incomplete data in input file: {}\n".format(line))
raise InvalidMSCfileInputError("Please verify the " "contents of the %s file" % mpfile)
else:
linelist = globalargs + cmdtorun + args + cmdargs
line = str(line).replace("\n", "")
self.queue.put(linelist)
data.append(linelist)
except Exception as excp:
raise excp
if data:
return data
return False
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"-f",
"--filename",
dest="filename",
help="Use this flag if you wish to use a different"
" filename than the default one. The default filename is"
" %s." % __filename__,
action="append",
default=None,
)
customparser.add_argument(
"-m",
"--multiprocessing",
dest="mpfilename",
help="""use the provided filename to obtain data""",
default=None,
)
customparser.add_argument(
"--outputdirectory",
dest="outdirectory",
help="""use the provided directory to output data for multiple server configuration""",
default=None,
)
customparser.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=None,
)
customparser.add_argument(
"--uniqueoverride",
dest="uniqueoverride",
action="store_true",
help="Override the measures stopping the tool from writing " "over items that are system unique.",
default=False,
)
customparser.add_argument(
"--encryption",
dest="encryption",
help="Optionally include this flag to encrypt/decrypt a file " "using the key provided.",
default=None,
)
customparser.add_argument(
"--reboot",
dest="reboot",
help="Use this flag to perform a reboot command function after"
" completion of operations. For help with parameters and"
" descriptions regarding the reboot flag, run help reboot.",
default=None,
)
customparser.add_argument(
"--force_network_config",
dest="force_network_config",
help="Use this flag to force set network configuration."
"Network settings will be skipped if the flag is not included.",
action="store_true",
default=None,
)
@@ -0,0 +1,582 @@
###
# Copyright 2016-2021 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 -*-
"""Login Command for RDMC"""
import getpass
import os
import socket
import redfish.ris
from redfish.hpilo.vnichpilo import AppAccount
from redfish.rest.connections import ChifDriverMissingOrNotFound, VnicNotEnabledError
import requests
from requests.exceptions import SSLError, RequestException
import platform
from argparse import SUPPRESS
try:
from rdmc_helper import (
Encryption,
LOGGER,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
PathUnavailableError,
ReturnCodes,
UsernamePasswordRequiredError,
VnicExistsError,
VnicLoginError,
InactiveAppAccountTokenError,
NoAppAccountError,
GenBeforeLoginError,
AppAccountExistsError,
)
except ModuleNotFoundError:
from ilorest.rdmc_helper import (
ReturnCodes,
LOGGER,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
PathUnavailableError,
Encryption,
UsernamePasswordRequiredError,
VnicExistsError,
VnicLoginError,
InactiveAppAccountTokenError,
NoAppAccountError,
GenBeforeLoginError,
AppAccountExistsError,
)
from redfish.rest.v1 import ServerDownOrUnreachableError
class LoginCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "login",
"usage": None,
"description": "To login remotely run using iLO url and iLO credentials"
"\n\texample: login <iLO url/hostname> -u <iLO username> "
"-p <iLO password>\n\n\tTo login on a local server run without "
"arguments\n\texample: login"
"\n\n\tTo login through VNIC run using --force_vnic and iLO credentials "
"\n\texample: login --force_vnic -u <iLO username> -p <iLO password>"
"\n\nLogin using OTP can be done in 2 ways."
"\n\n\t To login implicitly, use the tag --wait_for_otp."
"\n\t\texample: login -u <iLO username> -p <iLO password> --wait_for_otp"
"\n\n\n\t To login explicitly, use the tag -o/--otp and enter OTP after."
"\n\t\texample: login -u <iLO username> -p <iLO password> -o <iLO OTP>"
"\n\n\tNOTE: A [URL] can be specified with "
"an IPv4, IPv6, or hostname address.",
"summary": "Connects to a server, establishes a secure session," " and discovers data from iLO.",
"aliases": [],
"auxcommands": ["LogoutCommand"],
"cert_data": {},
}
self.cmdbase = None
self.rdmc = None
self.url = None
self.username = None
self.password = None
self.sessionid = None
self.biospassword = None
self.auxcommands = dict()
self.cert_data = dict()
self.login_otp = None
self.ilo_gen = 6
def run(self, line, help_disp=False):
"""wrapper function for main login function
:param line: command line input
:type line: string.
:param help_disp: flag to determine to display or not
:type help_disp: boolean
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
self.loginfunction(line)
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
if not self.rdmc.app.monolith._visited_urls:
self.auxcommands["logout"].run("")
raise PathUnavailableError("The path specified by the --path flag is unavailable.")
except Exception:
raise
# Return code
return ReturnCodes.SUCCESS
def perform_login(self, options, skipbuild, user_ca_cert_data, args, app_obj):
ilo_ver = self.ilo_gen
if ilo_ver < 7 or (ilo_ver >= 7 and (options.usechif or options.noapptoken or args)):
login_response = self.rdmc.app.login(
username=self.username,
password=self.password,
sessionid=self.sessionid,
base_url=self.url,
path=options.path,
skipbuild=skipbuild,
includelogs=options.includelogs,
biospassword=self.biospassword,
is_redfish=self.rdmc.opts.is_redfish,
proxy=self.rdmc.opts.proxy,
user_ca_cert_data=user_ca_cert_data,
json_out=self.rdmc.json,
login_otp=self.login_otp,
log_dir=self.rdmc.log_dir,
)
else:
if options.hostappname or options.hostappid or options.salt:
if not (options.hostappname and options.hostappid and options.salt):
raise InvalidCommandLineError("Please enter all the necessary host application information.\n")
# Code to check if vnic is enabled.
vnic_enabled = self.rdmc.app.vexists(app_obj)
if not vnic_enabled:
raise VnicExistsError(
"Unable to access iLO using virtual NIC. "
"Please ensure virtual NIC is enabled in iLO. "
"Ensure that virtual NIC in the host OS is "
"configured properly. Refer to documentation for more information.\n"
)
# To check if appaccount exists. If not, error out.
try:
apptoken_exists = self.rdmc.app.token_exists(app_obj)
except Exception as excp:
raise AppAccountExistsError("Error occurred while checking if app account exists.\n")
# If appaccount doesn't exist
if not apptoken_exists:
raise NoAppAccountError(
"The app account is not available for this host application. "
"Please retry using the --no_app_account option or generate "
"an application account using the appaccount create command.\n"
)
try:
# Function will log in, retrieve session id, call build monolith, .save()
login_response = self.rdmc.app.vnic_login(
app_obj=app_obj,
path=options.path,
skipbuild=skipbuild,
includelogs=options.includelogs,
json_out=self.rdmc.json,
base_url=self.url,
username=self.username,
password=self.password,
log_dir=self.rdmc.log_dir,
login_otp=self.login_otp,
is_redfish=self.rdmc.opts.is_redfish,
proxy=self.rdmc.opts.proxy,
user_ca_cert_data=user_ca_cert_data,
biospassword=self.biospassword,
sessionid=self.sessionid,
)
except redfish.hpilo.vnichpilo.InactiveAppAccountTokenError:
LOGGER.error("Login failed due to an inactive or expired App Account token.")
raise InactiveAppAccountTokenError("Login failed due to an inactive or expired App Account token.")
except Exception as excp:
error_msg = """Error occurred while performing login using app account.
Check if application account exists using 'appaccount details' command.
Otherwise, try with --no_app_account option.\n"""
LOGGER.error(error_msg)
raise VnicLoginError(error_msg)
if options.session_id and login_response and hasattr(login_response, "_auth_key"):
self.rdmc.ui.printer(f"\nSessionID:{login_response._auth_key}\n\n")
def loginfunction(self, line, skipbuild=None, json_out=False):
"""Main worker function for login class
:param line: entered command line
:type line: list.
:param skipbuild: flag to determine if monolith should be build
:type skipbuild: boolean.
:param json_out: flag to determine if json output neededd
:type skipbuild: boolean.
"""
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineError("Invalid command line arguments")
if platform.system() == "Darwin":
# Mac OS
app_obj = None
self.ilo_gen = 6
else:
app_obj = AppAccount(
appname=options.hostappname,
appid=options.hostappid,
salt=options.salt,
username=options.user,
password=options.password,
log_dir=self.rdmc.log_dir,
)
self.ilo_gen = self.get_ilover_beforelogin(args=args, app_obj=app_obj, options=options)
self.loginvalidation(options=options, args=args, app_obj=app_obj)
# if proxy server provided in command line as --useproxy, it will be used,
# otherwise it will the environment variable setting.
# else proxy will be set as None.
if self.rdmc.opts.proxy:
_ = self.rdmc.opts.proxy
elif "https_proxy" in os.environ and os.environ["https_proxy"]:
_ = os.environ["https_proxy"]
elif "http_proxy" in os.environ and os.environ["http_proxy"]:
_ = os.environ["http_proxy"]
else:
_ = self.rdmc.config.proxy
no_bundle = False
if getattr(options, "ca_cert_bundle", False):
user_ca_cert_data = {"ca_certs": options.ca_cert_bundle}
else:
user_ca_cert_data = {}
if getattr(options, "user_certificate", False):
no_bundle = True
user_ca_cert_data.update({"cert_file": options.user_certificate})
if getattr(options, "user_root_ca_key", False):
no_bundle = True
user_ca_cert_data.update({"key_file": options.user_root_ca_key})
if getattr(options, "user_root_ca_password", False):
no_bundle = True
user_ca_cert_data.update({"key_password": options.user_root_ca_password})
if not no_bundle:
if hasattr(user_ca_cert_data, "ca_certs"):
user_ca_cert_data.pop("ca_certs")
try:
if getattr(options, "force_vnic", False):
self.rdmc.ui.printer("\nAttempt to login with Vnic...\n")
try:
sock = socket.create_connection((args[0], 443))
if sock:
sock.close
except:
pass
self.sessionid = options.sessionid
self.login_otp = options.login_otp
if self.url and "blobstore" in self.url and (options.waitforOTP or options.login_otp):
options.waitforOTP = None
options.login_otp = None
self.rdmc.ui.printer(
"Warning: For local inband mode, TFA is not supported, options --wait_for_otp "
"and --otp will be ignored\n"
)
if options.waitforOTP:
try:
self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
except redfish.rest.connections.OneTimePasscodeError:
self.rdmc.ui.printer("One Time Passcode Sent to registered email.\n")
ans = input("Enter OTP: ")
self.login_otp = ans
self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
else:
self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
except ServerDownOrUnreachableError as excp:
self.rdmc.ui.printer("The following error occurred during login: '%s'\n" % str(excp.__class__.__name__))
self.username = None
self.password = None
# Warning for cache enabled, since we save session in plain text
if not self.rdmc.encoding:
self.rdmc.ui.warn("Cache is activated. Session keys are stored in plaintext.")
if self.rdmc.opts.debug:
self.rdmc.ui.warn("Logger is activated. Logging is stored in plaintext.")
if options.selector:
try:
self.rdmc.app.select(selector=options.selector)
if self.rdmc.opts.verbose:
self.rdmc.ui.printer(("Selected option: '%s'\n" % options.selector))
except Exception as excp:
raise redfish.ris.InstanceNotFoundError(excp)
def loginvalidation(self, options, args, app_obj):
"""Login helper function for login validations
:param options: command line options
:type options: list.
:param args: command line arguments
:type args: list.
"""
# Fill user name/password from config file
if not options.user:
options.user = self.rdmc.config.username
if not options.password:
options.password = self.rdmc.config.password
if not hasattr(options, "user_certificate"):
options.user_certificate = self.rdmc.config.user_cert
if not hasattr(options, "user_root_ca_key"):
options.user_root_ca_key = self.rdmc.config.user_root_ca_key
if not hasattr(options, "user_root_ca_password"):
options.user_root_ca_password = self.rdmc.config.user_root_ca_password
ilo_ver = self.ilo_gen
if (
options.user
and not options.password
and (
not hasattr(options, "user_certificate")
or not hasattr(options, "user_root_ca_key")
or hasattr(options, "user_root_ca_password")
)
):
# Option for interactive entry of password
tempinput = getpass.getpass().rstrip()
if tempinput:
options.password = tempinput
else:
raise InvalidCommandLineError("Empty or invalid password was entered.")
if options.user:
self.username = options.user
if options.password:
self.password = options.password
if options.encode:
self.username = Encryption.decode_credentials(self.username).decode("utf-8")
self.password = Encryption.decode_credentials(self.password).decode("utf-8")
if options.biospassword:
self.biospassword = options.biospassword
if ilo_ver >= 7:
if not args:
if self.username and self.password and not options.noapptoken and not options.usechif:
options.noapptoken = True
if options.noapptoken:
if (
not self.username or not self.password
): # Add another check to see if username and password is present in environment variables
raise UsernamePasswordRequiredError(
"Please enter username and password in order to login without app account.\n"
)
options.force_vnic = True
# Assignment of url in case no url is entered
if getattr(options, "force_vnic", False):
if not (getattr(options, "ca_cert_bundle", False) or getattr(options, "user_certificate", False)):
if not (self.username and self.password) and not options.sessionid:
raise UsernamePasswordRequiredError("Please provide credentials to login with virtual NIC.\n")
self.url = "https://16.1.15.1"
else:
if ilo_ver >= 7 and not options.usechif:
# TO DO
# self.url = self.rdmc.app.GetIPAddress()
self.url = "https://16.1.15.1"
else:
self.url = "blobstore://."
if args:
# Any argument should be treated as an URL
self.url = args[0]
# Verify that URL is properly formatted for https://
if "https://" not in self.url:
self.url = "https://" + self.url
if not (
hasattr(options, "user_certificate")
or hasattr(options, "user_root_ca_key")
or hasattr(options, "user_root_ca_password")
):
if not (options.user and options.password):
raise InvalidCommandLineError("Empty username or password was entered.")
else:
# Check to see if there is a URL in config file
if self.rdmc.config.url:
self.url = self.rdmc.config.url
def get_ilover_beforelogin(self, args, app_obj, options):
"""
Attempts to retrieve the iLO version before login.
:param args: List of command-line arguments; expects iLO IP/hostname at index 0.
:param app_obj: App object for fallback retrieval if args is empty.
:param options: Options object, may include flags like 'force_vnic' or 'usechif'.
:return: iLO version as an integer.
:raises: GenBeforeLoginError, VnicExistsError, ChifDriverMissingOrNotFound, VnicNotEnabledError
"""
ip = path = None # Initialize to avoid UnboundLocalError
try:
if args:
ip = args[0]
path = f"https://{ip}/redfish/v1"
response = requests.get(path, verify=False, timeout=10)
response.raise_for_status()
try:
json_data = response.json()
oem_data = json_data.get("Oem", {}).get("Hpe") or json_data.get("Oem", {}).get("Hp")
if not oem_data:
raise KeyError("Oem->Hpe or Oem->Hp block missing in response.")
manager = oem_data.get("Manager", [{}])[0]
manager_type = manager.get("ManagerType")
if manager_type:
ilo_ver = int(manager_type.split(" ")[1])
else:
moniker = oem_data.get("Moniker", {})
prodgen = moniker.get("PRODGEN")
if not prodgen:
raise KeyError("PRODGEN key not found in Moniker.")
ilo_ver = int(prodgen.split(" ")[1])
except (KeyError, ValueError, IndexError) as e:
raise GenBeforeLoginError(
f"Failed to parse iLO version from response. Error: {e}\n"
"Ensure the iLO firmware is Redfish-compliant and responds correctly.\n"
)
else:
ilo_ver, _ = self.rdmc.app.getilover_beforelogin(app_obj)
return ilo_ver
except ChifDriverMissingOrNotFound:
if getattr(options, "force_vnic", False):
return 5
raise
except VnicNotEnabledError:
if getattr(options, "usechif", False):
return 7
raise VnicExistsError(
"Unable to access iLO using virtual NIC.\n"
"Please ensure that virtual NIC is enabled in iLO and "
"properly configured in the host OS.\n"
"Refer to the documentation for setup instructions.\n"
)
except SSLError:
raise GenBeforeLoginError(
f"SSL Error: Unable to connect to the server at {ip or '[unknown IP]'}.\n"
"Please verify the iLO IP and try accessing the GUI.\n"
)
except RequestException as e:
raise GenBeforeLoginError(
f"Connection Error: Could not retrieve data from {path or '[unknown path]'}.\n" f"Details: {e}\n"
)
except Exception as e:
raise GenBeforeLoginError(
f"An unexpected error occurred while retrieving the iLO version.\n"
f"Details: {e}\n"
"Verify if virtual NIC is enabled for iLO7 or that the CHIF driver "
"is installed for iLO5 and iLO6 based servers.\n"
)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
def remove_argument(parser, arg):
for action in parser._actions:
opts = action.option_strings
if (opts and opts[0] == arg) or action.dest == arg:
parser._remove_action(action)
break
for action in parser._action_groups:
for group_action in action._group_actions:
opts = group_action.option_strings
if (opts and opts[0] == arg) or group_action.dest == arg:
action._group_actions.remove(group_action)
return
if not customparser:
return
customparser.add_argument(
"--wait_for_otp",
dest="waitforOTP",
help="Optionally include this flag to implicitly wait for OTP.",
action="store_true",
default=None,
)
self.cmdbase.add_login_arguments_group(customparser)
remove_argument(customparser, "url")
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run"
" the current command on. Use this flag when you wish to"
" select a type without entering another command, or if you"
" wish to work with a type that is different from the one"
" you currently have selected.",
default=None,
)
customparser.add_argument(
"--no_app_token",
"--no_app_account",
dest="noapptoken",
help="Include this parameter in order to login to iLO7 and above with credentials and not app account.",
default=None,
action="store_true",
)
customparser.add_argument("--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None)
customparser.add_argument(
"--hostappname", dest="hostappname", help="Parameter to specify hostappname", default=None
)
customparser.add_argument(
"--salt", dest="salt", help="Parameter to specify application owned salt", default=None
)
customparser.add_argument(
"--use_chif",
dest="usechif",
help="Include this parameter in order to operate iLO7 and above using chif.",
default=None,
action="store_true",
)
customparser.add_argument(
"--show_session_id",
"-s",
dest="session_id",
help=SUPPRESS,
default=None,
action="store_true",
)
@@ -0,0 +1,79 @@
###
# Copyright 2016-2021 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 -*-
"""Logout Command for RDMC"""
try:
from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
except ImportError:
from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
class LogoutCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "logout",
"usage": None,
"description": "Run to end the current session and disconnect" " from the server\n\tExample: logout",
"summary": "Ends the current session and disconnects from the server.",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
def logoutfunction(self, line):
"""Main logout worker function
:param line: command line input
:type line: string.
"""
try:
(_, _) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.rdmc.app.logout("")
def run(self, line, help_disp=False):
"""Wrapper function for main logout function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
self.rdmc.ui.printer("Logging session out.\n")
self.logoutfunction(line)
# Return code
return ReturnCodes.SUCCESS
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
@@ -0,0 +1,201 @@
###
# Copyright 2016-2021 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 -*-
"""Results Command for rdmc"""
import copy
import json
import sys
import jsondiff
try:
from rdmc_helper import HARDCODEDLIST
except:
from ilorest.rdmc_helper import HARDCODEDLIST
try:
from rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
class PendingChangesCommand:
"""PendingChanges class command"""
def __init__(self):
self.ident = {
"name": "pending",
"usage": None,
"description": "Run to show pending committed changes "
"that will be applied after a reboot.\n\texample: pending",
"summary": "Show the pending changes that will be applied on reboot.",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Show pending changes of settings objects
:param line: string of arguments passed in
:type line: str.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if args:
raise InvalidCommandLineError("Pending command does not take any arguments.")
self.pendingvalidation(options)
self.pendingfunction()
sys.stdout.write("\n")
self.cmdbase.logout_routine(self, options)
return ReturnCodes.SUCCESS
def pendingfunction(self):
"""Main pending command worker function"""
settingsuri = []
ignorekeys = ["@odata.id", "@odata.etag", "@redfish.settings", "oem"]
ignoreuri = [str("hpsut*")]
ignorekeys.extend(HARDCODEDLIST)
resourcedir = self.rdmc.app.get_handler(self.rdmc.app.monolith._resourcedir, service=True, silent=True)
for resource in resourcedir.dict["Instances"]:
if (resource["@odata.id"].split("/").__len__() - 1) > 4:
splitstr = resource["@odata.id"].split("/")[5]
for element in ignoreuri:
if "/settings" in resource["@odata.id"] and not self.wildcard_str_match(element, splitstr):
settingsuri.append(resource["@odata.id"])
self.rdmc.ui.printer("Current Pending Changes:\n")
for uri in settingsuri:
diffprint = {}
baseuri = uri.split("settings")[0]
base = self.rdmc.app.get_handler(baseuri, service=True, silent=True)
settings = self.rdmc.app.get_handler(uri, service=True, silent=True)
typestring = self.rdmc.app.monolith.typepath.defs.typestring
currenttype = ".".join(base.dict[typestring].split("#")[-1].split(".")[:-1])
differences = json.loads(jsondiff.diff(base.dict, settings.dict, syntax="symmetric", dump=True))
diffprint = self.recursdict(differences, ignorekeys)
self.rdmc.ui.printer("\n%s:" % currenttype)
if not diffprint:
self.rdmc.ui.printer("\nNo pending changes found.\n")
else:
self.rdmc.ui.pretty_human_readable(diffprint)
def wildcard_str_match(self, first, second):
"""
Recursive function for determining match between two strings. Accounts
for wildcard characters
:param first: comparison string (may contain '*' or '?' for wildcards)
:param type: str (unicode)
:param second: string value to be compared (must not contain '*' or '?')
:param type: str (unicode)
"""
if not first and not second:
return True
if len(first) > 1 and first[0] == "*" and not second:
return False
if (len(first) > 1 and first[0] == "?") or (first and second and first[0] == second[0]):
return self.wildcard_str_match(first[1:], second[1:])
if first and first[0] == "*":
return self.wildcard_str_match(first[1:], second) or self.wildcard_str_match(first, second[1:])
return False
def recursdict(self, diff, ignorekeys):
"""Recursively get dict ready for printing
:param diff: diff dict
:type options: dict.
"""
diffprint = copy.deepcopy(diff)
for item in diff:
if item.lower() in ignorekeys:
diffprint.pop(item)
elif item == "$delete":
for ditem in diff[item]:
if isinstance(diff[item], list):
continue
if isinstance(diff[item][ditem], dict):
diffprint[item].pop(ditem)
if ditem.lower() in ignorekeys or ditem.isdigit():
continue
else:
diffprint.update({"removed": ditem})
diffprint.pop(item)
elif item == "$insert":
for ditem in diff[item]:
del diffprint[item][diffprint[item].index(ditem)]
diffprint.update({"changed index position": ditem[1]})
diffprint.pop(item)
elif isinstance(diff[item], dict):
diffprint[item] = self.recursdict(diff[item], ignorekeys)
elif isinstance(diff[item], list):
diffprint.update({item: {"Current": diff[item][0], "Pending": diff[item][1]}})
else:
continue
return diffprint
def pendingvalidation(self, options):
"""Pending method validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
@@ -0,0 +1,80 @@
###
# Copyright 2016-2021 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 -*-
"""Exit Command for rdmc"""
import sys
try:
from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
except ImportError:
from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
class ExitCommand:
"""Exit class to handle exiting from interactive mode"""
def __init__(self):
self.ident = {
"name": "exit",
"usage": None,
"description": "Run to exit from the interactive shell\n\texample: exit",
"summary": "Exits from the interactive shell.",
"aliases": ["quit"],
"auxcommands": ["LogoutCommand"],
}
# self.rdmc = rdmcObj
# self.logoutobj = rdmcObj.commands_dict["LogoutCommand"](rdmcObj)
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""If an argument is present, print help else exit
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(_, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if not args or not line:
self.auxcommands["logout"].run("")
# System exit
sys.exit(ReturnCodes.SUCCESS)
else:
self.rdmc.ui.error("Exit command does not take any parameters.\n")
raise InvalidCommandLineErrorOPTS("Invalid command line arguments.")
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
@@ -0,0 +1,113 @@
###
# Copyright 2016-2021 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 -*-
"""Help Command for RDMC"""
try:
from rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
except ImportError:
from ilorest.rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
try:
from rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
class HelpCommand(RdmcCommandBase):
"""Constructor"""
def __init__(self):
self.ident = {
"name": "help",
"usage": None,
"description": "help [COMMAND]\n\tFor more detailed command descriptions"
" use the help command feature\n\texample: help login\n",
"summary": "Displays command line syntax and help menus for individual commands." " Example: help login\n",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Wrapper function for help main function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if not args or not line:
RdmcOptionParser().print_help()
if self.rdmc:
cmddict = self.rdmc.get_commands()
sorted_keys = sorted(list(cmddict.keys()))
for key in sorted_keys:
if key[0] == "_":
continue
else:
self.rdmc.ui.printer("\n%s\n" % key)
for cmd in cmddict[key]:
self.rdmc.ui.printer(
"%-25s - %s\n"
% (
self.rdmc.commands_dict[cmd].ident["name"],
self.rdmc.commands_dict[cmd].ident["summary"],
)
)
else:
if self.rdmc:
cmddict = self.rdmc.get_commands()
sorted_keys = list(cmddict.keys())
for key in sorted_keys:
for cmd in cmddict[key]:
cmd_s = cmd.split("Command")
cmd_s = cmd_s[0]
if args[0].lower() == cmd_s.lower():
self.rdmc.ui.printer(self.rdmc.commands_dict[cmd].ident["description"] + "\n")
return ReturnCodes.SUCCESS
raise InvalidCommandLineError("Command '%s' not found." % args[0])
# Return code
return ReturnCodes.SUCCESS
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
@@ -0,0 +1,156 @@
###
# Copyright 2016-2021 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 -*-
"""Results Command for rdmc"""
from redfish.ris.resp_handler import ResponseHandler
from redfish.ris.rmc_helper import EmptyRaiseForEAFP
try:
from rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
class ResultsCommand:
"""Monolith class command"""
def __init__(self):
self.ident = {
"name": "results",
"usage": None,
"description": "Run to show the results of the last" " changes after a server reboot.\n\texample: results",
"summary": "Show the results of changes which require a server reboot.",
"aliases": [],
"auxcommands": ["LoginCommand", "SelectCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Gather results of latest BIOS change
:param line: string of arguments passed in
:type line: str.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if args:
raise InvalidCommandLineError("Results command does not take any arguments.")
self.resultsvalidation(options)
results = {}
if self.rdmc.app.typepath.defs.biospath[-1] == "/":
iscsipath = self.rdmc.app.typepath.defs.biospath + "iScsi/"
bootpath = self.rdmc.app.typepath.defs.biospath + "Boot/"
else:
iscsipath = self.rdmc.app.typepath.defs.biospath + "/iScsi"
bootpath = self.rdmc.app.typepath.defs.biospath + "/Boot"
try:
self.auxcommands["select"].selectfunction("SmartStorageConfig")
smartarray = self.rdmc.app.getprops()
sapaths = [path["@odata.id"].split("settings")[0] for path in smartarray]
except:
sapaths = None
biosresults = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.biospath, service=True, silent=True)
iscsiresults = self.rdmc.app.get_handler(iscsipath, service=True, silent=True)
bootsresults = self.rdmc.app.get_handler(bootpath, service=True, silent=True)
saresults = []
if sapaths:
saresults = [self.rdmc.app.get_handler(path, service=True, silent=True) for path in sapaths]
try:
results.update({"Bios:": biosresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
except Exception:
results.update({"Bios:": None})
try:
results.update({"Iscsi:": iscsiresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
except:
results.update({"Iscsi:": None})
try:
results.update({"Boot:": bootsresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
except:
results.update({"Boot:": None})
try:
for result in saresults:
loc = "SmartArray"
if saresults.index(result) > 0:
loc += " %d:" % saresults.index(result)
else:
loc += ":"
results.update({loc: result.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
except:
results.update({"SmartArray:": None})
self.rdmc.ui.printer("Results of the previous reboot changes:\n\n")
for result in results:
self.rdmc.ui.printer("%s\n" % result)
try:
for msg in results[result]:
_ = ResponseHandler(
self.rdmc.app.validationmanager,
self.rdmc.app.typepath.defs.messageregistrytype,
).message_handler(response_data=msg, message_text="", verbosity=0, dl_reg=False)
pass
except EmptyRaiseForEAFP as exp:
raise EmptyRaiseForEAFP(exp)
except Exception:
self.rdmc.ui.error("No messages found for %s.\n" % result[:-1])
self.cmdbase.logout_routine(self, options)
return ReturnCodes.SUCCESS
def resultsvalidation(self, options):
"""Results method validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
@@ -0,0 +1,424 @@
###
# Copyright 2016-2021 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 -*-
"""Save Command for RDMC"""
import json
from collections import OrderedDict
import redfish.ris
try:
from rdmc_helper import (
Encryption,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidFileFormattingError,
ReturnCodes,
iLORisCorruptionError,
)
except ImportError:
from ilorest.rdmc_helper import (
Encryption,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidFileFormattingError,
ReturnCodes,
iLORisCorruptionError,
)
# default file name
__filename__ = "ilorest.json"
class SaveCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "save",
"usage": None,
"description": "Run to save a selected type to a file"
"\n\texample: save --selector HpBios.\n\n\tChange the default "
"output filename\n\texample: save --selector HpBios. -f "
"output.json\n\n\tTo save multiple types in one file\n\texample: "
"save --multisave Bios.,ComputerSystem.",
"summary": "Saves the selected type's settings to a file.",
"aliases": [],
"auxcommands": ["SelectCommand"],
}
self.filename = __filename__
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Main save worker function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if options.multisave:
options.selector = ""
if (
hasattr(options, "selector")
and options.selector is not None
and "hpbaseconfigs" not in options.selector.lower()
and "hpebaseconfigs" not in options.selector.lower()
and "bios" not in options.selector.lower()
):
self.savevalidation(options)
else:
# filename validations and checks
self.cmdbase.login_validation(self, options)
self.filename = None
if options.filename and len(options.filename) > 1:
raise InvalidCommandLineError("Save command doesn't support multiple filenames.")
elif options.filename:
self.filename = options.filename[0]
elif self.rdmc.config:
if self.rdmc.config.defaultsavefilename:
self.filename = self.rdmc.config.defaultsavefilename
if not self.filename:
self.filename = __filename__
# if args:
# raise InvalidCommandLineError("Save command takes no arguments.")
self.rdmc.ui.printer("Saving configuration...\n")
if options.filter:
try:
if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
options.filter = options.filter[1:-1]
(sel, val) = options.filter.split("=")
sel = sel.strip()
val = val.strip()
except:
raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
instances = self.rdmc.app.select(
selector=self.rdmc.app.selector,
fltrvals=(sel, val),
path_refresh=True,
)
contents = self.saveworkerfunction(options, instances=instances)
else:
contents = self.saveworkerfunction(options)
if options.multisave:
for select in options.multisave:
try:
self.auxcommands["select"].run(select)
except:
pass
contents += self.saveworkerfunction(options)
if not contents:
raise redfish.ris.NothingSelectedError
else:
# TODO: Maybe add this to the command. Not sure we use it elsewhere in the lib
contents = self.add_save_file_header(contents)
if options.encryption:
with open(self.filename, "wb") as outfile:
outfile.write(
Encryption().encrypt_file(
json.dumps(contents, indent=2, cls=redfish.ris.JSONEncoder),
options.encryption,
)
)
else:
with open(self.filename, "w") as outfile:
outfile.write(json.dumps(contents, indent=2, cls=redfish.ris.JSONEncoder, sort_keys=True))
self.rdmc.ui.printer("Configuration saved to: %s\n" % self.filename)
self.cmdbase.logout_routine(self, options)
# Return code
return ReturnCodes.SUCCESS
def saveworkerfunction(self, options, instances=None):
"""Returns the currently selected type for saving
:param instances: list of instances from select to save
:type instances: list.
"""
try:
if (
options.selector is not None
and (
"bios" in options.selector.lower()
or "hpbaseconfigs" in options.selector.lower()
or "hpebaseconfigs" in options.selector.lower()
)
) and (options.multisave is None or options.multisave == ""):
raise KeyError
content = self.rdmc.app.getprops(insts=instances)
try:
contents = [{val[self.rdmc.app.typepath.defs.hrefstring]: val} for val in content]
except KeyError:
try:
contents = [{val["links"]["self"][self.rdmc.app.typepath.defs.hrefstring]: val} for val in content]
except KeyError:
raise iLORisCorruptionError(
"iLO Database seems to be corrupted. Please check. Reboot the server to " "restore\n"
)
except Exception:
config_path = None
contents = list()
if options.selector is not None:
if options.selector.lower() == "bios." or "bios.v" in options.selector.lower():
config_path = ["/redfish/v1/systems/1/bios/settings/"]
elif options.selector.lower() == "bios":
config_path = [
"/redfish/v1/systems/1/bios/settings/",
"/redfish/v1/systems/1/bios/mappings/",
"/redfish/v1/systems/1/bios/oem/hpe/mappings/",
]
elif "hpbaseconfigs" in options.selector.lower() or "hpebaseconfigs" in options.selector.lower():
config_path = [
"/redfish/v1/systems/1/bios/oem/hpe/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/nvmeof/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/iscsi/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/tlsconfig/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/serverconfiglock/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/kmsconfig/baseconfigs/",
"/redfish/v1/systems/1/bios/oem/hpe/boot/baseconfigs/",
"/redfish/v1/systems/1/bios/baseconfigs/",
"/redfish/v1/systems/1/bios/nvmeof/baseconfigs/",
"/redfish/v1/systems/1/bios/iscsi/baseconfigs/",
"/redfish/v1/systems/1/bios/tlsconfig/baseconfigs/",
"/redfish/v1/systems/1/bios/serverconfiglock/baseconfigs/",
"/redfish/v1/systems/1/bios/kmsconfig/baseconfigs/",
"/redfish/v1/systems/1/bios/boot/baseconfigs/",
]
elif "hpebiosmapping" in options.selector.lower():
config_path = [
"/redfish/v1/systems/1/bios/mappings",
"/redfish/v1/systems/1/bios/oem/hpe/mappings/",
]
result = None
for b in config_path:
try:
result = self.rdmc.app.get_handler(b, silent=True, service=True)
except:
pass
if result is not None:
if result.status == 200:
d = {b: result.dict}
contents.append(d)
type_string = self.rdmc.app.typepath.defs.typestring
templist = list()
srnum_list = list()
for content in contents:
typeselector = None
pathselector = None
for path, values in content.items():
# if "Managers/1/EthernetInterfaces/1" not in path:
for dictentry in list(values.keys()):
if dictentry == type_string:
typeselector = values[dictentry]
pathselector = path
del values[dictentry]
if dictentry in [
"IPv4Addresses",
"IPv6Addresses",
"IPv6AddressPolicyTable",
"MACAddress",
"StaticNameServers",
# "AutoNeg",
# "FullDuplex",
"SpeedMbps",
]:
del values[dictentry]
if dictentry in [
"IPv6StaticAddresses",
"IPv6StaticDefaultGateways",
"IPv4StaticAddresses",
]:
if values[dictentry]:
del values[dictentry]
if dictentry in ["IPv4", "IPv6", "DHCPv6", "DHCPv4"]:
if "DNSServers" in values[dictentry]:
del values[dictentry]["DNSServers"]
del values["Oem"]["Hpe"][dictentry]["DNSServers"]
if "UseDNSServers" in values[dictentry]:
del values[dictentry]["UseDNSServers"]
del values["Oem"]["Hpe"][dictentry]["UseDNSServers"]
if values:
skip = False
if "SerialNumber" in values and values["SerialNumber"] is not None and values["SerialNumber"] != "":
if values["SerialNumber"] in srnum_list:
skip = True
else:
srnum_list.append(values["SerialNumber"])
tempcontents = dict()
if not skip:
if typeselector and pathselector:
tempcontents[typeselector] = {pathselector: values}
else:
raise InvalidFileFormattingError("Missing path or selector in input file.")
templist.append(tempcontents)
return templist
def nested_sort(self, data):
"""Helper function to sort all dictionary key:value pairs
:param data: dictionary to sort
:type data: dict.
"""
for key, value in data.items():
if isinstance(value, dict):
data[key] = self.nested_sort(value)
data = OrderedDict(sorted(list(data.items()), key=lambda x: x[0]))
return data
def savevalidation(self, options):
"""Save method validation function
:param options: command line options
:type options: list.
"""
if options.multisave:
options.multisave = options.multisave.replace('"', "").replace("'", "")
options.multisave = options.multisave.replace(" ", "").split(",")
if not len(options.multisave) >= 1:
raise InvalidCommandLineError("Invalid number of types in multisave option.")
options.selector = options.multisave[0]
options.multisave = options.multisave[1:]
self.cmdbase.login_select_validation(self, options)
# filename validations and checks
self.filename = None
if options.filename and len(options.filename) > 1:
raise InvalidCommandLineError("Save command doesn't support multiple filenames.")
elif options.filename:
self.filename = options.filename[0]
elif self.rdmc.config:
if self.rdmc.config.defaultsavefilename:
self.filename = self.rdmc.config.defaultsavefilename
if not self.filename:
self.filename = __filename__
def add_save_file_header(self, contents):
"""Helper function to retrieve the comments for save file
:param contents: current save contents
:type contents: list.
"""
templist = list()
headers = self.rdmc.app.create_save_header()
templist.append(headers)
for content in contents:
templist.append(content)
return templist
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"-f",
"--filename",
dest="filename",
help="Use this flag if you wish to use a different filename than the default one. "
"The default filename is %s." % __filename__,
action="append",
default=None,
)
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run the current command on. "
"Use this flag when you wish to select a type without entering another command, "
"or if you wish to work with a type that is different from the one currently "
"selected.",
default=None,
# required=True,
)
customparser.add_argument(
"--multisave",
dest="multisave",
help="Optionally include this flag to save multiple types to a single file. "
"Overrides the currently selected type.\n\t Usage: --multisave type1.,type2.,type3.",
default="",
)
customparser.add_argument(
"--filter",
dest="filter",
help="Optionally set a filter value for a filter attribute. This uses the provided "
"filter for the currently selected type. Note: Use this flag to narrow down your "
"results. For example, selecting a common type might return multiple objects that "
"are all of that type. If you want to modify the properties of only one of those "
"objects, use the filter flag to narrow down results based on properties."
"\n\t Usage: --filter [ATTRIBUTE]=[VALUE]",
default=None,
)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the displayed output to "
"JSON format. Preserving the JSON data structure makes the information easier to "
"parse.",
default=False,
)
customparser.add_argument(
"--encryption",
dest="encryption",
help="Optionally include this flag to encrypt/decrypt a file using the key provided.",
default=None,
)
@@ -0,0 +1,143 @@
###
# Copyright 2016-2021 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 -*-
"""Select Command for RDMC"""
from redfish.ris import NothingSelectedError
try:
from rdmc_helper import LOGGER, InvalidCommandLineErrorOPTS, ReturnCodes
except ImportError:
from ilorest.rdmc_helper import LOGGER, InvalidCommandLineErrorOPTS, ReturnCodes
class SelectCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "select",
"usage": None,
"description": "Selects the Redfish/HpRest type to be used.\nIn order to "
"remove the need of including the version while selecting you"
" can simply enter the type name until the first period\n\t"
"example: select HpBios.\n"
"Run without an argument to "
"display the currently selected type\n\texample: select",
"summary": "Selects the object type to be used.",
"aliases": ["sel"],
"auxcommands": ["LoginCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def selectfunction(self, line):
"""Main select worker function
:param line: command line input
:type line: string.
"""
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.selectvalidation(options)
if args:
if options.ref:
LOGGER.warning("Patches from current selection will be cleared.")
selector = args[0]
selections = self.rdmc.app.select(selector=selector, path_refresh=options.ref)
if "storage" in selector.lower():
storage_url = "/redfish/v1/Systems/1/Storage/"
list_storageid = self.rdmc.app.get_handler(storage_url, silent=True, service=True).dict["Members"]
for val in list_storageid:
storageid_fromurl = val["@odata.id"]
ctr = self.rdmc.app.get_handler(storageid_fromurl, silent=True, service=True).dict
if "Controllers" in ctr:
ctr_url = ctr["Controllers"]["@odata.id"]
self.rdmc.app.get_handler(ctr_url, silent=True, service=True).dict
if self.rdmc.opts.verbose and selections:
templist = list()
self.rdmc.ui.printer("Selected option(s): ")
for item in selections:
if item.type not in templist:
templist.append(item.type)
self.rdmc.ui.printer("%s\n" % ", ".join(map(str, templist)))
else:
selector = self.rdmc.app.selector
if selector:
sellist = [sel for sel in self.rdmc.app.monolith.typesadded if selector.lower() in sel.lower()]
self.rdmc.ui.printer("Current selection: ")
self.rdmc.ui.printer("%s\n" % ", ".join(map(str, sellist)))
else:
raise NothingSelectedError
self.cmdbase.logout_routine(self, options)
def selectvalidation(self, options):
"""Select data validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def run(self, line, help_disp=False):
"""Wrapper function for main select function
:param line: entered command line
:type line: list.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
self.selectfunction(line)
# Return code
return ReturnCodes.SUCCESS
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--refresh",
dest="ref",
action="store_true",
help="Optionally reload the data of selected type and clear " "patches from current selection.",
default=False,
)
@@ -0,0 +1,472 @@
###
# Copyright 2016-2023 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 -*-
"""Set Command for RDMC"""
import json
import os
import sys
import redfish.ris
from redfish.rest.connections import SecurityStateError
try:
from rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidOrNothingChangedSettingsError,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
ReturnCodes,
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
InvalidOrNothingChangedSettingsError,
)
from redfish.ris.rmc_helper import NothingSelectedError
class SetCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "set",
"usage": None,
"description": "Setting a "
"single level property example:\n\tset property=value\n\n\t"
"Setting multiple single level properties example:\n\tset "
"property=value property=value property=value\n\n\t"
"Setting a multi level property example:\n\tset property/"
"subproperty=value",
"summary": "Changes the value of a property within the" " currently selected type.",
"aliases": [],
"auxcommands": [
"CommitCommand",
"RebootCommand",
"RawPatchCommand",
"SelectCommand",
"LogoutCommand",
],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def setfunction(self, line, skipprint=False):
"""Main set worker function
:param line: command line input
:type line: string.
:param skipprint: boolean to determine output
:type skipprint: boolean.
"""
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if not self.rdmc.interactive and not self.rdmc.app.cache:
raise InvalidCommandLineError("The 'set' command is not useful in " "non-interactive and non-cache modes.")
self.setvalidation(options)
ilover = self.rdmc.app.getiloversion()
if ilover >= 7.000 and ("HighSecurity" in args[0] or "Production" in args[0]):
self.rdmc.ui.error(
"HighSecurity and Production modes are not supported on iLO 7 , Please use SecureStandard instead \n"
)
raise SecurityStateError(
"HighSecurity and Production modes are not supported on iLO 7 , Please use SecureStandard instead"
)
else:
if "SecureStandard" in args[0] and ilover < 7.000:
self.rdmc.ui.error(
"SecureStandard mode is not supported on iLO 6 and below , Please use HighSecurity instead \n"
)
raise SecurityStateError(
"SecureStandard mode is not supported on iLO 6 and below , Please use HighSecurity instead \n"
)
_ = args[0].find("HighSecurity")
if ilover >= 7.000:
_ = args[0].find("SecureStandard")
fsel = None
fval = None
if args:
if "ethernetinterface" in self.rdmc.app.selector.lower() and "ipv4addresses" in args[0].lower():
raise InvalidCommandLineError(
"For security reasons this request is discarded. "
"Use `rawpatch` or the `load --force_network_config` command "
"to modify the IP configuration of the ilO.\n"
)
if options.filter:
try:
(fsel, fval) = str(options.filter).strip("'\" ").split("=")
(fsel, fval) = (fsel.strip(), fval.strip())
except:
raise InvalidCommandLineError(
"Invalid filter" " parameter format [filter_attribute]=[filter_value]"
)
if any([s.lower().startswith("adminpassword=") for s in args]) and not any(
[s.lower().startswith("oldadminpassword=") for s in args]
):
raise InvalidCommandLineError(
"'OldAdminPassword' must also "
"be set with the current password \nwhen "
"changing 'AdminPassword' for security reasons."
)
count = 0
for arg in args:
if arg:
if len(arg) > 2:
if ('"' in arg[0] and '"' in arg[-1]) or ("'" in arg[0] and "'" in arg[-1]):
args[count] = arg[1:-1]
elif len(arg) == 2:
if (arg[0] == '"' and arg[1] == '"') or (arg[0] == "'" and arg[1] == "'"):
args[count] = None
count += 1
if not self.rdmc.app.selector:
raise NothingSelectedError
if "." not in self.rdmc.app.selector:
self.rdmc.app.selector = self.rdmc.app.selector + "."
if self.rdmc.app.selector:
if self.rdmc.app.selector.lower().startswith("bios."):
if "attributes" not in arg.lower():
arg = "Attributes/" + arg
try:
(sel, val) = arg.split("=")
sel = sel.strip().lower()
val = val.strip("\"'")
if val.lower() == "true" or val.lower() == "false":
val = val.lower() in ("yes", "true", "t", "1")
except:
raise InvalidCommandLineError("Invalid set parameter format. [Key]=[Value]")
newargs = list()
if "/" in sel and "/" not in str(val):
newargs = sel.split("/")
elif "/" in sel:
items = arg.split("=", 1)
newargs = items[0].split("/")
if not isinstance(val, bool):
if val:
if val[0] == "[" and val[-1] == "]":
val = val[1:-1].split(",")
payload = {newargs[-1]: val} if newargs else {sel: val}
if newargs:
for key in newargs[:-1][::-1]:
payload = {key: payload}
try:
contents = self.rdmc.app.loadset(
seldict=payload,
latestschema=options.latestschema,
fltrvals=(fsel, fval),
uniqueoverride=options.uniqueoverride,
)
if not contents:
if not sel.lower() == "oldadminpassword":
raise InvalidOrNothingChangedSettingsError(
"Nothing changed "
"for attribute '%s'.\nPlease check if the attribute is Oem/Hpe Attribute or Read-only "
"or System Unique property or the value trying to set is same or invalid" % sel
)
elif contents == "No entries found":
raise InvalidOrNothingChangedSettingsError(
"No " "entries found in the current " "selection for the setting '%s'." % sel
)
elif contents == "reverting":
self.rdmc.ui.error("Removing previous patch and returning to the " "original value.\n")
else:
for content in contents:
self.rdmc.ui.printer("Added the following patch:\n")
self.rdmc.ui.print_out_json(content)
except redfish.ris.ValidationError as excp:
errs = excp.get_errors()
for err in errs:
if err.sel and err.sel.lower() == "adminpassword":
types = self.rdmc.app.monolith.types
for item in types:
for instance in types[item]["Instances"]:
if "hpbios." in instance.maj_type.lower():
_ = [
instance.patches.remove(patch)
for patch in instance.patches
if patch.patch[0]["path"] == "/OldAdminPassword"
]
if isinstance(err, redfish.ris.RegistryValidationError):
self.rdmc.ui.printer(err.message)
raise redfish.ris.ValidationError(excp)
if options.commit:
self.auxcommands["commit"].commitfunction(options)
if options.reboot and not options.commit:
self.auxcommands["reboot"].run(options.reboot)
if options.logout:
self.auxcommands["logout"].run("")
else:
raise InvalidCommandLineError("Missing parameters for 'set' command.\n")
def patchfunction(self, line):
"""Main set worker function
:param line: command line input
:type line: string.
:param skipprint: boolean to determine output
:type skipprint: boolean.
"""
import platform
import tempfile
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
if not self.rdmc.interactive and not self.rdmc.app.cache:
raise InvalidCommandLineError("The 'set' command is not useful in " "non-interactive and non-cache modes.")
_ = self.rdmc.app.selector
self.setvalidation(options)
if args:
count = 0
for arg in args:
if arg:
if len(arg) > 2:
if ('"' in arg[0] and '"' in arg[-1]) or ("'" in arg[0] and "'" in arg[-1]):
args[count] = arg[1:-1]
elif len(arg) == 2:
if (arg[0] == '"' and arg[1] == '"') or (arg[0] == "'" and arg[1] == "'"):
args[count] = None
count += 1
if not self.rdmc.app.selector:
raise NothingSelectedError
if "." not in self.rdmc.app.selector:
self.rdmc.app.selector = self.rdmc.app.selector + "."
if self.rdmc.app.selector:
if self.rdmc.app.selector.lower().startswith("bios."):
if "attributes" not in arg.lower():
arg = "Attributes/" + arg
try:
(sel, val) = arg.split("=")
sel = sel.strip()
val = val.strip("\"'")
if val.lower() == "true" or val.lower() == "false":
val = val.lower() in ("yes", "true", "t", "1")
except:
raise InvalidCommandLineError("Invalid set parameter format. [Key]=[Value]")
newargs = list()
if "/" in sel and "/" not in str(val):
newargs = sel.split("/")
elif "/" in sel:
items = arg.split("=", 1)
newargs = items[0].split("/")
if not isinstance(val, bool):
if val:
if val[0] == "[" and val[-1] == "]":
val = val[1:-1].split(",")
if val.isdigit():
val = int(val)
patch_data = dict()
payload = {newargs[-1]: val} if newargs else {sel: val}
if "drive" in self.rdmc.app.selector.lower():
tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
temp_file = os.path.join(tempdir, "temp_patch.json")
out_file = open(temp_file, "w")
if getattr(options, "filter", None):
filt_arr = options.filter.split("=")
patchpath = filt_arr[-1]
else:
sys.stdout.write("Please provide the actual drive to modify using filter option\n")
return
patch_payload = {patchpath: payload}
json.dump(patch_payload, out_file, indent=6)
out_file.close()
sys.stdout.write("payload %s \n" % patch_payload)
self.auxcommands["rawpatch"].run(temp_file + " --service")
os.remove(temp_file)
elif newargs:
for key in newargs[:-1][::-1]:
if key == "PowerControl":
payload = {key: [payload]}
else:
payload = {key: payload}
patch_data.update(payload)
if "Oem" in key:
if (
"managernetworkprotocol." in self.rdmc.app.selector
or "thermal." in self.rdmc.app.selector
):
tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
temp_file = os.path.join(tempdir, "temp_patch.json")
out_file = open(temp_file, "w")
if "managernetworkprotocol." in self.rdmc.app.selector:
patchpath = self.rdmc.app.typepath.defs.managerpath + "NetworkProtocol/"
elif "thermal." in self.rdmc.app.selector:
patchpath = "/redfish/v1/Chassis/1/Thermal/"
patch_payload = {patchpath: payload}
json.dump(patch_payload, out_file, indent=6)
out_file.close()
sys.stdout.write("payload %s \n" % patch_payload)
self.auxcommands["rawpatch"].run(temp_file + " --service")
self.auxcommands["select"].run(self.rdmc.app.selector + " --refresh")
os.remove(temp_file)
if "PowerControl" in payload.keys():
import platform
import tempfile
tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
temp_file = os.path.join(tempdir, "temp_patch.json")
out_file = open(temp_file, "w")
patch_path = "/redfish/v1/Chassis/1/Power"
patch_payload = {patch_path: payload}
json.dump(patch_payload, out_file, indent=6)
out_file.close()
self.auxcommands["rawpatch"].run(temp_file + " --service")
os.remove(temp_file)
def run(self, line, skipprint=False, help_disp=False):
"""Main set function
:param line: command line input
:type line: string.
:param skipprint: boolean to determine output
:type skipprint: boolean.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
if (
("Oem/Hpe/EnhancedDownloadPerformanceEnabled" in line[0])
or ("Oem/Hpe/ThermalConfiguration" in line[0])
or ("Oem/Hpe/FanPercentMinimum" in line[0])
or ("PowerControl" in line[0])
or (self.rdmc.app.selector is not None and "drive" in self.rdmc.app.selector.lower())
):
self.patchfunction(line)
else:
try:
self.setfunction(line, skipprint=skipprint)
except SecurityStateError:
return ReturnCodes.V1_SECURITY_STATE_ERROR
# Return code
return ReturnCodes.SUCCESS
def setvalidation(self, options):
"""Set data validation function"""
if self.rdmc.opts.latestschema:
options.latestschema = True
if self.rdmc.config.commit.lower() == "true":
options.commit = True
try:
self.cmdbase.login_select_validation(self, options)
except redfish.ris.NothingSelectedError:
raise redfish.ris.NothingSelectedSetError("")
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--selector",
dest="selector",
help="Optionally include this flag to select a type to run"
" the current command on. Use this flag when you wish to"
" select a type without entering another command, or if you"
" wish to work with a type that is different from the one"
" you currently have selected.",
default=None,
)
customparser.add_argument(
"--filter",
dest="filter",
help="Optionally set a filter value for a filter attribute."
" This uses the provided filter for the currently selected"
" type. Note: Use this flag to narrow down your results. For"
" example, selecting a common type might return multiple"
" objects that are all of that type. If you want to modify"
" the properties of only one of those objects, use the filter"
" flag to narrow down results based on properties."
"\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
default=None,
)
customparser.add_argument(
"--commit",
dest="commit",
action="store_true",
help="Use this flag when you are ready to commit all pending"
" changes. Note that some changes made in this way will be updated"
" instantly, while others will be reflected the next time the"
" server is started.",
default=None,
)
customparser.add_argument(
"--reboot",
dest="reboot",
help="Use this flag to perform a reboot command function after"
" completion of operations. For help with parameters and"
" descriptions regarding the reboot flag, run help reboot.",
default=None,
)
customparser.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=None,
)
customparser.add_argument(
"--uniqueoverride",
dest="uniqueoverride",
action="store_true",
help="Override the measures stopping the tool from writing " "over items that are system unique.",
default=None,
)
@@ -0,0 +1,203 @@
###
# Copyright 2016-2021 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 -*-
"""Status Command for RDMC"""
from functools import reduce
from redfish.ris.utils import merge_dict
try:
from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
except ImportError:
from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
class StatusCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "status",
"usage": None,
"description": "Run to display all pending changes within"
" the currently\n\tselected type that need to be"
" committed\n\texample: status",
"summary": "Displays all pending changes within a selected type" " that need to be committed.",
"aliases": [],
"auxcommands": ["SelectCommand"],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def run(self, line, help_disp=False):
"""Main status worker function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
try:
(options, _) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.statusvalidation(options)
contents = self.rdmc.app.status()
selector = self.rdmc.app.selector
if contents and options.json:
self.jsonout(contents)
elif contents:
self.outputpatches(contents, selector)
else:
self.rdmc.ui.printer("No changes found\n")
# Return code
return ReturnCodes.SUCCESS
def jsonout(self, contents):
"""Helper function to print json output of patches
:param contents: contents for the selection
:type contents: string.
"""
self.rdmc.ui.printer("Current changes found:\n")
createdict = lambda y, x: {x: y}
totdict = {}
for item in contents:
for keypath, value in item.items():
path = keypath.split("(")[1].strip("()")
cont = {}
totdict[path] = cont
for content in value:
val = (
["List Manipulation"]
if content["op"] == "move"
else [content["value"].strip("\"'")] if len(content["value"]) else [""]
)
cont = reduce(
createdict,
reversed([path] + content["path"].strip("/").split("/") + val),
)
merge_dict(totdict, cont)
self.rdmc.ui.print_out_json(totdict)
def outputpatches(self, contents, selector):
"""Helper function for status for use in patches
:param contents: contents for the selection
:type contents: string.
:param selector: type selected
:type selector: string.
"""
self.rdmc.ui.printer("Current changes found:\n")
for item in contents:
moveoperation = ""
for key, value in item.items():
if selector and key.lower().startswith(selector.lower()):
self.rdmc.ui.printer("%s (Currently selected)\n" % key)
else:
self.rdmc.ui.printer("%s\n" % key)
for content in value:
try:
if content["op"] == "move":
moveoperation = "/".join(content["path"].split("/")[1:-1])
continue
except:
if content[0]["op"] == "move":
moveoperation = "/".join(content[0]["path"].split("/")[1:-1])
continue
try:
if isinstance(content[0]["value"], int):
self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], content[0]["value"]))
elif not isinstance(content[0]["value"], bool) and not len(content[0]["value"]) == 0:
if content[0]["value"][0] == '"' and content[0]["value"][-1] == '"':
self.rdmc.ui.printer(
"\t%s=%s"
% (
content[0]["path"][1:],
content[0]["value"][1:-1],
)
)
else:
self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], content[0]["value"]))
else:
output = content[0]["value"]
if not isinstance(output, bool):
if len(output) == 0:
output = '""'
self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], output))
except:
if isinstance(content["value"], int):
self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
elif (
content["value"]
and not isinstance(content["value"], bool)
and not len(content["value"]) == 0
):
if content["value"][0] == '"' and content["value"][-1] == '"':
self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
else:
self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
else:
output = content["value"]
if output and not isinstance(output, bool):
if len(output) == 0:
output = '""'
self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], output))
self.rdmc.ui.printer("\n")
if moveoperation:
self.rdmc.ui.printer("\t%s=List Manipulation\n" % moveoperation)
def statusvalidation(self, options):
"""Status method validation function"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"-j",
"--json",
dest="json",
action="store_true",
help="Optionally include this flag if you wish to change the"
" displayed output to JSON format. Preserving the JSON data"
" structure makes the information easier to parse.",
default=False,
)
@@ -0,0 +1,125 @@
###
# Copyright 2016-2021 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 -*-
"""Types Command for RDMC"""
try:
from rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
except ImportError:
from ilorest.rdmc_helper import (
InvalidCommandLineError,
InvalidCommandLineErrorOPTS,
ReturnCodes,
)
class TypesCommand:
"""Constructor"""
def __init__(self):
self.ident = {
"name": "types",
"usage": None,
"description": "Run to display currently " "available selectable types\n\tExample: types",
"summary": "Displays all selectable types within the currently logged in server.",
"aliases": [],
"auxcommands": [],
}
self.cmdbase = None
self.rdmc = None
self.auxcommands = dict()
def typesfunction(self, line, returntypes=False):
"""Main types worker function
:param line: command line input
:type line: string.
:param returntypes: flag to determine if types should be printed
:type returntypes: boolean.
"""
try:
(options, args) = self.rdmc.rdmc_parse_arglist(self, line)
except (InvalidCommandLineErrorOPTS, SystemExit):
if ("-h" in line) or ("--help" in line):
return ReturnCodes.SUCCESS
else:
raise InvalidCommandLineErrorOPTS("")
self.typesvalidation(options)
if not args:
typeslist = list()
typeslist = sorted(set(self.rdmc.app.types(options.fulltypes)))
if not returntypes:
self.rdmc.ui.printer("Type options:\n")
for item in typeslist:
self.rdmc.ui.printer("%s\n" % item)
else:
return typeslist
else:
raise InvalidCommandLineError("The 'types' command does not take any arguments.")
self.cmdbase.logout_routine(self, options)
def run(self, line, help_disp=False):
"""Wrapper function for types main function
:param line: command line input
:type line: string.
"""
if help_disp:
self.parser.print_help()
return ReturnCodes.SUCCESS
self.typesfunction(line)
# Return code
return ReturnCodes.SUCCESS
def typesvalidation(self, options):
"""types method validation function
:param options: command line options
:type options: list.
"""
self.cmdbase.login_select_validation(self, options)
def definearguments(self, customparser):
"""Wrapper function for new command main function
:param customparser: command line input
:type customparser: parser.
"""
if not customparser:
return
self.cmdbase.add_login_arguments_group(customparser)
customparser.add_argument(
"--fulltypes",
dest="fulltypes",
action="store_true",
help="Optionally include this flag if you would prefer to "
"return the full type name instead of the simplified versions"
" (Redfish only option).",
default=None,
)
Whitespace-only changes.