### # 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, )