python-ilorest-5.3.0.0a/0000775000175000017500000000000014702427156014710 5ustar carstencarstenpython-ilorest-5.3.0.0a/LICENSE0000664000175000017500000002642314702427156015724 0ustar carstencarstenApache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 Hewlett Packard Enterprise Development LP 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. python-ilorest-5.3.0.0a/test_build_drone.yml0000664000175000017500000001310014702427156020753 0ustar carstencarstenkind: pipeline name: Redfish_Library_Drone_Test steps: - name: Github_Analyzer image: python:3.7.2-alpine3.8 pull: true volumes: - name: drone_store path: /tmp/store - name: drone_slate path: /tmp/slate environment: HTTP_PROXY: http://web-proxy.houston.hpecorp.net:8080 GITHUB_USERNAME: from_secret: github_username GITHUB_PASSWORD: from_secret: github_password DRONE_STORE_PATH: /tmp/store/ SLATE_SHARE_PATH: /tmp/slate/ DOCS: 'library' commands: - easy_install distribute - apk add git - echo $(git --version) - pip install --upgrade pip - pip install -U sphinx - pip install sphinxcontrib.restbuilder jsonpatch jsonpath_rw - python /tmp/store/drone_pr_selector_data_gatherer.py - more tmp.json trigger: event: exclude: -push - name: RIS-library-Tester-2.7 image: python:2.7 pull: true volumes: - name: drone_store path: /tmp/store environment: HTTP_PROXY: http://web-proxy.houston.hpecorp.net:8080 PYTHON: 27 GITHUB_USERNAME: from_secret: github_username GITHUB_PASSWORD: from_secret: github_password DRONE_STORE_PATH: /tmp/store/ PYTYPE_CONFIG: tests/pytype/pytype.cfg REPO_OWNER: intelligent-provisioning REPO_NAME: python-redfish-library PYTYPE_DIR: redfish #LIBRARY: "http://github.hpe.com/intelligent-provisioning/python-redfish-library.git" RESULTS_URL: http://infinitymaster.us.rdlabs.hpecorp.net:1051/Drone_Git_Build_Data/ PYLINT: true UNIT_TEST: true commands: - easy_install distribute - echo "python_version = 2.7" >> tests/pytype/pytype.cfg - more tests/pytype/pytype.cfg - echo "Upgrading Pip" - pip install --upgrade pip - echo "Installing Python iLORest Library" - python setup.py sdist --formats=zip - pip install --upgrade dist/python-ilorest-library-*.zip - echo "Installing Test Libraries" - pip install --upgrade pip - pip install pylint pytest pytest-logging mock requests ntplib datetime pytz pytype inspect-it future six #- echo "Installing Library Requirements" #- pip install redfish #- pip install -r requirements.txt #- python /tmp/store/libs_parse_installer.py - echo "Running Analysis" - python /tmp/store/drone_analyzer.py - more tmp.json # --cov=./src/extensions/* # --html=./tests/ilorest_tests/_results/tests/index.html \ # --cov-report=html:./test/ilorest_tests/_results/py2/coverage \ # --junit-xml=./tests/ilorest_tests/_results/py2/test-results.xml \ # --cov-report=html:./tests/ilorest_tests/_results/py2/coverage.xml #- cat ./tests/pmemtests/_results/data/pylint-results.txt #- cat ./tests/pmemtests/_results/data/pylint-py3-results.txt #- echo $rc2 # fatal=1 | error=2 | usage error=32 #- exit $(( $rc & 35 )) - name: RIS-library-Tester-3.5 image: python:3.5 pull: true volumes: - name: drone_store path: /tmp/store environment: HTTP_PROXY: http://web-proxy.houston.hpecorp.net:8080 PYTHON: 35 GITHUB_USERNAME: from_secret: github_username GITHUB_PASSWORD: from_secret: github_password RESTLAX_FILE_PATH: /tmp/store/Restlax_web.txt DRONE_STORE_PATH: /tmp/store/ PYTYPE_CONFIG: tests/pytype/pytype.cfg REPO_OWNER: intelligent-provisioning REPO_NAME: python-redfish-library PYTYPE_DIR: redfish #LIBRARY: "http://github.hpe.com/intelligent-provisioning/python-redfish-library.git" RESULTS_URL: http://infinitymaster.us.rdlabs.hpecorp.net:1051/Drone_Git_Build_Data/ PYLINT: true UNIT_TEST: true commands: - echo "python_version = 3.5" >> tests/pytype/pytype.cfg - more tests/pytype/pytype.cfg - echo "Upgrading Pip" - pip install --upgrade pip - echo "Installing Python iLORest Library" #easier to reference packages by installing the library - python setup.py sdist --formats=zip - pip install --upgrade dist/python-ilorest-library-*.zip - echo "Installing Additional Libraries" - pip install --upgrade pip - pip install pylint pytest mock requests ntplib datetime pytz pytype redfish inspect-it six #- echo "Installing Library Requirements" #- pip install redfish #- pip install -r requirements.txt #- python /tmp/store/libs_parse_installer.py - echo "Running Analysis" - python /tmp/store/drone_analyzer.py - more tmp.json #- pytest tests/unit/ > pytype_results.txt #- more pytype_results.txt #- name: notify-email # hub.docker.hpecorp.net/ilorest/email_notify:latest # image: drillster/drone-email # settings: # host: smtp3.hpe.com # skip_verify: true # port: 25 # from: grant.oconnor@hpe.com # recipients: [grant.oconnor@hpe.com] # #- ILORest-Notify@groups.int.hpe.com - name: Comment_Issuer image: python:3.7.2-alpine3.8 pull: true volumes: - name: drone_store path: /tmp/store - name: drone_slate path: /tmp/slate environment: HTTP_PROXY: http://web-proxy.houston.hpecorp.net:8080 GITHUB_USERNAME: from_secret: github_username GITHUB_PASSWORD: from_secret: github_password DRONE_STORE_PATH: /tmp/store/ SLATE_SHARE_PATH: /tmp/slate/ RESTLAX_FILE_PATH: /tmp/store/Restlax_web.txt RESULTS_URL: http://infinitymaster.us.rdlabs.hpecorp.net:1051/Drone_Git_Build_Data/ DOCS: 'library' commands: - easy_install distribute - echo "Upgrading Pip" - pip install --upgrade pip - pip install requests - more tmp.json - python /tmp/store/github_comment_issuer.py trigger: event: exclude: -push volumes: - name: drone_store host: path: /opt/docker_drone_share/ - name: drone_slate host: path: /opt/docker_slate_share/ trigger: event: - pull_requestpython-ilorest-5.3.0.0a/src/0000775000175000017500000000000014702427156015477 5ustar carstencarstenpython-ilorest-5.3.0.0a/src/redfish/0000775000175000017500000000000014702427156017123 5ustar carstencarstenpython-ilorest-5.3.0.0a/src/redfish/__init__.py0000664000175000017500000000102614702427156021233 0ustar carstencarsten""" Redfish restful library """ __all__ = ["rest", "ris", "hpilo"] __version__ = "5.3.0.0" import logging from redfish.rest.v1 import AuthMethod, LegacyRestClient, RedfishClient def redfish_logger(file_name, log_format, log_level=logging.ERROR): """redfish logger""" formatter = logging.Formatter(log_format) fhdl = logging.FileHandler(file_name) fhdl.setFormatter(formatter) logger = logging.getLogger(__name__) logger.addHandler(fhdl) logger.setLevel(log_level) return logger python-ilorest-5.3.0.0a/src/redfish/hpilo/0000775000175000017500000000000014702427156020236 5ustar carstencarstenpython-ilorest-5.3.0.0a/src/redfish/hpilo/__init__.py0000664000175000017500000000010014702427156022336 0ustar carstencarsten""" Utilities to simplify communicating with iLO locally """ python-ilorest-5.3.0.0a/src/redfish/hpilo/rishpilo.py0000664000175000017500000001523514702427156022447 0ustar carstencarsten### # Copyright 2020 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 -*- """Base implementation for interaction with the iLO interface""" # ---------Imports--------- import logging import os import struct import time from ctypes import byref, c_uint32, c_char_p, c_void_p, create_string_buffer # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class BlobReturnCodes(object): """Blob store return codes. SUCCESS success """ SUCCESS = 0 if os.name != "nt": CHIFERR_NoDriver = 19 CHIFERR_AccessDenied = 13 else: CHIFERR_NoDriver = 2 CHIFERR_AccessDenied = 5 class HpIloInitialError(Exception): """Raised when error during initialization of iLO Chif channel""" pass class HpIloNoChifDriverError(Exception): """Raised when error during initialization of iLO Chif channel""" pass class HpIloChifAccessDeniedError(Exception): """Raised when error during initialization of iLO Chif channel""" pass class HpIloPrepareAndCreateChannelError(Exception): """Raised when error during initialization of iLO Chif channel""" pass class HpIloChifPacketExchangeError(Exception): """Raised when errors encountered when exchanging chif packet""" pass class HpIloReadError(Exception): """Raised when errors encountered when reading from iLO""" pass class HpIloWriteError(Exception): """Raised when errors encountered when writing to iLO""" pass class HpIloSendReceiveError(Exception): """Raised when errors encountered when reading form iLO after sending""" pass class HpIloNoDriverError(Exception): """Raised when errors encountered when there is no ilo driver""" pass class HpIlo(object): """Base class of interaction with iLO""" def __init__(self, dll=None, log_dir=None): fhandle = c_void_p() self.dll = dll self.log_dir = log_dir if LOGGER.isEnabledFor(logging.DEBUG): self.dll.enabledebugoutput.argtypes = [c_char_p] if log_dir is not None: logdir_c = create_string_buffer(log_dir.encode('UTF-8')) self.dll.enabledebugoutput(logdir_c) self.dll.ChifInitialize(None) self.dll.ChifCreate.argtypes = [c_void_p] self.dll.ChifCreate.restype = c_uint32 try: LOGGER.debug("Calling ChifCreate...") status = self.dll.ChifCreate(byref(fhandle)) if status != BlobReturnCodes.SUCCESS: raise HpIloInitialError("Error %s occurred while trying " "to create a channel." % status) self.fhandle = fhandle if "skip_ping" not in os.environ: status = self.dll.ChifPing(self.fhandle) if status != BlobReturnCodes.SUCCESS: errmsg = "Error {0} occurred while trying to open a " "channel to iLO".format(status) if status == BlobReturnCodes.CHIFERR_NoDriver: errmsg = "No devices were found." if os.name != "nt": errmsg = "{0} Ensure the hpilo kernel module is loaded.".format(errmsg) elif status == BlobReturnCodes.CHIFERR_AccessDenied: errmsg = "You must be root/Administrator to use this program." raise HpIloInitialError(errmsg) # LOGGER.debug("Calling ChifSetRecvTimeout...") self.dll.ChifSetRecvTimeout(self.fhandle, 60000) except: raise def chif_packet_exchange(self, data): """Function for handling chif packet exchange :param data: data to be sent for packet exchange :type data: str """ datarecv = self.dll.get_max_buffer_size() buff = create_string_buffer(bytes(data)) recbuff = create_string_buffer(datarecv) # LOGGER.debug("Calling ChifPacketExchange...") error = self.dll.ChifPacketExchange(self.fhandle, byref(buff), byref(recbuff), datarecv) if error != BlobReturnCodes.SUCCESS: raise HpIloChifPacketExchangeError("Error %s occurred while " "exchange chif packet" % error) pkt = bytearray() if datarecv is None: bytearray(recbuff[:]) else: pkt = bytearray(recbuff[:datarecv]) return pkt def send_receive_raw(self, data, retries=10): """Function implementing proper send receive retry protocol :param data: data to be sent for packet exchange :type data: str :param retries: number of retries for reading data from iLO :type retries: int """ tries = 0 sequence = struct.unpack(" 0: dll.initiate_credentials.argtypes = [c_char_p, c_char_p] dll.initiate_credentials.restype = POINTER(c_ubyte) usernew = create_string_buffer(username.encode("utf-8")) passnew = create_string_buffer(password.encode("utf-8")) dll.initiate_credentials(usernew, passnew) credreturn = dll.ChifVerifyCredentials() if not credreturn == BlobReturnCodes.SUCCESS: if credreturn == hpiloreturncodes.CHIFERR_AccessDenied: raise Blob2SecurityError() else: raise HpIloInitialError( "Error %s occurred while trying to open a channel to iLO" % credreturn ) else: dll.ChifDisableSecurity() else: # if high security return False # LOGGER.debug("Calling ChifIsSecurityRequired...") if dll.ChifIsSecurityRequired() > 0: return False else: # LOGGER.debug("Calling ChifDisableSecurity...") dll.ChifDisableSecurity() BlobStore2.unloadchifhandle(dll) return True @staticmethod def checkincurrdirectory(libname): """Check if the library is present in current directory. :param libname: The name of the library to search for. :type libname: str.""" libpath = libname if os.path.isfile(os.path.join(os.path.split(sys.executable)[0], libpath)): libpath = os.path.join(os.path.split(sys.executable)[0], libpath) elif os.path.isfile(os.path.join(os.getcwd(), libpath)): libpath = os.path.join(os.getcwd(), libpath) elif os.environ.get("LD_LIBRARY_PATH"): paths = os.getenv("LD_LIBRARY_PATH", libpath).split(";") libpath = [os.path.join(pat, libname) for pat in paths if os.path.isfile(os.path.join(pat, libname))] libpath = libpath[0] if libpath else libname return libpath @staticmethod def unloadchifhandle(lib): """Release a handle on the chif iLOrest library :param lib: The library handle provided by loading the chif library. :type lib: library handle. """ try: libhandle = lib._handle if os.name == "nt": windll.kernel32.FreeLibrary(None, handle=libhandle) else: dlclose(libhandle) except Exception: pass python-ilorest-5.3.0.0a/src/redfish/rest/0000775000175000017500000000000014702427156020100 5ustar carstencarstenpython-ilorest-5.3.0.0a/src/redfish/rest/__init__.py0000664000175000017500000000015114702427156022206 0ustar carstencarsten""" Base interface to simplify interaction with LegacyRest/Redfish data and Remote/Local connections.""" python-ilorest-5.3.0.0a/src/redfish/rest/connections.py0000664000175000017500000005467114702427156023011 0ustar carstencarsten### # Copyright 2020 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 -*- """All Connections for interacting with REST.""" import gzip import json import logging import time import urllib3 from urllib3 import PoolManager, ProxyManager from urllib3.exceptions import DecodeError, MaxRetryError try: urllib3.disable_warnings() from urllib3.contrib.socks import SOCKSProxyManager except ImportError: pass import six from six import BytesIO from six.moves.urllib.parse import urlencode, urlparse from redfish.hpilo.risblobstore2 import ( Blob2OverrideError, Blob2SecurityError, BlobStore2, ) from redfish.hpilo.rishpilo import HpIloChifPacketExchangeError from redfish.rest.containers import RestRequest, RestResponse, RisRestResponse # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class RetriesExhaustedError(Exception): """Raised when retry attempts have been exhausted.""" pass class VnicNotEnabledError(Exception): """Raised when retry attempts have been exhausted when VNIC is not enabled.""" pass class DecompressResponseError(Exception): """Raised when decompressing the response failed.""" pass class InvalidCredentialsError(Exception): """Raised when invalid credentials have been provided.""" pass class InvalidCertificateError(Exception): """Raised when a invalid certificate has been provided.""" pass class ChifDriverMissingOrNotFound(Exception): """Raised when CHIF driver is missing or not found.""" pass class SecurityStateError(Exception): """Raised when there is a strict security state without authentication.""" pass class OneTimePasscodeError(Exception): """Raised when OTP is sent to the registered email.""" pass class TokenExpiredError(Exception): """Raised when OTP entered has expired.""" pass class UnauthorizedLoginAttemptError(Exception): """Raised when Login is Unauthorized""" pass class HttpConnection(object): """HTTP connection capable of authenticating with HTTPS and Http/Socks Proxies :param base_url: The URL to make HTTP calls against :type base_url: str :param \\**client_kwargs: Arguments to pass to the connection initialization. These are" "passed to a urllib3 `PoolManager `_. All arguments that can be passed to " "a PoolManager are valid arguments." """ def __init__(self, base_url, cert_data, **client_kwargs): self._conn = None self.base_url = base_url # Default values for connection properties self._connection_properties = { 'timeout': urllib3.util.Timeout(connect=4800, read=4800), 'retries': urllib3.util.Retry(connect=50, read=50, redirect=50), } self._connection_properties.update(client_kwargs) if cert_data: if ("cert_file" in cert_data and cert_data["cert_file"]) or ( "ca_certs" in cert_data and cert_data["ca_certs"] ): self._connection_properties.update({"ca_cert_data": cert_data}) self._proxy = self._connection_properties.pop("proxy", None) self.session_key = self._connection_properties.pop("session_key", None) self.log_dir = self._connection_properties.pop("log_dir", None) self._init_connection() @property def proxy(self): """The proxy, if any.""" return self._proxy @proxy.setter def proxy(self, proxy): """set the proxy""" self._proxy = proxy def _init_connection(self): """Function for initiating connection with remote server""" cert_reqs = "CERT_NONE" if self._connection_properties.get("ca_cert_data"): LOGGER.info("Using CA cert to confirm identity.") cert_reqs = "CERT_NONE" self._connection_properties.update(self._connection_properties.pop("ca_cert_data")) if self.proxy: if self.proxy.startswith("socks"): LOGGER.info("Initializing a SOCKS proxy.") http = SOCKSProxyManager( self.proxy, cert_reqs=cert_reqs, maxsize=50, **self._connection_properties ) else: LOGGER.info("Initializing a HTTP proxy.") http = ProxyManager( self.proxy, cert_reqs=cert_reqs, maxsize=50, **self._connection_properties ) else: LOGGER.info("Initializing no proxy.") try: self._connection_properties.pop("ca_cert_data") except KeyError: pass http = PoolManager( cert_reqs=cert_reqs, maxsize=50, **self._connection_properties ) self._conn = http.request def rest_request(self, path, method="GET", args=None, body=None, headers=None): """Format and do HTTP Rest request :param path: The URI path to perform the operation on. :type path: str :param method: method to perform on the path. :type method: str :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param body: body payload to include in the request if needed. :type body: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ # TODO: Need to remove redfish.dmtf.org calls from here, add to their own HttpConnection files = None request_args = {} if isinstance(path, bytes): path = path.decode("utf-8") external_uri = True if "redfish.dmtf.org" in path else False else: external_uri = True if "redfish.dmtf.org" in path else False headers = {} if external_uri else headers reqpath = path.replace("//", "/") if not external_uri else path if body is not None: if body and isinstance(body, list) and isinstance(body[0], tuple): files = body body = None elif isinstance(body, (dict, list)): headers["Content-Type"] = "application/json" body = json.dumps(body) elif not files: headers["Content-Type"] = "application/octet-stream" if method == "PUT": resp = self.rest_request(method="HEAD", path=path, headers=headers) try: if resp.getheader("content-encoding") == "gzip": buf = BytesIO() gfile = gzip.GzipFile(mode="wb", fileobj=buf) try: gfile.write(str(body).encode("utf-8") if six.PY3 else str(body)) finally: gfile.close() compresseddata = buf.getvalue() if compresseddata: data = bytearray() data.extend(memoryview(compresseddata)) body = data except BaseException as excp: LOGGER.error("Error occur while compressing body: %s", excp) raise if args: if method == "GET": reqpath += "?" + urlencode(args) elif method == "PUT" or method == "POST" or method == "PATCH": headers["Content-Type"] = "application/x-www-form-urlencoded" body = urlencode(args) # TODO: ADD to the default headers? if headers is not None: headers["Accept-Encoding"] = "gzip" restreq = RestRequest(path, method, data=files if files else body, url=self.base_url) if LOGGER.isEnabledFor(logging.DEBUG): try: logbody = None if restreq.body: if restreq.body[0] == "{": logbody = restreq.body else: raise KeyError() if restreq.method in ["POST", "PATCH"]: debugjson = json.loads(restreq.body) if "Password" in debugjson.keys(): debugjson["Password"] = "******" if "OldPassword" in debugjson.keys(): debugjson["OldPassword"] = "******" if "NewPassword" in debugjson.keys(): debugjson["NewPassword"] = "******" logbody = json.dumps(debugjson) logbody = logbody.replace("\\\\", "\\") LOGGER.debug( "HTTP REQUEST: %s\n\tPATH: %s\n\t" "HEADERS: %s\n\tBODY: %s", restreq.method, restreq.path, headers, logbody, ) except: LOGGER.debug( "HTTP REQUEST: %s\n\tPATH: %s\n\tBODY: %s", restreq.method, restreq.path, "binary body", ) inittime = time.time() reqfullpath = self.base_url + reqpath if not external_uri else reqpath # To ensure we don't have unicode/string merging issues in httplib of Python 2 if isinstance(reqfullpath, six.text_type): reqfullpath = str(reqfullpath) if headers: request_args["headers"] = headers if files: request_args["fields"] = files else: request_args["body"] = body try: resp = self._conn(method, reqfullpath, **request_args) except MaxRetryError: vnic_url = "16.1.15.1" if reqfullpath.find(vnic_url) != -1: raise VnicNotEnabledError() raise RetriesExhaustedError() except DecodeError: raise DecompressResponseError() endtime = time.time() LOGGER.info("Response Time to %s: %s seconds.", restreq.path, str(endtime - inittime)) restresp = RestResponse(restreq, resp) # if restresp.request.body: # respbody = restresp.read # respbody = respbody.replace("\\\\", "\\") if LOGGER.isEnabledFor(logging.DEBUG): headerstr = "" if restresp is not None: respheader = restresp.getheaders() for kiy, headerval in respheader.items(): headerstr += "\t" + kiy + ": " + headerval + "\n" try: LOGGER.debug( "HTTP RESPONSE for %s:\nCode:%s\nHeaders:" "\n%s\nBody Response of %s: %s", restresp.request.path, str(restresp._http_response.status) + " " + restresp._http_response.reason, headerstr, restresp.request.path, restresp.read, ) except: LOGGER.debug("HTTP RESPONSE:\nCode:%s", restresp) else: LOGGER.debug("HTTP RESPONSE: No HTTP Response obtained") return restresp def cert_login(self): """Login using a certificate.""" resp = self.rest_request("/html/login_cert.html", "GET") if resp.status == 200 or resp.status == 201: token = resp.getheader("X-Auth-Token") location = resp.getheader("Location") else: raise InvalidCertificateError("") return token, location class Blobstore2Connection(object): """A connection for communicating locally with HPE servers :param \\**conn_kwargs: Arguments to pass to the connection initialization. Possible arguments for *\\**conn_kwargs* include: :username: The username to login with :password: The password to login with """ _http_vsn_str = "HTTP/1.1" blobstore_headers = {"Accept": "*/*", "Connection": "Keep-Alive"} def __del__(self): """Clear channel""" self._conn = None def __init__(self, **conn_kwargs): self._conn = None self.base_url = "blobstore://." self._connection_properties = conn_kwargs self.session_key = self._connection_properties.pop("sessionid", None) self._init_connection(**self._connection_properties) def _init_connection(self, **kwargs): """Initiate blobstore connection""" # mixed security modes require a password at all times username = kwargs.pop("username", "nousername") if isinstance(username, bytes): username = username.decode("utf-8") password = kwargs.pop("password", "nopassword") if isinstance(password, bytes): password = password.decode("utf-8") log_dir = kwargs.pop("log_dir", "") try: correctcreds = BlobStore2.initializecreds(username=username, password=password, log_dir=log_dir) bs2 = BlobStore2() if not correctcreds: security_state = int(bs2.get_security_state()) if security_state == 3: raise InvalidCredentialsError(0) else: raise SecurityStateError(security_state) except Blob2SecurityError: raise InvalidCredentialsError(0) except HpIloChifPacketExchangeError as excp: LOGGER.info("Exception: %s", str(excp)) raise ChifDriverMissingOrNotFound() except Exception as excp: if str(excp) == "chif": raise ChifDriverMissingOrNotFound() else: raise else: self._conn = bs2 def rest_request(self, path="", method="GET", args=None, body=None, headers=None): """Rest request for blobstore client :param path: The URI path to perform the operation on. :type path: str :param method: method to perform on the path. :type method: str :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param body: body payload to include in the request if needed. :type body: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ # default headers if not passed in - otherwise will throw on .update call if headers is None: headers = {} else: headers.update(Blobstore2Connection.blobstore_headers) if isinstance(path, bytes): path = path.decode("utf-8") reqpath = path.replace("//", "/") oribody = body if body is not None: if isinstance(body, (dict, list)): headers["Content-Type"] = "application/json" if isinstance(body, bytes): body = body.decode("utf-8") body = json.dumps(body) elif isinstance(body, bytes): headers["Content-Type"] = "application/octet-stream" body = bytearray(body) else: headers["Content-Type"] = "application/x-www-form-urlencoded" body = urlencode(body) if method == "PUT": resp = self.rest_request(path=path, headers=headers) try: if resp.getheader("content-encoding") == "gzip": buf = BytesIO() gfile = gzip.GzipFile(mode="wb", fileobj=buf) try: gfile.write(str(body).encode("utf-8") if six.PY3 else str(body)) finally: gfile.close() compresseddata = buf.getvalue() if compresseddata: data = bytearray() data.extend(memoryview(compresseddata)) body = data except BaseException as excp: LOGGER.error("Error occur while compressing body: %s", excp) raise headers["Content-Length"] = len(body) if args: if method == "GET": reqpath += "?" + urlencode(args) elif method == "PUT" or method == "POST" or method == "PATCH": headers["Content-Type"] = "application/x-www-form-urlencoded" body = urlencode(args) str1 = "{} {} {}\r\n".format(method, reqpath, Blobstore2Connection._http_vsn_str) str1 += "Host: \r\n" str1 += "Accept-Encoding: gzip\r\n" for header, value in headers.items(): str1 += "{}: {}\r\n".format(header, value) str1 += "\r\n" if body and len(body) > 0: if isinstance(body, bytearray): str1 = bytearray(str1.encode("utf-8")) + body else: #if isinstance(body, bytes): # body = body.decode("utf-8") str1 += body if not isinstance(str1, bytearray): str1 = bytearray(str1.encode("utf-8")) if LOGGER.isEnabledFor(logging.DEBUG): try: logbody = None if body: if body[0] == "{": logbody = body else: raise if method in ["POST", "PATCH"]: debugjson = json.loads(body) if "Password" in debugjson.keys(): debugjson["Password"] = "******" if "OldPassword" in debugjson.keys(): debugjson["OldPassword"] = "******" if "NewPassword" in debugjson.keys(): debugjson["NewPassword"] = "******" logbody = json.dumps(debugjson) LOGGER.debug( "Blobstore REQUEST: %s\n\tPATH: %s\n\tHEADERS: " "%s\n\tBODY: %s", method, str(headers), path, logbody, ) except: LOGGER.debug( "Blobstore REQUEST: %s\n\tPATH: %s\n\tHEADERS: " "%s\n\tBODY: %s", method, str(headers), path, "binary body", ) inittime = time.time() for idx in range(5): try: resp_txt = self._conn.rest_immediate(str1) break except Blob2OverrideError: if idx == 4: raise Blob2OverrideError(2) else: continue endtime = time.time() LOGGER.info("iLO Response Time to %s: %s secs.", path, str(endtime - inittime)) if resp_txt is not None: # Dummy response to support a bad host response if len(resp_txt) == 0: resp_txt = ( "HTTP/1.1 500 Not Found\r\nAllow: " "GET\r\nCache-Control: no-cache\r\nContent-length: " "0\r\nContent-type: text/html\r\nDate: Tues, 1 Apr 2025 " "00:00:01 GMT\r\nServer: " "HP-iLO-Server/1.30\r\nX_HP-CHRP-Service-Version: 1.0.3\r\n\r\n\r\n" ) restreq = RestRequest(path, method, data=body, url=self.base_url) rest_response = RisRestResponse(restreq, resp_txt) if rest_response.status in range(300, 399) and rest_response.status != 304: newloc = rest_response.getheader("location") newurl = urlparse(newloc) rest_response = self.rest_request(newurl.path, method, args, oribody, headers) try: if rest_response.getheader("content-encoding") == "gzip": if hasattr(gzip, "decompress"): rest_response.read = gzip.decompress(rest_response.ori) else: compressedfile = BytesIO(rest_response.ori) decompressedfile = gzip.GzipFile(fileobj=compressedfile) rest_response.read = decompressedfile.read() except Exception: pass if LOGGER.isEnabledFor(logging.DEBUG): headerstr = "" headerget = rest_response.getheaders() for header in headerget: headerstr += "\t" + header + ": " + headerget[header] + "\n" try: LOGGER.debug( "Blobstore RESPONSE for %s:\nCode: %s\nHeaders:" "\n%s\nBody of %s: %s", rest_response.request.path, str(rest_response._http_response.status) + " " + rest_response._http_response.reason, headerstr, rest_response.request.path, rest_response.read, ) except: LOGGER.debug( "Blobstore RESPONSE for %s:\nCode:%s", rest_response.request.path, rest_response, ) return rest_response def cert_login(self): """Login using a certificate.""" # local cert login is only available on iLO 5 token = self.cert_login() resp = self.rest_request("/redfish/v1/SessionService/Sessions/", "GET") if resp.status == 200: try: location = resp.obj.Oem.Hpe.Links.MySession["@odata.id"] except KeyError: raise InvalidCertificateError("") else: raise InvalidCertificateError("") return token, location python-ilorest-5.3.0.0a/src/redfish/rest/v1.py0000664000175000017500000005677114702427156021020 0ustar carstencarsten### # Copyright 2020 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 -*- """Direct module for working with Redfish/REST technology.""" # ---------Imports--------- import base64 import hashlib import logging import sys from six.moves.urllib.parse import urlparse from redfish.rest.connections import ( Blobstore2Connection, HttpConnection, InvalidCredentialsError, OneTimePasscodeError, UnauthorizedLoginAttemptError, TokenExpiredError, ) # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class ServerDownOrUnreachableError(Exception): """Raised when server is unreachable.""" pass class JsonDecodingError(Exception): """Raised when there is an error in json data.""" pass class AuthMethod(object): """AUTH Method class **BASIC**, **SESSION**, **CERTIFICATE** variables translate to their string counterparts `basic`, `session`, `certificate`.""" BASIC = "basic" SESSION = "session" CERTIFICATE = "certificate" class RestClientBase(object): """Base REST client. Each RestClientBase has a connection object built by parsing the client_kwargs. This connection is used for communicating remotely or locally. :param biospassword: The iLO Gen9 bios password. See :func:`bios_password` :type biospassword: str :param \\**client_kwargs: Arguments to pass to the client argument. For possible values see :mod:`redfish.rest.connections.Blobstore2Connection` for a local connection or :mod:`redfish.rest.connections.HttpConnection` for remote connection.""" def __init__(self, biospassword=None, **client_kwargs): self.connection = None self._biospassword = biospassword self._build_connection(**client_kwargs) @property def bios_password(self): """Property for the biospassword. Only required on Gen9 iLO 4 when RBSU bios password is set and modifying bios settings """ return self._biospassword @bios_password.setter def bios_password(self, bios_pw): """set the bios password""" self._biospassword = bios_pw def _build_connection(self, **conn_kwargs): """Build the appropriate connection for the client""" base_url = conn_kwargs.pop("base_url", None) if not base_url or base_url.startswith("blobstore://"): self.connection = Blobstore2Connection(**conn_kwargs) elif not base_url.startswith("https://"): base_url = "https://" + base_url _ = conn_kwargs.pop("username", None) _ = conn_kwargs.pop("password", None) _ = conn_kwargs.pop("sessionid", None) _ = conn_kwargs.pop("login_otp", None) self.connection = HttpConnection(base_url, self._cert_data, **conn_kwargs) else: _ = conn_kwargs.pop("username", None) _ = conn_kwargs.pop("password", None) _ = conn_kwargs.pop("sessionid", None) _ = conn_kwargs.pop("login_otp", None) self.connection = HttpConnection(base_url, self._cert_data, **conn_kwargs) def _get_req_headers(self, headers=None): """Base _get_req_headers function""" return headers if headers else {} def get(self, path, args=None, headers=None): """Perform a GET request :param path: The URI path. :type path: str :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ try: return self.connection.rest_request( path, method="GET", args=args, headers=self._get_req_headers(headers=headers), ) except ValueError: LOGGER.debug("Error in json object getting path: %s", path) raise JsonDecodingError("Error in json decoding.") def patch(self, path, body, args=None, headers=None): """Perform a PATCH request :param path: The URI path. :type path: str :param body: The body to pass with the request. :type body: dict :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ return self.connection.rest_request( path, body=body, method="PATCH", args=args, headers=self._get_req_headers(headers=headers), ) def post(self, path, body, args=None, headers=None): """Perform a POST request :param path: The URI path. :type path: str :param body: The body to pass with the request. :type body: dict :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ return self.connection.rest_request( path, body=body, method="POST", args=args, headers=self._get_req_headers(headers=headers), ) def put(self, path, body, args=None, headers=None): """Perform a PUT request :param path: The URI path. :type path: str :param body: The body to pass with the request. :type body: dict :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ return self.connection.rest_request( path, body=body, method="PUT", args=args, headers=self._get_req_headers(headers=headers), ) def head(self, path, headers=None): """Perform a HEAD request :param path: The URI path. :type path: str :param headers: Any extra headers to add to the request. :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ return self.connection.rest_request(path, method="HEAD", headers=self._get_req_headers(headers=headers)) def delete(self, path, headers=None): """Perform a DELETE request :param path: The URI path. :type path: str :param args: Any query to add to the URI. (Can also be directly added to the URI) :type args: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ return self.connection.rest_request(path, method="DELETE", headers=self._get_req_headers(headers=headers)) class RestClient(RestClientBase): """REST client with Redfish and LegacyRest support built on top. :param default_prefix: The root of the REST API, either /redfish/v1/ or /rest/v1. :type default_prefix: str :param is_redfish: Flag to force redfish conformance, even on LegacyRest clients. :type is_redfish: bool :param username: The username of the account to login with. :type username: str :param password: The password of the account to login with. :type password: str :param auth: The authentication type to force. :type auth: str or :class:`AuthMethod` class variable. :param ca_cert_data: Dictionary containing the certificate authority data (user CA, \ user root CA, user root CA key :type ca_cert_data: dict :param \\**client_kwargs: Arguments to create a :class:`RestClientBase` instance. """ def __init__( self, default_prefix="/redfish/v1/", is_redfish=True, username=None, password=None, sessionid=None, base_url=None, auth=None, ca_cert_data=None, login_otp=None, **client_kwargs ): """Create a Rest Client object""" self.default_prefix = default_prefix self.is_redfish = is_redfish self.root = None self.auth_type = self._get_auth_type(auth, ca_cert_data=ca_cert_data, **client_kwargs) self._auth_key = None self._user_pass = (username, password) self._session_location = None self._cert_data = ca_cert_data self.login_otp = login_otp super(RestClient, self).__init__( username=username, password=password, sessionid=sessionid, base_url=base_url, login_otp=login_otp, **client_kwargs ) def __enter__(self): """Create a connection and return the session object""" self.login() return self def __exit__(self, exc_type, exc_val, exc_tb): """Close the connection""" self.logout() def _get_auth_type(self, auth_param, ca_cert_data=None, **client_kwargs): """Get the auth type based on key args or positional argument. Defaults to session auth.""" if not auth_param: # _ca_cert_data = client_kwargs.get('ca_cert_data') if ca_cert_data: if ("ca_certs" in ca_cert_data and ca_cert_data["ca_certs"]) or ( "cert_file" in ca_cert_data and ca_cert_data["cert_file"] ): if (ca_cert_data.get("cert_file") and ca_cert_data.get("key_file")) or ca_cert_data.get("ca_certs"): return AuthMethod.CERTIFICATE return AuthMethod.SESSION return auth_param @property def base_url(self): """The connection's URL to make calls against""" return self.connection.base_url @property def proxy(self): """The connection's proxy, if any.""" try: proxy = self.connection.proxy except AttributeError: proxy = None return proxy @property def session_key(self): """The Client's session key, if any.""" return self._auth_key if self.auth_type in [AuthMethod.SESSION, AuthMethod.CERTIFICATE] else None @session_key.setter def session_key(self, ses_key): """Set _auth_key to a session key""" self._auth_key = ses_key @property def basic_auth(self): """The Client's basic auth header, if any.""" return self._auth_key if self.auth_type == AuthMethod.BASIC else None @basic_auth.setter def basic_auth(self, bas_key): """Set _auth_key to a basic auth header""" self._auth_key = bas_key @property def session_location(self): """The session URI. Used for deleting the session when we logout.""" session_loc = None if self._session_location: if self.base_url == "blobstore://.": session_loc = self._session_location.replace("https://", "") session_loc = session_loc.replace(" ", "%20") else: parse_object = urlparse(self.base_url) if parse_object.hostname is not None: newurl = "https://" + parse_object.hostname session_loc = self._session_location.replace(newurl, "") else: session_loc = self._session_location.replace(self.base_url, "") return session_loc @session_location.setter def session_location(self, ses_loc): """Set the session URI""" login_url_p = urlparse(self.base_url) new_port = login_url_p.port # The session_url returned by iLO does not include the port information # If we're using a non-standard port to connect to iLO we have to # manually insert it, based on the base_url. if new_port and ses_loc: session_location_p = urlparse(ses_loc) session_location_p = session_location_p._replace( netloc="{}:{}".format(session_location_p.hostname, new_port) ) self._session_location = session_location_p.geturl() else: self._session_location = ses_loc @property def username(self): """The username, if any. Once a login function has been called the credentials are removed from memory for security and this will return None.""" return self._user_pass[0] @property def password(self): """The password, if any. Once a login function has been called the credentials are removed from memory for security and this will return None.""" return self._user_pass[1] @property def login_url(self): """The login URI from the root response. This is where we post the credentials for a login.""" login_url = None try: login_url = self.root.obj.Links.Sessions["@odata.id"] except KeyError: login_url = self.root.obj.links.Sessions.href finally: if not login_url: raise ServerDownOrUnreachableError("Cannot locate the login url. Is this a Rest or" " Redfish server?") return login_url def login(self, auth=AuthMethod.SESSION): """Login to a Redfish or LegacyRest server. If auth is not supplied login will intelligently choose the authentication mode based on the arguments passed. Basic authentication MUST be specified with `auth`. :param auth: The auth type to login with. :type auth: str or :class:`AuthMethod` class variable """ if auth: auth = self._get_auth_type(auth) self.auth_type = auth if self.auth_type is AuthMethod.BASIC: self._get_root() self._basic_login() elif self.auth_type is AuthMethod.SESSION: self._get_root() self._session_login() elif self.auth_type is AuthMethod.CERTIFICATE: self._cert_login() self._get_root() def logout(self): """Logout of session. YOU MUST CALL THIS WHEN YOU ARE DONE TO FREE UP SESSIONS""" if self.session_location: resp = self.delete(self.session_location) LOGGER.info("User logged out: %s", resp.read) self._auth_key = None self.session_location = None def _get_root(self): """Get the root response of the server""" if not self.root: resp = self.get(self.default_prefix) if resp.status != 200: raise ServerDownOrUnreachableError("Server not reachable, " "return code: %d" % resp.status) self.root = resp def _basic_login(self): """Login using basic authentication""" LOGGER.info("Performing basic authentication.") if not self.basic_auth: auth_key = base64.b64encode(("{}:{}".format(self.username, self.password)).encode("utf-8")).decode("utf-8") self.basic_auth = "Basic {}".format(auth_key) headers = dict() headers["Authorization"] = self.basic_auth respvalidate = self.get(self.login_url, headers=headers) if respvalidate.status == 401: self._credential_err() else: self._user_pass = (None, None) def _session_login(self): """Login using session authentication""" if not self.connection.session_key: LOGGER.info("Performing session authentication.") data = dict() data["UserName"] = self.username data["Password"] = self.password if self.login_otp is not None: data["Token"] = self.login_otp headers = dict() resp = self.post(self.login_url, body=data, headers=headers) try: respread = resp.read respread = respread.replace("\\\\", "\\") # LOGGER.info("%s" % respread) except ValueError: pass LOGGER.debug("Login returned code %s: %s", resp.status, respread) self.session_key = resp.session_key self.session_location = resp.session_location self.login_return_code = resp.status self.login_response = respread else: self.session_key = self.connection.session_key if hasattr(self, "login_response") and self.login_response: if "OneTimePasscodeSent" in self.login_response: raise OneTimePasscodeError() elif "UnauthorizedLogin" in self.login_response: raise UnauthorizedLoginAttemptError("Error " + str( self.login_return_code) + ". Login is unauthorized.\nPlease check the credentials/OTP entered.\n") elif "TokenExpired" in self.login_response: raise TokenExpiredError("Error " + str( self.login_return_code) + ". The OTP entered has expired. Please enter credentials again.\n") elif not self.session_key and not resp.status == 200: self._credential_err() else: self._user_pass = (None, None) def _cert_login(self): """Login using certificate authentication""" self.session_key, self.session_location = self.connection.cert_login() def _credential_err(self): """Return credential error based on delay""" try: if self.is_redfish: delay = self.root.obj.Oem.Hpe.Sessions.LoginFailureDelay else: delay = self.root.obj.Oem.Hp.Sessions.LoginFailureDelay except KeyError: delay = 5 raise InvalidCredentialsError(delay) def _get_req_headers(self, headers=None, optionalpassword=None): """Get the request headers :param headers: additional headers to be utilized :type headers: str :param optionalpassword: provide password for authentication. :type optionalpassword: str. :returns: returns headers """ headers = headers if isinstance(headers, dict) else dict() h_list = [header.lower() for header in headers] auth_headers = True if "x-auth-token" in h_list or "authorization" in h_list else False token = self._biospassword if self._biospassword else optionalpassword if token: token = optionalpassword.encode("utf-8") if type(optionalpassword).__name__ in "basestr" else token hash_object = hashlib.new("SHA256") hash_object.update(token) headers["X-HPRESTFULAPI-AuthToken"] = hash_object.hexdigest().upper() if self.session_key and not auth_headers: headers["X-Auth-Token"] = self.session_key elif self.basic_auth and not auth_headers: headers["Authorization"] = self.basic_auth if self.is_redfish: headers["OData-Version"] = "4.0" return headers def get_resource_directory(self): try: resource_uri = self.root.obj.Oem.Hpe.Links.ResourceDirectory["@odata.id"] except KeyError: sys.stderr.write("Resource directory is only available on HPE servers.\n") return None response = self.get(resource_uri) resources = [] if response.status == 200: sys.stdout.write("\tFound resource directory at /redfish/v1/resourcedirectory" + "\n\n") resources = response.dict["Instances"] else: sys.stderr.write("\tResource directory missing at /redfish/v1/resourcedirectory" + "\n") return resources def get_gen(self): rootresp = self.root.obj # Default iLO 5 ilogen = 5 iloversion = None gencompany = next(iter(rootresp.get("Oem", {}).keys()), None) in ("Hpe", "Hp") comp = "Hp" if gencompany else None comp = "Hpe" if rootresp.get("Oem", {}).get("Hpe", None) else comp if comp and next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType", None): ilogen = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType") ilover = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerFirmwareVersion") if ilogen.split(" ")[-1] == "CM": # Assume iLO 4 types in Moonshot ilogen = 4 iloversion = None else: ilogen = ilogen.split(" ")[1] iloversion = float(ilogen.split(" ")[-1] + "." + "".join(ilover.split("."))) return (ilogen, iloversion) class LegacyRestClient(RestClient): """Class for a **Legacy REST** client instance. Instantiates appropriate Rest object based on existing configuration. Use this to retrieve a pre-configured Legacy Rest Class. Basic arguments include (These can be omitted for most local connections): * **base_url**: The IP or Hostname of the server to perform operations on. * **username**: The username of the account to login with. * **password**: The username of the account to login with. For full description of the arguments allowed see :class:`RestClient`""" def __init__(self, **client_kwargs): kwargs = { "default_prefix": client_kwargs.pop("default_prefix", "/rest/v1"), "is_redfish": client_kwargs.pop("is_redfish", False), } kwargs.update(client_kwargs) super(LegacyRestClient, self).__init__(**kwargs) class RedfishClient(RestClient): """Class for a **Redfish** client instance. Instantiates appropriate Redfish object based on existing configuration. Use this to retrieve a pre-configured Redfish Class. Basic arguments include (These can be omitted for most local connections): * **base_url**: The IP or Hostname of the server to perform operations on. None for local. * **username**: The username of the account to login with. * **password**: The username of the account to login with. For full description of the arguments allowed see :class:`RestClient`""" def __init__(self, **client_kwargs): kwargs = { "default_prefix": client_kwargs.pop("default_prefix", "/redfish/v1"), "is_redfish": client_kwargs.pop("is_redfish", True), } kwargs.update(client_kwargs) super(RedfishClient, self).__init__(**kwargs) python-ilorest-5.3.0.0a/src/redfish/rest/containers.py0000664000175000017500000002616014702427156022624 0ustar carstencarsten### # Copyright 2020 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 -*- """Containers used for REST requests and responses.""" import json import sys try: from collections import OrderedDict except ImportError: from collections.abc import OrderedDict from six import BytesIO, StringIO, string_types, text_type from six.moves import http_client from urllib3.util import Timeout, Retry class JSONEncoder(json.JSONEncoder): """JSON Encoder class""" def default(self, obj): """Set defaults in JSON encoder class :param obj: object to be encoded into JSON. :type obj: RestResponse :returns: A JSON :class:`OrderedDict` """ if isinstance(obj, RestResponse): jsondict = OrderedDict() jsondict["Status"] = obj.status jsondict["Headers"] = obj.getheaders() if obj.read: jsondict["Content"] = obj.dict return jsondict if isinstance(obj, bytes): obj = obj.decode("utf-8") if isinstance(obj, Timeout): jsondict = OrderedDict() jsondict["connect_timeout"] = obj.connect_timeout jsondict["read_timeout"] = obj.read_timeout return jsondict if isinstance(obj, Retry): jsondict = OrderedDict() jsondict["connect"] = obj.connect jsondict["read"] = obj.read jsondict["redirect"] = obj.redirect return jsondict return json.JSONEncoder.default(self, obj) class JSONDecoder(json.JSONDecoder): """Custom JSONDecoder that understands our types""" def decode(self, json_string): """Decode JSON string :param json_string: The JSON string to be decoded into usable data. :type json_string: str :returns: returns a parsed dict """ parsed_dict = super(JSONDecoder, self).decode(json_string) return parsed_dict class _FakeSocket(BytesIO): """slick way to parse a http response. http://pythonwise.blogspot.com/2010/02/parse-http-response.html""" def makefile(self, *args, **kwargs): """Return self object""" return self class RisObject(dict): """Converts a JSON/Rest dict into a object so you can use .property notation :param d: dictionary to be converted :type d: dict """ __getattr__ = dict.__getitem__ def __init__(self, d): """Initialize RisObject""" super(RisObject, self).__init__() self.update(**dict((k, self.parse(value)) for k, value in list(d.items()))) @classmethod def parse(cls, value): """Parse for RIS value :param cls: class referenced from class method :type cls: RisObject :param value: value to be parsed :type value: data type :returns: returns parsed value """ if isinstance(value, dict): return cls(value) elif isinstance(value, list): return [cls.parse(i) for i in value] return value class RestRequest(object): """Holder for Request information :param path: The URI path. :type path: str :param method: method to be implemented :type method: str :param data: body payload for the rest call :type data: dict """ def __init__(self, path, method="GET", data="", url=None): self._path = path self._body = data self._method = method self.url = url @property def path(self): """The path the request is made against.""" return self._path @property def method(self): """The method to implement.""" return self._method @property def body(self): """The body to pass along with the request, if any.""" return self._body def __str__(self): """Format string""" body = "" if not self._body else self._body try: return "{} {}\n\n{}".format(self.method, self.path, body) except: return "{} {}\n\n{}".format(self.method, self.path, "") class RestResponse(object): """Returned by Rest requests :param rest_request: Holder for request information :type rest_request: :class:`RestRequest` object :param http_response: Response from HTTP :type http_response: :class:`HTTPResponse` object """ def __init__(self, rest_request, http_response): self._read = None self._status = None self._headers = None self._session_key = None self._session_location = None self._rest_request = rest_request self._http_response = http_response self._read = self._http_response.data if http_response is not None else None self._ori = self._read @property def read(self): """The response body, attempted to be translated into json, else is a string.""" if self._read and not isinstance(self._read, text_type): self._read = self._read.decode("utf-8", "ignore") return self._read @read.setter def read(self, read): """Property for setting _read :param read: The data to set to read. :type read: str """ if read is not None: if isinstance(read, dict): read = json.dumps(read, indent=4) self._read = read def getheaders(self): """Get all headers included in the response.""" return dict(self._http_response.headers) if self._http_response is not None else self._headers def getheader(self, name): """Case-insensitive search for an individual header :param name: The header name to retrieve. :type name: str :returns: returns a header from HTTP response or None if not found. """ def search_dict(search_key, dct): for key, val in dct.items(): if key.lower() == search_key.lower(): return val return None if self._http_response: return search_dict(name, self._http_response.headers) return search_dict(name, self._headers) def loaddict(self, newdict): """Property for setting JSON data. Used during initialization. :param newdict: The string data to set as JSON data. :type newdict: str """ self._read = json.dumps(newdict, indent=4) @property def dict(self): """The response body data as an dict""" try: return json.loads(self.read) except ValueError as exp: if self.path != "/smbios" and self.path != "/cgi-bin/uploadFile": sys.stderr.write("An invalid response body was returned: %s" % exp) return None @property def obj(self): """The response body data as an object""" return RisObject.parse(self.dict) @property def ori(self): """The original response body data""" return self._ori @property def status(self): """The status code of the request.""" if self._status: return self._status return self._http_response.status if self._http_response is not None else self._status @property def session_key(self): """The saved session key for the connection.""" if self._session_key: return self._session_key self._session_key = self.getheader("x-auth-token") return self._session_key @property def session_location(self): """The saved session location, used for logging out.""" if self._session_location: return self._session_location self._session_location = self.getheader("location") return self._session_location @property def request(self): """The saved http request the response was generated by.""" return self._rest_request @property def path(self): """The path the request was made against.""" return self.request.path def __str__(self): """Class string formatter""" headerstr = "" for kiy, val in self.getheaders().items(): headerstr += "%s %s\n" % (kiy, val) return "%(status)s\n%(headerstr)s\n\n%(body)s" % { "status": self.status, "headerstr": headerstr, "body": self.read, } class RisRestResponse(RestResponse): """Returned by Rest requests from CHIF :param rest_request: Holder for request information :type rest_request: :class:`RestRequest` object :param resp_text: text from response to be buffered and read :type resp_text: str """ def __init__(self, rest_request, resp_txt): """Initialization of RisRestResponse""" if not isinstance(resp_txt, string_types): resp_txt = "".join(map(chr, resp_txt)) self._respfh = StringIO(resp_txt) self._socket = _FakeSocket(bytearray(list(map(ord, self._respfh.read())))) #self._respfh = BytesIO(resp_txt.encode('utf-8')) #self._socket = _FakeSocket(bytearray(self._respfh.read())) response = http_client.HTTPResponse(self._socket) #response = response.decode('utf-8') response.begin() response.data = response.read() response.headers = {ki[0]: ki[1] for ki in response.getheaders()} super(RisRestResponse, self).__init__(rest_request, response) class StaticRestResponse(RestResponse): """A RestResponse object used when data is being cached.""" def __init__(self, **kwargs): restreq = None if "restreq" in kwargs: restreq = kwargs["restreq"] super(StaticRestResponse, self).__init__(restreq, None) if "Status" in kwargs: self._status = kwargs["Status"] if "Headers" in kwargs: self._headers = kwargs["Headers"] if "session_key" in kwargs: self._session_key = kwargs["session_key"] if "session_location" in kwargs: self._session_location = kwargs["session_location"] if "Content" in kwargs: content = kwargs["Content"] if isinstance(content, string_types): self._read = content else: self._read = json.dumps(content) else: self._read = "" def getheaders(self): """Function for accessing the headers""" returnlist = {} if isinstance(self._headers, dict): returnlist = self._headers elif isinstance(self._headers, (list, tuple)): returnlist = {ki[0]: ki[1] for ki in self._headers} else: for item in self._headers: returnlist.update(item.items()[0]) return returnlist python-ilorest-5.3.0.0a/src/redfish/ris/0000775000175000017500000000000014702427156017720 5ustar carstencarstenpython-ilorest-5.3.0.0a/src/redfish/ris/ris.py0000664000175000017500000013062514702427156021076 0ustar carstencarsten### # Copyright 2020 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 -*- """Monolith database implementation. Crawls Redfish and Legacy REST implementations and holds all data retrieved. The created database is called the **monolith** and referenced as such in the code documentation.""" # ---------Imports--------- import logging import re import sys import threading from re import error as regexerr # Added for py3 compatibility import six try: from collections import OrderedDict, defaultdict except ImportError: from collections.abc import OrderedDict, defaultdict from queue import Queue import jsonpath_rw import jsonpointer from jsonpointer import set_pointer from six.moves.urllib.parse import urlparse, urlunparse from six.moves.urllib.parse import ParseResult from six.moves.urllib.parse import quote from redfish.rest.containers import RestRequest, StaticRestResponse from redfish.ris.ris_threaded import LoadWorker from redfish.ris.sharedtypes import Dictable # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class BiosUnregisteredError(Exception): """Raised when BIOS has not been registered correctly in iLO""" pass class SchemaValidationError(Exception): """Schema Validation Class Error""" pass class SessionExpired(Exception): """Raised when session has expired""" pass class RisMonolithMemberBase(Dictable): """RIS monolith member base class""" pass class RisInstanceNotFoundError(Exception): """Raised when attempting to select an instance that does not exist""" pass class RisMonolithMemberv100(RisMonolithMemberBase): """Class used by :class:`RisMonolith` for holding information on a response and adds extra data for monolith usage. A member can be marked as *modified* which means another operation may have rendered this member out of date. It should be reloaded before continuing to ensure data is up to date. :param restresp: `RestResponse` to create a member from. :type restresp: :class:`redfish.rest.containers.RestResponse` :param isredfish: Flag if the response is redfish or not :type isredfish: bool """ def __init__(self, restresp=None, isredfish=True): self._resp = restresp self._patches = list() # Check if typedef can be used here self._typestring = "@odata.type" if isredfish else "Type" self.modified = False self.defpath = self.deftype = self.defetag = self._type = None self.__bool__ = self.__nonzero__ @property def type(self): """Get type of the monolith member's response""" try: if self and self._typestring in self.resp.dict: return self.resp.dict[self._typestring] # Added for object type elif self and "type" in self.resp.dict: return self.resp.dict["type"] except (AttributeError, ValueError, TypeError): return self.deftype # data not yet fetched, probably empty dict, so assume deftype return None @property def maj_type(self): """Get major type of the monolith member's response""" if self.type: if "." in self.type: types = ".".join(self.type.split(".", 2)[:2]) retval = types[1:] if types.startswith("#") else types else: retval = self.type return retval return self.deftype def __nonzero__(self): """Defining the bool value for the class""" return True if self.resp else False @property def resp(self): """Get the entire response of the monolith member""" return self._resp @property def path(self): """Get path of the monolith member's response""" try: if self: return self.resp.request.path except (AttributeError, ValueError): pass return self.defpath @property def patches(self): """Get patches for the monolith member""" return self._patches @patches.setter def patches(self, val): """Set patches for the monolith member""" self._patches = val @property def dict(self): """Get the dictionary of the monolith member's response""" return self._resp.dict @property def etag(self): """Get the etag of the response""" return self.defetag if not self.resp else self.resp.getheader("etag") def popdefs(self, typename, pathval, etagval): """Populate the default values in the class :param typename: The default **Type** string. Example: @odata.type :type typename: str :param pathval: The default **Path** string. Example: @odata.id :type pathval: str :param etagval: The default **ETag** value. :type etagval: str """ self.defetag = etagval self.deftype = typename self.defpath = pathval def to_dict(self): """Converts Monolith Member to a dictionary. This is the reverse of :func:`load_from_dict`. :returns: returns the Monolith Member in dictionary form """ result = OrderedDict() if self.maj_type: result["Type"] = self.type if self.resp: if self.maj_type == "Collection.1" and "MemberType" in self.resp.dict: result["MemberType"] = self.resp.dict["MemberType"] result["links"] = OrderedDict() result["links"]["href"] = "" result["ETag"] = self.etag if self.resp: result["Content"] = self.resp.dict result["Status"] = self.resp.status result["Headers"] = self.resp.getheaders() result["OriginalUri"] = self.path result["Patches"] = self._patches result["modified"] = self.modified result["MajType"] = self.maj_type return result def load_from_dict(self, src): """Load variables to a monolith member from a dictionary. This is the reverse of :func:`to_dict`. :param src: Source to load member data from. :type src: dict """ if "Type" in src: self._type = src["Type"] if "Content" in src: restreq = RestRequest(method="GET", path=src["OriginalUri"]) src["restreq"] = restreq self._resp = StaticRestResponse(**src) self.deftype = src["MajType"] self.defpath = src["OriginalUri"] self.defetag = src["ETag"] self._patches = src["Patches"] self.modified = src["modified"] class RisMonolith(Dictable): """Monolithic cache of RIS data. This takes a :class:`redfish.rest.v1.RestClient` and uses it to gather data from a server and saves it in a modifiable database called monolith. :param client: client to use for data retrieval. :type client: :class:`redfish.rest.v1.RestClient` :param typepath: The compatibility class to use for differentiating between Redfish/LegacyRest. :type typepath: :class:`redfish.rest.ris.Typesandpathdefines` :param directory_load: The flag to quick load using resource directory if possible. When set to True this will load paths, etags, and types, but not create responses for every monolith member. When responses are needed, they will need to be loaded separately. :type directory_load: bool """ def __init__(self, client, typepath, directory_load=True): self._client = client self.name = "Monolithic output of RIS Service" self._visited_urls = list() self._type = None self._name = None self.progress = 0 self._directory_load = directory_load self.is_redfish = self.client.is_redfish self.typesadded = defaultdict(set) self.paths = dict() self.ctree = defaultdict(set) self.colltypes = defaultdict(set) self.typepath = typepath self.collstr = self.typepath.defs.collectionstring self.etagstr = "ETag" if self.is_redfish: self._resourcedir = "/redfish/v1/ResourceDirectory/" else: self._resourcedir = "/rest/v1/ResourceDirectory" # MultiThreading self.get_queue = Queue() self.threads = [] @property def directory_load(self): """The flag to gather information about a tree without downloading every path. Only usable on HPE systems with a ResourceDirectory. type""" return self._directory_load @directory_load.setter def directory_load(self, dir_load): """Set the directory_load flag""" self._directory_load = dir_load @property def type(self): """Return monolith version type""" return "Monolith.1.0.0" @property def visited_urls(self): """The urls visited by the monolith""" return list(set(self._visited_urls) | set(self.paths.keys())) @visited_urls.setter def visited_urls(self, visited_urls): """Set visited URLS.""" self._visited_urls = visited_urls @property def types(self): """Returns list of types for members in the monolith :rtype: list """ return list(self.typesadded.keys()) @types.setter def types(self, member): """Adds a member to monolith :param member: Member created based on response. :type member: RisMonolithMemberv100 """ self.typesadded[member.maj_type].add(member.path) patches = [] if member.path in list(self.paths.keys()): patches = self.paths[member.path].patches self.paths[member.path] = member self.paths[member.path].patches.extend([patch for patch in patches]) @property def client(self): """Returns the current client object reference :rtype: class object """ return self._client @client.setter def client(self, curr_client): """Set the current client :param curr_client: current client object :type curr_client: class object """ self._client = curr_client def path(self, path): """Provides the member corresponding to the path specified. Case sensitive. :param path: path of the monolith member to return :type path: str :rtype: RisMonolithMemberv100 """ try: return self.paths[path] except: return None def iter(self, typeval=None): """An iterator that yields each member of monolith associated with a specific type. In the case that no type is included this will yield all members in the monolith. :rtype: RisMonolithMemberv100 """ if not typeval: for _, val in list(self.paths.items()): yield val else: for typename in self.gettypename(typeval): for item in self.typesadded[typename]: yield self.paths[item] # types = next(self.gettypename(typeval), None) # if types in self.typesadded: # for item in self.typesadded[types]: # yield self.paths[item] # else: # raise RisInstanceNotFoundError("Unable to locate instance for" \ # " '%s'\n" % typeval) def itertype(self, typeval): """Iterator that yields member(s) of given type in the monolith and raises an error if no member of that type is found. :param typeval: type name of the requested member. :type typeval: str :rtype: RisMonolithMemberv100 """ typeiter = self.gettypename(typeval) types = next(typeiter, None) if types: while types: for item in self.typesadded[types]: yield self.paths[item] types = next(typeiter, None) else: raise RisInstanceNotFoundError("Unable to locate instance for '%s'\n" % typeval) def typecheck(self, types): """Check if a member of given type exists in the monolith :param types: type to check. :type types: str :rtype: bool """ if any(types in val for val in self.types): return True return False def gettypename(self, types): """Takes a full type response and returns all major types associated. Example: #Type.v1_0_0.Type will return iter(Type.1) :param types: The type of the requested response. :type types: str :rtype: iter of major types """ types = types[1:] if types[0] in ("#", "#") else types return iter((xt for xt in self.types if xt and types.lower() in xt.lower())) def update_member(self, member=None, resp=None, path=None, init=True): """Adds member to the monolith. If the member already exists the data is updated in place. Takes either a RisMonolithMemberv100 instance or a :class:`redfish.rest.containers.RestResponse` along with that responses path. :param member: The monolith member to add to the monolith. :type member: RisMonolithMemberv100 :param resp: The rest response to add to the monolith. :type resp: :class:`redfish.rest.containers.RestResponse` :param path: The path correlating to the response. :type path: str :param init: Flag if addition is part of the initial load. Set this to false if you are calling this by itself. :type init: bool """ if not member and resp and path: self._visited_urls.append(path.lower()) member = RisMonolithMemberv100(resp, self.is_redfish) if not member: # Assuming for lack of member and not member.type return if not member.type: member.deftype = "object" # Hack for general schema with no type self.types = member if init: self.progress += 1 if LOGGER.getEffectiveLevel() == 40: self._update_progress() def load( self, path=None, includelogs=False, init=False, crawl=True, loadtype="href", loadcomplete=False, path_refresh=False, json_out=False, ): """Walks the entire data model and caches all responses or loads an individual path into the monolith. Supports both threaded and sequential crawling. :param path: The path to start the crawl from the provided path if crawling or loads the path into monolith. If path is not included, crawl will start with the default. The default is */redfish/v1/* or */rest/v1* depending on if the system is Redfish or LegacyRest. :type path: str. :param includelogs: Flag to determine if logs should be downloaded as well in the crawl. :type includelogs: bool :param init: Flag to determine if this is the initial load. :type init: bool :param crawl: Flag to determine if load should crawl through found links. :type crawl: bool :param loadtype: Flag to determine if loading standard links: *href* or schema links: *ref*. :type loadtype: str. :param loadcomplete: Flag to download the entire data model including registries and schemas. :type loadcomplete: bool :param path_refresh: Flag to reload the path specified, clearing any patches and overwriting the current data in the monolith. :type path_refresh: bool """ if init: if LOGGER.getEffectiveLevel() == 40 and not json_out: sys.stdout.write("Discovering data...") else: LOGGER.info("Discovering data...") self.name = self.name + " at %s" % self.client.base_url selectivepath = path if not selectivepath: selectivepath = self.client.default_prefix if loadtype == "href" and not self.client.base_url.startswith("blobstore://."): if not self.threads: for _ in range(6): workhand = LoadWorker(self.get_queue) workhand.setDaemon(True) workhand.start() self.threads.append(workhand) self.get_queue.put( ( selectivepath, includelogs, loadcomplete, crawl, path_refresh, init, None, None, self, ) ) self.get_queue.join() # Raise any errors from threads, and set them back to None after excp = None for thread in self.threads: if excp is None: excp = thread.get_exception() thread.exception = None if excp: raise excp # self.member_queue.join() else: # We can't load ref or local client in a threaded manner self._load( selectivepath, originaluri=None, crawl=crawl, includelogs=includelogs, init=init, loadtype=loadtype, loadcomplete=loadcomplete, path_refresh=path_refresh, prevpath=None, ) if init: if LOGGER.getEffectiveLevel() == 40 and not json_out: sys.stdout.write("Done\n") else: LOGGER.info("Done\n") if self.directory_load and init: self._populatecollections() def _load( self, path, crawl=True, originaluri=None, includelogs=False, init=True, loadtype="href", loadcomplete=False, path_refresh=False, prevpath=None, ): """Sequential version of loading monolith and parsing schemas. :param path: path to start load from. :type path: str :param crawl: flag to determine if load should traverse found links. :type crawl: bool :param originaluri: variable to assist in determining originating path. :type originaluri: str :param includelogs: flag to determine if logs should be downloaded also. :type includelogs: bool :param init: flag to determine if first run of load. :type init: bool :param loadtype: flag to determine if load is meant for only href items. :type loadtype: str. :param loadcomplete: flag to download the entire monolith :type loadcomplete: bool :param path_refresh: flag to reload the members in the monolith instead of skip if they exist. :type path_refresh: bool """ if (path.endswith("?page=1") or path.endswith(".json")) and not loadcomplete: # Don't download schemas in crawl unless we are loading absolutely everything return elif not includelogs and crawl: # Only include logs when asked as there can be an extreme amount of entries if "/log" in path.lower(): return # remove fragments and quote characters in path p = urlparse(path) p = ParseResult( scheme=p.scheme, netloc=p.netloc, path=quote(p.path), params=p.params, query=p.query, fragment="" ) path = urlunparse(p) if prevpath and prevpath != path: self.ctree[prevpath].update([path]) if not path_refresh: if path.lower() in self.visited_urls: return LOGGER.debug("_loading %s", path) resp = self.client.get(path) if resp.status != 200 and path.lower() == self.typepath.defs.biospath: raise BiosUnregisteredError() elif resp.status == 401: raise SessionExpired("Invalid session. Please logout and " "log back in or include credentials.") elif resp.status not in (201, 200): self.removepath(path) return if loadtype == "ref": try: if resp.status in (201, 200): self.update_member(resp=resp, path=path, init=init) self._parse_schema(resp) except jsonpointer.JsonPointerException: raise SchemaValidationError() self.update_member(resp=resp, path=path, init=init) fpath = lambda pa, path: ( path if pa.endswith(self.typepath.defs.hrefstring) and pa.startswith((self.collstr, "Entries")) else None ) if loadtype == "href": # follow all the href attributes if self.is_redfish: jsonpath_expr = jsonpath_rw.parse("$..'@odata.id'") else: jsonpath_expr = jsonpath_rw.parse("$..href") matches = jsonpath_expr.find(resp.dict) if "links" in resp.dict and "NextPage" in resp.dict["links"]: if originaluri: next_link_uri = originaluri + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri self._load( href, originaluri=originaluri, includelogs=includelogs, crawl=crawl, init=init, prevpath=None, loadcomplete=loadcomplete, ) else: next_link_uri = path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri self._load( href, originaluri=path, includelogs=includelogs, crawl=crawl, init=init, prevpath=None, loadcomplete=loadcomplete, ) # Only use monolith if we are set to matchrdirpath = ( next((match for match in matches if match.value == self._resourcedir), None) if self.directory_load else None ) if not matchrdirpath and crawl: for match in matches: if path == "/rest/v1" and not loadcomplete: if ( str(match.full_path) == "links.Schemas.href" or str(match.full_path) == "links.Registries.href" ): continue elif not loadcomplete: if ( str(match.full_path) == "Registries.@odata.id" or str(match.full_path) == "JsonSchemas.@odata.id" ): continue if match.value == path: continue elif not isinstance(match.value, six.string_types): continue href = "%s" % match.value self._load( href, crawl=crawl, originaluri=originaluri, includelogs=includelogs, init=init, prevpath=fpath(str(match.full_path), path), loadcomplete=loadcomplete, ) elif crawl: href = "%s" % matchrdirpath.value self._load( href, crawl=crawl, originaluri=originaluri, includelogs=includelogs, init=init, prevpath=path, loadcomplete=loadcomplete, ) if loadcomplete: if path == "/rest/v1": schemamatch = jsonpath_rw.parse("$..extref") else: schemamatch = jsonpath_rw.parse("$..Uri") smatches = schemamatch.find(resp.dict) matches = matches + smatches for match in matches: if isinstance(match.value, six.string_types): self._load( match.value, crawl=crawl, originaluri=originaluri, includelogs=includelogs, init=init, loadcomplete=loadcomplete, prevpath=fpath(str(match.full_path), path), ) def _parse_schema(self, resp): """Function to get and replace schema $ref with data :param resp: response data containing ref items. :type resp: str """ # pylint: disable=maybe-no-member if not self.typepath.gencompany: return self._parse_schema_gen(resp) jsonpath_expr = jsonpath_rw.parse('$.."$ref"') matches = jsonpath_expr.find(resp.dict) respcopy = resp.dict typeregex = "([#,@].*?\.)" if matches: for match in matches: fullpath = str(match.full_path) jsonfile = match.value.split("#")[0] jsonpath = match.value.split("#")[1] listmatch = None found = None if "redfish.dmtf.org" in jsonfile: if "odata" in jsonfile: jsonpath = jsonpath.replace(jsonpath.split("/")[-1], "odata" + jsonpath.split("/")[-1]) jsonfile = "Resource.json" found = re.search(typeregex, fullpath) if found: repitem = fullpath[found.regs[0][0] : found.regs[0][1]] schemapath = "/" + fullpath.replace(repitem, "~").replace(".", "/").replace("~", repitem) else: schemapath = "/" + fullpath.replace(".", "/") if ".json" in jsonfile: itempath = schemapath if self.is_redfish: if resp.request.path[-1] == "/": newpath = "/".join(resp.request.path.split("/")[:-2]) + "/" + jsonfile + "/" else: newpath = "/".join(resp.request.path.split("/")[:-1]) + "/" + jsonfile + "/" else: newpath = "/".join(resp.request.path.split("/")[:-1]) + "/" + jsonfile if "href.json" in newpath: continue if newpath.lower() not in self.visited_urls: self.load( newpath, crawl=False, includelogs=False, init=False, loadtype="ref", ) instance = list() # deprecated type "string" for Type.json if "string" in self.types: for item in self.iter("string"): instance.append(item) if "object" in self.types: for item in self.iter("object"): instance.append(item) for item in instance: if jsonfile in item.path: if "anyOf" in fullpath: break dictcopy = item.dict try: # TODO may need to really verify this is acceptable regex listmatch = re.search("[][0-9]+[]", itempath) except regexerr: pass # LOGGER.info("An error occurred with regex match on path: %s\n%s\n"\ # % (itempath, str(excp))) if listmatch: start = listmatch.regs[0][0] end = listmatch.regs[0][1] newitempath = [itempath[:start], itempath[end:]] start = jsonpointer.JsonPointer(newitempath[0]) end = jsonpointer.JsonPointer(newitempath[1]) del start.parts[-1], end.parts[-1] vals = start.resolve(respcopy) count = 0 for val in vals: try: if "$ref" in six.iterkeys(end.resolve(val)): end.resolve(val).pop("$ref") end.resolve(val).update(dictcopy) replace_pointer = jsonpointer.JsonPointer(end.path + jsonpath) data = replace_pointer.resolve(val) set_pointer(val, end.path, data) start.resolve(respcopy)[count].update(val) break except: count += 1 else: itempath = jsonpointer.JsonPointer(itempath) del itempath.parts[-1] try: if "$ref" in six.iterkeys(itempath.resolve(respcopy)): itempath.resolve(respcopy).pop("$ref") itempath.resolve(respcopy).update(dictcopy) break except jsonpointer.JsonPointerException: pass if jsonpath: if "anyOf" in fullpath: continue if not jsonfile: replacepath = jsonpointer.JsonPointer(jsonpath) schemapath = schemapath.replace("/$ref", "") if re.search("\[\d]", schemapath): schemapath = schemapath.translate(str.maketrans("", "", "[]")) schemapath = jsonpointer.JsonPointer(schemapath) data = replacepath.resolve(respcopy) if "$ref" in schemapath.resolve(respcopy): schemapath.resolve(respcopy).pop("$ref") schemapath.resolve(respcopy).update(data) else: if not listmatch: schemapath = schemapath.replace("/$ref", "") replacepath = schemapath + jsonpath replace_pointer = jsonpointer.JsonPointer(replacepath) try: data = replace_pointer.resolve(respcopy) set_pointer(respcopy, schemapath, data) except jsonpointer.JsonPointerException: # TODO pass resp.loaddict(respcopy) else: resp.loaddict(respcopy) def _parse_schema_gen(self, resp): """Redfish general function to get and replace schema $ref with data :param resp: response data containing ref items. :type resp: str """ # pylint: disable=maybe-no-member getval = lambda inmat: getval(inmat.left) + "/" + str(inmat.right) if hasattr(inmat, "left") else str(inmat) respcopy = resp.dict jsonpath_expr = jsonpath_rw.parse('$.."anyOf"') while True: matches = jsonpath_expr.find(respcopy) if not matches: break match = matches[0] newval = None schlist = match.value schlist = [ele for ele in list(schlist) if ele != {"type": "null"}] norefsch = [ele for ele in list(schlist) if isinstance(ele, dict) and len(ele.keys()) > 1] if norefsch: newval = norefsch[0] else: newsc = [ele for ele in list(schlist) if not ele["$ref"].split("#")[0]] newval = newsc[0] if newsc else None if not newval: schlist = [ ele["$ref"] for ele in list(schlist) if "$ref" in ele.keys() and (ele["$ref"].split("#")[0].endswith(".json") and "odata" not in ele["$ref"].split("#")[0]) ] maxsch = max(schlist) newval = {"$ref": maxsch} itempath = "/" + getval(match.full_path) if re.search("\[\d+]", itempath): itempath = itempath.translate(str.maketrans("", "", "[]")) itempath = jsonpointer.JsonPointer(itempath) del itempath.parts[-1] if "anyOf" in six.iterkeys(itempath.resolve(respcopy)): itempath.resolve(respcopy).pop("anyOf") itempath.resolve(respcopy).update(newval) jsonpath_expr = jsonpath_rw.parse('$.."$ref"') matches = jsonpath_expr.find(respcopy) if matches: for _, match in enumerate(matches): jsonfile = match.value.split("#")[0] jsonfile = "" if jsonfile.lower() == resp.request.path.lower() else jsonfile jsonpath = match.value.split("#")[1] schemapath = "/" + getval(match.full_path) if jsonfile: itempath = schemapath if "/" not in jsonfile: inds = -2 if resp.request.path[-1] == "/" else -1 jsonfile = "/".join(resp.request.path.split("/")[:inds]) + "/" + jsonfile + "/" if jsonfile not in self.paths: self.load( jsonfile, crawl=False, includelogs=False, init=False, loadtype="ref", ) item = self.paths[jsonfile] if jsonfile in self.paths else None if not item: if "anyOf" not in schemapath: raise SchemaValidationError() continue if re.search("\[\d+]", itempath): itempath = itempath.translate(str.maketrans("", "", "[]")) itempath = jsonpointer.JsonPointer(itempath) del itempath.parts[-1] try: if "$ref" in six.iterkeys(itempath.resolve(respcopy)): itempath.resolve(respcopy).pop("$ref") itempath.resolve(respcopy).update(item.dict) except jsonpointer.JsonPointerException: pass if jsonpath: schemapath = schemapath.replace("/$ref", "") if re.search("\[\d+]", schemapath): schemapath = schemapath.translate(str.maketrans("", "", "[]")) if not jsonfile: replacepath = jsonpointer.JsonPointer(jsonpath) schemapath = jsonpointer.JsonPointer(schemapath) data = replacepath.resolve(respcopy) if "$ref" in schemapath.resolve(respcopy): schemapath.resolve(respcopy).pop("$ref") schemapath.resolve(respcopy).update(data) else: replacepath = schemapath + jsonpath replace_pointer = jsonpointer.JsonPointer(replacepath) data = replace_pointer.resolve(respcopy) set_pointer(respcopy, schemapath, data) resp.loaddict(respcopy) else: resp.loaddict(respcopy) def load_from_dict(self, src): """Load data to monolith from a dict. This is the reverse of :func:`to_dict`. :param src: data receive from rest operation. :type src: str """ self._type = src["Type"] self._name = src["Name"] self.typesadded = defaultdict(set, {ki: set(val) for ki, val in src["typepath"].items()}) self.ctree = defaultdict(set, {ki: set(val) for ki, val in src["ctree"].items()}) self.colltypes = defaultdict(set, {ki: set(val) for ki, val in src["colls"].items()}) for _, resp in list(src["resps"].items()): member = RisMonolithMemberv100(None, self.is_redfish) member.load_from_dict(resp) self.update_member(member=member, init=False) def to_dict(self): """Convert data to a dict from monolith. This is the reverse of :func:`load_from_dict`.""" result = OrderedDict() result["Type"] = self.type result["Name"] = self.name result["typepath"] = self.typesadded result["ctree"] = self.ctree result["colls"] = self.colltypes result["resps"] = {x: v.to_dict() for x, v in list(self.paths.items())} return result def markmodified(self, opath, path=None, modpaths=None): """Mark the paths to be modifed which are connected to current path. When calling this function you only need to include `opath`. :param opath: original path which has been modified :type opath: str """ modpaths = set() if modpaths is None else modpaths path = path if path else opath if not path: return modpaths.update(self.ctree[path] if path in self.ctree else set()) self.paths[path].modified = True for npath in [ unmodpath for unmodpath in modpaths if unmodpath in self.paths and not self.paths[unmodpath].modified ]: self.markmodified(opath, path=npath, modpaths=modpaths) return modpaths def checkmodified(self, opath, path=None, modpaths=None): """Check if the path or its children are modified. When calling this function you only need to include `opath`. :param opath: original path which has been modified :type opath: str """ # return [paths for paths in self.ctree[path] if self.paths[paths].modified] modpaths = set() if modpaths is None else modpaths path = path if path else opath newpaths = set() if not path: return if path in self.paths and self.paths[path].modified: newpaths = ( set([conn for conn in self.ctree[path] if conn in self.paths and self.paths[path].modified]) - modpaths ) modpaths.update(newpaths | set([path])) for npath in [unmodpath for unmodpath in newpaths]: self.checkmodified(opath, path=npath, modpaths=modpaths) return modpaths def removepath(self, path): """Remove a given path from the cache :param path: path which is to be checked if modified :type path: str """ if path in self._visited_urls: self._visited_urls.remove(path) if path not in self.paths: return if path in self.typesadded[self.paths[path].maj_type]: self.typesadded[self.paths[path].maj_type].remove(path) if not self.typesadded[self.paths[path].maj_type]: del self.typesadded[self.paths[path].maj_type] del self.paths[path] if path in self.ctree: del self.ctree[path] _ = [self.ctree[paths].remove(path) for paths in self.ctree if path in self.ctree[paths]] def _populatecollections(self): """Populate the collections type and types depending on resourcedirectory""" if self._resourcedir not in self.paths: return self.colltypes = defaultdict(set) alltypes = [] colls = [] for item in self.paths[self._resourcedir].dict["Instances"]: # Fix for incorrect RDir instances. if self.typepath.defs.typestring not in item or item[self.typepath.defs.hrefstring] in self.paths: continue typename = ".".join(item[self.typepath.defs.typestring].split(".", 2)[:2]).split("#")[-1] _ = [alltypes.append(typename) if "Collection" not in typename else None] _ = [colls.append(typename) if "Collection" in typename else None] member = RisMonolithMemberv100(None, self.is_redfish) member.popdefs(typename, item[self.typepath.defs.hrefstring], item[self.etagstr]) self.update_member(member=member, init=False) for coll in colls: collname = coll.split("Collection")[0].split("#")[-1] typename = next((name for name in alltypes if name.startswith(collname)), None) colltype = ".".join(coll.split(".", 2)[:2]).split("#")[-1] self.colltypes[typename].add(colltype) def capture(self, redmono=False): """Crawls the server specified by the client and returns the entire monolith. :param redmono: Flag to return only the headers and responses instead of the entire monolith member data. :type redmono: bool :rtype: dict """ self.load(includelogs=True, crawl=True, loadcomplete=True, path_refresh=True, init=True) return ( self.to_dict() if not redmono else { x: {"Headers": v.resp.getheaders(), "Response": v.resp.dict} for x, v in list(self.paths.items()) if v } ) def killthreads(self): """Function to kill threads on logout""" threads = [] for thread in threading.enumerate(): if isinstance(thread, LoadWorker): self.get_queue.put( ( "KILL", "KILL", "KILL", "KILL", "KILL", "KILL", "KILL", "KILL", "KILL", "KILL", ) ) threads.append(thread) for thread in threads: thread.join() def _update_progress(self): """Simple function to increment the dot progress""" if self.progress % 6 == 0: sys.stdout.write(".") python-ilorest-5.3.0.0a/src/redfish/ris/ris_threaded.py0000664000175000017500000002272414702427156022736 0ustar carstencarsten### # Copyright 2020 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 -*- """A threaded version of RIS _load for quicker searching""" # ---------Imports--------- import logging import threading from queue import Empty import jsonpath_rw # Added for py3 compatibility import six from six.moves.urllib.parse import urlparse, urlunparse import redfish.ris # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class LoadWorker(threading.Thread): """A threaded implementation of _load for quicker crawling""" def __init__(self, queue): threading.Thread.__init__(self) self.queue = queue self.exception = None def run(self): """Main worker function""" try: while True: ( path, includelogs, loadcomplete, crawl, rel, init, prevpath, originaluri, theobj, ) = self.queue.get() if ( path == includelogs == loadcomplete == crawl == rel == init == prevpath == originaluri == theobj == "KILL" ): break if path.endswith("?page=1") and not loadcomplete: # Don't download schemas in crawl unless we are loading absolutely everything self.queue.task_done() continue elif not includelogs and crawl: # Only include logs when asked as there can be an extreme amount of entries if "/log" in path.lower(): self.queue.task_done() continue # TODO: need to find a better way to support non ascii characters path = path.replace("|", "%7C") # remove fragments newpath = urlparse(path) newpath = list(newpath[:]) newpath[-1] = "" path = urlunparse(tuple(newpath)) if prevpath and prevpath != path: theobj.ctree[prevpath].update([path]) if not rel: if path.lower() in theobj.visited_urls: self.queue.task_done() continue LOGGER.debug("_loading %s", path) resp = theobj._client.get(path) if resp.status != 200 and path.lower() == theobj.typepath.defs.biospath: self.queue.task_done() raise redfish.ris.ris.BiosUnregisteredError() elif resp.status == 401: self.queue.task_done() raise redfish.ris.ris.SessionExpired( "Invalid session. Please logout and " "log back in or include credentials." ) elif resp.status not in (201, 200): theobj.removepath(path) self.queue.task_done() continue theobj.update_member(resp=resp, path=path, init=init) fpath = ( lambda pa, path: path if pa.endswith(theobj.typepath.defs.hrefstring) and pa.startswith((theobj.collstr, "Entries")) else None ) # follow all the href attributes if theobj.is_redfish: jsonpath_expr = jsonpath_rw.parse("$..'@odata.id'") else: jsonpath_expr = jsonpath_rw.parse("$..href") matches = jsonpath_expr.find(resp.dict) if "links" in resp.dict and "NextPage" in resp.dict["links"]: if originaluri: next_link_uri = originaluri + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri theobj.get_queue.put( ( href, includelogs, loadcomplete, crawl, rel, init, None, originaluri, theobj, ) ) else: next_link_uri = path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri theobj.get_queue.put( ( href, includelogs, loadcomplete, crawl, rel, init, None, path, theobj, ) ) # Only use monolith if we are set to matchrdirpath = ( next( (match for match in matches if match.value == theobj._resourcedir), None, ) if theobj.directory_load else None ) if not matchrdirpath and crawl: for match in matches: if path == "/rest/v1" and not loadcomplete: if ( str(match.full_path) == "links.Schemas.href" or str(match.full_path) == "links.Registries.href" ): continue elif not loadcomplete: if ( str(match.full_path) == "Registries.@odata.id" or str(match.full_path) == "JsonSchemas.@odata.id" ): continue if match.value == path: continue elif not isinstance(match.value, six.string_types): continue href = "%s" % match.value theobj.get_queue.put( ( href, includelogs, loadcomplete, crawl, rel, init, fpath(str(match.full_path), path), originaluri, theobj, ) ) elif crawl: href = "%s" % matchrdirpath.value theobj.get_queue.put( ( href, includelogs, loadcomplete, crawl, rel, init, path, originaluri, theobj, ) ) if loadcomplete: if path == "/rest/v1": schemamatch = jsonpath_rw.parse("$..extref") else: schemamatch = jsonpath_rw.parse("$..Uri") smatches = schemamatch.find(resp.dict) matches = matches + smatches for match in matches: if isinstance(match.value, six.string_types): theobj.get_queue.put( ( match.value, includelogs, loadcomplete, crawl, rel, init, fpath(str(match.full_path), path), originaluri, theobj, ) ) self.queue.task_done() except Empty: pass except Exception as excp: self.exception = excp def get_exception(self): """Get any exception from the thread""" return self.exception python-ilorest-5.3.0.0a/src/redfish/ris/__init__.py0000664000175000017500000000147114702427156022034 0ustar carstencarsten# -*- coding: utf-8 -*- """ Expanded LegacyREST/Redfish interface for schema validation, database for responses, caching, and error registries. """ from .ris import ( RisInstanceNotFoundError, RisMonolith, RisMonolithMemberBase, RisMonolithMemberv100, SessionExpired, ) from .rmc import RmcApp from .rmc_helper import ( CurrentlyLoggedInError, IdTokenError, IloLicenseError, InstanceNotFoundError, InvalidSelectionError, NothingSelectedError, NothingSelectedFilterError, NothingSelectedSetError, RmcCacheManager, RmcFileCacheManager, ScepenabledError, UndefinedClientError, ValidationError, ValueChangedError, ) from .sharedtypes import JSONEncoder from .validation import RegistryValidationError, ValidationManager python-ilorest-5.3.0.0a/src/redfish/ris/resp_handler.py0000664000175000017500000003401514702427156022743 0ustar carstencarsten### # Copyright 2020 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 -*- """Error response handler for Redfish or LegacryRest responses. Extended information only available with registries available on system, otherwise will return generic error responses.""" import logging from redfish.ris.ris import SessionExpired from redfish.ris.rmc_helper import ( EmptyRaiseForEAFP, IdTokenError, IloLicenseError, IloResponseError, ScepenabledError, ValueChangedError, ) from redfish.ris.utils import ( get_errmsg_type, json_traversal, print_handler, warning_handler, ) # ---------Debug logger--------- LOGGER = logging.getLogger() # ---------End of debug logger--------- class ResponseHandler(object): """Class to handle error responses from the server. :param validation_mgr: ValidationManager instance to gather registries if needed. Available in an RmcApp class as an attribute. :type validation_mgr: ValidationManager :param msg_reg_type: Redfish (#MessageRegistry.) or LegacyRest (MessageRegistry.) message registry string. Available in Typesandpathdefines class. :type msg_reg_type: str """ def __init__(self, validaition_mgr, msg_type): self.validation_mgr = validaition_mgr self.msg_reg_type = msg_type def output_resp(self, response, dl_reg=False, verbosity=1): """Prints or logs parsed MessageId response. Will raise an IloResponseError or return a list of message response data which includes the information returned from message_handler. :param response: message response of a call. :type response: :class:`redfish.rest.containers.RestResponse` :param dl_reg: Flag to download registry. If this is set to True a generic message response will be returned instead of gathered from registries. :type dl_reg: bool :param verbosity: Optional verbosity level. Only modifies what is output to log or screen. :type verbosity: int :returns: List of error response dictionaries. """ retdata = None if response.status > 299: message_text = "No error message returned or unable to parse error response." else: message_text = "The operation completed successfully." if response.status < 300 and (response._rest_request.method == "GET" or not response.read): # for rawget print_handler( self.verbosity_levels( message=message_text, response_status=response.status, verbosity=verbosity, dl_reg=dl_reg ) ) elif response.status == 401: raise SessionExpired() elif response.status == 403: results = response.dict["error"]["@Message.ExtendedInfo"] for result in results: if "License" in list(result.values())[0] or "license" in list(result.values())[0]: raise IloLicenseError("") raise IdTokenError() elif response.status == 412: warning_handler( "The property you are trying to change has been updated. " "Please check entry again before manipulating it.\n", override=True, ) raise ValueChangedError() else: retdata = self.message_handler( response_data=response, verbosity=verbosity, message_text=message_text, dl_reg=dl_reg, ) if response.status == 400: results = response.dict["error"]["@Message.ExtendedInfo"] for result in results: if "License" in list(result.values())[0] or "license" in list(result.values())[0]: raise IloLicenseError("") if "UnsupportedOperationACEEnabled" in list(result.values())[0]: raise ScepenabledError("") if response.status > 299: raise IloResponseError("") else: return retdata def message_handler(self, response_data, verbosity=0, message_text="No Response", dl_reg=False): """Prints or logs parsed MessageId response based on verbosity level and returns the following message information in a list: * MessageArgs * MessageId * RestResponse status * Resolution * Full error message text :param response_data: message response of a call. :type response_data: :class:`redfish.rest.containers.RestResponse` :param verbosity: Optional verbosity level. Only modifies what is output to log or screen. :type verbosity: int :param message_text: Response message text. If not provided, message_handler will attempt to parse it from the RestResponse and registries. :type message_text: str :param dl_reg: Flag to download registry. If this is set to True a generic message response will be returned instead of gathered from registries. :type dl_reg: bool :returns: List of error response dictionaries. """ _tmp_message_id = _tmp_description = _tmp_resolution = message_text retlist = list() response_error_str = "" try: response_status = response_data.status except (AttributeError, ValueError): response_status = "???" try: response_data = response_data.dict except (AttributeError, ValueError): pass try: for inst in self.get_message_data(response_data, dl_reg): try: for _key in inst.keys(): if "messageid" in str(_key.lower()): _tmp_message_id = inst[_key] if "description" in str(_key.lower()): _tmp_description = inst[_key] if inst.get("Message") and inst.get("MessageArgs"): for i in range(inst["Message"].count("%")): inst["Message"] = inst["Message"].replace( "%" + str(i + 1), '"' + inst["MessageArgs"][i] + '"' ) message_text = inst.get("Message", " ") elif inst.get("Message"): message_text = inst.get("Message", " ") elif response_status not in [200, 201]: message_text = _tmp_message_id _tmp_resolution = inst.get("Resolution", " ") except (KeyError, ValueError, TypeError): pass finally: response_error_str += "[%s] %s\n" % (response_status, message_text) print_handler( self.verbosity_levels( message_text, _tmp_message_id, _tmp_description, _tmp_resolution, response_status, verbosity, dl_reg, ) ) retlist.append(inst) except Exception: if not message_text: message_text = _tmp_message_id response_error_str += "[%s] %s\n" % (response_status, message_text) print_handler( self.verbosity_levels( message_text, _tmp_message_id, _tmp_description, _tmp_resolution, response_status, verbosity, dl_reg, ) ) retlist.append(inst) finally: return retlist def get_message_data(self, resp_data, dl_reg=False): """Obtain relevant keys from rest response. :param resp: response :type resp: :class:`redfish.rest.containers.RestResponse` :returns: list of error response dictionaries """ err_response_keys = ["MessageId", "Message", "MessageArgs", "Resolution"] try: if "messageid" in [_key.lower() for _key in resp_data.keys()]: data_extract = [resp_data] else: raise TypeError except (TypeError, KeyError): data_extract = json_traversal(resp_data, "messageid", ret_dict=True) if data_extract: try: if not dl_reg: for inst in data_extract: if [key.lower() for key in inst.keys()] not in [erk.lower() for erk in err_response_keys]: if "messageid" in [str(key.lower()) for key in inst.keys()]: inst.update(self.get_error_messages(inst[key])) continue finally: return data_extract else: return None def verbosity_levels( self, message, messageid=" ", description=" ", resolution=" ", response_status=None, verbosity=0, dl_reg=False, ): """Formatting based on verbosity level. :param message: Message from BMC response combined with the registry model/schema. :type message: str :param messageid: Error code as classified by the BMC's error code registry. :type messageid: str :param resolution: Message from BMC registry model/schema with the suggested resolution for the given error. :type resolution: str :param response_status: HTTP response status code. :type response_status: int :param verbosity: Option to set/control output message (stderr) verbosity. :type verbosity: int :returns: Message to be returned to caller. """ resp_str = "" if response_status: resp_str = "[" + str(response_status) + "] " if (verbosity == 1 or dl_reg) and message: return resp_str + message + "\n" elif verbosity > 1 and messageid and message and resolution: if not resp_str: resp_str = "None " return ( "\nHTTP Response Code: " + resp_str[:-1] + "\nMessageId: " + messageid + "\nDescription: " + description + "\nMessage: " + message + "\nResolution: " + resolution + "\n" ) else: if response_status == 400: return "" + message + "\n" else: return "" # unused? (removal pending) @staticmethod def _get_errmsg_type(results): """Return the registry type of a response. :param resuts: rest response. :type results: RestResponse. :returns: returns a Registry Id type string, None if not match is found, or no_id if the response is not an error message. """ return get_errmsg_type(results) def get_error_messages(self, regtype=None): """Returns registry error messages. Can specify a specific registry to return by Id. :param regtype: Id of registry type to add to list. :type regtype: str :returns: A list of error messages. """ LOGGER.info("Entering validation...") messages = None errmessages = {} reglist = [] # An error occurred during the shortcut method so let's go through each registry, # obtain the schema and narrow down the selected schema for the registry type provided try: _regtype = regtype.split(".")[0] for reg in self.validation_mgr.iterregmems(): # gen 10 / gen 9 rest if _regtype: if reg and "Id" in reg and reg["Id"] == _regtype: try: reglist.append(reg["Registry"]) except KeyError: reglist.append(reg["Schema"]) break else: continue if not reglist: # gen 9 redfish regval = [reg.get(arg, None) for arg in ["Registry", "Schema", "Id"]] regval = next( (val for val in regval if val and "biosattributeregistry" not in val), None, ) if not regval and reg: reg = reg["@odata.id"].split("/") reg = reg[len(reg) - 2] if "biosattributeregistry" not in reg.lower(): reglist.append(reg) elif regval: reglist.append(regval) for reg in reglist: reg = reg.replace("%23", "#") messages = self.validation_mgr.get_registry_model( getmsg=True, currtype=reg, searchtype=self.msg_reg_type ) if messages: errmessages.update(messages.get(next(iter(messages)))[regtype.split(".")[-1]]) if not reglist or not errmessages: raise Exception except Exception: raise EmptyRaiseForEAFP("Unable to find registry schema with provided registry " "type: %s" % regtype) else: return errmessages python-ilorest-5.3.0.0a/src/redfish/ris/rmc_helper.py0000664000175000017500000003627614702427156022430 0ustar carstencarsten### # Copyright 2020 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 -*- """RMC helper file. Includes RMC errors and caching functionality for monolith.""" # ---------Imports--------- import errno import hashlib import json import logging import os from redfish.rest.containers import RestRequest, StaticRestResponse from redfish.rest.v1 import RestClient from .ris import RisMonolith from .sharedtypes import JSONEncoder # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class RdmcError(Exception): """Base class for all RDMC Exceptions""" errcode = 1 def __init__(self, message): Exception.__init__(self, message) class InvalidCommandLineError(RdmcError): """Raised when user enter incorrect command line arguments""" pass class TfaEnablePreRequisiteError(RdmcError): """Raised when pre-requisites not met while enabling TFA""" pass class FailureDuringCommitError(RdmcError): """Raised when there is an error while committing.""" pass class UserNotAdminError(RdmcError): """Raised when user doesn't have admin priviledges, but they are required.""" pass class IncompatibleiLOVersionError(RdmcError): """Raised when iLO version is incompatible.""" pass class UndefinedClientError(Exception): """Raised when there are no clients active (usually when user hasn't logged in).""" pass class InstanceNotFoundError(Exception): """Raised when attempting to select an instance that does not exist.""" pass class CurrentlyLoggedInError(Exception): """Raised when attempting to select an instance that does not exist""" pass class IloLicenseError(Exception): """Raised when the proper iLO license is not available for a command""" pass class ScepenabledError(Exception): """Raised when the generation csr or deletion of https cert is issues when scep is enabled""" pass class NothingSelectedError(Exception): """Raised when attempting to access an object without first selecting it.""" pass class NothingSelectedFilterError(Exception): """Raised when the filter applied doesn't match any selection (general).""" pass class NothingSelectedSetError(Exception): """Raised when attempting to access an object without first selecting it (In set).""" pass class InvalidSelectionError(Exception): """Raised when selection argument fails to match anything.""" pass class IdTokenError(Exception): """Raised when user is not authorized to complete the operation.""" pass class ValueChangedError(Exception): """Raised if user tries to set/commit a value when monolith has older data.""" pass class LoadSkipSettingError(Exception): """Raised when one or more settings are absent in given server.""" pass class InvalidPathError(Exception): """Raised when requested path is not found.""" pass class UnableToObtainIloVersionError(Exception): """Raised when iloversion is missing from default path.""" pass class ValidationError(Exception): """Raised when there is a problem with user input.""" def __init__(self, errlist): super(ValidationError, self).__init__(errlist) self._errlist = errlist def get_errors(self): """Returns error list.""" return self._errlist class IloResponseError(Exception): """Raised when iLO returns with a non 2XX response.""" pass class EmptyRaiseForEAFP(Exception): """Raised when you need to check for issues and take different action.""" pass class IncorrectPropValue(Exception): """Raised when you pass an incorrect value to for the associated property.""" pass class RmcCacheManager(object): """Manages caching/uncaching of data for RmcApp. :param rmc: RmcApp to be managed :type rmc: :class:`redfish.ris.rmc.RmcApp` """ def __init__(self, rmc): """Initialize RmcCacheManager :param rmc: RmcApp to be managed :type rmc: RmcApp object """ self._rmc = rmc self.encodefunct = lambda data: data self.decodefunct = lambda data: data class RmcFileCacheManager(RmcCacheManager): """RMC file cache manager. :param rmc: RmcApp to be managed :type rmc: :class:`redfish.ris.rmc.RmcApp` """ def __init__(self, rmc): super(RmcFileCacheManager, self).__init__(rmc) def logout_del_function(self, url=None): """Searches for a specific url in cache or returns all urls and returns them for RmcApp to run logout on, clearing the session. :param url: The URL to pass back for logout. :type url: str """ if self._rmc.cache: cachedir = self._rmc.cachedir indexfn = os.path.join(cachedir, "index") # %s\\index' % cachedir else: indexfn = "" sessionlocs = [] if os.path.isfile(indexfn): try: indexfh = open(indexfn, "r") index_cache = json.load(indexfh) indexfh.close() for index in index_cache: if url: if url in index["url"]: os.remove(os.path.join(cachedir, index["href"])) break else: if os.path.isfile(os.path.join(cachedir, index["href"])): monolith = open(os.path.join(cachedir, index["href"]), "r") data = json.load(monolith) monolith.close() for item in data: if "login" in item and "session_location" in data["login"]: if "blobstore" in data["login"]["url"]: loc = data["login"]["session_location"].split("//")[-1] sesurl = None else: loc = None if data["login"]["session_location"] is not None: loc = data["login"]["session_location"].split(data["login"]["url"])[-1] sesurl = data["login"]["url"] sessionlocs.append( ( loc, sesurl, self._rmc._cm.decodefunct(data["login"]["session_key"]), ) ) os.remove(os.path.join(cachedir, index["href"])) except BaseException as excp: LOGGER.warning("Unable to read cache data %s", excp) return sessionlocs def uncache_rmc(self, creds=None, enc=False): """Uncaches monolith data from cache location specified by RmcApp. :param creds: Dictionary of username and password. Only required for restoring high security local calls. :type creds: dict :param enc: Flag if credentials passed are encoded. :type enc: bool """ cachedir = self._rmc.cachedir indexfn = "%s/index" % cachedir if os.path.isfile(indexfn): try: indexfh = open(indexfn, "r") index_cache = json.load(indexfh) indexfh.close() for index in index_cache: clientfn = index["href"] self._uncache_client(clientfn, creds=creds, enc=enc) except BaseException as excp: LOGGER.warning("Unable to read cache data %s", excp) def _uncache_client(self, cachefn, creds=None, enc=False): """Monolith uncache function for parsing and passing all client data and associated credential attributes. :param cachefn: The cache file name. :type cachefn: str. """ cachedir = self._rmc.cachedir clientsfn = "%s/%s" % (cachedir, cachefn) if os.path.isfile(clientsfn): try: clientsfh = open(clientsfn, "r") client = json.load(clientsfh) clientsfh.close() if "login" not in client: return login_data = client["login"] if "url" not in login_data: return self._rmc.typepath.getgen( login_data.get("ilo"), url=login_data.get("url"), isredfish=login_data.get("redfish", None), ca_cert_data=login_data.get("ca_cert_data", {}), ) if creds and login_data.get("url", "").startswith("blobstore://"): if enc: creds["username"] = self._rmc._cm.decodefunct(creds["username"]) creds["password"] = self._rmc._cm.decodefunct(creds["password"]) login_data["username"] = creds["username"] login_data["password"] = creds["password"] if isinstance(login_data["username"], bytes): login_data["username"] = login_data["username"].decode("utf-8") if isinstance(login_data["password"], bytes): login_data["password"] = login_data["password"].decode("utf-8") redfishinst = RestClient( username=login_data.get("username", "Administrator"), password=login_data.get("password", None), base_url=login_data.get("url", None), biospassword=login_data.get("bios_password", None), is_redfish=login_data.get("redfish", None), default_prefix=self._rmc.typepath.defs.startpath, proxy=login_data.get("proxy", None), ca_cert_data=login_data.get("ca_cert_data", {}), ) if login_data.get("authorization_key"): redfishinst.basic_auth = login_data.get("authorization_key") elif login_data.get("session_key"): redfishinst.session_key = self._rmc._cm.decodefunct(login_data.get("session_key")) if isinstance(redfishinst.session_key, bytes): redfishinst.session_key = redfishinst.session_key.decode("utf-8") redfishinst.session_location = login_data.get("session_location") if "selector" in client: self._rmc.selector = client["selector"] if login_data.get("iloversion"): redfishinst.iloversion = login_data.get("iloversion") else: redfishinst.iloversion = None self._rmc.typepath.iloversion = redfishinst.iloversion getdata = client["get"] for key in list(getdata.keys()): if key == redfishinst.default_prefix: restreq = RestRequest(method="GET", path=key) getdata[key]["restreq"] = restreq redfishinst.root = StaticRestResponse(**getdata[key]) break self._rmc.monolith = RisMonolith(redfishinst, self._rmc.typepath) self._rmc.monolith.load_from_dict(client["monolith"]) self._rmc.redfishinst = redfishinst # make sure root is there _ = redfishinst.root self._rmc.typepath.defineregschemapath(redfishinst.root.dict) except BaseException as excp: LOGGER.warning("Unable to read cache data %s", excp) def cache_rmc(self): """Saves monolith data to the file path specified in RmcApp.""" if not self._rmc.cache: return cachedir = self._rmc.cachedir if not os.path.isdir(cachedir): try: os.makedirs(cachedir) except OSError as ex: if ex.errno == errno.EEXIST: pass else: raise index_map = dict() index_cache = list() if self._rmc.redfishinst: shaobj = hashlib.new("SHA256") shaobj.update(self._rmc.redfishinst.base_url.encode("utf-8")) md5str = shaobj.hexdigest() index_map[self._rmc.redfishinst.base_url] = md5str index_data = dict( url=self._rmc.redfishinst.base_url, href="%s" % md5str, ) index_cache.append(index_data) indexfh = open("%s/index" % cachedir, "w") json.dump(index_cache, indexfh, indent=2, cls=JSONEncoder) indexfh.close() if self._rmc.redfishinst: login_data = dict( username=None, password=None, url=self._rmc.redfishinst.base_url, session_key=self._rmc._cm.encodefunct(self._rmc.redfishinst.session_key), session_location=self._rmc.redfishinst.session_location, authorization_key=self._rmc.redfishinst.basic_auth, bios_password=self._rmc.redfishinst.bios_password, redfish=self._rmc.monolith.is_redfish, ilo=self._rmc.typepath.ilogen, iloversion=self._rmc.typepath.iloversion, proxy=self._rmc.redfishinst.proxy, ca_cert_data=self._rmc.redfishinst.connection._connection_properties if self._rmc.redfishinst.connection._connection_properties else dict(), ) clients_data = dict( selector=self._rmc.selector, login=login_data, monolith=self._rmc.monolith, get=self._rmc.monolith.paths, ) clientsfh = open("%s/%s" % (cachedir, index_map[self._rmc.redfishinst.base_url]), "w") if isinstance(clients_data, bytes): clients_data = clients_data.decode("utf-8") json.dump(clients_data, clientsfh, indent=2, cls=JSONEncoder) clientsfh.close() python-ilorest-5.3.0.0a/src/redfish/ris/validation.py0000664000175000017500000014212514702427156022431 0ustar carstencarsten### # Copyright 2020 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 -*- """Handles schema and registry gathering as well as schema parsing and validation.""" # ---------Imports--------- import json import logging import re import textwrap import six from redfish.rest.containers import RisObject from .sharedtypes import JSONEncoder # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class InvalidPathsError(Exception): """Raised when requested path is not found""" pass class RegistryValidationError(Exception): """Registration Validation Class Error""" def __init__(self, msg, regentry=None, selector=None): super(RegistryValidationError, self).__init__(msg) self.reg = regentry self.sel = selector self.message = msg class UnknownValidatorError(Exception): """Raised when we find an attribute type that we don't know how to validate.""" pass class ValidationManager(object): """Keeps track of all the schemas and registries and provides helpers to simplify validation.""" def __init__(self, monolith, defines=None): """init of ValidationManager""" super(ValidationManager, self).__init__() self._schemaid = Typepathforval.typepath.schemapath self._regid = Typepathforval.typepath.regpath self._classes = list() self._classpaths = list() # type and path defines object self.defines = defines self.monolith = monolith # error self._errors = list() self._warnings = list() self.updatevalidationdata() @property def errors(self): """All errors found by the last validation.""" return self._errors @property def warnings(self): """All warnings found by the last validation.""" return self._warnings def reset_errors_warnings(self): """Resets warnings and errors, getting ready for the next validation.""" self._errors = list() self._warnings = list() def updatevalidationdata(self): """Loads the types into the validation manager from monolith.""" monolith = self.monolith for instance in monolith.iter(): if ( ( x.lower() in instance.maj_type.lower() for x in ( self.defines.defs.schemafilecollectiontype, "Collection.", self.defines.defs.regfilecollectiontype, ) ) and any(x.lower() in instance.path.lower() for x in (self._schemaid, self._regid)) and instance and instance.path not in self._classpaths ): self._classpaths.append(instance.path) self._classes.append(instance.resp.dict) def find_prop(self, propname, latestschema=False, proppath=None): """Searches through all locations and returns the schema found for the provided propname type. :param propname: String containing the schema name. :type propname: str :param proppath: String containing the schema path if you wish to use that instead. :type proppath: str :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool """ if proppath: self.monolith.load(path=proppath, crawl=False, loadtype="ref") return True for cls in self._classes: found = self.find_property(propname, cls=cls, latestschema=latestschema) if found: return found return None def itermems(self, membername=None): """Searches through all locations and yields each entry. :param membername: string containing the registry name. If not passed we use the typedefines string by default. :type membername: str """ if not membername: membername = self.defines.defs.collectionstring for items in self._classes: for item in items[membername]: yield item def iterregmems(self, membername=None): """Searches through all registries and yields each entry. :param membername: string containing the registry name. If not passed we use the typedefines string by default. :type membername: str """ if not membername: membername = self.defines.defs.collectionstring for items in self._classes: if "registr" in items["Name"].lower(): # For Gen9 type/name issue for item in items[membername]: yield item def iterschemamems(self, membername=None): """Searches through all schemas and yields each entry :param membername: string containing the registry name. If not passed we use the typedefines string by default. :type membername: str """ if not membername: membername = self.defines.defs.collectionstring for items in self._classes: if "schema" in items["Name"].lower(): # For Gen9 type/name issue for item in items[membername]: yield item def find_property(self, propname, cls=None, latestschema=False): """Returns iLO/BIOS registries/schemas :param propname: string containing the registry/schema name. :type propname: str :param cls: self._classes list of dictionaries. :type cls: list :param latestschema: flag to drop the versioning in the type string. :type latestschema: bool. :returns: iLO/BIOS registries/schemas that match the supplied name. """ result = [] dataloc = cls.get("Items", None) dataloc = cls.get("Members", None) if not dataloc else dataloc keyword = "Schema" if dataloc and isinstance(dataloc, list): _ = propname.split(".")[0].strip("#") propname = propname.split(".")[0].strip("#") if latestschema else propname for entry in dataloc: if entry: if "Schema" in entry: if propname.lower() in entry["Schema"].lower(): result.append(entry) elif "Registry" in entry: if ( propname.lower() in entry["@odata.id"].lower() and propname.lower() in entry["Registry"].lower() ): result.append(entry) keyword = "Registry" else: if "@odata.id" in entry: reglink = entry["@odata.id"].split("/") reglink = reglink[len(reglink) - 2] if reglink.lower().startswith(propname.lower()): self.monolith.load(path=entry["@odata.id"], crawl=False) result.append(self.monolith.paths[entry["@odata.id"]].dict) if result: result = max( result, key=lambda res: res[Typepathforval.typepath.defs.hrefstring] if res.get(Typepathforval.typepath.defs.hrefstring, None) else res[keyword], ) schemapath = self.geturidict(result["Location"][0]) self.monolith.load(path=schemapath, crawl=False, loadtype="ref") return result def geturidict(self, locationobj): """Return the external reference link. :param locationobj: Dictionary to get the URI reference from. :type locationobj: dict """ if Typepathforval.typepath.defs.isgen10: try: return locationobj["Uri"] except KeyError: raise InvalidPathsError("Error accessing Uri path!/n") elif Typepathforval.typepath.defs.isgen9: try: return locationobj["Uri"]["extref"] except KeyError: raise InvalidPathsError("Error accessing extref path!/n") def validatedict( self, tdict, currtype=None, proppath=None, latestschema=False, searchtype=None, monolith=None, reg=None, unique=None, ): """Load the schema file and validate tdict against it. :param tdict: the dictionary to test against. :type tdict: dict :param currtype: String containing the type the tdict dictionary is. :type currtype: str :param proppath: String containing the schema path of the tdict dictionary if you wish to use that instead. :type proppath: str :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool :param searchtype: Include the attribute registry of you are validating a bios registry. :type searchtype: str :param monolith: Full data model retrieved from server. :type monolith: dict :param unique: Flag to override for skipping unique properties. :type unique: bool :param reg: Registry entry of the given attribute. If this is not provided we will attempt to search based on the searchtype and currtype/proppath arguments. :type reg: dict. :returns: returns an error list. """ if not reg: reg = self.get_registry_model( currtype=currtype, searchtype=searchtype, proppath=proppath, latestschema=latestschema, ) if reg: list( map( lambda x: self.checkreadunique( tdict, x, reg=reg, warnings=self._warnings, unique=unique, searchtype=searchtype, ), list(tdict.keys()), ) ) orireg = reg.copy() ttdict = {key: val for key, val in list(tdict.items()) if not isinstance(val, (dict, list))} results = reg.validate_attribute_values(ttdict) self._errors.extend(results) for ki, val in list(tdict.items()): if ki in ttdict: tdict[ki] = ttdict[ki] continue reg = orireg.copy() valexists = False if val and isinstance(val, list): valexists = True # TODO: only validates if its a single dict within list if len(val) == 1 and isinstance(val[0], dict): treg = self.nestedreg(reg=reg, args=[ki]) self.validatedict( val[0], unique=unique, monolith=monolith, reg=treg, currtype=currtype, searchtype=searchtype, ) else: continue elif val and isinstance(val, dict): valexists = True treg = self.nestedreg(reg=reg, args=[ki]) self.validatedict( val, monolith=monolith, reg=treg, unique=unique, searchtype=searchtype, ) if not val and valexists: del tdict[ki] else: self._errors.append(RegistryValidationError("Unable to locate registry model")) return self._errors, self._warnings def checkreadunique(self, tdict, tkey, reg=None, warnings=None, unique=None, searchtype=None): """Check for and remove the readonly and unique attributes if required. :param tdict: the dictionary to test against. :type tdict: dict. :param tkey: The attribute key value to be tested. :type tkey: str. :param warnings: list containing found warnings. :type warnings: list. :param unique: flag to determine override for unique properties. :type unique: str. :param reg: Registry entry of the given attribute. :type reg: dict. :returns: returns boolean. """ if "Attributes" in tdict: return False # key = list(tdict[tkey])[0] # reg = reg["Attributes"][key] else: if tkey in reg: reg = reg[tkey] if not unique and reg.get("IsSystemUniqueProperty", None): if tdict[tkey] and not isinstance(tdict[tkey], bool): self._warnings.append("Property '%s' is unique and override not authorized. Skipping...\n" % str(tkey)) del tdict[tkey] return True if not reg.get("ReadOnly") or (reg.get(tkey, None) and not reg[tkey].get("readonly")): if unique and reg.get("IsSystemUniqueProperty", None): self._warnings.append("Property '%s' is unique, but override authorized...Patching..\n" % str(tkey)) return False # if not searchtype or (reg.get("ReadOnly") or (reg.get(tkey) # and reg[tkey].get("readonly"))): # self._warnings.append("Property is read-only. skipping... '%s'" % str(tkey)) # del tdict[tkey] # return True elif reg.get("ReadOnly") or (reg.get(tkey) and reg[tkey].get("readonly")): if tdict[tkey]: self._warnings.append("Property '%s' is read-only. Skipping...\n" % str(tkey)) del tdict[tkey] return True else: return False def get_registry_model( self, currtype=None, proppath=None, getmsg=False, searchtype=None, newarg=None, latestschema=False, ): """Loads the schema file and find the registry model if available. A registry model is a object built for schema/bios registry data. :param currtype: Type selection string. :type currtype: dict. :param proppath: String containing the schema path if you wish to use that instead. :type proppath: str :param getmsg: Flag to determine if commit should be skipped. :type getmsg: bool :param searchtype: Include the attribute registry of you are validating a bios registry. :type searchtype: str :param newarg: List of multi level properties to be modified. :type newarg: list :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool :returns: Schema in object form called a registry object. """ regdict = None monolith = self.monolith currtype = currtype.split("#")[-1].split(".")[0] + "." if currtype and latestschema else currtype if ( not currtype or not self.find_prop( currtype, latestschema=latestschema, proppath=proppath if not searchtype else None, ) ) and (not searchtype): self._errors.append(RegistryValidationError("Location info is missing.\n")) return None if not searchtype: searchtype = "object" try: for instance in monolith.iter(searchtype): if ( (searchtype == Typepathforval.typepath.defs.attributeregtype) or ( searchtype == "object" and any( currtype in xtitle for xtitle in ( instance.resp.dict.get("title", ""), instance.resp.dict.get("oldtitle", ""), ) ) ) or ( searchtype != "object" and currtype.split("#")[-1].split(".")[0] == instance.dict.get("RegistryPrefix", "") ) ): regdict = instance.resp.dict break except BaseException: pass if not regdict: self._errors.append(RegistryValidationError("Location data is empty.\n")) return None jsonreg = json.loads(json.dumps(regdict, indent=2, cls=JSONEncoder)) if getmsg: return {jsonreg["RegistryPrefix"]: jsonreg["Messages"]} # This was done for bios registry model compatibility if "RegistryEntries" in jsonreg: regitem = jsonreg["RegistryEntries"] if "Attributes" in regitem: newitem = {item[Typepathforval.typepath.defs.attributenametype]: item for item in regitem["Attributes"]} regitem["Attributes"] = newitem if not Typepathforval.typepath.flagiften: del regitem["Attributes"] newitem.update(regitem) regitem = newitem reg = HpPropertiesRegistry.parse(regitem) return self.nestedreg(reg=reg, args=newarg) if newarg else reg if "properties" in jsonreg: regitem = jsonreg["properties"] if "Properties" in regitem: regitem.update(regitem["Properties"]) del regitem["Properties"] reg = HpPropertiesRegistry.parse(regitem) return self.nestedreg(reg=reg, args=newarg) if newarg else reg def nestedreg(self, reg=None, args=None): """Go through the registry entry to find the required nested attribute. :param reg: Registry entry of the given attribute. :type reg: dict :param args: List of multi level properties to be modified. :type args: list :returns: dict of Registry entry """ for arg in args: try: arg = next((key for key in list(reg.keys()) if key.lower() == arg.lower()), None) if not arg: return None if ("properties" in reg[arg].keys()) and ("patternProperties" in reg[arg].keys()): reg[arg]["properties"].update(reg[arg]["patternProperties"]) reg = reg[arg]["properties"] elif "oneOf" in reg[arg]: oneof = reg[arg]["oneOf"] for item in oneof: reg = item["properties"] elif ( "type" in reg[arg] and reg[arg]["type"] == "array" and "items" in reg[arg] and "properties" in reg[arg]["items"] ): reg = reg[arg]["items"]["properties"] elif ("properties" not in reg[arg].keys()) or ("patternProperties" in reg[arg].keys()): reg = reg[arg] else: reg = reg[arg]["properties"] except: try: reg = reg[arg]["patternProperties"] except: return None return reg class HpPropertiesRegistry(RisObject): """Models a schema or bios attribute registry. Registry model.""" def __init__(self, d): super(HpPropertiesRegistry, self).__init__(d) def validate_attribute_values(self, tdict): """Look for tdict in the attribute list and attempt to validate its value. :param tdict: the dictionary to test against. :type tdict: dict :returns: A validated list """ result = list() for tkey in tdict: if tkey not in self: # Added for Gen 9 Bios properties not in registry continue elif self[tkey] and (checkattr(self[tkey], "type") or checkattr(self[tkey], "Type")): keyval = list() keyval.append(tdict[tkey]) temp = self.validate_attribute(self[tkey], keyval, tkey) tdict[tkey] = keyval[0] for err in temp: if isinstance(err, RegistryValidationError): if err.reg: err.sel = tkey result.extend(temp) return result def get_validator(self, attrname, newargs=None, oneof=None): """Returns attribute validator type. :param attrname: attribute name to validate. Ex: In A/B/C, this will be A. :type attrname: str :param newargs: List of multi level properties to be modified. Ex: In A/B/C this will be a list of B and C. :type newargs: list :param oneof: Special string for "oneof" options within validation. :type oneof: string :returns: The validator type class for the property passed. """ if oneof: self = oneof if newargs: for arg in newargs: try: self = self["properties"] except Exception: pass if not checkattr(self, arg): return None elif not arg == newargs[-1]: self = self[arg] if not checkattr(self, attrname): return None validator = None if EnumValidator.is_type(self[attrname]): validator = EnumValidator.parse(self[attrname]) elif StringValidator.is_type(self[attrname]): validator = StringValidator.parse(self[attrname]) elif ObjectValidator.is_type(self[attrname]): validator = ObjectValidator.parse(self[attrname]) elif IntegerValidator.is_type(self[attrname]): validator = IntegerValidator.parse(self[attrname]) elif BoolValidator.is_type(self[attrname]): validator = BoolValidator.parse(self[attrname]) elif PasswordValidator.is_type(self[attrname]): validator = PasswordValidator.parse(self[attrname]) elif "oneOf" in list(self[attrname].keys()): for item in self[attrname]["oneOf"]: validator = self.get_validator(attrname, newargs, HpPropertiesRegistry({attrname: item})) if validator: break return validator def validate_attribute(self, attrentry, attrvallist, name): """Function to validate attribute against its schema. :param attrentry: Key of property to validate. :type attrentry: str :param attrval: Value of Key to validate. :type attrval: str :param name: Clean name for outputting information to users. :type name: str :returns: returns list with validated attributes """ result = list() validator = None if self.nulltypevalidationcheck(attrval=attrvallist[0], attrentry=attrentry): return result if EnumValidator.is_type(attrentry): validator = EnumValidator.parse(attrentry) elif StringValidator.is_type(attrentry): validator = StringValidator.parse(attrentry) elif IntegerValidator.is_type(attrentry): validator = IntegerValidator.parse(attrentry) elif BoolValidator.is_type(attrentry): validator = BoolValidator.parse(attrentry) elif ObjectValidator.is_type(attrentry): validator = ObjectValidator.parse(attrentry) elif PasswordValidator.is_type(attrentry): validator = PasswordValidator.parse(attrentry) else: raise UnknownValidatorError(attrentry) if validator: result.extend(validator.is_array(attrentry, attrvallist, name)) result.extend(validator.validate(attrvallist, name)) return result def nulltypevalidationcheck(self, attrval=None, attrentry=None): """Function to validate null attributes against iLO schema :param attrentry: Key of property to validate. :type attrentry: str :param attrval: Value of Key to validate. :type attrval: str :returns: True if entry is null and valid. """ if "type" in attrentry and attrval is None: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "null": return True return False class BaseValidator(RisObject): """Base class for all validators.""" def __init__(self, d): super(BaseValidator, self).__init__(d) def validate(self): """Overridable function for validation""" raise RuntimeError("You must override this method in your derived class") def common_print_help(self, name): """Common human readable schema data. :param name: clean name for outputting. :type name: str :returns: A human readable string of schema data. """ outdata = "" wrapper = textwrap.TextWrapper() wrapper.initial_indent = " " * 4 wrapper.subsequent_indent = " " * 4 outdata += "\nNAME\n" outdata += "%s\n" % wrapper.fill("%s" % name) outdata += "\n" if "DisplayName" in self: outdata += "\nDISPLAY NAME\n" outdata += "%s\n" % wrapper.fill("%(DisplayName)s" % self) outdata += "\n" if "description" in self: outdata += "\nDESCRIPTION\n" outdata += "%s\n" % wrapper.fill("%(description)s" % self) outdata += "\n" if "HelpText" in self: outdata += "\nHELP TEXT\n" outdata += "%s\n" % wrapper.fill("%(HelpText)s" % self) outdata += "\n" if "WarningText" in self: outdata += "\n************************************************\n" outdata += "\nWARNING\n" outdata += "%s\n" % wrapper.fill("%(WarningText)s" % self) outdata += "\n\n**********************************************\n" outdata += "\n" if "type" in self and isinstance(self["type"], list): outdata += "\nTYPE\n" for item in self["type"]: outdata += "%s\n" % wrapper.fill("%s" % item) outdata += "\n" elif "type" in self: outdata += "\nTYPE\n" outdata += "%s\n" % wrapper.fill("%(type)s" % self) outdata += "\n" elif "Type" in self: outdata += "\nTYPE\n" outdata += "%s\n" % wrapper.fill("%(Type)s" % self) outdata += "\n" if "ReadOnly" in self: outdata += "\nREAD-ONLY\n" outdata += "%s\n" % wrapper.fill("%(ReadOnly)s" % self) outdata += "\n" elif "readonly" in self: outdata += "\nREAD-ONLY\n" outdata += "%s\n" % wrapper.fill("%(readonly)s" % self) outdata += "\n" return outdata def is_arrtype(self, attrentry): """Validate that the type is an array. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is an array. """ if "type" in attrentry and attrentry["type"] == "array": return True return False def is_array(self, attrentry, arrval, name): """Validate that the given value is an array type. :param attrentry: Registry model entry used for validation. :type attrentry: dict :param attrval: Value of Key to validate. :type attrval: str :returns: A boolean based on whether type is array and the value is valid for array type. """ result = [] if self.is_arrtype(attrentry): if isinstance( arrval[0], ( frozenset, list, set, tuple, ), ): return [] else: result.append( RegistryValidationError( "'%s' is not a valid setting " "for '%s', expecting an array" % (arrval[0], name), regentry=self, ) ) return result class EnumValidator(BaseValidator): """Enum validator class""" def __init__(self, d): super(EnumValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is enumeration. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is eneumeration. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "enumeration": return True elif "enum" in attrentry and item.lower() == "string": return True elif "enum" in attrentry and attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type" and value.lower() == "string": return True else: if attrentry["type"].lower() == "enumeration": return True elif "enum" in attrentry and attrentry["type"].lower() == "string": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "enumeration": return True return False def validate(self, keyval, name): """Validate against schemas. :param keyval: New value to be used for validation in a list :type keyval: list :param name: Clean name for outputting human readable info. :type name: str :returns: An error if validation fails. """ result = list() newval = keyval[0] try: for possibleval in self.enum: if ( possibleval and ( isinstance(possibleval, type(newval)) or (isinstance(possibleval, six.string_types) and isinstance(newval, six.string_types)) ) and possibleval.lower() == str(newval).lower() ): keyval[0] = possibleval return result except Exception: for possibleval in self.Value: if possibleval.ValueName.lower() == str(newval).lower(): keyval[0] = possibleval.ValueName return result result.append( RegistryValidationError("'%s' is not a valid setting " "for '%s'" % (newval, name), regentry=self) ) return result def print_help(self, name): """Human readable schema information specific to Enum data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ outdata = self.common_print_help(name) outdata += "\nPOSSIBLE VALUES\n" try: for possibleval in self.enum: outdata += " %s\n" % possibleval except Exception: for possibleval in self.Value: outdata += " %(ValueName)s\n" % possibleval outdata += "\n" return outdata class BoolValidator(BaseValidator): """Bool validator class""" def __init__(self, d): super(BoolValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is boolean. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is boolean. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "boolean": return True elif attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type" and value.lower() == "boolean": return True else: if attrentry["type"].lower() == "boolean": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "boolean": return True return False def validate(self, newval, name): """Validate against schemas. :param newval: New value to be used for validation in a list :type newval: list :param name: Clean name for outputting human readable info. :type name: str :returns: An error if value is invalid. """ result = list() if newval[0] is False or newval[0] is True: return result result.append( RegistryValidationError("'%s' is not a valid setting for '%s'" % (newval[0], name), regentry=self) ) return result def print_help(self, name): """Human readable schema information specific to Boolean data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ outdata = self.common_print_help(name) outdata += "\nPOSSIBLE VALUES\n" outdata += " True or False\n" outdata += "\n" return outdata class StringValidator(BaseValidator): """Constructor""" def __init__(self, d): super(StringValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is string. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is string. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "string": return True elif attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type" and "string" in value: return True else: if attrentry["type"].lower() == "string": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "string": return True return False def validate(self, newvallist, _): """Validate against schemas. :param newvallist: New value to be used for validation in a list :type newvallist: list :returns: An error if validation fails criteria. """ newval = newvallist[0] result = list() namestr = Typepathforval.typepath.defs.attributenametype if not isinstance(newval, str): result.append(RegistryValidationError("Given value must be a string")) return result if "MinLength" in self: if len(newval) < int(self["MinLength"]): result.append( RegistryValidationError( "'%s' must be at least '%s' characters long" % (self[namestr], int(self["MinLength"])), regentry=self, ) ) if "MaxLength" in self: if len(newval) > int(self["MaxLength"]): result.append( RegistryValidationError( "'%s' must be less than '%s' characters long" % (self[namestr], int(self["MaxLength"])), regentry=self, ) ) if "ValueExpression" in self: if self["ValueExpression"]: pat = re.compile(self["ValueExpression"]) if newval and not pat.match(newval): result.append( RegistryValidationError( "'%s' must match the regular expression " "'%s'" % (self[namestr], self["ValueExpression"]), regentry=self, ) ) return result def print_help(self, name): """Human readable schema information specific to String data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ wrapper = textwrap.TextWrapper() wrapper.initial_indent = " " * 4 wrapper.subsequent_indent = " " * 4 outdata = self.common_print_help(name) if "MinLength" in self: outdata += "\nMIN LENGTH\n" outdata += "%s" % wrapper.fill("%(MinLength)s" % self) outdata += "\n" if "MaxLength" in self: outdata += "\nMAX LENGTH\n" outdata += "%s" % wrapper.fill("%(MaxLength)s" % self) outdata += "\n" return outdata class IntegerValidator(BaseValidator): """Interger validator class""" def __init__(self, d): super(IntegerValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is integer. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is integer. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "integer" or item.lower() == "number": return True elif attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type": if value.lower() == "integer" or value.lower() == "number": return True else: if attrentry["type"].lower() == "integer" or attrentry["type"].lower().lower() == "number": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "integer": return True return False def validate(self, newvallist, _): """Validate against schemas. :param newvallist: New value to be used for validation in a list :type newvallist: list :returns: An error if validation fails criteria. """ result = list() try: intval = int(newvallist[0]) newvallist[0] = intval except: result.append(RegistryValidationError("'%(Name)s' must " "be an integer value'" % (self), regentry=self)) return result if newvallist[0] and not str(intval).isdigit(): result.append(RegistryValidationError("'%(Name)s' must " "be an integer value'" % (self), regentry=self)) return result if "LowerBound" in self: if intval < int(self["LowerBound"]): result.append( RegistryValidationError( "'%s' must be greater" " than or equal to '%s'" % (self.Name, int(self["LowerBound"])), regentry=self, ) ) if "UpperBound" in self: if intval > int(self["UpperBound"]): result.append( RegistryValidationError( "'%s' must be less " "than or equal to '%s'" % (self.Name, int(self["LowerBound"])), regentry=self, ) ) return result def print_help(self, name): """Human readable schema information specific to Integer data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ outdata = self.common_print_help(name) return outdata class ObjectValidator(BaseValidator): """Object validator class""" def __init__(self, d): super(ObjectValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is object. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is object. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "object": return True elif attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type" and value.lower() == "object": return True elif key.lower() == "anyof": try: if value[0]["type"] == "object": return True except Exception: continue else: if attrentry["type"].lower() == "object": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "object": return True return False def validate(self, newval, name): """Validate against schemas. :param newval: New value to be used for validation in a list :type newval: list :param name: Clean name for outputting human readable info. :type name: str :returns: An error if value is invalid. """ # TODO: need to add logic for true postive and false negatives. result = list() if isinstance(newval[0], (dict, six.string_types, int)): result.append( RegistryValidationError( "'%s' is not a valid setting for '%s'" % (newval[0], name), regentry=self, ) ) return result def print_help(self, name): """Human readable schema information specific to Object data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ wrapper = textwrap.TextWrapper() wrapper.initial_indent = " " * 4 wrapper.subsequent_indent = " " * 4 outdata = self.common_print_help(name) if "properties" in self: outdata += "\nSUB-PROPERTIES\n" propdata = ", ".join(self.properties.keys()) outdata += "%s" % wrapper.fill("%s" % propdata) outdata += "\n" elif "items" in self: outdata += "\nSUB-PROPERTIES\n" propdata = ", ".join(self["items"].properties.keys()) outdata += "%s" % wrapper.fill("%s" % propdata) outdata += "\n" return outdata class PasswordValidator(BaseValidator): """Password validator class""" def __init__(self, d): super(PasswordValidator, self).__init__(d) @staticmethod def is_type(attrentry): """Validate that the type is a password. :param attrentry: Registry model entry used for validation. :type attrentry: dict :returns: A boolean based on whether type is a password. """ if "type" in attrentry: if isinstance(attrentry["type"], list): for item in attrentry["type"]: if item.lower() == "password": return True elif attrentry["type"] == "array": for key, value in attrentry["items"].items(): if key.lower() == "type" and value.lower() == "password": return True else: if attrentry["type"].lower() == "password": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "password": return True return False def validate(self, newvallist, _): """Validate against schemas. :param newvallist: New value to be used for validation in a list :type newvallist: list :returns: An error if validation fails criteria. """ result = list() newval = newvallist[0] if newval is None: return result if not isinstance(newval, str): result.append(RegistryValidationError("Given value must be a string")) if "MinLength" in self: if len(newval) < int(self["MinLength"]): result.append( RegistryValidationError( "'%s' must be at least" " '%s' characters long" % (self.Name, int(self["MinLength"])), regentry=self, ) ) if "MaxLength" in self: if len(newval) > int(self["MaxLength"]): result.append( RegistryValidationError( "'%s' must be less " "than '%s' characters long" % (self.Name, int(self["MaxLength"])), regentry=self, ) ) if "ValueExpression" in self: if self["ValueExpression"]: pat = re.compile(self["ValueExpression"]) if newval and not pat.match(newval): result.append( RegistryValidationError( "'%(Name)s' must " "match the regular expression '%(Value" "Expression)s'" % (self), regentry=self, ) ) return result def print_help(self, name): """Human readable schema information specific to Password data. :param name: Clean name for outputting human readable info. :type name: str :returns: A human readable string of schema data. """ wrapper = textwrap.TextWrapper() wrapper.initial_indent = " " * 4 wrapper.subsequent_indent = " " * 4 outdata = self.common_print_help(name) if "MinLength" in self: outdata += "\nMIN LENGTH\n" outdata += "%s" % wrapper.fill("%(MinLength)s" % self) outdata += "\n" if "MaxLength" in self: outdata += "\nMAX LENGTH\n" outdata += "%s" % wrapper.fill("%(MaxLength)s" % self) outdata += "\n" return outdata class Typepathforval(object): """Way to store the typepath defines object.""" typepath = None def __new__(cls, typepathobj): if typepathobj: Typepathforval.typepath = typepathobj def checkattr(aobj, prop): """Check if an attribute exists""" try: if hasattr(aobj, prop): return True except: pass return False python-ilorest-5.3.0.0a/src/redfish/ris/rmc.py0000664000175000017500000022022414702427156021055 0ustar carstencarsten### # Copyright 2020 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 -*- """A convenience layer that combines multiple lower level classes and functions into one.""" import copy import hashlib import logging import shutil # ---------Imports--------- import sys import time import six try: from collections import OrderedDict except ImportError: from collections.abc import OrderedDict import jsonpatch import jsonpointer import redfish.ris.gen_compat import redfish.ris.validation from redfish.rest.v1 import RestClient from redfish.ris.resp_handler import ResponseHandler from redfish.ris.ris import RisMonolith, SchemaValidationError, SessionExpired from redfish.ris.rmc_helper import ( EmptyRaiseForEAFP, IloResponseError, IncompatibleiLOVersionError, InstanceNotFoundError, LoadSkipSettingError, NothingSelectedError, NothingSelectedSetError, RmcFileCacheManager, UndefinedClientError, ValidationError, ValueChangedError, ) from redfish.ris.utils import ( checkallowablevalues, diffdict, getattributeregistry, iterateandclear, merge_dict, navigatejson, print_handler, skipnonsettingsinst, validate_headers, warning_handler, ) from redfish.ris.validation import Typepathforval, ValidationManager # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class RmcApp(object): """A convenience class that combines the client, compatibility, validation, caching, and monolith into one class. :param showwarnings: Flag to print warnings to std.out (True) or log in log file (False) :type showwarnings: bool :param cache_dir: Cache directory to save cache data to, if None, RmcApp will not cache data. Cache can allow your RmcApp to persist between scripts. :type cache_dir: str """ def __init__(self, showwarnings=False, cache_dir=None): self.logger = LOGGER self.redfishinst = None self.sessioninst = None self._cm = RmcFileCacheManager(self) self.monolith = None self._iloversion = None self._validationmanager = None self._selector = None self._cachedir = cache_dir self.verbose = 1 self._sessionid = None self.typepath = redfish.ris.gen_compat.Typesandpathdefines() Typepathforval(typepathobj=self.typepath) if not showwarnings: self.logger.setLevel(logging.WARNING) if self.logger.handlers and self.logger.handlers[0].name == "lerr": self.logger.handlers.remove(self.logger.handlers[0]) @property def monolith(self): """Get the monolith from the current client""" return self._monolith @monolith.setter def monolith(self, monolith): """Set the monolith""" self._monolith = monolith @property def current_client(self): """Get the current client""" if self.redfishinst: return self.redfishinst raise UndefinedClientError() @property def validationmanager(self): """Get the valdation manager""" if self.getiloversion(): if self._validationmanager: self._validationmanager.reset_errors_warnings() else: monolith = self.monolith self._validationmanager = ValidationManager(monolith, defines=self.typepath) self._validationmanager.updatevalidationdata() else: self._validationmanager = None return self._validationmanager @property def selector(self): """The selector that will be used to gather data if no selector or instance argument is passed.""" return self._selector @selector.setter def selector(self, sel): """Set the selector""" self._selector = sel @property def cachedir(self): """The cache directory that is used to cache app data to a file, None if not caching data.""" return self._cachedir @cachedir.setter def cachedir(self, cache_dir): """Set the cachedir""" self._cachedir = cache_dir @property def cache(self): """True if we are caching data, False if we are not""" return True if self.cachedir else False def restore(self, creds=None, enc=False): """Restores the monolith from cache. Used to load a monolith data back into a new app class. Keyword arguments are only needed in a local client when in a high security mode. :param creds: Credentials to create the client with. :type creds: str :param enc: Flag to determine if encoding functions are being used. True if being used false if not. :type enc: bool """ self._cm.uncache_rmc(creds=creds, enc=enc) def set_encode_funct(self, funct): """Set the encoding function for cache to use. Can be used to protect sensitive data when it is at rest. :param funct: The function to use for encoding data :type funct: function """ self._cm.encodefunct = funct def set_decode_funct(self, funct): """Set the decoding function for cache to use. Is used in conjunction with the `set_encode_funct` to turn the encoded data back into a usable string. :param funct: The function to use for decoding data :type funct: function """ self._cm.decodefunct = funct def save(self): """Updates the cache with the latest monolith data.""" self._cm.cache_rmc() def login( self, username=None, password=None, sessionid=None, base_url="blobstore://.", path=None, skipbuild=False, includelogs=False, biospassword=None, is_redfish=False, proxy=None, ssl_cert=None, user_ca_cert_data=None, json_out=False, login_otp=None, log_dir=None, ): """Performs a login on a the server specified by the keyword arguments. Will also create a monolith, client, and update the compatibility classes for the app instance. If base_url is not included the login is assumed to be locally on the OS. :param username: The user name required to login to server. :type: str :param password: The password credentials required to login. :type password: str :param base_url: The redfish host name or ip address to login to. :type base_url: str :param path: The path to initiate the monolith crawl from. If None, it will start from the root. See monolith documentation on how the path is used. :type path: str :param proxy: The proxy required for connection (if any). :type proxy: str :param ssl_cert: The path to the CA bundle or SSL certificate to use with connection (if any). :type ssl_cert: str :param user_ca_cert_data: Dictionary of user certificate data for iLO Certificate-based authentication including iLO User TLS certificate, iLO User CA Root Key, \ iLO User CA Root Key Password (for encrypted CAs) :type: user_ca_pass: str :param skipbuild: The flag to determine monolith download. If True, monolith will be initiated empty, if False will build the monolith. :type skipbuild: bool :param includelogs: The flag to determine id logs should be downloaded in the crawl. :type includelogs: bool :param biospassword: The BIOS password for the server if set. :type biospassword: str :param is_redfish: If True, a Redfish specific header (OData) will be added to every request. Only required if the system has both LegacyREST and Redfish. :type is_redfish: bool """ self.typepath.getgen( url=base_url, username=username, password=password, sessionid=sessionid, ca_cert_data=user_ca_cert_data, proxy=proxy, isredfish=is_redfish, login_otp=login_otp, log_dir=log_dir, ) if user_ca_cert_data and self.typepath.iloversion < 5.23: raise IncompatibleiLOVersionError( "Certificate based login is incompatible with this " "iLO version: %s\n" % self.typepath.iloversion ) is_redfish = self.typepath.updatedefinesflag(redfishflag=is_redfish) if self.redfishinst and self.redfishinst.session_key: self.logout() self.redfishinst = RestClient( base_url=base_url, username=username, password=password, session_key=sessionid, default_prefix=self.typepath.defs.startpath, biospassword=biospassword, is_redfish=is_redfish, proxy=proxy, ca_cert_data=user_ca_cert_data, login_otp=login_otp, log_dir=log_dir, ) self.current_client.login(self.current_client.auth_type) inittime = time.time() self._build_monolith(path=path, includelogs=includelogs, skipbuild=skipbuild, json_out=json_out) endtime = time.time() if self.verbose > 1: sys.stdout.write("Monolith build process time: %s\n" % (endtime - inittime)) self.save() if not self.monolith: self.monolith.update_member( resp=self.current_client.root, path=self.typepath.defs.startpath, init=False, ) def logout(self, url=None): """Performs a logout of the server and prepares the app for another system, setting app variables to default values. :param url: The URL for the logout request. Only needed when using a cache. :type url: str """ sessionlocs = [] self._validationmanager = None self._iloversion = None try: self.monolith.killthreads() except Exception: pass try: self.current_client.logout() except Exception: sessionlocs = self._cm.logout_del_function(url) else: self._cm.logout_del_function(url) for session in sessionlocs: try: self.delete_handler(session[0], silent=True, service=True) except: pass self.redfishinst = None cachedir = self.cachedir if cachedir: try: shutil.rmtree(cachedir) except Exception: pass def select(self, selector=None, fltrvals=(None, None), path_refresh=False): """Selects instances based on selector and filter values. The select specified is saved in the app for further use. If another selector is sent, it overwrites the current one. :param selector: The type (@odata.type for Redfish) to select. :type selector: str :param fltrvals: The filter values for the select operation (Key,Val). If a selector returns multiple instances fltrvals can filter the instances by a key/value pair, limiting the returned instances to the one you want. :type fltrvals: tuple :param path_refresh: The flag to reload the selected instances. If True, each instance will be grabbed again from the server to make sure responses are up to date. :type path_refresh: bool :returns: A list of selected monolith member instances. :rtype: RisMonolithMemberv100 """ if not selector: selector = self.selector # Still nothing selected and selector is NULL if not selector: raise NothingSelectedError() selector = self.typepath.modifyselectorforgen(selector) instances = self._getinstances(selector=selector, path_refresh=path_refresh) val = fltrvals[1].strip("'\"") if isinstance(fltrvals[1], six.string_types) else fltrvals[1] instances = [ inst for inst in instances if not fltrvals[0] or navigatejson(fltrvals[0].split("/"), copy.deepcopy(inst.dict), val) ] if any(instances): self.selector = selector self.save() return instances errmsg = ( "Unable to locate instance for '{0}' and filter '{1}={2}'".format(selector, fltrvals[0], fltrvals[1]) if fltrvals[0] and fltrvals[1] else "Unable to locate instance for {}\n".format(selector) ) raise InstanceNotFoundError(errmsg) def types(self, fulltypes=False): """Returns a list of types available to be queried and selected with monolith. :param fulltypes: Flag to determine if types return Redfish full name, if False will return a shortened version of the type string. :type fulltypes: bool :returns: A list of type strings. :rtype: list """ instances = list() if self.monolith: monolith = self.monolith rdirtype = next(monolith.gettypename(self.typepath.defs.resourcedirectorytype), None) if not rdirtype: for inst in monolith.iter(): if not any([x for x in ["ExtendedError", "object", "string"] if x in inst.type]): instances.append(inst.type) else: for instance in monolith.iter(rdirtype): for item in instance.resp.dict["Instances"]: if ( item and instance._typestring in list(item.keys()) and "ExtendedError" not in item[instance._typestring] ): if not fulltypes and instance._typestring == "@odata.type": tval = item["@odata.type"].split("#") tval = tval[-1].split(".")[:-1] tval = ".".join(tval) instances.append(tval) elif item: instances.append(item[instance._typestring]) return instances def getprops( self, selector=None, props=None, nocontent=None, skipnonsetting=True, remread=False, insts=None, ): """Gets properties from a specified selector. If no selector is specified, uses the selector property in the app class. Instead of a selector a list of instances to search can be used instead. If both **selector** and **insts** are passed, **insts** is used. Specific values for multi-level dictionaries can be returned by passing each key separated by a "/" Ex: Key/Sub-Key/Sub-Sub-Key :param selector: The type selection for the get operation. :type selector: str :param skipnonsetting: Flag to remove non settings path. :type skipnonsetting: bool :param nocontent: Keys not found are added to the list provided. :type nocontent: list :param remread: Flag to remove readonly properties. :type remread: bool :param props: The keys to search for within current selection. :type props: list :param insts: List of RisMonolithMemberv100 to be searched for specific keys. :type insts: list :returns: A list of properties found in dictionary form. :rtype: list """ results = list() nocontent = set() if nocontent is None else nocontent if props: if isinstance(props, list): noprop = {prop: False for prop in props} else: noprop = {props: False} else: noprop = {} instances = insts if insts else self._getinstances(selector=selector) instances = skipnonsettingsinst(instances) if skipnonsetting else instances if selector != "UpdateService.": if not instances or len(instances) == 0: if not selector: raise NothingSelectedError() for instance in instances: currdict = instance.dict for patch in instance.patches: currdict = jsonpatch.apply_patch(currdict, patch) _ = self.removereadonlyprops(currdict, emptyraise=True) if remread else None temp_dict = dict() if props: if isinstance(props, six.string_types): props = [props] for prop in props: copydict = copy.deepcopy(currdict) propsdict = navigatejson(prop.split("/"), copydict) if propsdict is None: continue noprop[prop] = True merge_dict(temp_dict, propsdict) if temp_dict: if temp_dict not in results: results.append(temp_dict) else: results.append(currdict) if props: _ = [nocontent.add(prop) for prop in props if not noprop[prop]] return results def info(self, selector=None, props=None, dumpjson=True, latestschema=False): """Gets schema information for properties from a specified selector. If no selector is specified, uses the selector property in the app class. If no properties are specified the entire schema dictionary is returned in a list. :param selector: The type selection for the info operation. :type selector: str :param props: The keys to gather schema data for within current selection. :type props: str or list :param dumpjson: Flag to determine if output should be human readable or json schema. :type dumpjson: bool :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool :returns: A list of property schema information if dumpjson is True or string if dumpjson is False. :rtype: list or string """ model = None outdata = "" nokey = False results = None typestring = self.typepath.defs.typestring iloversion = self.getiloversion() if not iloversion: return results instances = self._getinstances(selector) attributeregistry = getattributeregistry(instances) instances = skipnonsettingsinst(instances) if not instances or len(instances) == 0: raise NothingSelectedError() for inst in instances: bsmodel = None currdict = inst.resp.dict proppath = inst.resp.getheader("Link").split(";")[0].strip("<>") if inst.resp.getheader("Link") else None seldict = {} if not props: model, bsmodel = self.get_model(currdict, attributeregistry, latestschema, proppath=proppath) results = model break if isinstance(props, six.string_types): props = props.split("/") if "/" in props else props props = [props] if not isinstance(props, (list, tuple)) else props seldict = navigatejson(props, copy.deepcopy(currdict)) if seldict is None: nokey = True continue if self.typepath.defs.typestring in currdict: seldict[typestring] = currdict[typestring] model, bsmodel = self.get_model( currdict, attributeregistry, latestschema, newarg=props[:-1], proppath=proppath, ) if not model and not bsmodel: errmsg = "/".join(props) warning_handler( "Unable to locate registry model or " "No data available for entry: {}\n".format(errmsg) ) continue found = model.get_validator(props[-1]) if model else None found = bsmodel.get_validator(props[-1]) if not found and bsmodel else found outdata = found if found and dumpjson else found.print_help(props[-1]) if found else outdata if outdata or results: return outdata if outdata else results errmsg = ( "Entry {} not found in current selection\n".format("/".join(props)) if nokey else "Entry {} not found in current" " selection\n".format("/".join(props)) ) warning_handler(errmsg) def loadset( self, seldict=None, fltrvals=(None, None), diffonly=False, latestschema=False, uniqueoverride=False, selector=None, ): """Creates json patches in monolith if the supplied dictionary passes schema validation. In the event schemas are unavailable the patches are always added. Patches that are created this way are not sent to the server until the :meth:`commit` function is called, sending the patches to the server. A list of patches that have not been sent to the server can be returned with the :meth:`status` function. :param selector: The type selection for the loadset operation. :type selector: str :param seldict: Dictionary with the patches to apply to the selected instances. :type seldict: dict :param fltrvals: The filter values for the operation (Key,Val). If a selector returns multiple instances fltrvals can filter the instances by a key/value pair, limiting the returned instances to the one you want. If no filter is supplied the patch dictionary will be applied to all instances. :type fltrvals: tuple :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool :param diffonly: flag to differentiate only existing properties. :type diffonly: bool :param uniqueoverride: Flag to determine if system unique properties should also be patched. If this is True, then unique properties will be patched. :type uniqueoverride: bool :returns: returns a list of properties that have successfully been set """ results = list() nochangesmade = False settingskipped = [False] selector = self.selector if not selector else selector instances = self.select(selector=selector, fltrvals=fltrvals) attributeregistry = getattributeregistry(instances=instances) instances = skipnonsettingsinst(instances=instances) if not instances or len(instances) == 0: raise NothingSelectedSetError() for instance in instances: if validate_headers(instance, verbose=self.verbose): continue else: nochangesmade = True currdict = instance.resp.dict if "@odata.id" in seldict: if currdict["@odata.id"] != seldict["@odata.id"]: continue diff_resp = diffdict( newdict=copy.deepcopy(seldict), oridict=copy.deepcopy(currdict), settingskipped=settingskipped, ) iloversion = self.getiloversion() if iloversion: proppath = ( instance.resp.getheader("Link").split(";")[0].strip("<>") if instance.resp.getheader("Link") else None ) try: self._validatechanges( instance=instance, attributeregistry=attributeregistry, newdict=diff_resp, oridict=currdict, unique=uniqueoverride, latestschema=latestschema, proppath=proppath, ) except SchemaValidationError: LOGGER.error("Cannot validate changes, error found in schema.") patches = jsonpatch.make_patch(currdict, diff_resp) if patches: torem = [] _ = [torem.append(patch) for patch in patches.patch if patch["op"] == "remove"] _ = [patches.patch.remove(patch) for patch in torem] for ind, item in enumerate(instance.patches): ppath = item.patch[0]["path"] if hasattr(item, "patch") else item[0]["path"] # ppath = ["path"](getattr(item, "patch"), item)[0]["path"] jpath = jsonpointer.JsonPointer(ppath.lower()) jval = jpath.resolve(seldict, default="kasjdk?!") if not jval == "kasjdk?!": del instance.patches[ind] if patches: for patch in patches.patch: forprint = patch["value"] if "value" in patch else (patch["op"] + " " + patch["from"]) results.append({patch["path"][1:]: forprint}) if "Managers/1/EthernetInterfaces/1" not in instance.path: self.monolith.path(instance.path).patches.append(patches) else: nochangesmade = True if not nochangesmade: return results elif settingskipped[0] is True: raise LoadSkipSettingError() else: return results def status(self): """Returns all pending changes that have not been committed yet.""" iloversion = self.getiloversion() finalresults = list() monolith = self.monolith (_, _) = self.get_selection(setenable=True) attrreg = getattributeregistry([ele for ele in monolith.iter() if ele]) for instance in monolith.iter(): results = list() if not (instance.patches and len(instance.patches) > 0): continue for item in instance.patches: if isinstance(item, list): results.extend(jsonpatch.JsonPatch(item)) else: results.extend(item) currdict = instance.resp.dict itemholder = list() for mainitem in results: item = copy.deepcopy(mainitem) if iloversion: _, bsmodel = self.get_model(currdict, attrreg) if bsmodel: prop = item["path"][1:].split("/")[-1] validator = bsmodel.get_validator(prop) if validator: if isinstance(validator, redfish.ris.validation.PasswordValidator): item["value"] = "******" itemholder.append(item) if itemholder: finalresults.append({instance.maj_type + "(" + instance.path + ")": itemholder}) return finalresults def commit(self): """Applies all pending json patches to the server. :yields: Two strings. 1. Path being PATCHed 2. True if an error occurred during the PATCH, False if no error. """ # Protect iLO Network Interface changes. instances = [ inst for inst in self.monolith.iter() if inst.patches and "Managers/1/EthernetInterfaces/1" not in inst.path ] if not instances or len(instances) == 0: raise NothingSelectedError() for instance in instances: if validate_headers(instance, verbose=self.verbose): continue currdict = dict() oridict = instance.resp.dict totpayload = dict() # apply patches to represent current edits for patches in instance.patches: if not self._iloversion: self._iloversion = self.getiloversion() if self._iloversion < 5.130: self._checkforetagchange(instance=instance) fulldict = jsonpatch.apply_patch(oridict, patches) for patch in patches: currdict = copy.deepcopy(fulldict) patchpath = patch["path"] pobj = jsonpointer.JsonPointer(patchpath) indpayloadcount = 0 for item in pobj.parts: payload = pobj.walk(currdict, item) indpayloadcount = indpayloadcount + 1 if isinstance(payload, list): break else: if not isinstance(payload, dict): break currdict = copy.deepcopy(payload) indices = pobj.parts[:indpayloadcount] createdict = lambda x, y: {x: y} while len(indices): payload = createdict(indices.pop(), payload) merge_dict(totpayload, payload) currdict = copy.deepcopy(totpayload) if "PersistentBootConfigOrder" in currdict and type(currdict) is dict: refdict = list(set(currdict["PersistentBootConfigOrder"])) items = list(currdict["PersistentBootConfigOrder"]) items_to_keep = items[: int(len(refdict))] currdict = {"PersistentBootConfigOrder": items_to_keep} if currdict: yield instance.resp.request.path put_path = instance.resp.request.path etag = self.monolith.paths[put_path].etag headers = dict([("If-Match", etag)]) if self._iloversion > 5.130 else None for key, value in currdict.items(): if key == "OldAdminPassword" and (value != ""): if (value == "null") or (value == "none") or (value == "None"): currdict["OldAdminPassword"] = "" else: hash = hashlib.sha256(value.encode()).hexdigest().upper() headers = dict([("X-HPRESTFULAPI-AuthToken", hash)]) elif key == "AdminPassword" and ( (value is None) or (value == "none") or (value == "None") or (value == "null") ): currdict["AdminPassword"] = "" try: self.patch_handler( put_path, currdict, optionalpassword=self.current_client.bios_password, headers=headers, ) except IloResponseError: yield True # Failure else: yield False # Success def patch_handler( self, put_path, body, headers=None, silent=False, service=False, optionalpassword=None, ): """Performs the client HTTP PATCH operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the patch operation on. :type put_path: str :param body: the body to perform the operation with. :type body: dict :param headers: Any additional headers to be added to the request. :type headers: dict :param optionalpassword: The bios password if it is required for the operation. :type optionalpassword: str :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object containing response data """ (put_path, body) = self._checkpostpatch(body=body, path=put_path, patch=True) if optionalpassword: self.current_client.bios_password = optionalpassword results = self.current_client.patch(put_path, body=body, headers=headers) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() self._modifiedpath(results, replace=True) # if results and getattr(results, "status", None) and results.status == 412: if results and hasattr(results, "status") and results.status == 412: self._updatemono(path=put_path, path_refresh=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) return results def get_handler( self, get_path, sessionid=None, silent=False, uncache=False, headers=None, service=False, username=None, password=None, base_url=None, ): """Performs the client HTTP GET operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the get operation on. :type put_path: str :param uncache: flag to not store the data downloaded into monolith. :type uncache: bool :param headers: Any additional headers to be added to the request. :type headers: dict :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object """ try: results = self.current_client.get(get_path, headers=headers) except UndefinedClientError: if sessionid: self.redfishinst = RestClient( sessionid=sessionid, username=username, password=password, base_url=base_url, ) if not headers: headers = dict() headers["X-Auth-Token"] = sessionid results = self.current_client.get(get_path, headers=headers) if results and getattr(results, "status", None) and results.status == 404: if not silent: if hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) else: print_handler("[" + str(results.status) + "]" + " The operation completed successfully.\n") return results if results and results.status == 200 and sessionid: if not silent: if hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) else: print_handler("[" + str(results.status) + "]" + " The operation completed successfully.\n") return results if not uncache and results.status == 200 and not sessionid: if self.monolith: self.monolith.update_member(resp=results, path=get_path, init=False) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() if not silent: if hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) else: print_handler("[" + str(results.status) + "]" + " The operation completed successfully.\n") return results def post_handler(self, put_path, body, headers=None, silent=False, service=False): """Performs the client HTTP POST operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the post operation on. :type put_path: str :param body: the body to perform the operation with. :type body: dict :param headers: Any additional headers to be added to the request. :type headers: dict :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object containing response data """ (put_path, body) = self._checkpostpatch(body=body, path=put_path) results = self.current_client.post(put_path, body=body, headers=headers) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() self._modifiedpath(results) if results.status == 400 and results.dict is None: return results if not silent and hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) return results def put_handler( self, put_path, body, headers=None, silent=False, optionalpassword=None, service=False, ): """Performs the client HTTP PUT operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the put operation on. :type put_path: str :param body: the body to perform the operation with. :type body: dict :param headers: Any additional headers to be added to the request. :type headers: dict :param optionalpassword: The bios password if it is required for the operation. :type optionalpassword: str :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object containing response data """ if optionalpassword: self.current_client.bios_password = optionalpassword results = self.current_client.put(put_path, body=body, headers=headers) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() self._modifiedpath(results, replace=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) return results def delete_handler(self, put_path, headers=None, silent=False, service=False): """Performs the client HTTP DELETE operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the delete operation on. :type put_path: str :param headers: Any additional headers to be added to the request. :type headers: dict :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object containing response data """ results = self.current_client.delete(put_path, headers=headers) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() self._modifiedpath(results, delete=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) return results def head_handler(self, put_path, silent=False, service=False): """Performs the client HTTP HEAD operation with monolith and response handling support. Response handling will output to logger or string depending on showmessages app argument. :param put_path: The REST path to perform the head operation on. :type put_path: str :param silent: If False response will be parsed based on service flag and output to a log or stdout. If True response will not be parsed and no message output or error messages raised from the response handler. :type silent: bool :param service: When handling the response, if True registries will be gathered and a full, response will be output if False they will not and response handler will instead return a generic message. :type service: bool :returns: A :class:`redfish.rest.containers.RestResponse` object containing response data """ results = self.current_client.head(put_path) if results and getattr(results, "status", None) and results.status == 401: raise SessionExpired() if not silent and hasattr(self.typepath.defs, "messageregistrytype"): ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( results, dl_reg=service, verbosity=self.verbose ) return results def removereadonlyprops(self, currdict, emptyraise=False, removeunique=True, specify_props=None): """Remove read only properties from a dictionary. Requires schemas to be available. :param currdict: The dictionary to remove read only properties from. :type currdict: dictionary :param emptyraise: Flag to raise an empty error for handling and failure to parse. :type emptyraise: boolean :type removeunique: Flag to remove system unique values as well as read only. :type removeunique: boolean :parm specify_props: Optionally set list of properties to be removed instead of the default. :type specify_props: list """ try: type_str = self.typepath.defs.typestring currtype = currdict.get(type_str, None) oridict = copy.deepcopy(currdict) if specify_props: templist = specify_props else: templist = [ "Modified", "Type", "Description", "Status", "links", "SettingsResult", "Attributes", "@odata.context", "@odata.type", "@odata.id", "@odata.etag", "Links", "Actions", "AvailableActions", "BiosVersion", "AddressOrigin", ] # Attributes removed and readded later as a validation workaround currdict = iterateandclear(currdict, templist) iloversion = self.getiloversion() if not iloversion: return currdict self.validationmanager.validatedict( currdict, currtype=currtype, monolith=self.monolith, unique=removeunique, searchtype=None, ) if oridict.get("Attributes", None): currdict["Attributes"] = oridict["Attributes"] return currdict except: if emptyraise is True: raise EmptyRaiseForEAFP() elif emptyraise == "pass": pass else: raise def getidbytype(self, tpe): """Return a list of URIs that correspond to the supplied type string. :param tpe: type string to search for. :type tpe: string. """ urls = list() val = next(self.monolith.gettypename(tpe), None) urls.extend(self.monolith.typesadded[val] if val else []) return urls def getcollectionmembers(self, path, fullresp=False): """Returns collection/item lists of the provided path. :param path: path to return. :type path: string. :param fullresp: Return full json data instead of only members. :type path: bool. :returns: list of collection members """ if ( self.typepath.defs.isgen10 and hasattr(self.typepath, "gencompany") and self.typepath.gencompany and "?$expand=." not in path ): path += "?$expand=." if path.endswith("/") else "/?$expand=." members = self.get_handler(path, service=True, silent=True) if members and not fullresp: try: members = members.dict["Members"] if self.typepath.defs.isgen10 else members.dict["Current"] except KeyError: members = members elif fullresp: members = [members.dict] return members def getbiosfamilyandversion(self): """Function that returns the current BIOS version information.""" self._updatemono(currtype="ComputerSystem.", crawl=False) try: for inst in self.monolith.iter("ComputerSystem."): if "Current" in inst.resp.obj["Bios"]: oemjson = inst.resp.obj["Bios"]["Current"] parts = oemjson["VersionString"].split(" ") return parts[0], parts[1][1:] else: parts = inst.resp.obj["BiosVersion"].split(" ") return parts[0], parts[1][1:] except Exception: pass return None, None def getiloversion(self, skipschemas=False): """Function that returns the current iLO version. :param skipschemas: flag to determine whether to skip schema download. If False, this will also verify if schemas are available. :type skipschemas: bool :returns: returns current iLO version """ iloversion = self._iloversion = self._iloversion if self._iloversion else self.typepath.iloversion if not iloversion and hasattr(self.redfishinst, "iloversion"): iloversion = self._iloversion = self.typepath.iloversion = self.redfishinst.iloversion if ( hasattr(self.typepath, "gencompany") and self.typepath.gencompany and not self._iloversion and not self.typepath.noschemas ): self.monolith.load(self.typepath.defs.managerpath, crawl=False) results = next(iter(self.getprops("Manager.", ["FirmwareVersion", "Firmware"]))) def quickdrill(_dict, key): """function to find key in nested dictionary""" return _dict[key] while isinstance(results, dict): results = quickdrill(results, next(iter(results.keys()))) iloversionlist = results.replace("v", "").replace(".", "").split(" ") iloversion = float(".".join(iloversionlist[1:3])) model = self.getprops("Manager.", ["Model"]) if model: if next(iter(model))["Model"] == "iLO CM": # Assume iLO 4 types in Moonshot iloversion = None self._iloversion = iloversion elif ( hasattr(self.typepath, "gencompany") and not self.typepath.gencompany ): # Assume schemas are available somewhere in non-hpe redfish self._iloversion = iloversion = 4.210 conf = None if not skipschemas else True if not skipschemas: if iloversion and iloversion >= 4.210: conf = self._verifyschemasdownloaded(self.monolith) elif iloversion and iloversion < 4.210: warning_handler("Please upgrade to iLO 4 version 2.1 or above for schema support.") else: warning_handler("Schema support unavailable on the currently logged in system.") return iloversion if iloversion and iloversion >= 4.210 and conf else None def get_selection(self, selector=None, setenable=False, path_refresh=False): """Gathers instances and optionally the attributeregistry based on selector. :param selector: The type selection for the get operation. :type selector: str. :param setenable: Flag to determine if registry should also be returned. :type setenable: boolean. :param path_refresh: Flag to reload the selected instances. :type path_refresh: boolean. :returns: returns a list of selected items """ instances = self._getinstances(selector=selector, path_refresh=path_refresh) if setenable: attributeregistryfound = getattributeregistry(instances=instances) instances = skipnonsettingsinst(instances=instances) return instances, attributeregistryfound return instances def create_save_header(self): """Adds save file headers to show what server the data came from. :param selector: The type selection for the get save operation. :type selector: str. :param selectignore: Return the save header even if there isn't a selection to add it to. :type selectignore: boolean :returns: returns an header ordered dictionary """ instances = OrderedDict() monolith = self.monolith self._updatemono(currtype="ComputerSystem.", crawl=False) self._updatemono(currtype=self.typepath.defs.biostype, crawl=False) self._updatemono(currtype="Manager.", crawl=False) instances["Comments"] = OrderedDict() for instance in monolith.iter("ComputerSystem."): if instance.resp.obj.get("Manufacturer"): instances["Comments"]["Manufacturer"] = instance.resp.obj["Manufacturer"] if instance.resp.obj.get("Model"): instances["Comments"]["Model"] = instance.resp.obj["Model"] try: if instance.resp.obj["Oem"][self.typepath.defs.oemhp]["Bios"]["Current"]: oemjson = instance.resp.obj["Oem"][self.typepath.defs.oemhp]["Bios"]["Current"] instances["Comments"]["BIOSFamily"] = oemjson["Family"] instances["Comments"]["BIOSDate"] = oemjson["Date"] except KeyError: pass for instance in monolith.iter(self.typepath.defs.biostype): try: if getattr(instance.resp.obj, "Attributes", False): if instance.resp.obj["Attributes"].get("SerialNumber"): instances["Comments"]["SerialNumber"] = instance.resp.obj["Attributes"]["SerialNumber"] if instance.resp.obj.get("SerialNumber"): instances["Comments"]["SerialNumber"] = instance.resp.obj["SerialNumber"] except KeyError: pass for instance in monolith.iter("Manager."): if instance.resp.obj.get("FirmwareVersion"): instances["Comments"]["iLOVersion"] = instance.resp.obj["FirmwareVersion"] return instances def download_path(self, paths, crawl=True, path_refresh=False): """Loads paths into the monolith. :param paths: list of paths to download :type paths: list :param path_refresh: Flag to reload the paths or not. :type path_refresh: bool. :param crawl: Flag to determine if load should traverse found links. :type crawl: boolean. """ if not paths: return try: list( map( lambda x: self.monolith.load( path=x, init=False, path_refresh=path_refresh, crawl=crawl, includelogs=True, ), paths, ) ) except Exception as excp: try: if excp.errno == 10053: raise SessionExpired() except: raise excp else: raise excp def get_model(self, currdict, attributeregistry, latestschema=None, newarg=None, proppath=None): """Returns a model and possibly a bios model for the current instance's schema/registry. This model can be used to read schema data and validate patches. :param currdict: The dictionary to gather the schema model from. :type currdict: dict :param attributeregistry: The current systems attribute registry. If not gathering a bios registry this can be set to None. :type attributeregistry: dict :param latestschema: Flag to determine if we should drop the schema version when we try to match schema information. If True, the version will be dropped. :type latestschema: bool :param newargs: List of multi level properties to be gathered. :type newargs: list :param proppath: The path of the schema you want to validate (from Location header). :type proppath: str :returns: model and bios model """ type_str = self.typepath.defs.typestring bsmodel = None valobj = self.validationmanager model = valobj.get_registry_model( currtype=currdict[type_str], newarg=newarg, latestschema=latestschema, proppath=proppath, ) if not attributeregistry and model: return model, bsmodel if not model and not attributeregistry: LOGGER.warning("Unable to locate registry/schema for %s\n", currdict[type_str]) return None, None attrval = currdict.get("AttributeRegistry", None) attrval = list(attributeregistry.values())[0] if not attrval and attributeregistry else attrval bsmodel = valobj.get_registry_model( currtype=attrval if attrval else currdict[type_str], newarg=newarg, latestschema=latestschema, searchtype=self.typepath.defs.attributeregtype, ) return model, bsmodel def _build_monolith(self, path=None, includelogs=False, skipbuild=False, json_out=False): """Run through the RIS tree to build monolith :param path: path to initiate login to. :type path: str. :param includelogs: flag to determine id logs should be downloaded. :type includelogs: boolean. :param skipbuild: if true, skip build of monolith (initialize empty) :type skipbuild: True """ self.monolith = RisMonolith(self.current_client, self.typepath) if not skipbuild: self.monolith.load(path=path, includelogs=includelogs, init=True, json_out=json_out) else: self.monolith.update_member( resp=self.current_client.root, path=self.current_client.default_prefix, init=False, ) def _modifiedpath(self, results, delete=False, replace=False): """Check the path and set the modified flag :param delete: Flag to delete the path in the results :type delete: bool :param replace: Flag to replace the path from the results :type replace: bool :param results: Response for the path :type results: RestResponse """ if not results or results.status not in (200, 201): return path = results.path path = path.split("/Actions")[0] if "Actions" in path else path path = path + "/" if self.typepath.defs.isgen10 and path[-1] != "/" else path if not replace and path in self.monolith.paths: self.monolith.paths[path].modified = True _ = self.monolith.markmodified(path) if delete and path in self.monolith.paths: self.monolith.removepath(path) if replace and path in self.monolith.paths: self.monolith.paths[path].modified = True self.monolith.paths[path].patches = [] def _checkforchange(self, paths, crawl=True): """Check if the given paths have been modified and updates monolith if it has :param paths: paths to be checked :type paths: list """ (pathtoetag, _) = self._gettypeswithetag() mono = self.monolith self.download_path(list(paths), crawl=crawl, path_refresh=True) etags = [None if path not in mono.paths else mono.paths[path].etag for path in paths] sametag = [ path for ind, path in enumerate(paths) if path in pathtoetag and path in self.monolith.paths and pathtoetag[path] != etags[ind] ] for path in sametag: self.monolith.paths[path].patches = [] if sametag: LOGGER.warning( "The data in the following paths have been updated. " "Recheck the changes made to . %s", ",".join([str(path) for path in sametag]), ) def _updatemono(self, currtype=None, path=None, crawl=False, path_refresh=False): """Check if type/path exists in current monolith :param entrytype: the found entry type. :type entrytype: str. :param currtype: the current entry type. :type currtype: str. :param crawl: flag to determine if load should traverse found links. :type crawl: boolean. """ monolith = self.monolith currtype = None if currtype == '"*"' else currtype paths = set() if currtype: for path, resp in monolith.paths.items(): if currtype and currtype.lower() not in resp.maj_type.lower(): continue if path_refresh or not resp: paths.add(path) if resp: try: if not resp.dict: raise AttributeError except AttributeError: paths.add(path) if resp.modified: paths.add(path) paths.update(monolith.checkmodified(path) if path in monolith.ctree else set()) elif path: if monolith.paths and not list(monolith.paths)[0][-1] == "/": path = path[:-1] if path[-1] == "/" else path if path_refresh or not monolith.path(path): paths.add(path) if path in monolith.paths and monolith.paths[path].modified: paths.add(path) paths.update(monolith.checkmodified(path) if path in monolith.ctree else set()) if paths: self._checkforchange(list(paths), crawl=crawl) def _verifyschemasdownloaded(self, monolith): """Function to verify that the schema has been downloaded :param monolith: full data model retrieved from server. :type monolith: dict. """ schemaid = self.typepath.schemapath regid = self.typepath.regpath if not (schemaid and regid): warning_handler("Missing Schemas or registries.") return None schemacoll = next(monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None) if not schemacoll or any( paths.lower() == schemaid and monolith.paths[paths] for paths in monolith.typesadded[schemacoll] ): self.download_path([schemaid], crawl=False) schemacoll = next(monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None) regcoll = next(monolith.gettypename(self.typepath.defs.regfilecollectiontype), None) if not regcoll or any( paths.lower() == regid and monolith.paths[paths] for paths in monolith.typesadded[regcoll] ): self.download_path([regid], crawl=False) regcoll = next(monolith.gettypename(self.typepath.defs.regfilecollectiontype), None) return any( paths.lower() in (schemaid.lower(), regid.lower()) and monolith.paths[paths] for paths in monolith.paths ) def _validatechanges( self, instance=None, attributeregistry=None, latestschema=None, proppath=None, newdict=None, oridict=None, unique=False, ): """Validate the changes that are requested by the user. :param newdict: dictionary with only the properties that have changed :type newdict: dict. :param oridict: selection dictionary with current state. :type oridict: dict. :param unique: flag to determine override for unique properties. :type unique: str. :param iloversion: current iLO version. :type iloversion: float. :param instance: current selection instance. :type instance: RisMonolithMemberv100. :param attrreg: Registry entry of the given attribute. :type attrreg: RepoRegistryEntry. """ entrymono = self.monolith currtype = oridict[self.typepath.defs.typestring] validation_manager = self.validationmanager errors, warnings = validation_manager.validatedict( newdict, currtype=attributeregistry[instance.maj_type] if attributeregistry else currtype, monolith=entrymono, unique=unique, searchtype=self.typepath.defs.attributeregtype if attributeregistry else None, latestschema=latestschema, proppath=proppath, ) validation_errors = errors for warninngs in warnings: warning_handler(warninngs, override=True) if validation_errors and len(validation_errors) > 0: raise ValidationError(validation_errors) checkallowablevalues(newdict=newdict, oridict=oridict) def _getinstances(self, selector=None, path_refresh=False, crawl=False): """Main function to get instances of particular type and reload :param selector: the type selection for the get operation. :type selector: str. :param setenable: flag to determine if registry should also be returned. :type setenable: boolean. :param setenable: flag to determine if registry should also be returned. :type setenable: boolean. :param path_refresh: flag to reload the selected instances. :type path_refresh: boolean. :returns: returns a list of selected items """ instances = list() selector = self.selector if not selector else selector if selector: selector = ".".join(selector.split("#")[-1].split(".")[:2]) if self.monolith: self._updatemono(currtype=selector, crawl=crawl, path_refresh=path_refresh) if not selector: return instances selector = None if selector == '"*"' else selector if self.monolith: if self.redfishinst.is_redfish: instances = [ inst for inst in self.monolith.iter(selector) if inst.maj_type not in ["object", "string"] and "redfish" in inst.path ] else: instances = [ inst for inst in self.monolith.iter(selector) if inst.maj_type not in ["object", "string"] and "rest" in inst.path ] _ = [setattr(inst, "patches", []) for inst in instances if path_refresh] return instances def _checkpostpatch(self, body=None, path=None, patch=False): """Make the post file compatible with the system generation :param body: contents to be checked :type body: str. :param path: The URL location to check :type path: str. :param service: flag to determine if minimum calls should be done. :type service: boolean. :param url: originating url. :type url: str. :param sessionid: session id to be used instead of iLO credentials. :type sessionid: str. :param headers: additional headers to be added to the request. :type headers: str. :param iloresponse: flag to return the iLO response. :type iloresponse: str. :param silent: flag to determine if no output should be done. :type silent: boolean. :param patch: flag to determine if a patch is being made :type patch: boolean. :returns: modified body and path parameter for target and action respectively """ try: if self.typepath.defs.flagforrest: if "Target" not in body and not patch: if "/Oem/Hp" in path: body["Target"] = self.typepath.defs.oempath if path.startswith("/redfish/v1"): path = path.replace("/redfish", "/rest", 1) if "/Actions/" in path: ind = path.find("/Actions/") path = path[:ind] if path.endswith("/"): path = path[:-1] elif path.startswith("/rest/") and self.typepath.defs.isgen9: results = self.get_handler(put_path=path, service=True, silent=True) if results and results.status == 200: if results.dict: if "Target" in body: actions = results.dict["Oem"][self.typepath.defs.oemhp]["Actions"] elif "Actions" in body: actions = results.dict["Actions"] else: return path, body allkeys = list(actions.keys()) targetkey = [x for x in allkeys if x.endswith(body["Action"])] if targetkey[0].startswith("#"): targetkey[0] = targetkey[0][1:] path = path.replace("/rest", "/redfish", 1) path = path + "/Actions" if "Target" in body: path = path + self.typepath.defs.oempath del body["Target"] if targetkey: path = path + "/" + targetkey[0] + "/" return path, body except Exception as excp: raise excp def _checkforetagchange(self, instance=None): """Function to check the status of the etag :param instance: retrieved instance to check etag for change. :type instance: dict. """ if instance: path = instance.path (oldtag, _) = self._gettypeswithetag() self._updatemono(path=path, path_refresh=True) (newtag, _) = self._gettypeswithetag() if (oldtag[path] != newtag[path]) and self.typepath.defs.hpilodatetimetype not in instance.maj_type: warning_handler( "The property you are trying to change " "has been updated. Please check entry again " " before manipulating it.\n" ) raise ValueChangedError() def _gettypeswithetag(self): """Gathers etags of all paths in monolith and their type associations""" instancepath = dict() instances = dict() for inst in self.monolith.iter(): instancepath[inst.path] = inst.maj_type instances[inst.path] = inst.etag return [instances, instancepath] python-ilorest-5.3.0.0a/src/redfish/ris/utils.py0000664000175000017500000004763414702427156021450 0ustar carstencarsten### # Copyright 2020 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 -*- """Utility functions for internal and external use. Contains general json navigating functions as well as some monolith utility functions.""" import copy import logging import re import sys import six if six.PY3: from functools import reduce try: from collections import Mapping except ImportError: from collections.abc import Mapping import jsonpath_rw from six import iterkeys, string_types from redfish.ris.rmc_helper import IncorrectPropValue try: # itertools ifilter compatibility for python 2 from future_builtins import filter except ImportError: # filter function provides the same functionality in python 3 pass # ---------Debug logger--------- LOGGER = logging.getLogger() # ---------End of debug logger--------- def print_handler(msg): """Helper function for handling warning messages appropriately. If LOGGER level is set to 40 print out the warnings, else log them as a warning. :param msg: The warning message. :type msg: str """ # if override: sys.stdout.write(msg) # else: # if LOGGER.getEffectiveLevel() == 40: # LOGGER.warning(msg) def warning_handler(msg, override=False): """Helper function for handling warning messages appropriately. If LOGGER level is set to 40 print out the warnings, else log them as a warning. :param msg: The warning message. :type msg: str """ # if override: # sys.stdout.write(msg) # else: # if LOGGER.getEffectiveLevel() == 40: LOGGER.warning(msg) def validate_headers(instance, verbose=False): """Validates an instance is patchable. :param instance: Instance of the property to check. :type instance: :class:`redfish.ris.RisMonolithMemberv100` :param verbose: Flag to print or log more information. :type verbose: bool :returns: True if the setting is not patchable, False if it is. """ skip = False try: headervals = instance.resp.getheaders() for kii, val in headervals.items(): if kii.lower() == "allow": if "PATCH" not in val: if verbose: warning_handler("Skipping read-only path: %s\n" % instance.resp.request.path) skip = True except: pass return skip def merge_dict(currdict, newdict): """Merges dictionaries together. :param currdict: Dictionary that will absorb the second. :type currdict: dict :param newdict: Dictionary to merge into the first. :type newdict: dict """ for k, itemv2 in list(newdict.items()): itemv1 = currdict.get(k) if isinstance(itemv1, Mapping) and isinstance(itemv2, Mapping): merge_dict(itemv1, itemv2) elif isinstance(itemv1, list) and isinstance(itemv2, list): j = 0 for i in itemv2: if isinstance(i, dict): for k, v in i.items(): if k not in [d.keys() for d in itemv1]: itemv1[j][k] = v j = j + 1 else: itemv1.append(i) else: currdict[k] = itemv2 def get_errmsg_type(results): """Return the registry type of a response. :param results: rest response. :type results: :class:`redfish.rest.containers.RestResponse` :returns: A Registry Id type string, None if not match is found, or no_id if the response is not an error message :rtype: None or string """ message_type = None try: jsonpath_expr = jsonpath_rw.parse("$..MessageId") messageid = [match.value for match in jsonpath_expr.find(results.dict)] if not messageid: jsonpath_expr = jsonpath_rw.parse("$..MessageID") messageid = [match.value for match in jsonpath_expr.find(results.dict)] if messageid: message_type = messageid[0].split(".")[0] except: pass return message_type def filter_output(output, sel, val): """Filters a list of dictionaries based on a key:value pair only returning the dictionaries that include the key and value. :param output: List of dictionaries to check for the key:value. :type output: list :param sel: the key for the property to be filtered by. :type sel: str :param val: value for the property be filtered by. :type val: str :returns: A filtered list from output parameter :rtype: list """ # TODO: check if this can be replaced by navigatejson newoutput = [] if isinstance(output, list): for entry in output: if isinstance(entry, dict): if "/" in sel: sellist = sel.split("/") newentry = copy.copy(entry) for item in sellist: if item in list(newentry.keys()): if item == sellist[-1] and str(newentry[item]) == str(val): newoutput.append(entry) else: newentry = newentry[item] else: if sel in list(entry.keys()) and entry[sel] == val: newoutput.append(entry) else: return output return newoutput def checkallowablevalues(newdict=None, oridict=None): """Validate dictionary changes with Redfish allowable values. This will raise an :class:`redfish.ris.rmc_helper.IncorrectPropValue` error if the dictionary is not valid. :param newdict: dictionary with only the properties that have changed. :type newdict: dict :param oridict: Full dictionary with current state. (Includes @Redfish.AllowableValues) :type oridict: dict """ for strmatch in re.finditer("@Redfish.AllowableValues", str(oridict)): propname = str(oridict)[: strmatch.start()].split("'")[-1] strtomatch = "$..'{0}@Redfish.AllowableValues'".format(propname) jsonpath_expr = jsonpath_rw.parse(strtomatch) matches = jsonpath_expr.find(oridict) if matches: for match in matches: fullpath = str(match.full_path) if "Actions" in fullpath: continue checkpath = fullpath.split("@Redfish.AllowableValues")[0] jexpr2 = jsonpath_rw.parse(checkpath) valmatches = jexpr2.find(newdict) if valmatches: for mat in valmatches: res = [val for val in match.value if mat.value.lower() == val.lower()] if not res: raise IncorrectPropValue( "Incorrect Value " "entered. Please enter one of the below " "values for {0}:\n{1}".format("/".join(checkpath.split(".")), str(match.value)[1:-1]) ) def navigatejson(selector, currdict, val=None): """Function for navigating the json dictionary. Searches a dictionary for specific keys and possibly values, returning only the dictionary sections for the requested keys and values. :param selector: the property required from current dictionary. :type selector: list :param val: value to be filtered by. :type val: str or int or bool :param currdict: json dictionary of list to be filtered :type currdict: json dictionary/list :returns: returns a dictionary of selected items """ # TODO: Check for val of different types(bool, int, etc) temp_dict = dict() createdict = lambda y, x: {x: y} getkey = lambda cdict, sel: next((item for item in iterkeys(cdict) if sel.lower() == item.lower()), sel) getval = lambda cdict, sele: [cdict[sel] if sel in cdict else "~!@#$%^&*)()" for sel in [getkey(cdict, sele)]][0] fullbreak = False seldict = copy.deepcopy(currdict) for ind, sel in enumerate(selector): if isinstance(seldict, dict): selector[ind] = getkey(seldict, sel) seldict = getval(seldict, sel) if seldict == "~!@#$%^&*)()": return None if val and ind == len(selector) - 1: cval = ",".join(seldict) if isinstance(seldict, (list, tuple)) else seldict if not ( (val[-1] == "*" and str(cval).lower().startswith(val[:-1].lower())) or str(cval).lower() == val.lower() ): fullbreak = True elif isinstance(seldict, (list, tuple)): returndict = [] for items in seldict: correctcase = selector[ind:] returnseldict = navigatejson(correctcase, items) selector[ind:] = correctcase if returnseldict is not None: returndict.append(returnseldict) if returndict: seldict = returndict else: fullbreak = True if seldict: seldict = {selector[ind - 1]: seldict} selsdict = reduce(createdict, [seldict] + selector[: ind - 1][::-1]) merge_dict(temp_dict, selsdict) return temp_dict else: break else: fullbreak = True break if fullbreak: return None else: selsdict = reduce(createdict, [seldict] + selector[::-1]) merge_dict(temp_dict, selsdict) return temp_dict def iterateandclear(dictbody, proplist): """Iterate over a dictionary and remove listed properties. :param dictbody: json body :type dictbody: dict or list :param proplist: property list :type proplist: list """ if isinstance(dictbody, dict): _ = [dictbody.pop(key) for key in proplist if key in dictbody] for key in dictbody: dictbody[key] = iterateandclear(dictbody[key], proplist) if isinstance(dictbody, list): for ind, val in enumerate(dictbody): dictbody[ind] = iterateandclear(val, proplist) return dictbody def skipnonsettingsinst(instances): """Removes non /settings sections. Useful for only returning settings monolith members. Example: Members with paths `/redfish/v1/systems/1/bios/` and `/redfish/v1/systems/1/bios/settings` will return only the `/redfish/v1/systems/1/bios/settings` member. :param instances: list of :class:`redfish.ris.ris.RisMonolithMemberv100` instances to check for settings paths. :type instances: list :returns: list of :class:`redfish.ris.ris.RisMonolithMemberv100` setting instances :rtype: list """ instpaths = [inst.path.lower() for inst in instances] cond = list(filter(lambda x: x.endswith(("/settings", "settings/")), instpaths)) paths = [path.split("settings/")[0].split("/settings")[0] for path in cond] newinst = [inst for inst in instances if inst.path.lower() not in paths] return newinst def getattributeregistry(instances, adict=None): # add try except return {} after test """Gets an attribute registry in given monolith instances. :param instances: list of :class:`redfish.ris.ris.RisMonolithMemberv100` instances to be checked for attribute registry. :type instances: list :param adict: A dictionary containing an AttributeRegistry :type adict: dict :return: returns a dictionary containing the attribute registry string(s) :rtype: dict """ if adict: return adict.get("AttributeRegistry", None) newdict = {} for inst in instances: try: if "AttributeRegistry" in inst.resp.dict: if inst.defpath is not None: if not ("bios/settings" in inst.defpath): newdict[inst.maj_type] = inst.resp.obj["AttributeRegistry"] return newdict newdict[inst.maj_type] = inst.resp.obj["AttributeRegistry"] except AttributeError: LOGGER.warning("Invalid/Unpopulated Response: %s\nType:%s\nPath:%s\n" % (inst.resp, inst.type, inst.path)) return newdict def diffdict(newdict=None, oridict=None, settingskipped=[False]): """Diff two dictionaries, returning only the values that are different between the two. :param newdict: The first dictionary to check for differences. :type newdict: dict :param oridict: The second dictionary to check for differences. :type oridict: dict :param settingskipped: Flag to determine if any settings were missing. :type settingskipped: list :returns: dictionary with only the properties that have changed. :rtype: dict """ try: if newdict == oridict: return {} except: try: if set(newdict) == set(oridict): return {} except: pass newdictkeys = list(newdict.keys()) newdictlist = [] if type(oridict) is list: oridict = oridict[0] newdictlist.append(newdict) oridictkeys = list(oridict.keys()) newdictkeyslower = [ki.lower() for ki in newdictkeys] oridictkeyslower = [ki.lower() for ki in list(oridict.keys())] missingkeys = list(set(newdictkeyslower) - set(oridictkeyslower)) for kis in missingkeys: del newdict[newdictkeys[newdictkeyslower.index(kis)]] warning_handler("Attribute {0} not found in the selection...".format(kis)) settingskipped = [True] for key, val in list(newdict.items()): if key not in oridict: keycase = oridictkeys[oridictkeyslower.index(key.lower())] del newdict[key] key = keycase newdict[key] = val if isinstance(val, dict): res = diffdict(newdict[key], oridict[key]) if res: newdict[key] = res else: del newdict[key] elif isinstance(val, list): if val == oridict[key]: del newdict[key] continue if len(val) == 1 and isinstance(val[0], dict): if newdict[key] and oridict[key]: res = diffdict(newdict[key][0], oridict[key][0], settingskipped) if res: newdict[key][0] = res else: del newdict[key] if [li for li in val if not isinstance(li, string_types)]: continue else: if val: if [va.lower() for va in val] == [va.lower() if va else va for va in oridict[key]]: del newdict[key] # TODO: check if lowercase is correct or buggy for string types elif isinstance(val, (string_types, int, type(None))): if newdict[key] == oridict[key]: del newdict[key] if not newdictlist: return newdict else: return newdictlist def json_traversal(data, key_to_find, ret_dict=False): """ PENDING MODIFICATION TO MORE GENERALIZED NOTATION Recursive function to traverse a JSON resposne object and retrieve the array of relevant data (value or full key/value pair). Only a single key needs to be found within the dictionary in order to return a valid dictionary or value. #Intended Usage: - Error response message parsing - Checkreadunique in Validation :param data: json data to be parsed :type data: JSON error response object :param key_to_find: JSON key to be found :type key_to_find: String :param ret_dict: return dictionary instead of just value :type ret_dict: boolean :returns: value or dictionary containing 'key_to_find' (and all additional keys at the same level). """ try: for i, _iter in enumerate(data): try: if _iter == data: return None except Exception: pass try: if key_to_find.lower() == _iter.lower(): if ret_dict: return data else: return data[_iter] except Exception: pass try: if key_to_find.lower() in [str(_.lower()) for _ in _iter.keys()]: if ret_dict: return data else: return data[_iter] except Exception: pass _tmp = None try: if isinstance(data[_iter], dict): for k in data[_iter].keys(): if k.lower() == key_to_find.lower(): if ret_dict: return data[_iter] else: return data[_iter][k] _tmp = json_traversal(data[_iter], key_to_find, ret_dict) elif isinstance(data[_iter], list) or isinstance(data[_iter], tuple): try: _tmp = json_traversal(data[i], key_to_find, ret_dict) except Exception: _tmp = json_traversal(data[_iter], key_to_find, ret_dict) except Exception: _tmp = json_traversal(data[i], key_to_find, ret_dict) finally: if _tmp: return _tmp except Exception: pass def json_traversal_delete_empty(data, old_key=None, _iter=None, remove_list=None): """ Recursive function to traverse a dictionary and delete things which match elements in the remove_list :param data: to be truncated :type data: list or dict :param old_key: key from previous recursive call (higher in stack) :type old_key: dictionary key :param _iter: iterator tracker for list (tracks iteration across recursive calls) :type _iter: numerical iterator :param remove_list: list of items to be removed :type: list :returns: none """ if not remove_list: remove_list = ["NONE", None, "", {}, [], "::", "0.0.0.0", "Unknown"] list_quick_scan = False if isinstance(data, list): if _iter is None: for idx, val in enumerate(data): if idx is (len(data) - 1): list_quick_scan = True json_traversal_delete_empty(val, old_key, idx, remove_list) if list_quick_scan: for j in remove_list: for _ in range(data.count(j)): data.remove(j) elif isinstance(data, dict): delete_list = [] for key, value in data.items(): if ( (isinstance(value, dict) and len(value) < 1) or (isinstance(value, list) and len(value) < 1) or None or value in remove_list or key in remove_list ): delete_list.append(key) else: json_traversal_delete_empty(value, key, remove_list=remove_list) # would be great to not need this section; however, # since recursive deletion is not possible, this is needed # if you can figure out how to pass by reference then fix me! if (isinstance(value, dict) and len(value) < 1) or None or value in remove_list: delete_list.append(key) for dl_entry in delete_list: try: del data[dl_entry] except KeyError: pass python-ilorest-5.3.0.0a/src/redfish/ris/rmc_api.py0000664000175000017500000000052214702427156021703 0ustar carstencarstenfrom rmc import RmcApp class Rmc_API(RmcApp): def __init__(self, Args=None): RmcApp.__init__(self, Args) def Api_login(self, url=None, username=None, password=None): self.login(base_url=url, username=username, password=password) # for testing if __name__ == "__main__": api = Rmc_API() api.do_stuff() python-ilorest-5.3.0.0a/src/redfish/ris/gen_compat.py0000664000175000017500000004164314702427156022416 0ustar carstencarsten### # Copyright 2020 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 -*- """Compatibility functionality in between iLO versions and generic Redfish/LegacyRest servers. Used to provide convenient string variables that are usable on any iLO irrespective of version or API type.""" # ---------Imports--------- import logging from redfish import LegacyRestClient, RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from redfish.ris.rmc_helper import ( NothingSelectedError, UnableToObtainIloVersionError, UserNotAdminError, ) # ---------End of imports--------- LOGGER = logging.getLogger(__name__) # TODO: This will be a part of the compatability class class Typesandpathdefines(object): """The global types and path definitions class. Holds information on a system and automatically creates the correct type strings along with some generic paths. Paths are meant to be used with iLO systems. Paths may be different on generic Redfish systems. Self variables are created when the `getgen` function is called. Useful self variables that are created include: * **url**: The url of the system that the defines were created for. * **defs**: The string defines for the system that was passed to `getgen`. Includes key property keys, paths, types, and flags to check what the system type is. * **ilogen**: The iLO generation of the system that the defines were created for. For non-iLO Redfish systems this is set to **5**. * **iloversion**: The iLO version of the system that the defines were created for. * **flagiften**: Flag is set to true if the system is Gen 10 or a non-iLO Redfish system. """ def __init__(self): self.url = None self.defs = None self.ilogen = None self.iloversion = None self.flagiften = False self.adminpriv = True def getgen( self, gen=None, url=None, username=None, password=None, sessionid=None, logger=None, proxy=None, ca_cert_data={}, isredfish=True, login_otp=None, log_dir=None, ): """Function designed to verify the servers platform. Will generate the `Typeandpathdefines` variables based on the system type that is detected. :param url: The URL to perform the request on. :type url: str :param username: The username to login with. :type username: str :param password: The password to login with. :type password: str :param proxy: The proxy to connect to the system with. :type proxy: str :param ca_certs: Dictionary including the TLS certificate information of user based authentication :type ca_certs: dict :param isredfish: The flag to force redfish conformance on iLO 4 systems. You will still need to call `updatedefinesflag` to update the defines to Redfish. :type isredfish: bool :param logger: The logger handler to log data too uses the default if none is provided. :type logger: str """ if self.adminpriv is False and url.startswith("blob"): raise UserNotAdminError("") self.url = url self.is_redfish = isredfish self.gencompany = self.rootresp = False self.ilogen = 5 # If no iLO or Anonymous data , default to iLO 5 types logger = logger if not logger else LOGGER client = None self.noschemas = False self.schemapath = self.regpath = "" if not gen: try_count = 0 try: client = RedfishClient( base_url=self.url, username=username, password=password, sessionid=sessionid, proxy=proxy, ca_cert_data=ca_cert_data, login_otp=login_otp, log_dir=log_dir, ) client._get_root() except ServerDownOrUnreachableError as excp: if self.is_redfish: raise excp try_count += 1 if not self.is_redfish: try: restclient = LegacyRestClient( base_url=self.url, username=username, password=password, sessionid=sessionid, proxy=proxy, ca_cert_data=ca_cert_data, login_otp=login_otp ) restclient._get_root() # Check that the response is actually legacy rest and not a redirect _ = restclient.root.obj.Type self.is_redfish = False client = restclient except Exception as excp: try_count += 1 if not client: logger.info("Gen get rest error:" + str(excp) + "\n") raise excp else: self.is_redfish = True if try_count > 1: raise ServerDownOrUnreachableError( "Server not reachable or does not support " "HPRest or Redfish \n" ) rootresp = client.root.obj self.rootresp = rootresp client.logout() self.gencompany = next(iter(self.rootresp.get("Oem", {}).keys()), None) in ( "Hpe", "Hp", ) comp = "Hp" if self.gencompany else None comp = "Hpe" if rootresp.get("Oem", {}).get("Hpe", None) else comp if comp and next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType", None): self.ilogen = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType") self.ilover = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get( "ManagerFirmwareVersion" ) if self.ilogen.split(" ")[-1] == "CM": # Assume iLO 4 types in Moonshot self.ilogen = 4 self.iloversion = None else: self.iloversion = float(self.ilogen.split(" ")[-1] + "." + "".join(self.ilover.split("."))) else: self.ilogen = int(gen) try: if not isinstance(self.ilogen, int): self.ilogen = int(self.ilogen.split(" ")[-1]) self.flagiften = True if int(self.ilogen) >= 5 else False except: raise UnableToObtainIloVersionError("Unable to find the iLO generation.") self.noschemas = ( True if self.rootresp and "JsonSchemas" in self.rootresp and not self.rootresp.get("JsonSchemas", None) else False ) if self.noschemas: self.ilogen = self.ilover = self.iloversion = None if self.rootresp and not self.noschemas: self.defineregschemapath(self.rootresp) if self.flagiften: self.defs = Definevalstenplus() else: self.defs = DefinevalsNine() def defineregschemapath(self, rootobj): """Defines the schema and registry paths using data in root path. :param rootobj: The root path data. :type rootobj: dict. """ self.gencompany = next(iter(rootobj.get("Oem", {}).keys()), None) in ("Hpe", "Hp") self.schemapath = ( rootobj["JsonSchemas"]["@odata.id"] if rootobj.get("JsonSchemas", None) else rootobj["links"]["Schemas"]["href"] ) self.schemapath = ( self.schemapath.rstrip("/") + "/?$expand=." if self.is_redfish and self.flagiften and self.gencompany else self.schemapath ) self.regpath = ( rootobj["Registries"]["@odata.id"] if rootobj.get("Registries", None) else rootobj["links"]["Registries"]["href"] ) self.regpath = ( self.regpath.rstrip("/") + "/?$expand=." if self.is_redfish and self.flagiften and self.gencompany else self.regpath ) # TODO: Move these to a compatability class def updatedefinesflag(self, redfishflag=None): """Updates the redfish and rest flag depending on system and redfishflag input. On an iLO 5 system or another Redfish system, this will do nothing. On an iLO 4 system with both Redfish and LegacyRest this will update the defines to redfish if the *redfishflag* is set to True and stay with the LegacyRest defines otherwise. :param redfishflag: User input for redfish :type redfishflag: bool :returns: True if the system should use Redfish, False for legacy Rest. :rtype: bool """ if self.defs: is_redfish = redfishflag or self.defs.isgen10 self.defs.flagforrest = not is_redfish if is_redfish: self.defs.redfishchange() return is_redfish else: return redfishflag def modifyselectorforgen(self, sel): """Changes the select to match the Generation's HP string based to the correct type for specific iLO versions. :param sel: query to be changed to match Generation's HP string :type sel: str :returns: A modified selector matching the Generation's HP string. :rtype: string """ if not sel: raise NothingSelectedError() sel = sel.lower() returnval = sel if sel.startswith(("hpeeskm", "#hpeeskm", "hpeskm", "#hpeskm")): returnval = self.defs.hpeskmtype elif "bios." in sel[:9].lower(): returnval = self.defs.biostype elif sel.startswith(("hpe", "#hpe")) and self.defs and self.defs.isgen9: returnval = sel[:4].replace("hpe", "hp") + sel[4:] elif not sel.startswith(("hpe", "#hpe")) and self.defs and self.defs.isgen10: returnval = sel[:3].replace("hp", "hpe") + sel[3:] return returnval class Definevals(object): """Base class for setting platform dependent variables.""" def __init__(self): pass class Definevalstenplus(Definevals): """Platform dependent variables for iLO 5+ (Gen 10).""" # pylint: disable=too-many-instance-attributes # As a defines class this will need all the attributes def __init__(self): self.oemhp = "Hpe" self.oempath = "/Oem/Hpe" self.startpath = "/redfish/v1/" self.systempath = "/redfish/v1/Systems/1/" self.managerpath = "/redfish/v1/Managers/1/" self.biospath = "/redfish/v1/systems/1/bios/" self.addlicensepath = "/redfish/v1/Managers/1/LicenseService/" self.accountspath = "/redfish/v1/AccountService/Accounts/" self.federationpath = "/redfish/v1/Managers/1/FederationGroups/" self.resourcedirpath = "/redfish/v1/ResourceDirectory/" self.biostype = "Bios." self.hpeskmtype = "HpeESKM." self.hpcommontype = "HpeCommon" self.hpilossotype = "HpeiLOSSO." self.hpsecureboot = "SecureBoot." self.logservicetype = "#LogService." self.iscsisource = "iSCSISources" self.iscsiattemptinstance = "iSCSIAttemptInstance" self.iscsiattemptname = "iSCSIAttemptName" self.hphttpscerttype = "HpeHttpsCert." self.snmpservice = "HpeiLOSnmpService." self.attributenametype = "AttributeName" self.hpilodatetimetype = "HpeiLODateTime." self.attributeregtype = "#AttributeRegistry." self.hpilofirmwareupdatetype = "UpdateService." self.resourcedirectorytype = "HpeiLOResourceDirectory." self.hpilofederationgrouptype = "HpeiLOFederationGroup." self.managernetworkservicetype = "ManagerNetworkProtocol." self.schemafilecollectiontype = "#JsonSchemaFileCollection." self.regfilecollectiontype = "#MessageRegistryFileCollection." self.hpilolicensecollectiontype = "HpeiLOLicenseCollection." self.hpiloactivehealthsystemtype = "#HpeiLOActiveHealthSystem." self.securityservice = "HpeSecurityService." self.hpiscsisoftwareinitiatortype = "HpeiSCSISoftwareInitiator." self.hpilofederationgrouptypecoll = "HpeiLOFederationGroupCollection." self.bootoverridetargettype = "BootSourceOverrideTarget@Redfish.AllowableValues" self.messageregistrytype = "#MessageRegistry." self.typestring = "@odata.type" self.hrefstring = "@odata.id" self.collectionstring = "Members" self.biossettingsstring = "@Redfish.Settings" self.attname = "AttributeName" self.iscsistring = "iSCSISources" self.isgen9 = False self.isgen10 = True self.flagforrest = False super(Definevalstenplus, self).__init__() def redfishchange(self): """Empty function to update redfish variables (unneeded when the system is already redfish).""" pass class DefinevalsNine(Definevals): """Platform dependent variables for iLO 4 LegacyRest (Gen 9).""" # pylint: disable=too-many-instance-attributes # As a defines class this will need all the attributes def __init__(self): self.oemhp = "Hp" self.oempath = "/Oem/Hp" self.startpath = "/rest/v1" self.systempath = "/rest/v1/Systems/1" self.managerpath = "/rest/v1/Managers/1" self.biospath = "/rest/v1/systems/1/bios" self.addlicensepath = "/rest/v1/Managers/1/LicenseService" self.accountspath = "/rest/v1/AccountService/Accounts" self.federationpath = "/rest/v1/Managers/1/FederationGroups" self.resourcedirpath = "/rest/v1/ResourceDirectory" self.biostype = "HpBios." self.hpeskmtype = "HpESKM." self.hpcommontype = "HpCommon" self.hpilossotype = "HpiLOSSO." self.snmpservice = "SnmpService." self.attributenametype = "Name" self.logservicetype = "LogService." self.iscsisource = "iSCSIBootSources" self.iscsiattemptinstance = "iSCSIBootAttemptInstance" self.iscsiattemptname = "iSCSIBootAttemptName" self.hpsecureboot = "HpSecureBoot." self.hphttpscerttype = "HpHttpsCert." self.hpilodatetimetype = "HpiLODateTime." self.hpilofirmwareupdatetype = "HpiLOFirmwareUpdate." self.resourcedirectorytype = "HpiLOResourceDirectory." self.hpilofederationgrouptype = "HpiLOFederationGroup." self.attributeregtype = "HpBiosAttributeRegistrySchema." self.schemafilecollectiontype = "#SchemaFileCollection." self.regfilecollectiontype = "#SchemaFileCollection." self.managernetworkservicetype = "ManagerNetworkService." self.hpiloactivehealthsystemtype = "HpiLOActiveHealthSystem." self.messageregistrytype = "MessageRegistry." self.hpilolicensecollectiontype = None self.hpilofederationgrouptypecoll = None self.bootoverridetargettype = "BootSourceOverrideSupported" self.hpiscsisoftwareinitiatortype = "HpiSCSISoftwareInitiator" self.typestring = "Type" self.hrefstring = "href" self.collectionstring = "Items" self.biossettingsstring = "SettingsResult" self.attname = "Name" self.iscsistring = "iSCSIBootSources" self.isgen9 = True self.isgen10 = False self.flagforrest = True super(DefinevalsNine, self).__init__() def redfishchange(self): """Function to update redfish variables from LegacyRest to iLO 4 Redfish (Gen 9).""" self.startpath = "/redfish/v1/" self.systempath = "/redfish/v1/Systems/1/" self.managerpath = "/redfish/v1/Managers/1/" self.biospath = "/redfish/v1/systems/1/bios/" self.addlicensepath = "/redfish/v1/Managers/1/LicenseService/" self.resourcedirpath = "/redfish/v1/ResourceDirectory/" self.typestring = "@odata.type" self.hrefstring = "@odata.id" self.collectionstring = "Members" self.logservicetype = "#LogService." self.hpiloactivehealthsystemtype = "#HpiLOActiveHealthSystem." self.hpilolicensecollectiontype = "HpiLOLicenseCollection." self.hpilofederationgrouptypecoll = "HpiLOFederationGroupCollection." self.managernetworkservicetype = "ManagerNetworkProtocol." self.flagforrest = False python-ilorest-5.3.0.0a/src/redfish/ris/sharedtypes.py0000664000175000017500000000332214702427156022625 0ustar carstencarsten### # Copyright 2020 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 -*- """Shared types used in this library.""" # ---------Imports--------- import logging import jsonpatch from redfish.rest.containers import JSONEncoder # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- class JSONEncoder(JSONEncoder): """Custom JSONEncoder that understands our types""" def default(self, obj): """Set defaults :param obj: json object. :type obj: str. """ if isinstance(obj, Dictable): return obj.to_dict() elif isinstance(obj, set): return list(obj) elif isinstance(obj, jsonpatch.JsonPatch): return obj.patch return super(JSONEncoder, self).default(obj) class Dictable(object): """A base class which adds the to_dict method used during json encoding""" def to_dict(self): """Overridable funciton""" raise RuntimeError("You must override this method in your derived class") python-ilorest-5.3.0.0a/src/redfish/ris/config.py0000664000175000017500000001125314702427156021541 0ustar carstencarsten### # Copyright 2019 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 -*- """Module for working with global configuration options.""" #---------Imports--------- import os import re import logging import six from six.moves import configparser #---------End of imports--------- #---------Debug logger--------- LOGGER = logging.getLogger(__name__) #---------End of debug logger--------- class AutoConfigParser(object): """Auto configuration parser""" # properties starting with _ac__ are automatically # serialized to config file _config_pattern = re.compile(r'_ac__(?P.*)') def __init__(self, filename=None): """Initialize AutoConfigParser :param filename: file name to be used for config loading. :type filename: str. """ self._sectionname = 'globals' self._configfile = filename def _get_ac_keys(self): """Retrieve parse option keys""" result = [] for key in six.iterkeys(self.__dict__): match = AutoConfigParser._config_pattern.search(key) if match: result.append(match.group('confkey')) return result def _get(self, key): """Retrieve parse option key :param key: key to retrieve. :type key: str. """ ackey = '_ac__%s' % key.replace('-', '_') if ackey in self.__dict__: return self.__dict__[ackey] return None def _set(self, key, value): """Set parse option key :param key: key to be set. :type key: str. :param value: value to be given to key. :type value: str. """ ackey = '_ac__%s' % key.replace('-', '_') if ackey in self.__dict__: self.__dict__[ackey] = value return None def load(self, filename=None): """Load configuration settings from the file filename, if filename is None then the value from get_configfile() is used :param filename: file name to be used for config loading. :type filename: str. """ fname = self.get_configfile() if filename: fname = filename if not fname or not os.path.isfile(fname): return try: config = configparser.RawConfigParser() config.read(fname) for key in self._get_ac_keys(): configval = None try: configval = config.get(self._sectionname, key) except configparser.NoOptionError: # also try with - instead of _ try: configval = config.get(self._sectionname, key.replace('_', '-')) except configparser.NoOptionError: pass if configval: ackey = '_ac__%s' % key self.__dict__[ackey] = configval except configparser.NoOptionError: pass except configparser.NoSectionError: pass def save(self, filename=None): #TODO: Maybe unused """Save configuration settings from the file filename, if filename is None then the value from get_configfile() is used :param filename: file name to be used for config saving. :type filename: str. """ fname = self.get_configfile() if filename: fname = filename if fname: return config = configparser.RawConfigParser() try: config.add_section(self._sectionname) except configparser.DuplicateSectionError: pass # ignored for key in self._get_ac_keys(): ackey = '_ac__%s' % key config.set(self._sectionname, key, str(self.__dict__[ackey])) fileh = open(self._configfile, 'wb') config.write(fileh) fileh.close() def get_configfile(self): """ The current configuration file location""" return self._configfile python-ilorest-5.3.0.0a/tox.ini0000664000175000017500000000110014702427156016213 0ustar carstencarsten# tox (https://tox.readthedocs.io/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py27, pylint, py3 [testenv:py27] changedir = tests deps = pytest mock commands = pytest --junit-xml={toxinidir}/python2.xml [testenv:py3] changedir = tests deps = pytest commands = pytest --junit-xml={toxinidir}/python3.xml [testenv:pylint] deps = pylint==1.6.1 commands = - bash {toxinidir}/pylint.shpython-ilorest-5.3.0.0a/.project0000664000175000017500000000057214702427156016363 0ustar carstencarsten redfish org.python.pydev.PyDevBuilder org.python.pydev.pythonNature python-ilorest-5.3.0.0a/examples/0000775000175000017500000000000014702427156016526 5ustar carstencarstenpython-ilorest-5.3.0.0a/examples/Legacy_Rest/0000775000175000017500000000000014702427156020727 5ustar carstencarstenpython-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_service.py0000664000175000017500000000431214702427156024630 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_service(restobj, bios_properties, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(bios_path, bios_properties, bios_password) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 33: Set BIOS Service.\n") set_bios_service(REST_OBJ, {'ServiceName':'HPE', 'ServiceEmail':'me@hpe.com'}) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/reset_server.py0000664000175000017500000000367614702427156024025 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def reset_server(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ComputerSystem." in instance.Type: system_path = instance.href break body = dict() body["Action"] = "Reset" body["ResetType"] = "ForceRestart" response = restobj.post(system_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nExample 4: Reset a server\n") reset_server(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/dump_iml.py0000664000175000017500000000510614702427156023111 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def dump_iml(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "LogService." in instance.Type: if instance["href"].endswith("IML"): IML_path = instance.href break tmp = restobj.get(IML_path) for entry in tmp.dict["links"]["Entries"]: response = restobj.get(entry["href"]) print_log_entries(response.dict["Items"]) while 'NextPage' in response.dict["links"]: response = restobj.get(entry["href"] + '?page=' + \ str(response.dict["links"]['NextPage']['page'])) print_log_entries(response.dict["Items"]) def print_log_entries(log_entries): for log_entry in log_entries: sys.stdout.write(log_entry["Severity"] + ": Class " + \ str(log_entry["Oem"]["Hp"]["Class"]) + \ " / Code " + str(log_entry["Oem"]["Hp"]["Code"]) + \ ":\t" + log_entry["Message"] + "\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 22: Dump Integrated Management Log\n") dump_iml(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/change_temporary_boot_order.py0000664000175000017500000000506014702427156027047 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def change_temporary_boot_order(restobj, boottarget, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ComputerSystem." in instance.Type: system_path = instance.href break response = restobj.get(system_path) bootoptions = response.dict["Boot"] if boottarget not in bootoptions["BootSourceOverrideSupported"]: sys.stderr.write("ERROR: %s is not a supported boot option.\n" % boottarget) return body = dict() body["Boot"] = dict() body["Boot"]["BootSourceOverrideTarget"] = boottarget #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(system_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 8: Change temporary boot order (one time boot" \ " or temporary override)\n") change_temporary_boot_order(REST_OBJ, "Hdd") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_uefi_shell_startup.py0000664000175000017500000000460614702427156027077 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_uefi_shell_startup(restobj, uefienabled="Enabled", \ networkpath="", urlpath="", \ bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password body = dict() body["UefiShellStartup"] = uefienabled body["UefiShellStartupLocation"] = networkpath body["UefiShellStartupUrl"] = urlpath response = restobj.patch(bios_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() set_bios_uefi_shell_startup(REST_OBJ, "Enabled", "10.0.0.0", "test.com") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/add_ilo_user_account.py0000664000175000017500000000541614702427156025454 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. """ An example of adding a user account by iLO privileges """ import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def add_ilo_user_account(restobj, new_ilo_loginname, new_ilo_username, \ new_ilo_password, irc=False, cfg=False, \ virtual_media=False, usercfg=False, vpr=False): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Collection." in instance.Type: if instance.MemberType.startswith("ManagerAccount."): accounts_path = instance.href break body = {"UserName": new_ilo_loginname, "Password": new_ilo_password, "Oem": {}} body["Oem"]["Hp"] = {} body["Oem"]["Hp"]["LoginName"] = new_ilo_username body["Oem"]["Hp"]["Privileges"] = {} body["Oem"]["Hp"]["Privileges"]["RemoteConsolePriv"] = irc body["Oem"]["Hp"]["Privileges"]["iLOConfigPriv"] = cfg body["Oem"]["Hp"]["Privileges"]["VirtualMediaPriv"] = virtual_media body["Oem"]["Hp"]["Privileges"]["UserConfigPriv"] = usercfg body["Oem"]["Hp"]["Privileges"]["VirtualPowerAndResetPriv"] = vpr response = restobj.post(accounts_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 10: Create an iLO User Account\n") add_ilo_user_account(REST_OBJ, "name", "username", "password") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/computer_details.py0000664000175000017500000001151014702427156024642 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def computer_details(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ComputerSystem." in instance.Type: system_path = instance.href break response = restobj.get(system_path) sys.stdout.write("\tManufacturer: " + \ str(response.dict["Manufacturer"]) + "\n") sys.stdout.write("\tModel: " + str(response.dict["Model"]) + "\n") sys.stdout.write("\tSerial Number: " + \ str(response.dict["SerialNumber"]) + "\n") if "VirtualSerialNumber" in response.dict: sys.stdout.write("\tVirtual Serial Number: " + str(response.dict["VirtualSerialNumber"]) + "\n") else: sys.stderr.write("\tVirtual Serial Number information not " \ "available on system resource\n") sys.stdout.write("\tUUID: " + str(response.dict["UUID"]) + "\n") if "VirtualUUID" in response.dict["Oem"]["Hp"]: sys.stdout.write("\tVirtualUUID: " + \ str(response.dict["Oem"]["Hp"]["VirtualUUID"]) + "\n") else: sys.stderr.write("\tVirtualUUID not available system " \ "resource\n") if "AssetTag" in response.dict: sys.stdout.write("\tAsset Tag: " + response.dict["AssetTag"] \ + "\n") else: sys.stderr.write("\tNo Asset Tag information on system " \ "resource\n") sys.stdout.write("\tBIOS Version: " + \ response.dict["Bios"]["Current"]["VersionString"] + "\n") sys.stdout.write("\tMemory: " + str(response.dict["Memory"]["TotalSystemMemoryGB"]) +" GB\n") sys.stdout.write("\tProcessors: " + \ str(response.dict["Processors"]["Count"]) + " x " + \ str(response.dict["Processors"]["ProcessorFamily"])+ "\n") if "Status" not in response.dict or "Health" not in \ response.dict["Status"]: sys.stdout.write("\tStatus/Health information not available in " "system resource\n") else: sys.stdout.write("\tHealth: " + \ str(response.dict["Status"]["Health"]) + "\n") if "HostCorrelation" in response.dict: if "HostFQDN" in response.dict["HostCorrelation"]: sys.stdout.write("\tHost FQDN: " + \ response.dict["HostCorrelation"]["HostFQDN"] + "\n") if "HostMACAddress" in response.dict["HostCorrelation"]: for mac in response.dict["HostCorrelation"]["HostMACAddress"]: sys.stdout.write("\tHost MAC Address: " + str(mac) + "\n") if "HostName" in response.dict["HostCorrelation"]: sys.stdout.write("\tHost Name: " + \ response.dict["HostCorrelation"]["HostName"] + "\n") if "IPAddress" in response.dict["HostCorrelation"]: for ip_address in response.dict["HostCorrelation"]["IPAddress"]: if ip_address: sys.stdout.write("\tHost IP Address: " + str(ip_address) + "\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 16: Dump host computer details\n") computer_details(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/clear_iml.py0000664000175000017500000000373414702427156023237 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def clear_iml(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "LogService." in instance.Type: if instance["href"].endswith("IML"): IEL_path = instance.href break body = {"Action": "ClearLog"} response = restobj.post(instance["href"], body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 24: Clear Integrated Management Log\n") clear_iml(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_csr.py0000664000175000017500000000427114702427156022733 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_csr(restobj, filename): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpHttpsCert." in instance.Type: https_path = instance.href break response = restobj.get(https_path) try: csr_response = response.dict["CertificateSigningRequest"] with open(filename, 'wb') as csroutput: csroutput.write(csr_response) csroutput.close() sys.stdout.write("\tCSR Data saved successfully as "+ filename + "\n") except KeyError: sys.stdout.write("\tCSR cannot be accessed right now, please try again later") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 50: Get CSR\n") get_csr(REST_OBJ, "csr.txt") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/enable_secure_boot.py0000664000175000017500000000424714702427156025127 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def enable_secure_boot(restobj, secure_boot_enable, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpSecureBoot." in instance.Type: secure_boot_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password body = {"SecureBootEnable": secure_boot_enable} response = restobj.patch(secure_boot_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 5: Enable/Disable UEFI Secure Boot\n") enable_secure_boot(REST_OBJ, False) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_active_ilo_nic.py0000664000175000017500000000551414702427156025130 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_active_ilo_nic(restobj, shared_nic): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break selected_nic_uri = None tmp = restobj.get(instance["href"]) response = restobj.get(tmp.dict["links"]["EthernetNICs"]["href"]) for nic in response.dict["Items"]: try: if (nic["Oem"]["Hp"]["SupportsFlexibleLOM"] == True and shared_nic == True): selected_nic_uri = nic["links"]["self"]["href"] break except KeyError: pass try: if (nic["Oem"]["Hp"]["SupportsLOM"] == True and shared_nic == True): selected_nic_uri = nic["links"]["self"]["href"] break except KeyError: pass if not shared_nic: selected_nic_uri = nic["links"]["self"]["href"] break elif not selected_nic_uri: sys.stderr.write("\tShared NIC is not supported\n") break if selected_nic_uri: body = {"Oem": {"Hp": {"NICEnabled": True}}} response = restobj.patch(selected_nic_uri, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 21: Set the active iLO NIC\n") set_active_ilo_nic(REST_OBJ, False) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/mount_virtual_media_iso.py0000664000175000017500000000472714702427156026234 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def mount_virtual_media_iso(restobj, iso_url, boot_on_next_server_reset): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break rsp = restobj.get(manager_path) rsp = restobj.get(rsp.dict["links"]["VirtualMedia"]["href"]) for vmlink in rsp.dict["links"]["Member"]: response = restobj.get(vmlink["href"]) if response.status == 200 and "DVD" in response.dict["MediaTypes"]: body = {"Image": iso_url} if (iso_url is not None and boot_on_next_server_reset is not None): body["Oem"] = {"Hp": {"BootOnNextServerReset": boot_on_next_server_reset}} response = restobj.patch(vmlink["href"], body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 17: Mount iLO Virtual Media DVD ISO from URL\n") mount_virtual_media_iso(REST_OBJ, "http://10.0.0.100/test.iso", True) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/change_boot_order.py0000664000175000017500000000457414702427156024756 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def change_boot_order(restobj, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ServerBootSettings." in instance.Type: if "PATCH" in instance.HttpMethods: boot_settings_path = instance.href break response = restobj.get(boot_settings_path) bootorder = response.dict["PersistentBootConfigOrder"] #TODO: Need to change the persistent boot order here body = dict() body["PersistentBootConfigOrder"] = bootorder #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(boot_settings_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 7: Change Boot Order (UEFI)\n") change_boot_order(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/dump_eskm_event_log.py0000664000175000017500000000374514702427156025340 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def dump_eskm_event_log(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break response = restobj.get(eskm_path) for entry in response.dict["ESKMEvents"]: sys.stdout.write(entry["Timestamp"] + "\n" + entry["Event"] + "\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 41: Dump ESKM Event Log\n") dump_eskm_event_log(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_schema.py0000664000175000017500000000442114702427156023401 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_schema(restobj, schema_prefix): sys.stdout.write("\nEXAMPLE 27: Find and return schema " + \ schema_prefix + "\n") response = restobj.get("/rest/v1/Schemas") for schema in response.dict["Items"]: if schema["Schema"].startswith(schema_prefix): for location in schema["Location"]: extref_uri = location["Uri"]["extref"] response = restobj.get(extref_uri) if response.status == 200: sys.stdout.write("\tFound " + schema_prefix + " at " + extref_uri + "\n") return else: sys.stderr.write("\t" + schema_prefix + " not found at " + extref_uri + "\n") return sys.stderr.write("Registry " + schema_prefix + " not found.\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() get_schema(REST_OBJ, "ComputerSystem") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/change_bios_setting.py0000664000175000017500000000432614702427156025304 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def change_bios_setting(restobj, bios_property, property_value, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break body = {bios_property: property_value} #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(bios_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 3: Change a BIOS setting\n") change_bios_setting(REST_OBJ, "AdminName", "admin") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/dump_ilo_event_log.py0000664000175000017500000000470014702427156025154 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def dump_ilo_event_log(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "LogService." in instance.Type: if instance["href"].endswith("IEL"): IEL_path = instance.href break tmp = restobj.get(IEL_path) for entry in tmp.dict["links"]["Entries"]: response = restobj.get(entry["href"]) print_log_entries(response.dict["Items"]) while 'NextPage' in response.dict["links"]: response = restobj.get(entry["href"] + '?page=' + \ str(response.dict["links"]['NextPage']['page'])) print_log_entries(response.dict["Items"]) sys.stdout.write("%s" % response) def print_log_entries(log_entries): for log_entry in log_entries: sys.stdout.write(log_entry["Message"] + "\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 23: Dump iLO Event Log\n") dump_ilo_event_log(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_ilo_ip.py0000664000175000017500000000431514702427156023416 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_ilo_ip(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break response = restobj.get(manager_path) ethernet_rsp = restobj.get(response.dict["links"]["EthernetNICs"]["href"]) for item in ethernet_rsp.dict["Items"]: if not item["IPv4Addresses"][0]["Address"] == "0.0.0.0": sys.stdout.write("\t" + item["IPv4Addresses"][0]["Address"] + "\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # While this example can be run remotely, it is used locally to locate the # iLO IP address SYSTEM_URL = None LOGIN_ACCOUNT = None LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" # SYSTEM_URL = "https://10.0.0.100" # LOGIN_ACCOUNT = "admin" # LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 52: Get iLO IP locally\n") get_ilo_ip(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/configure_snmp.py0000664000175000017500000000373414702427156024326 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def configure_snmp(restobj, snmp_mode, snmp_alerts): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "SnmpService." in instance.Type: snmp_path = instance.href break body = {"Mode": snmp_mode, "AlertsEnabled": snmp_alerts} response = restobj.patch(snmp_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 26: Configure iLO SNMP Settings\n") configure_snmp(REST_OBJ, "Agentless", False) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/update_ilo_firmware.py0000664000175000017500000000424414702427156025326 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def update_ilo_firmware(restobj, fw_url=None, tpm_flag=False): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLOFirmwareUpdate." in instance.Type: update_path = instance.href break body = dict() body["Action"] = "InstallFromURI" body["FirmwareURI"] = fw_url body["TPMOverrideFlag"] = tpm_flag response = restobj.post(update_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # While this example can be run remotely, it is used locally to locate the # iLO IP address #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 53: Update iLO Firmware\n") update_ilo_firmware(REST_OBJ, "http://test.com/ilo4_244.bin", False) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/bios_revert_default.py0000664000175000017500000000373014702427156025333 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def bios_revert_default(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break body = {"BaseConfig": "default"} response = restobj.put(bios_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 6: Revert BIOS settings to default\n") bios_revert_default(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_ESKM.py0000664000175000017500000000667714702427156022717 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys import json from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_ESKM(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break response = restobj.get(eskm_path) sys.stdout.write("\tPrimaryKeyServerAddress: " + json.dumps(response.dict["PrimaryKeyServerAddress"]) + "\n") sys.stdout.write("\tPrimaryKeyServerPort: " + json.dumps(response.dict["PrimaryKeyServerPort"]) + "\n") sys.stdout.write("\tSecondaryKeyServerAddress: " + json.dumps(response.dict["SecondaryKeyServerAddress"])\ + "\n") sys.stdout.write("\tSecondaryKeyServerPort: " + json.dumps(response.dict["SecondaryKeyServerPort"])\ + "\n") sys.stdout.write("\tType: " + json.dumps(response.dict["Type"]) + "\n") sys.stdout.write("\tKeyServerRedundancyReq: " + json.dumps(response.dict["KeyServerRedundancyReq"]) + "\n") sys.stdout.write("\tAccountGroup: " + json.dumps(response.dict["KeyManagerConfig"]\ ["AccountGroup"]) + "\n") sys.stdout.write("\tESKMLocalCACertificateName: " + json.dumps(response.dict["KeyManagerConfig"]\ ["ESKMLocalCACertificateName"]) + "\n") sys.stdout.write("\tImportedCertificateIssuer: " + json.dumps(response.dict["KeyManagerConfig"]\ ["ImportedCertificateIssuer"]) + "\n") sys.stdout.write("\tESKMEvents: " + json.dumps(response.dict["ESKMEvents"]) + "\n") tmp = response.dict["ESKMEvents"] for entry in tmp: sys.stdout.write("\tTimestamp : " + entry["Timestamp"] + "Event: " + json.dumps(entry["Event"]) + "\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 42: Get ESKM configuration\n") get_ESKM(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/sessions.py0000664000175000017500000000436614702427156023160 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from urllib3.util import parse_url from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def sessions(restobj, login_account, login_password): new_session = {"UserName": login_account, "Password": login_password} response = restobj.post("/rest/v1/Sessions", new_session) sys.stdout.write("%s" % response) if response.status == 201: session_uri = response.getheader("location") session_uri = parse_url(session_uri) sys.stdout.write("\tSession " + session_uri.path + " created\n") x_auth_token = response.getheader("x-auth-token") sys.stdout.write("\tSession key " + x_auth_token + " created\n") # Delete the created session sessresp = restobj.delete(session_uri.path) sys.stdout.write("%s" % response) else: sys.stderr.write("ERROR: failed to create a session.\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) sys.stdout.write("\nEXAMPLE 14: Create/Use/Delete a user session\n") sessions(REST_OBJ, "admin", "admin123") python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_resource_directory.py0000664000175000017500000000375014702427156026060 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient def get_resource_directory(restobj): response = restobj.get("/rest/v1/resourcedirectory") resources = [] if response.status == 200: resources = response.obj.Instances else: sys.stderr.write("\tResource directory missing at /rest/v1/resourcedirectory" + "\n") return resources if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 1: Find the resource directory " + "\n") resources = get_resource_directory(REST_OBJ) for resource in resources: try: sys.stdout.write("\t" + str(resource["@odata.type"]) + \ "\n\t\t" + str(resource["@odata.id"]) + "\n") except KeyError: pass REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_ESKM_PrimaryKeyServer.py0000664000175000017500000000430314702427156026256 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_ESKM_PrimaryKeyServer(restobj, PrimaryKeyServerAddress, PrimaryKeyServerPort): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break body = dict() body["PrimaryKeyServerAddress"] = PrimaryKeyServerAddress body["PrimaryKeyServerPort"] = int(PrimaryKeyServerPort) response = restobj.patch(eskm_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" PrimaryKeyServerAddress = "10.0.0.100" PrimaryKeyServerPort = "9000" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 37: Set ESKM Primary Key Server\n") set_ESKM_PrimaryKeyServer(REST_OBJ, PrimaryKeyServerAddress, PrimaryKeyServerPort) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_LogicalDrives.py0000664000175000017500000000456514702427156024701 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys import json from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_LogicalDrives(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Collection.1.0.0" in instance.Type: if "HpSmartStorageArrayController." in instance.MemberType: array_controllers = instance.href break collection = restobj.get(array_controllers) for instance in collection.obj.links.Member: controller = restobj.get(instance.href) sys.stdout.write("Location: %s\n" % controller.obj.Location) logicaldrivecollection = restobj.get(controller.obj.links.LogicalDrives.href) for logicaldrive in logicaldrivecollection.obj.links.Member: drive = restobj.get(logicaldrive.href) sys.stdout.write("\t%s\n" % json.dumps(drive.dict)) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) sys.stdout.write("\nEXAMPLE 44: Dump LogicalDrive details\n") REST_OBJ.login() get_LogicalDrives(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_license_key.py0000664000175000017500000000401314702427156024444 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_license_key(restobj, iLO_Key): sys.stdout.write("\nEXAMPLE 31: Set iLO License Key\n") resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break rsp = restobj.get(manager_path) body = dict() body["LicenseKey"] = iLO_Key response = restobj.post(rsp.dict["Oem"]["Hp"]["links"]["LicenseService"]["href"], body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() set_license_key(REST_OBJ, "test_iLO_Key") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_server_asset_tag.py0000664000175000017500000000367714702427156025531 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_server_asset_tag(restobj, asset_tag): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ComputerSystem." in instance.Type: system_path = instance.href break body = {"AssetTag": asset_tag} response = restobj.patch(system_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 18: Set Computer Asset Tag\n") set_server_asset_tag(REST_OBJ, "assettaghere") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/import_ssl.py0000664000175000017500000000410214702427156023471 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def import_ssl(restobj, filename): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpHttpsCert." in instance.Type: https_path = instance.href break with open(filename, 'r') as csr_data: ssl_cert = csr_data.read() csr_data.close() body = dict() body["Action"] = "ImportCertificate" body["Certificate"] = ssl_cert response = restobj.post(https_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 51: Import SSL Certificate\n") import_ssl(REST_OBJ, "ssl.txt") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_ESKM_username_password.py0000664000175000017500000000453114702427156026537 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_ESKM_username_password(restobj, username, password, accountgroup): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break body = dict() body["KeyManagerConfig"] = dict() body["KeyManagerConfig"]["LoginName"] = username body["KeyManagerConfig"]["Password"] = password body["KeyManagerConfig"]["AccountGroup"] = accountgroup body["KeyManagerConfig"]["ESKMLocalCACertificateName"] = "" response = restobj.patch(eskm_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" eskm_username = "admin" eskm_password = "password" eskm_accountgroup = "group" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 38: Set ESKM username, password\n") set_ESKM_username_password(REST_OBJ, eskm_username, eskm_password, eskm_accountgroup) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/modify_ilo_user_account.py0000664000175000017500000000737714702427156026223 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def modify_ilo_user_account(restobj, ilo_login_name_to_modify, \ new_ilo_loginname, new_ilo_username, new_ilo_password, \ irc=None, cfg=None, virtual_media=None, usercfg=None, vpr=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Collection." in instance.Type: if "ManagerAccount." in instance.MemberType: accounts_path = instance.href break accounts = restobj.get(accounts_path) for account in accounts.obj.Items: for account in accounts.dict["Items"]: if account["UserName"] == ilo_login_name_to_modify: body = {} body_oemhp = {} body_oemhp_privs = {} # if new loginname or password specified if new_ilo_password: body["Password"] = new_ilo_password if new_ilo_loginname: body["UserName"] = new_ilo_loginname # if different username specified if new_ilo_username: body_oemhp["LoginName"] = new_ilo_username # if different privileges were requested (None = no change) if irc != None: body_oemhp_privs["RemoteConsolePriv"] = irc if virtual_media != None: body_oemhp_privs["VirtualMediaPriv"] = virtual_media if cfg != None: body_oemhp_privs["iLOConfigPriv"] = cfg if usercfg != None: body_oemhp_privs["UserConfigPriv"] = usercfg if vpr != None: body_oemhp_privs["VirtualPowerAndResetPriv"] = vpr # component assembly if len(body_oemhp_privs): body_oemhp["Privileges"] = body_oemhp_privs if len(body_oemhp): body["Oem"] = {"Hp": body_oemhp} newrsp = restobj.patch(account["links"]["self"]["href"], body) sys.stdout.write("%s" % newrsp) return sys.stderr.write("Account not found\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 11: Modify an iLO user account\n") modify_ilo_user_account(REST_OBJ, "name", "newname", "newusername", "newpassword") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/dump_ilo_nic.py0000664000175000017500000000650114702427156023744 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def dump_ilo_nic(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break manager = restobj.get(manager_path) response = restobj.get(manager.dict["links"]["EthernetNICs"]["href"]) for nic in response.dict["Items"]: if nic["Status"]["State"] == "Enabled": sys.stdout.write("\t" + nic["Name"] + "\n") if "MacAddress" not in nic: sys.stderr.write("\tNo MacAddress information available (no" " 'MacAddress' property in NIC resource)\n") else: sys.stdout.write("\tMAC: " + str(nic["MacAddress"]) + "\n") sys.stdout.write("\tSpeed: " + str(nic["SpeedMbps"]) + "\n") sys.stdout.write("\tAutosense: " + str(nic["Autosense"]) + "\n") sys.stdout.write("\tFull Duplex: " + str(nic["FullDuplex"]) + "\n") if "FQDN" not in nic: sys.stderr.write("\tNo FQDN information available\n") else: sys.stdout.write("\tFQDN: " + str(nic["FQDN"]) + "\n") for addr in nic["IPv4Addresses"]: sys.stdout.write("\tIPv4 Address: " + addr["Address"] + " from " + addr["AddressOrigin"] + "\n") if "IPv6Addresses" not in nic: sys.stderr.write("\tIPv6Addresses information not "\ "available\n") else: for addr in nic["IPv6Addresses"]: sys.stdout.write("\tIPv6 Address: " + addr["Address"] + " from " + addr["AddressOrigin"] + "\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 13: Get iLO NIC state\n") dump_ilo_nic(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_iscsi.py0000664000175000017500000000455714702427156024315 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_iscsi(restobj, bios_properties, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiSCSISoftwareInitiator." in instance.Type: if "PUT" in instance.HttpMethods: iscsi_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(iscsi_path, bios_properties) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 35: Set ISCSI Config\n") set_bios_iscsi(REST_OBJ, {"iSCSIBootAttemptInstance": 2, \ "iSCSIBootAttemptName": \ "Updated Attempt Name", \ "iSCSIConnectRetry": 5}) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_base_registry.py0000664000175000017500000000457714702427156025017 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_base_registry(restobj): response = restobj.get("/rest/v1/Registries") messages = {} identifier = None for entry in response.dict["Items"]: if "Id" in entry: identifier = entry["Id"] else: identifier = entry["Schema"].split(".")[0] if identifier not in ["Base", "iLO"]: continue for location in entry["Location"]: reg_resp = restobj.get(location["Uri"]["extref"]) if reg_resp.status == 200: sys.stdout.write("\tFound " + identifier + " at " + \ location["Uri"]["extref"] + "\n") messages[identifier] = reg_resp.dict["Messages"] else: sys.stdout.write("\t" + identifier + " not found at "\ + location["Uri"]["extref"] + "\n") return messages if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 2: Find and return registry " + "\n") get_base_registry(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/clear_ahs_data.py0000664000175000017500000000362214702427156024216 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def clear_ahs_data(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLOActiveHealthSystem." in instance.Type: AHS_path = instance.href break body = {"Action": "ClearLog"} response = restobj.post(AHS_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 47: Clear AHS Data\n") clear_ahs_data(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/generate_csr.py0000664000175000017500000000510214702427156023740 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def generate_csr(restobj, csr_properties): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpHttpsCert." in instance.Type: https_path = instance.href break body = dict() body["Action"] = "GenerateCSR" body["City"] = csr_properties["City"] body["CommonName"] = csr_properties["CommonName"] body["Country"] = csr_properties["Country"] body["OrgName"] = csr_properties["OrgName"] body["OrgUnit"] = csr_properties["OrgUnit"] body["State"] = csr_properties["State"] response = restobj.post(https_path, body) sys.stdout.write("%s" % response) sys.stdout.write("Generating CSR, this may take a few minutes\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 49: Generate CSR\n") generate_csr(REST_OBJ, {"City" : "City",\ "CommonName": "Common Name",\ "Country": "US",\ "OrgName": "Organization",\ "OrgUnit": "Unit",\ "State": "State"}) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_powermetrics_average.py0000664000175000017500000000473414702427156026365 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_powermetrics_average(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "PowerMetrics." in instance.Type: power_path = instance.href break response = restobj.get(power_path) if "PowerMetrics" not in response.dict or \ "AverageConsumedWatts" not in response.dict["PowerMetrics"] or \ "IntervalInMin" not in response.dict["PowerMetrics"]: sys.stdout.write("\tPowerMetrics resource does not contain "\ "'AverageConsumedWatts' or 'IntervalInMin' property\n") else: sys.stdout.write("\t" + " AverageConsumedWatts = " + \ str(response.dict["PowerMetrics"]["AverageConsumedWatts"]) + \ " watts over a " + str(response.dict["PowerMetrics"]\ ["IntervalInMin"]) + " minute moving average\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 30: Report PowerMetrics Average Watts\n") get_powermetrics_average(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_dhcp.py0000664000175000017500000000464714702427156024121 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_dhcp(restobj, bios_properties, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password response = restobj.patch(bios_path, bios_properties) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 32: Set DHCP\n") set_bios_dhcp(REST_OBJ, {'Ipv4Address':'192.168.0.1', \ 'Ipv4Gateway':'192.168.0.2', \ 'Ipv4PrimaryDNS':'192.168.0.3', \ 'Ipv4SecondaryDNS':'192.168.0.4', \ 'Ipv4SubnetMask':'192.168.0.5'}) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/test_ESKM_connection.py0000664000175000017500000000366314702427156025326 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def test_ESKM_connection(restobj): sys.stdout.write("\nEXAMPLE 39: Test ESKM connection\n") resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break body = dict() body["Action"] = "TestESKMConnections" response = restobj.post(eskm_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() test_ESKM_connection(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/reset_ilo.py0000664000175000017500000000356714702427156023301 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def reset_ilo(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break body = {"Action": "Reset"} response = restobj.post(manager_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 19: Reset iLO\n") reset_ilo(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_license_key.py0000664000175000017500000000433414702427156024436 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_license_key(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLOLicense." in instance.Type: manager_path = instance.href break license_result = dict() licenseproperties = ["License", "LicenseKey", "LicenseType"] response = restobj.get(instance["href"]) license_result["License"] = response.dict["License"] for licenseproperty in licenseproperties: sys.stdout.write("\t" + licenseproperty + ": " + \ str(response.dict[licenseproperty]) + "\n") sys.stdout.write("%s" % response) return (license_result) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 45: Get iLO License Key\n") get_license_key(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_url_boot_file.py0000664000175000017500000000430414702427156026015 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_url_boot_file(restobj, path='', bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password body = {"UrlBootFile": path} response = restobj.patch(bios_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 36: Set BIOS url boot file\n") set_bios_url_boot_file(REST_OBJ, "http://test.test/test.efi") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_bios_password.py0000664000175000017500000000436714702427156025044 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_bios_password(restobj, new_password, bios_password=None): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpBios." in instance.Type: if "PUT" in instance.HttpMethods: bios_path = instance.href break #BIOS password is the password if secondary authentication is #required before entering RBSU screen Only required on Gen9 systems restobj.bios_password = bios_password body = {"AdminPassword": new_password, \ "OldAdminPassword": bios_password} response = restobj.patch(bios_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 48: Set Bios Password\n") set_bios_password(REST_OBJ, "newpassword", "biospassword") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_uid_light.py0000664000175000017500000000376214702427156024134 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_uid_light(restobj, uid): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "ComputerSystem." in instance.Type: system_path = instance.href break body = dict() if uid: body["IndicatorLED"] = "Lit" else: body["IndicatorLED"] = "Off" response = restobj.patch(system_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 15: Set UID Light on or off\n") set_uid_light(REST_OBJ, True) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_Ilo_ntp_servers.py0000664000175000017500000000442614702427156025337 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys import json from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_ilo_ntp_servers(restobj, ntp_servers): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLODateTime." in instance.Type: datetime_path = instance.href break response = restobj.get(datetime_path) sys.stdout.write("\tCurrent iLO Date/Time Settings: " + json.dumps(response.dict["ConfigurationSettings"]) + "\n") sys.stdout.write("\tCurrent iLO NTP Servers: " + json.dumps(response.dict["NTPServers"]) + "\n") body = {"StaticNTPServers": ntp_servers} response = restobj.patch(datetime_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 29: Set iLO's NTP Servers\n") set_ilo_ntp_servers(REST_OBJ, ["192.168.0.1", "192.168.0.2"]) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_ahs_data.py0000664000175000017500000000420214702427156023702 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_ahs_data(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLOActiveHealthSystem." in instance.Type: AHS_path = instance.href break sys.stdout.write("Fetching AHS Data, this may take minutes to hours\n") response = restobj.get(AHS_path) ahslink = restobj.get(response.dict["links"]["AHSLocation"]["extref"]) with open("data.ahs", 'wb') as ahsoutput: ahsoutput.write(ahslink.ori) ahsoutput.close() sys.stdout.write("AHS Data saved successfully as data.ahs\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 46: Get AHS Data\n") get_ahs_data(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_EncryptionSettings.py0000664000175000017500000000547214702427156026023 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_EncryptionSettings(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Collection.1.0.0" in instance.Type: if "HpSmartStorageArrayController." in instance.MemberType: array_controllers = instance.href break types = ["Name", "Model", "SerialNumber", "EncryptionBootPasswordSet",\ "EncryptionCryptoOfficerPasswordSet",\ "EncryptionLocalKeyCacheEnabled", "EncryptionMixedVolumesEnabled",\ "EncryptionPhysicalDriveCount", "EncryptionRecoveryParamsSet",\ "EncryptionStandaloneModeEnabled", "EncryptionUserPasswordSet"] collection = restobj.get(array_controllers) for instance in collection.obj.links.Member: response = restobj.get(instance.href) for item in types: sys.stdout.write("\tID: " + str(response.dict["@odata.id"]) + "\n") if item in response.dict: sys.stdout.write("\t" + item + ": " + str(response.dict[item]) + "\n") else: sys.stderr.write("\t" + item + "is not " \ "available on HpSmartStorageArrayController resource\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 43: Dump EncryptionSettings\n") get_EncryptionSettings(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/clear_ilo_event_log.py0000664000175000017500000000372014702427156025276 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def clear_ilo_event_log(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "LogService." in instance.Type: if instance["href"].endswith("IEL"): IEL_path = instance.href break body = {"Action": "ClearLog"} response = restobj.post(IEL_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 25: Clear iLO Event Log\n") clear_ilo_event_log(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/get_ilo_nic.py0000664000175000017500000000460014702427156023554 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys import json from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def get_ilo_nic(restobj, get_active): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break tmp = restobj.get(manager_path) response = restobj.get(tmp.dict["links"]["EthernetNICs"]["href"]) for nic in response.dict["Items"]: if get_active and nic["Status"]["State"] == "Enabled": sys.stdout.write("Active\t" + nic["links"]["self"]["href"] + \ ": " + json.dumps(nic) + "\n") elif get_active == False and nic["Status"]["State"] == "Disabled": sys.stdout.write("InActive\t" + nic["links"]["self"]["href"] + \ ": " + json.dumps(nic) + "\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 20: Get iLO's NIC configuration\n") get_ilo_nic(REST_OBJ, True) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/find_ilo_mac_address.py0000664000175000017500000000441514702427156025415 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def find_ilo_mac_address(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Manager." in instance.Type: manager_path = instance.href break tmp = restobj.get(manager_path) response = restobj.get(tmp.dict["links"]["EthernetNICs"]["href"]) for item in response.dict["Items"]: if "MacAddress" not in item: sys.stderr.write("\tNIC resource does not contain 'MacAddress' property\n") else: sys.stdout.write("\t" + item["Name"] + " = " + item["MacAddress"] + "\t(" + \ item["Status"]["State"] + ")\n") sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) sys.stdout.write("\nEXAMPLE 9: Find iLO's MAC Addresses\n") REST_OBJ.login() find_ilo_mac_address(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/remove_ilo_account.py0000664000175000017500000000431714702427156025162 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def remove_ilo_account(restobj, ilo_loginname_to_remove): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "Collection." in instance.Type: if "ManagerAccount." in instance.MemberType: accounts_path = instance.href break accounts = restobj.get(accounts_path) for account in accounts.dict["Items"]: if account["UserName"] == ilo_loginname_to_remove: newrsp = restobj.delete(account["links"]["self"]["href"]) sys.stdout.write("%s" % newrsp) return sys.stderr.write("Account not found\n") if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 12: Remove an iLO account\n") remove_ilo_account(REST_OBJ, "newname") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/set_ilo_timezone.py0000664000175000017500000000445514702427156024661 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def set_ilo_timezone(restobj, olson_timezone): sys.stdout.write("\tNOTE: This only works if iLO is NOT configured to " \ "take time settings from DHCP v4 or v6\n") resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpiLODateTime." in instance.Type: datetime_path = instance.href break response = restobj.get(datetime_path) for timezone in response.dict["TimeZoneList"]: if timezone["Name"].startswith(olson_timezone): body = {"TimeZone": {"Name": timezone["Name"]}} response = restobj.patch(datetime_path, body) sys.stdout.write("%s" % response) break if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://16.83.63.89" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 28: Set iLO's Timezone\n") set_ilo_timezone(REST_OBJ, "America/Chicago") REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/Legacy_Rest/reset_ESKM_eventlog.py0000664000175000017500000000365314702427156025154 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys from redfish import LegacyRestClient from get_resource_directory import get_resource_directory def reset_ESKM_eventlog(restobj): resource_instances = get_resource_directory(restobj) if resource_instances: #Get URI from resource directory for instance in resource_instances: if "HpESKM." in instance.Type: eskm_path = instance.href break body = dict() body["Action"] = "ClearESKMLog" response = restobj.post(eskm_path, body) sys.stdout.write("%s" % response) if __name__ == "__main__": # When running on the server locally use the following commented values # SYSTEM_URL = None # LOGIN_ACCOUNT = None # LOGIN_PASSWORD = None # When running remotely connect using the iLO secured (https://) address, # iLO account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Create a REST object REST_OBJ = LegacyRestClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REST_OBJ.login() sys.stdout.write("\nEXAMPLE 40: Reset ESKM event logs\n") reset_ESKM_eventlog(REST_OBJ) REST_OBJ.logout() python-ilorest-5.3.0.0a/examples/quickstart_redfish.py0000664000175000017500000000301314702427156022773 0ustar carstencarsten### # Copyright 2020 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 -*- """A quickstart example for RedfishClient""" import sys import redfish # When running on the server locally use the following commented values # HOST = "blobstore://." # LOGIN_ACCOUNT = "None" # LOGIN_PASSWORD = "None" # When running remotely connect using the iLO address, iLO account name, # and password to send https requests SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" ## Create a REDFISH object REDFISH_OBJ = redfish.RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT,\ password=LOGIN_PASSWORD) # Login into the server and create a session REDFISH_OBJ.login() # Do a GET on a given path RESPONSE = REDFISH_OBJ.get("/redfish/v1/systems/1") # Print out the response sys.stdout.write("%s\n" % RESPONSE) # Logout of the current session REDFISH_OBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/0000775000175000017500000000000014702427156020112 5ustar carstencarstenpython-ilorest-5.3.0.0a/examples/Redfish/eject_virtual_media_iso.py0000664000175000017500000001045614702427156025343 0ustar carstencarsten# Copyright 2016-2022 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of mounting virtual media for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def mount_virtual_media_iso(_redfishobj, media_type,): virtual_media_uri = None virtual_media_response = [] resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) virtual_media_uri = managers_members_response.obj['VirtualMedia']['@odata.id'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#VirtualMediaCollection.' in instance['@odata.type']: virtual_media_uri = instance['@odata.id'] if virtual_media_uri: virtual_media_response = _redfishobj.get(virtual_media_uri) for virtual_media_slot in virtual_media_response.obj['Members']: data = _redfishobj.get(virtual_media_slot['@odata.id']) if media_type in data.dict['MediaTypes']: virtual_media_mount_uri = data.obj['Actions']['#VirtualMedia.EjectMedia']['target'] post_body = {"Action": "HpeiLOVirtualMedia.EjectVirtualMedia"} resp = _redfishobj.post(virtual_media_mount_uri, post_body) if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO" "Extended Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) break if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #specify the type of content the media represents MEDIA_TYPE = "CD" #current possible options: Floppy, USBStick, CD, DVD # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() mount_virtual_media_iso(REDFISHOBJ, MEDIA_TYPE) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/reset_server.py0000664000175000017500000001275514702427156023206 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development, LP. # # 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. import sys import json import argparse from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError global DISABLE_RESOURCE_DIR from ilorest_util import get_resource_directory from ilorest_util import get_gen def reset_server(_redfishobj): managers_members_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Systems']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) else: #Use Resource directory to find the relevant URI for instance in resource_instances: if "ComputerSystem." in instance['@odata.type']: managers_members_uri = instance['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) if managers_members_response: path = managers_members_response.obj["Actions"]["#ComputerSystem.Reset"]["target"] body = dict() resettype = ['ForceRestart','GracefulRestart'] body["Action"] = "ComputerSystem.Reset" for reset in resettype: if reset.lower() == "forcerestart": body['ResetType'] = "ForceRestart" resp = _redfishobj.post(path, body) elif reset.lower() == "gracefulrestart": body['ResetType'] = "GracefulRestart" resp = _redfishobj.post(path, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) def reset_server_gen9(_redfishobj): managers_uri = "/redfish/v1/Systems/1/" managers_response = _redfishobj.get(managers_uri) system_path = managers_response.obj["Actions"]["#ComputerSystem.Reset"]["target"] print(system_path) body = dict() body["Action"] = "Reset" body["ResetType"] = "ForceRestart" resp = _redfishobj.post(system_path, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # Initialize parser parser = argparse.ArgumentParser(description = "Script to upload and flash NVMe FW") parser.add_argument( '-i', '--ilo', dest='ilo_ip', action="store", help="iLO IP of the server", default=None) parser.add_argument( '-u', '--user', dest='ilo_user', action="store", help="iLO username to login", default=None) parser.add_argument( '-p', '--password', dest='ilo_pass', action="store", help="iLO password to log in.", default=None) options = parser.parse_args() system_url = "https://" + options.ilo_ip print (system_url) # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = True try: # Create a Redfish client object redfish_obj = RedfishClient(base_url=system_url, username=options.ilo_user, password=options.ilo_pass) # Login with the Redfish client redfish_obj.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() (ilogen,_) = get_gen(redfish_obj) print ("Generation is ", ilogen) if int(ilogen) == 5: reset_server(redfish_obj) else: reset_server_gen9(redfish_obj) redfish_obj.logout() python-ilorest-5.3.0.0a/examples/Redfish/change_temporary_boot_order.py0000664000175000017500000001003014702427156026223 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of changing the temporary boot order """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def change_temporary_boot_order(_redfishobj, boottarget): systems_members_uri = None systems_members_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) else: for instance in resource_instances: if '#ComputerSystem.' in instance['@odata.type']: systems_members_uri = instance['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) if systems_members_response: print("\n\nShowing BIOS attributes before changes:\n\n") print(json.dumps(systems_members_response.dict.get('Boot'), indent=4, sort_keys=True)) body = {'Boot': {'BootSourceOverrideTarget': boottarget}} resp = _redfishobj.patch(systems_members_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("\nSuccess!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if systems_members_response: print("\n\nShowing boot override target:\n\n") print(json.dumps(systems_members_response.dict.get('Boot'), indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # string to represent the selected temporary boot device TEMP_DEVICE = "Hdd" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() change_temporary_boot_order(REDFISHOBJ, TEMP_DEVICE) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/computer_details.py0000664000175000017500000000607514702427156024037 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of gathering the computer system details """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def computer_details(_redfishobj): systems_members_uri = None systems_members_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#ComputerSystem.' in instance['@odata.type']: systems_members_uri = instance['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) print("\n\nPrinting computer system details:\n\n") print(json.dumps(systems_members_response.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://16.83.61.9" LOGIN_ACCOUNT = "Administrator" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() computer_details(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/enable_secure_boot.py0000664000175000017500000001065414702427156024311 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of enabling secure boot """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def enable_secure_boot(_redfishobj, secure_boot_enable): secure_boot_uri = None secure_boot_data = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) secure_boot_uri = systems_members_response.obj['SecureBoot']['@odata.id'] secure_boot_data = _redfishobj.get(secure_boot_uri) else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#SecureBoot.' in instance['@odata.type']: secure_boot_uri = instance['@odata.id'] secure_boot_data = _redfishobj.get(secure_boot_uri) if secure_boot_data: print("\n\nShowing Secure Boot properties before changes:\n\n") print(json.dumps(secure_boot_data.dict, indent=4, sort_keys=True)) if secure_boot_uri: body = {'SecureBootEnable': secure_boot_enable} resp = _redfishobj.patch(secure_boot_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception: sys.stderr.write("A response error occurred, unable to access iLO Extended " \ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("\nSuccess!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) print("\n\nShowing Secure Boot properties after changes:\n\n") secure_boot_data = _redfishobj.get(secure_boot_uri) print(json.dumps(secure_boot_data.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Properties: #secure boot enable property SECURE_BOOT_ENABLE = True # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() enable_secure_boot(REDFISHOBJ, SECURE_BOOT_ENABLE) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_ilo_ntp_servers.py0000664000175000017500000001276514702427156024567 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting NTP servers for HPE iLO systems """ import sys import json import time from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory from reset_ilo import reset_ilo from enable_ntp_servers import enable_ntp def set_ilo_ntp_servers(_redfishobj, ntp_server_list): date_time_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) date_time_uri = managers_members_response.obj.Oem.Hpe.Links['DateTimeService']['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeiLODateTime.' in instance['@odata.type']: date_time_uri = instance['@odata.id'] if date_time_uri: data = _redfishobj.get(date_time_uri) if data.dict.get('StaticNTPServers'): resp = _redfishobj.patch(date_time_uri, {'StaticNTPServers': ntp_server_list}) else: raise Exception("\'StaticNTPServers\' property is not available/modifyable.\n") #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended " \ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) ntp_data = _redfishobj.get(date_time_uri).dict.get('StaticNTPServers') print("Printing updated NTP Servers:\n") print(json.dumps(ntp_data, indent=4, sort_keys=True)) def give_client(): try: # Create a Redfish client object rf_obj = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client rf_obj.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() return rf_obj if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://16.83.61.9" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Static NTP Servers NTP_SERVER_LIST = ["192.168.0.1", "192.168.0.2"] # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False REDFISHOBJ = give_client() #must enable ntp servers for the primary iLO network management interface enable_ntp(REDFISHOBJ, True, DISABLE_RESOURCE_DIR) sys.stdout.write("NTP Servers property has been set on the relevant ethernet management "\ "interfaces...\n") #must reset iLO for ntp servers changes to be applied. sys.stdout.write("iLO must be reset to apply the NTP Server properties and reveal the "\ " static NTP servers in the DateTime URI.\n") reset_ilo(REDFISHOBJ, DISABLE_RESOURCE_DIR) sys.stdout.write("iLO has been reset...sleeping 60 seconds before trying login.\n") time.sleep(60) #logout to release the channel REDFISHOBJ.logout() #delete the redfish object. By a new token must be issued when iLO is reset #(all channels are cleared), and this library does not cache credentials (for security reasons) #A new redfish object must be created and asigned the appropriate credentials. del REDFISHOBJ REDFISHOBJ = give_client() set_ilo_ntp_servers(REDFISHOBJ, NTP_SERVER_LIST) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/add_user_account.py0000664000175000017500000001646014702427156023775 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of adding a user account by iLO privileges or redfish standard roles """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError global DISABLE_RESOURCE_DIR from ilorest_util import get_resource_directory from ilorest_util import get_gen def add_ilo_user_account(_redfishobj, new_loginname, new_username, new_password, role_id, \ privilege_dict): resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #resource directory is not available so we will navigate through paths manually to obtain #account info account_service_uri = _redfishobj.root.obj['AccountService']['@odata.id'] account_service_response = _redfishobj.get(account_service_uri) account_collection_uri = account_service_response.obj['Accounts']['@odata.id'] #Add via role id body = {"RoleId": role_id} else: #obtain all account instances from resource directory for instance in resource_instances: if '#ManagerAccountCollection.' in instance['@odata.type']: account_collection_uri = instance['@odata.id'] body = {"Oem": {"Hpe": {"Privileges": {}}}} #HPE server, so add via privileges for priv in privilege_dict: body["Oem"]["Hpe"]["Privileges"][priv] = privilege_dict[priv] #Add login name body["Oem"]["Hpe"]["LoginName"] = new_loginname #Fill in the rest of the payload body["UserName"] = new_username body["Password"] = new_password #We pass the URI and the dictionary as a POST command (part of the redfish object) resp = _redfishobj.post(account_collection_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif not resp.status in [200, 201]: sys.stderr.write("An http response of '%s' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) def add_ilo_user_account_gen9(_redfishobj, new_loginname, new_username, new_password): account_collection_uri = "/redfish/v1/AccountService/Accounts/" #Add via gen9 priv dic body = {'Oem': {'Hp': {'Privileges': {"LoginPriv": True, "RemoteConsolePriv": True, "UserConfigPriv": True, "VirtualMediaPriv": True, "VirtualPowerAndResetPriv": True, "iLOConfigPriv": True}, 'LoginName': new_loginname}},'UserName': new_username, 'Password': new_password} #We pass the URI and the dictionary as a POST command (part of the redfish object) resp = _redfishobj.post(account_collection_uri, body) print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.0" # "https://ilo.hostname" if len(sys.argv) == 4: # Remote mode SYSTEM_URL = sys.argv[1] LOGIN_ACCOUNT = sys.argv[2] LOGIN_PASSWORD = sys.argv[3] else: # Local mode SYSTEM_URL = None LOGIN_ACCOUNT = None LOGIN_PASSWORD = None #account login name (iLO GUI actually considers this to be 'UserName', but #this is the redfish standard username) ACCOUNT_LOGIN_NAME = "batman" #account user name (iLO GUI actually considers this to be 'LoginName', but #this is the redfish login) ACCOUNT_USER_NAME = "bruce_wayne" #account password ACCOUNT_PASSWORD = "thedarkknight123" #A predefined role for the user, (The redfish standard method for accounts). #This is a translated to a pre-configured arrangement of privileges on HPE servers ROLE_ID = "Administrator" #Administrator, ReadOnly or Operator are available #Dictionary of modifiable privileges for HPE servers (modify this if you wish to directly set #an account with specific privileges PRIVILEGE_DICT = {"iLOConfigPriv": True, "VirtualMediaPriv": True, "RemoteConsolePriv": True,\ "UserConfigPriv": True, "VirtualPowerAndResetPriv": True, \ "SystemRecoveryConfigPriv": False, "LoginPriv": True, \ "HostStorageConfigPriv": True, "HostNICConfigPriv": True, \ "HostBIOSConfigPriv": True} # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False #For certificate login #ca_cert_data = {} #ca_cert_data["cert_file"] = "c:\\test\\ppcacuser.crt" #ca_cert_data["key_file"] = "c:\\test\\ppcacuserpriv.key" #ca_cert_data["key_password"] = "password" #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD) REDFISHOBJ.login() # For Certificate login #REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD, ca_cert_data=ca_cert_data) #REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, ca_cert_data=ca_cert_data) # Login with the Redfish client #if ca_cert_data is None: # REDFISHOBJ.login() #else: # REDFISHOBJ.login(auth='certificate') except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() #obtain all account instances, by navigating set paths and keys to find the relevant URI #(account_collection_uri, accounts, rd) = get_accounts(redfishobj, DISABLE_RESOURCE_DIR) #print("\n\nShowing accounts before changes:\n\n") #show_accounts(redfishobj, accounts) #if account_collection_uri and accounts: #add specified account (ilogen,_) = get_gen(REDFISHOBJ) print ("Generation is ", ilogen) if int(ilogen) >= 5: add_ilo_user_account(REDFISHOBJ,ACCOUNT_LOGIN_NAME,ACCOUNT_USER_NAME,ACCOUNT_PASSWORD,ROLE_ID,PRIVILEGE_DICT) else: add_ilo_user_account_gen9(REDFISHOBJ,ACCOUNT_LOGIN_NAME,ACCOUNT_USER_NAME,ACCOUNT_PASSWORD) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_active_ilo_nic.py0000664000175000017500000001271614702427156024315 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting the active Manager NIC """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def set_active_ilo_nic(_redfishobj): ethernet_data = {} resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) manager_ethernet_interfaces = managers_members_response.obj['EthernetInterfaces']\ ['@odata.id'] manager_ethernet_interfaces_response = _redfishobj.get(manager_ethernet_interfaces) manager_ethernet_interfaces_members = manager_ethernet_interfaces_response.\ obj['Members'] for _member in manager_ethernet_interfaces_members: _tmp = _redfishobj.get(_member['@odata.id']).obj ethernet_data[_member['@odata.id']] = _tmp else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#EthernetInterfaceCollection.' in instance['@odata.type'] and 'Managers' in \ instance['@odata.id']: ethernet_uri = instance['@odata.id'] ethernet_interfaces = _redfishobj.get(ethernet_uri).obj['Members'] for _ethernet_interface in ethernet_interfaces: ethernet_data[_ethernet_interface['@odata.id']] = _redfishobj.\ get(_ethernet_interface['@odata.id']).dict break if ethernet_data: print("\n\nShowing all available ethernet management interfaces before changes:\n\n") for interface in ethernet_data: sys.stdout.write("Ethernet Management Inteface \'%s\'\n" % ethernet_data\ [interface].get('Id')) sys.stdout.write("\t\'Interface Enabled\': %s\n" % ethernet_data[interface].\ get('InterfaceEnabled')) for ethernet in ethernet_data: sys.stdout.write("Ethernet Interface: %s\n" % ethernet) ans = input("Would you like to enable/disable this interface? (enable/disable)\n") if "en" in ans: body = {"InterfaceEnabled": True} else: body = {"InterfaceEnabled": False} resp = _redfishobj.patch(ethernet, body) if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success! You will need to reset iLO for this change to take effect.\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) break if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() set_active_ilo_nic(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/enable_ntp_servers.py0000664000175000017500000001236414702427156024352 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of enabling NTP servers on HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def enable_ntp(_redfishobj, ntp_servers, DISABLE_RESOURCE_DIR): ethernet_data = {} resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) manager_ethernet_interfaces = managers_members_response.obj['EthernetInterfaces']\ ['@odata.id'] manager_ethernet_interfaces_response = _redfishobj.get(manager_ethernet_interfaces) manager_ethernet_interfaces_members = manager_ethernet_interfaces_response.\ obj['Members'] for _member in manager_ethernet_interfaces_members: _tmp = _redfishobj.get(_member['@odata.id']).obj ethernet_data[_member['@odata.id']] = _tmp else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#EthernetInterfaceCollection.' in instance['@odata.type'] and 'Managers' in \ instance['@odata.id']: ethernet_uri = instance['@odata.id'] ethernet_interfaces = _redfishobj.get(ethernet_uri).obj['Members'] for _ethernet_interface in ethernet_interfaces: ethernet_data[_ethernet_interface['@odata.id']] = _redfishobj.\ get(_ethernet_interface['@odata.id']).dict break if ethernet_data: print("\n\nShowing all available ethernet management interfaces before changes:\n\n") print(json.dumps(ethernet_data, indent=4, sort_keys=True)) body = {"Oem": {"Hpe": {"DHCPv4": {"UseNTPServers": ntp_servers}, \ "DHCPv6": {"UseNTPServers": ntp_servers}}}} for ethernet in ethernet_data: resp = _redfishobj.patch(ethernet, body) if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) _data = _redfishobj.get(ethernet).dict sys.stdout.write("\nShowing \'%s\' interface after changes:\n" % ethernet) print(json.dumps(_data, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Properties # NTP Servers Enable/Disable Flag NTP_SERVERS = True # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() enable_ntp(REDFISHOBJ, NTP_SERVERS, DISABLE_RESOURCE_DIR) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/mount_virtual_media_iso.py0000664000175000017500000001221714702427156025410 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of mounting virtual media for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def mount_virtual_media_iso(_redfishobj, iso_url, media_type, boot_on_next_server_reset): virtual_media_uri = None virtual_media_response = [] resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) virtual_media_uri = managers_members_response.obj['VirtualMedia']['@odata.id'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#VirtualMediaCollection.' in instance['@odata.type']: virtual_media_uri = instance['@odata.id'] if virtual_media_uri: virtual_media_response = _redfishobj.get(virtual_media_uri) for virtual_media_slot in virtual_media_response.obj['Members']: data = _redfishobj.get(virtual_media_slot['@odata.id']) if media_type in data.dict['MediaTypes']: virtual_media_mount_uri = data.obj['Actions']['#VirtualMedia.InsertMedia']['target'] post_body = {"Image": iso_url} if iso_url: resp = _redfishobj.post(virtual_media_mount_uri, post_body) if boot_on_next_server_reset is not None: patch_body = {} patch_body["Oem"] = {"Hpe": {"BootOnNextServerReset": \ boot_on_next_server_reset}} boot_resp = _redfishobj.patch(data.obj['@odata.id'], patch_body) if not boot_resp.status == 200: sys.stderr.write("Failure setting BootOnNextServerReset") if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO" "Extended Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) break if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" MEDIA_URL = "http:///media.iso" #specify the type of content the media represents MEDIA_TYPE = "CD" #current possible options: Floppy, USBStick, CD, DVD #specify if the server should attempt to boot this media on system restart BOOT_ON_NEXT_SERVER_RESET = True # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() mount_virtual_media_iso(REDFISHOBJ, MEDIA_URL, MEDIA_TYPE, BOOT_ON_NEXT_SERVER_RESET) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/get_SmartArray_EncryptionSettings.py0000664000175000017500000001126014702427156027343 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of gathering the smart array encryption settings on HPE iLO systems """ import sys from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def get_SmartArray_EncryptionSettings(_redfishobj, desired_properties): smartstorage_response = [] smartarraycontrollers = dict() resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) smart_storage_uri = systems_members_response.obj.Oem.Hpe.Links\ ['SmartStorage']['@odata.id'] smart_storage_arraycontrollers_uri = _redfishobj.get(smart_storage_uri).obj.Links\ ['ArrayControllers']['@odata.id'] smartstorage_response = _redfishobj.get(smart_storage_arraycontrollers_uri).obj['Members'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#HpeSmartStorageArrayControllerCollection.' in instance['@odata.type']: smartstorage_uri = instance['@odata.id'] smartstorage_response = _redfishobj.get(smartstorage_uri).obj['Members'] break for controller in smartstorage_response: smartarraycontrollers[controller['@odata.id']] = _redfishobj.get(controller['@odata.id']).\ obj sys.stdout.write("Encryption Properties for Smart Storage Array Controller \'%s\' : \n" \ % smartarraycontrollers[controller['@odata.id']].get('Id')) for data in smartarraycontrollers[controller['@odata.id']]: if data in desired_properties: sys.stdout.write("\t %s : %s\n" % (data, smartarraycontrollers[controller\ ['@odata.id']].get(data))) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #list of desired properties related to Smart Array controller encryption DESIRED_PROPERTIES = ["Name", "Model", "SerialNumber", "EncryptionBootPasswordSet",\ "EncryptionCryptoOfficerPasswordSet",\ "EncryptionLocalKeyCacheEnabled", "EncryptionMixedVolumesEnabled",\ "EncryptionPhysicalDriveCount", "EncryptionRecoveryParamsSet",\ "EncryptionStandaloneModeEnabled", "EncryptionUserPasswordSet"] # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() get_SmartArray_EncryptionSettings(REDFISHOBJ, DESIRED_PROPERTIES) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/change_boot_order.py0000664000175000017500000001177614702427156024143 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of changing the boot order for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def change_boot_order(_redfishobj, bios_password): bios_boot_uri = None bios_boot_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) bios_uri = systems_members_response.obj['Bios']['@odata.id'] bios_response = _redfishobj.get(bios_uri) bios_boot_uri = bios_response.obj.Oem.Hpe.Links.Boot['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeServerBootSettings.' in instance['@odata.type']: bios_boot_uri = instance['@odata.id'] break if bios_boot_uri: bios_boot_response = _redfishobj.get(bios_boot_uri) #Bios boot settings URI is needed bios_boot_settings_uri = bios_boot_response.obj['@Redfish.Settings']['SettingsObject']\ ['@odata.id'] #update BIOS password if bios_password: _redfishobj.bios_password = bios_password sys.stdout.write("Rotating the first boot device to the end of the boot order.\n") sys.stdout.write('Current Order:\n') boot_order = bios_boot_response.obj['DefaultBootOrder'] for indx, boot_device in enumerate(boot_order): sys.stdout.write('Pos ' + str(indx) + ' : ' + boot_device + '\n') device = boot_order.pop(0) sys.stdout.write("Rotating device: \'%s\' to the end of the boot order.\n" % device) boot_order.append(device) body = {'DefaultBootOrder': boot_order} resp = _redfishobj.patch(bios_boot_settings_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message"\ " Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success! Your system may need to be restarted.\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) else: sys.stderr.write("Unable to find Boot Order URI.\n") if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" BIOS_PASSWORD = None # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() change_boot_order(REDFISHOBJ, BIOS_PASSWORD) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/get_schema.py0000664000175000017500000000523214702427156022565 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of obtaining system schema data """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError def get_schema(_redfishobj): schema_members_uris = [] schema_members_expanded = {} schema_uri = _redfishobj.root.obj['JsonSchemas']['@odata.id'] schema_response = _redfishobj.get(schema_uri) schema_members_uris = schema_response.obj['Members'] for member in schema_members_uris: data = _redfishobj.get(member['@odata.id']).dict instance_id = data['Location'].index(next(iter(data['Location']))) schema_uri = data['Location'][instance_id]['Uri'] schema = _redfishobj.get(schema_uri).dict #I am just replacing the URI link for the sub-schema with the actual schema. You will #see EVERYTHING _tmp = {schema_uri + ' ->': schema} data['Location'][instance_id]['Uri'] = _tmp schema_members_expanded[member['@odata.id']] = data print(json.dumps(schema_members_expanded, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() get_schema(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/change_bios_setting.py0000664000175000017500000001153614702427156024470 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of changing BIOS settings """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def change_bios_setting(_redfishobj, bios_property, property_value, bios_password): bios_uri = None bios_data = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) bios_uri = systems_members_response.obj['Bios']['@odata.id'] bios_data = _redfishobj.get(bios_uri) else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#Bios.' in instance['@odata.type']: bios_uri = instance['@odata.id'] bios_data = _redfishobj.get(bios_uri) break if bios_data: print("\n\nShowing BIOS attributes before changes:\n\n") print(json.dumps(bios_data.dict, indent=4, sort_keys=True)) if bios_uri: #BIOS settings URI is needed bios_settings_uri = bios_data.obj['@Redfish.Settings']['SettingsObject']['@odata.id'] body = {'Attributes': {bios_property: property_value}} #update BIOS password if bios_password: _redfishobj.bios_password = bios_password resp = _redfishobj.patch(bios_settings_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("\nSuccess!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) #uncomment if you would like to see the full list of attributes #print("\n\nShowing BIOS attributes after changes:\n\n") #bios_data = _redfishobj.get(bios_uri) #print(json.dumps(bios_data.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" BIOS_PASSWORD = "" #provide the attribute name and the associated attribute value. Note: some, values may #be containers (arrays or dictionaries) so keep this in mind. ATTRIBUTE = "AdminName" ATTRIBUTE_VAL = "Luigi" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() change_bios_setting(REDFISHOBJ, ATTRIBUTE, ATTRIBUTE_VAL, BIOS_PASSWORD) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/get_ilo_ip.py0000664000175000017500000001042114702427156022574 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of getting the iLO Network Management Interface(s) IP Address(s) """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def get_ilo_ip(_redfishobj, DISABLE_RESOURCE_DIR): ethernet_data = {} resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) manager_ethernet_interfaces = managers_members_response.obj['EthernetInterfaces']\ ['@odata.id'] manager_ethernet_interfaces_response = _redfishobj.get(manager_ethernet_interfaces) manager_ethernet_interfaces_members = manager_ethernet_interfaces_response.\ obj['Members'] for _member in manager_ethernet_interfaces_members: _tmp = _redfishobj.get(_member['@odata.id']).obj ethernet_data[_member['@odata.id']] = _tmp else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#EthernetInterfaceCollection.' in instance['@odata.type'] and 'Managers' in \ instance['@odata.id']: ethernet_uri = instance['@odata.id'] ethernet_interfaces = _redfishobj.get(ethernet_uri).obj['Members'] for _ethernet_interface in ethernet_interfaces: ethernet_data[_ethernet_interface['@odata.id']] = _redfishobj.\ get(_ethernet_interface['@odata.id']).dict break if ethernet_data: for ethernet_interface in ethernet_data: sys.stdout.write("\n\nShowing iLO IPv4 Address Info on: %s\n\n" % ethernet_interface) print(json.dumps(ethernet_data[ethernet_interface]['IPv4Addresses'],\ indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() get_ilo_ip(REDFISHOBJ, DISABLE_RESOURCE_DIR) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/configure_snmp.py0000664000175000017500000001011414702427156023477 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of configuring SNMP for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def configure_snmp(_redfishobj, read_communities, snmp_alerts): snmp_service_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) snmp_service_uri = managers_members_response.obj.Oem.Hpe.Links['Snmp']['@odata.id'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#HpeiLOSnmpService.' in instance['@odata.type']: snmp_service_uri = instance['@odata.id'] if snmp_service_uri: body = {"AlertsEnabled": snmp_alerts, "ReadCommunities": read_communities} resp = _redfishobj.patch(snmp_service_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #Properties: #read communities array READ_COMMUNITIES = ["public", "", ""] #alerts_enabled primitive (boolean) ALERTS_ENABLED = True # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() configure_snmp(REDFISHOBJ, READ_COMMUNITIES, ALERTS_ENABLED, DISABLE_RESOURCE_DIR) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/update_ilo_firmware.py0000664000175000017500000000763314702427156024516 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of updating firmware via HTTP URL """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def update_ilo_firmware(_redfishobj, fw_url, tpm_flag): body = dict() update_service_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI update_service_uri = _redfishobj.root.obj['UpdateService']['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#UpdateService.' in instance['@odata.type']: update_service_uri = instance['@odata.id'] break if update_service_uri and fw_url: update_uri = _redfishobj.get(update_service_uri).obj['Actions']\ ['#UpdateService.SimpleUpdate']['target'] body["ImageURI"] = fw_url if tpm_flag: body["TPMOverrideFlag"] = tpm_flag resp = _redfishobj.post(update_uri, body) if resp.status == 400: try: print(json.dumps(resp.obj['error']\ ['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # firmware file path and filename FIRMWARE_URL = "http://" # If a TPM module (Trusted Platform Module/ Cryptographic Co-processor) is installed/present # onboard then be sure to include this. TPM_FLAG = False # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = True try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() update_ilo_firmware(REDFISHOBJ, FIRMWARE_URL, TPM_FLAG) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/remove_account.py0000664000175000017500000000767514702427156023514 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of removing a user account """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def remove_ilo_user_account(_redfishobj, username_to_delete): resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #resource directory is not available so we will navigate through paths manually to obtain #account info account_service_uri = _redfishobj.root.obj['AccountService']['@odata.id'] account_service_response = _redfishobj.get(account_service_uri) account_collection_uri = account_service_response.obj['Accounts']['@odata.id'] else: #obtain all account instances from resource directory for instance in resource_instances: if '#ManagerAccountCollection.' in instance['@odata.type']: account_collection_uri = instance['@odata.id'] break #find the account to delete account_uri_to_delete = None account_uris = _redfishobj.get(account_collection_uri) for account_uri in account_uris.dict['Members']: account = _redfishobj.get(account_uri['@odata.id']) if account.dict['UserName'] == username_to_delete: account_uri_to_delete = account_uri['@odata.id'] break if not account_uri_to_delete: sys.stderr.write("Cannot find account to modify") return resp = _redfishobj.delete(account_uri_to_delete) if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # The username of the account to remove ACCOUNT_TO_DELETE = "joker" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() remove_ilo_user_account(REDFISHOBJ, ACCOUNT_TO_DELETE) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/modify_user_account.py0000664000175000017500000001316014702427156024526 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of modifying a user account """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def modify_ilo_user_account(_redfishobj, username_to_modify, new_loginname, new_username, \ new_password, role_id, privilege_dict): account_collection_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #resource directory is not available so we will navigate through paths manually to obtain #account info account_service_uri = _redfishobj.root.obj['AccountService']['@odata.id'] account_service_response = _redfishobj.get(account_service_uri) account_collection_uri = account_service_response.obj['Accounts']['@odata.id'] #modify role id if role_id: body = {"RoleId": role_id} else: #obtain all account instances from resource directory for instance in resource_instances: if '#ManagerAccountCollection.' in instance['@odata.type']: account_collection_uri = instance['@odata.id'] if privilege_dict: #HPE server, so modify privileges body = {"Oem": {"Hpe": {"Privileges": {}}}} for priv in privilege_dict: body["Oem"]["Hpe"]["Privileges"][priv] = privilege_dict[priv] if new_loginname: #modify login name body["Oem"]["Hpe"]["LoginName"] = new_loginname if new_username: body["UserName"] = new_username if new_password: body["Password"] = new_password #find the account to modify account_uri_to_modify = None account_uris = REDFISHOBJ.get(account_collection_uri) for account_uri in account_uris.dict['Members']: account = REDFISHOBJ.get(account_uri['@odata.id']) if account.dict['UserName'] == username_to_modify: account_uri_to_modify = account_uri['@odata.id'] break if not account_uri_to_modify: sys.stderr.write("Cannot find account to modify") return #modify the account resp = REDFISHOBJ.patch(account_uri_to_modify, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of '%s' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #username of the account to modify USERNAME_TO_MODIFY = "bruce_wayne" #account login name to change the account to NEW_LOGINNAME = "joker" #account user name to change the account to NEW_USERNAME = "joker" #account password to change the account to NEW_PASSWORD = "joker123" #role to change account to ROLE_ID = "ReadOnly" #Administrator, ReadOnly or Operator are available #update HPE account privileges PRIVILEGE_DICT = {"iLOConfigPriv": False, "VirtualMediaPriv": False, "RemoteConsolePriv": True,\ "UserConfigPriv": False, "VirtualPowerAndResetPriv": False, \ "SystemRecoveryConfigPriv": False, "LoginPriv": True, \ "HostStorageConfigPriv": False, "HostNICConfigPriv": False, \ "HostBIOSConfigPriv": False} # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() modify_ilo_user_account(REDFISHOBJ, USERNAME_TO_MODIFY, NEW_LOGINNAME, NEW_USERNAME, \ NEW_PASSWORD, ROLE_ID, PRIVILEGE_DICT) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/bios_revert_default.py0000664000175000017500000000765614702427156024531 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example for reverting the BIOS to default values """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def bios_revert_default(_redfishobj): bios_reset_action_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) bios_uri = systems_members_response.obj['Bios']['@odata.id'] bios_response = _redfishobj.get(bios_uri) bios_reset_action_uri = bios_response.obj['Actions']['#Bios.ResetBios']['target'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#Bios.' in instance['@odata.type']: bios_uri = instance['@odata.id'] bios_data = _redfishobj.get(bios_uri) bios_reset_action_uri = bios_data.obj['Actions']['#Bios.ResetBios']['target'] break body = {'Action': 'Bios.ResetBios', 'ResetType':'default'} resp = _redfishobj.post(bios_reset_action_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, sort_keys=True)) except Exception: sys.stderr.write("A response error occurred, unable to access iLO Extended Message "\ "Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT,\ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() bios_revert_default(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/get_ESKM.py0000664000175000017500000000655314702427156022073 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of gathering ESKM data for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def get_ESKM(_redfishobj): security_service_eskm_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) security_service_uri = managers_members_response.obj.Oem.Hpe.Links\ ['SecurityService']['@odata.id'] security_service_response = _redfishobj.get(security_service_uri) security_service_eskm_uri = security_service_response.obj.Links['ESKM']['@odata.id'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#HpeESKM.' in instance['@odata.type']: security_service_eskm_uri = instance['@odata.id'] break if security_service_eskm_uri: security_service_eskm_resp = _redfishobj.get(security_service_eskm_uri) print(json.dumps(security_service_eskm_resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() get_ESKM(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/sessions.py0000664000175000017500000000606214702427156022336 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of manually managing sessions with Redfish """ import sys import json from six.moves import urllib from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError def sessions(_redfishobj, login_account, login_password): new_session = {"UserName": login_account, "Password": login_password} response = _redfishobj.post('/redfish/v1/Sessions', new_session) if response.status == 201: print("Success!\n") session_uri = response.getheader("location") session_uri = urllib.parse.urlparse(session_uri) sys.stdout.write("\tSession " + session_uri.path + " created.\n") x_auth_token = response.getheader("x-auth-token") sys.stdout.write("\tThis is the session X-Auth Token key " + x_auth_token + ".\n") print(json.dumps(response.dict, indent=4, sort_keys=True)) # Delete the created session sys.stdout.write("\tTerminating this session.\n") sessresp = _redfishobj.delete(session_uri.path) print(json.dumps(sessresp.dict, indent=4, sort_keys=True)) else: sys.stderr.write("ERROR: failed to create a session.\n") try: print(json.dumps(sessresp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended" " Message Info...") if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() sessions(REDFISHOBJ, LOGIN_ACCOUNT, LOGIN_PASSWORD) python-ilorest-5.3.0.0a/examples/Redfish/get_resource_directory.py0000664000175000017500000000456214702427156025245 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of getting the ilo information like ilo generation, version and resource directory for HPE iLO systems """ import sys from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError def get_resource_directory(redfishobj): try: resource_uri = redfishobj.root.obj.Oem.Hpe.Links.ResourceDirectory['@odata.id'] except KeyError: sys.stderr.write("Resource directory is only available on HPE servers.\n") return None response = redfishobj.get(resource_uri) resources = [] if response.status == 200: sys.stdout.write("\tFound resource directory at /redfish/v1/resourcedirectory" + "\n\n") resources = response.dict["Instances"] else: sys.stderr.write("\tResource directory missing at /redfish/v1/resourcedirectory" + "\n") return resources def get_gen(_redfishobj): rootresp = _redfishobj.root.obj #Default iLO 5 ilogen = 5 gencompany = next(iter(rootresp.get("Oem", {}).keys()), None) in ('Hpe', 'Hp') comp = 'Hp' if gencompany else None comp = 'Hpe' if rootresp.get("Oem", {}).get('Hpe', None) else comp if comp and next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).\ get('ManagerType', None): ilogen = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})))\ .get("ManagerType") ilover = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).\ get("ManagerFirmwareVersion") if ilogen.split(' ')[-1] == "CM": # Assume iLO 4 types in Moonshot ilogen = 4 iloversion = None else: ilogen = ilogen.split(' ')[1] iloversion = float(ilogen.split(' ')[-1] + '.' + \ ''.join(ilover.split('.'))) return (ilogen, iloversion) python-ilorest-5.3.0.0a/examples/Redfish/set_ESKM_PrimaryKeyServer.py0000664000175000017500000001055714702427156025451 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting the primary ESKM key server for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def set_ESKM_PrimaryKeyServer(_redfishobj, primary_key_server_address, primary_key_server_port): eskm_uri = None body = dict() resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) security_service_uri = managers_members_response.obj.Oem.Hpe.Links['SecurityService']\ ['@odata.id'] security_service_response = _redfishobj.get(security_service_uri) eskm_uri = security_service_response.obj.Links['ESKM']['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeESKM.' in instance['@odata.type']: eskm_uri = instance['@odata.id'] break if eskm_uri: body["PrimaryKeyServerAddress"] = primary_key_server_address body["PrimaryKeyServerPort"] = int(primary_key_server_port) resp = _redfishobj.post(eskm_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" PRIMARY_KEY_SERVER_ADDRESS = "192.168.1.1" PRIMARY_KEY_SERVER_PORT = "9000" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() set_ESKM_PrimaryKeyServer(REDFISHOBJ, PRIMARY_KEY_SERVER_ADDRESS, PRIMARY_KEY_SERVER_PORT) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_license_key.py0000664000175000017500000001120314702427156023626 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of adding an license key for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def set_license_key(_redfishobj, ilo_key): ilo_lic_uri = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) ilo_lic_uri = managers_members_response.obj.Oem.Hpe.Links['LicenseService']['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeiLOLicense.' in instance['@odata.type']: ilo_lic_uri = instance['@odata.id'] if ilo_lic_uri: ilo_license_collection = _redfishobj.get(ilo_lic_uri) ilo_license_member_uri = next(iter(ilo_license_collection.obj['Members']))['@odata.id'] try: ilo_license_data = _redfishobj.get(ilo_license_member_uri).obj['ConfirmationRequest']\ ['EON'] except KeyError: sys.stdout.write("This machine will not show the full License Key.\n") ilo_license_data = _redfishobj.get(ilo_license_member_uri).obj['LicenseKey'] sys.stdout.write("Current iLO License Data:\n") print(json.dumps(ilo_license_data, indent=4, sort_keys=True)) resp = _redfishobj.post(ilo_lic_uri, {'LicenseKey' : ilo_key}) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) sys.stderr.write("Check the validity of your license key...\n") except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO " \ "Extended Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # Must be a valid iLO License Key ILO_LICENSE_KEY = "XXXX-XXXX-XXXX-XXXX-XXXXX" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = True try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() set_license_key(REDFISHOBJ, ILO_LICENSE_KEY) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_server_asset_tag.py0000664000175000017500000001044114702427156024677 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting the server asset tag """ import sys import json import time from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def set_server_asset_tag(_redfishobj, tag): systems_members_uri = None systems_members_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#ComputerSystem.' in instance['@odata.type']: systems_members_uri = instance['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) if systems_members_response and systems_members_uri and tag: print("Current Asset Tag: \'%s\'\n" % systems_members_response.dict.get("AssetTag")) resp = _redfishobj.patch(systems_members_uri, {"AssetTag" : tag}) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) time.sleep(10) #going to wait 10 seconds before obtaining the LED indicator state sys.stdout.write("\nUpdated Asset Tag: \'%s\'\n" % _redfishobj.\ get(systems_members_uri).dict.get("AssetTag")) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #asset tag property in computer systems ASSET_TAG = "Asset01" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = True try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() set_server_asset_tag(REDFISHOBJ, ASSET_TAG) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/import_ssl.py0000664000175000017500000001133614702427156022663 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of importing an SSL certificate for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def import_ssl(_redfishobj, ssl_file_path): https_cert_uri = None body = dict() resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) security_service_uri = managers_members_response.obj.Oem.Hpe.Links['SecurityService']\ ['@odata.id'] security_service_response = _redfishobj.get(security_service_uri) https_cert_uri = security_service_response.obj.Links['HttpsCert']['@odata.id'] else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#HpeHttpsCert.' in instance['@odata.type']: https_cert_uri = instance['@odata.id'] break if https_cert_uri: https_cert_import_uri = _redfishobj.get(https_cert_uri).obj['Actions']\ ['#HpeHttpsCert.ImportCertificate']['target'] body = dict() body["Action"] = "HpeHttpsCert.ImportCertificate" body["Certificate"] = ssl_cert resp = _redfishobj.post(https_cert_import_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) print("\nImporting SSL Certificate may take a few minutes...\n "\ "iLO will reset with new changes.\n") if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" SSL_FILE_PATH = "certificate.txt" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() with open(SSL_FILE_PATH, 'r') as csr_data: ssl_cert = csr_data.read() csr_data.close() if ssl_cert: import_ssl(REDFISHOBJ, ssl_cert) else: raise Exception("Invalid SSL certificate.\n") REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_ESKM_username_password.py0000664000175000017500000001160514702427156025722 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting the ESKM username and password for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def ESKM_username_pass(_redfishobj, eskm_username, eskm_password, eskm_accountgroup, \ eskm_primarykeyserver_addr, eskm_primarykeyserver_port): eskm_uri = None body = dict() resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI managers_uri = _redfishobj.root.obj['Managers']['@odata.id'] managers_response = _redfishobj.get(managers_uri) managers_members_uri = next(iter(managers_response.obj['Members']))['@odata.id'] managers_members_response = _redfishobj.get(managers_members_uri) security_service_uri = managers_members_response.obj.Oem.Hpe.Links['SecurityService']\ ['@odata.id'] security_service_response = _redfishobj.get(security_service_uri) eskm_uri = security_service_response.obj.Links['ESKM']['@odata.id'] else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeESKM.' in instance['@odata.type']: eskm_uri = instance['@odata.id'] break if eskm_uri: body["KeyManagerConfig"] = dict() body["KeyManagerConfig"]["LoginName"] = eskm_username body["KeyManagerConfig"]["Password"] = eskm_password body["KeyManagerConfig"]["AccountGroup"] = eskm_accountgroup body["KeyManagerConfig"]["ESKMLocalCACertificateName"] = "" body["PrimaryKeyServerAddress"] = eskm_primarykeyserver_addr body["PrimaryKeyServerPort"] = eskm_primarykeyserver_port resp = _redfishobj.patch(eskm_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended "\ "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" ESKM_USERNAME = "admin" ESKM_PASSWORD = "password" ESKM_ACCOUNTGROUP = "group" ESKM_PRIMARYKEYSERVER_ADDR = "192.168.1.10" ESKM_PRIMARYKEYSERVER_PORT = 5927 # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() ESKM_username_pass(REDFISHOBJ, ESKM_USERNAME, ESKM_PASSWORD, ESKM_ACCOUNTGROUP, \ ESKM_PRIMARYKEYSERVER_ADDR, ESKM_PRIMARYKEYSERVER_PORT) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/reboot_server.py0000664000175000017500000001031714702427156023346 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of rebooting a server """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def reboot_server(_redfishobj): systems_members_response = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_response = _redfishobj.get(systems_uri) else: for instance in resource_instances: #Use Resource directory to find the relevant URI if '#ComputerSystem.' in instance['@odata.type']: systems_uri = instance['@odata.id'] systems_response = _redfishobj.get(systems_uri) if systems_response: system_reboot_uri = systems_response.obj['Actions']['#ComputerSystem.Reset']['target'] body = dict() resettype = ['ForceRestart','GracefulRestart'] body['Action'] = 'ComputerSystem.Reset' for reset in resettype: if reset.lower() == "forcerestart": body['ResetType'] = "ForceRestart" resp = _redfishobj.post(system_reboot_uri, body) elif reset.lower() == "gracefulrestart": body['ResetType'] = "GracefulRestart" resp = _redfishobj.post(system_reboot_uri, body) #If iLO responds with soemthing outside of 200 or 201 then lets check the iLO extended info #error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], indent=4, \ sort_keys=True)) except Exception as excp: sys.stderr.write("A response error occurred, unable to access iLO Extended " "Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success!\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() reboot_server(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/upload_firmware_ilo_repository.py0000664000175000017500000001060214702427156027005 0ustar carstencarsten # Copyright 2019 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of uploading a component to the iLO Repository for flashing """ import os import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def upload_firmware(_redfishobj, firmware_loc, compsig_loc, update_repo=True, update_target=False): resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #resource directory is not available so we will navigate through paths manually update_service_uri = _redfishobj.root.obj['UpdateService']['@odata.id'] else: #obtain all account instances from resource directory for instance in resource_instances: if '#UpdateService.' in instance['@odata.type']: update_service_uri = instance['@odata.id'] update_service_response = _redfishobj.get(update_service_uri) path = update_service_response.obj.HttpPushUri body = [] json_data = {'UpdateRepository': update_repo, 'UpdateTarget': update_target, 'ETag': 'atag', 'Section': 0} session_key = _redfishobj.session_key filename = os.path.basename(firmware_loc) with open(firmware_loc, 'rb') as fle: filenameOutput = fle.read() filename = os.path.basename(compsig_loc) with open(compsig_loc, 'rb') as fle: compsigOutput = fle.read() session_tuple = ('sessionKey', session_key) parameters_tuple = ('parameters', json.dumps(json_data)) file_tuple = ('file', (filename, filenameOutput, 'application/octet-stream')) comp_tuple = ('compsig', (filename, compsigOutput, 'application/octet-stream')) #Build the payload from each multipart-form data tuple body.append(session_tuple) body.append(parameters_tuple) body.append(comp_tuple) body.append(file_tuple) #Create our header dictionary header = {'Cookie': 'sessionKey=' + session_key} #We pass the whole list payload to post resp = _redfishobj.post(path, body, headers=header) if resp.status == 400: sys.stderr.write("Failed to upload firmware...") elif not resp.status in [200, 201]: sys.stderr.write("An http response of '%s' was returned.\n" % resp.status) else: print("Upload complete!\n") if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.0" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.0" LOGIN_ACCOUNT = "admin LOGIN_PASSWORD = "password # The path to the firmware file to upload FIRMWARE_PATH = "/path/to/component.exe" COMPSIG_PATH = "/path/to/component.compsiq" # Upload the firmware file to the iLO Repository UPDATE_REPO = True # Update the system with the firmware file UPDATE_TARGET = False # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = True try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() upload_firmware(REDFISHOBJ, FIRMWARE_PATH, COMPSIG_PATH, UPDATE_REPO, UPDATE_TARGET) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_bios_iscsi.py0000664000175000017500000001134414702427156023470 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of setting an ISCSI boot instance for HPE iLO systems """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from get_resource_directory import get_resource_directory def set_bios_iscsi(_redfishobj, iscsi_properties): iscsi_uri = None iscsi_data = None resource_instances = get_resource_directory(_redfishobj) if DISABLE_RESOURCE_DIR or not resource_instances: #if we do not have a resource directory or want to force it's non use to find the #relevant URI systems_uri = _redfishobj.root.obj['Systems']['@odata.id'] systems_response = _redfishobj.get(systems_uri) systems_members_uri = next(iter(systems_response.obj['Members']))['@odata.id'] systems_members_response = _redfishobj.get(systems_members_uri) bios_uri = systems_members_response.obj['Bios']['@odata.id'] bios_response = _redfishobj.get(bios_uri) iscsi_uri = bios_response.obj.Oem.Hpe.Links['iScsi']['@odata.id'] iscsi_data = _redfishobj.get(iscsi_uri) else: #Use Resource directory to find the relevant URI for instance in resource_instances: if '#HpeiSCSISoftwareInitiator.' in instance['@odata.type']: iscsi_uri = instance['@odata.id'] iscsi_data = _redfishobj.get(iscsi_uri) if iscsi_data: for indx, inst in enumerate(iscsi_data.obj['iSCSISources']): if iscsi_properties['iSCSIBootInstance'] == indx: iscsi_data.dict['iSCSISources'][indx].update(iscsi_properties) resp = _redfishobj.patch(iscsi_uri, {'iSCSISources' : iscsi_data.dict['iSCSISources']}) #If iLO responds with soemthing outside of 200 or 201 then lets check the #iLO extended info error message to see what went wrong if resp.status == 400: try: print(json.dumps(resp.obj['error']['@Message.ExtendedInfo'], \ indent=4, sort_keys=True)) except Exception as excp: print(json.dumps(resp.ori, indent=4, sort_keys=True)) sys.stderr.write("A response error occurred, unable to access iLO " \ "Extended Message Info...") elif resp.status != 200: sys.stderr.write("An http response of \'%s\' was returned.\n" % resp.status) else: print("Success! A system reboot will be required to complete the change.\n") print(json.dumps(resp.dict, indent=4, sort_keys=True)) break if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" #iscsi properties ISCSI_PROPERTIES = {"iSCSIBootInstance": 2, \ "iSCSITargetName": "Target", \ "iSCSIAttemptName": "Empty", \ "iSCSIConnectRetry": 5} # flag to force disable resource directory. Resource directory and associated operations are # intended for HPE servers. DISABLE_RESOURCE_DIR = False try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() set_bios_iscsi(REDFISHOBJ, ISCSI_PROPERTIES) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/get_base_registry.py0000664000175000017500000000416314702427156024171 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of getting the base message registry """ import sys import json from redfish import RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError def get_base_registry(_redfishobj): registries_uri = _redfishobj.root.obj['Registries']['@odata.id'] if registries_uri: registries_members = _redfishobj.get(registries_uri).obj['Members'] for registry in registries_members: sys.stdout.write("Registry URI at '%s'\n" % registry['@odata.id']) if __name__ == "__main__": # When running on the server locally use the following commented values #SYSTEM_URL = None #LOGIN_ACCOUNT = None #LOGIN_PASSWORD = None # When running remotely connect using the secured (https://) address, # account name, and password to send https requests # SYSTEM_URL acceptable examples: # "https://10.0.0.100" # "https://ilo.hostname" SYSTEM_URL = "https://10.0.0.100" LOGIN_ACCOUNT = "admin" LOGIN_PASSWORD = "password" try: # Create a Redfish client object REDFISHOBJ = RedfishClient(base_url=SYSTEM_URL, username=LOGIN_ACCOUNT, \ password=LOGIN_PASSWORD) # Login with the Redfish client REDFISHOBJ.login() except ServerDownOrUnreachableError as excp: sys.stderr.write("ERROR: server not reachable or does not support RedFish.\n") sys.exit() get_base_registry(REDFISHOBJ) REDFISHOBJ.logout() python-ilorest-5.3.0.0a/examples/Redfish/set_snmp_alert.py0000664000175000017500000001426114702427156023507 0ustar carstencarsten # Copyright 2020 Hewlett Packard Enterprise Development LP # # 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 -*- """ An example of configuring SNMP alert for HPE iLO systems Usage: python