From 48d1d9b9dd1a35466c69ef9503c4afccf9849948 Mon Sep 17 00:00:00 2001 From: Eric MacDonald Date: Mon, 22 Apr 2024 18:58:38 +0000 Subject: [PATCH] Add new redfishtool package to the Debian build system Premise: WRCP has recently moved from Bullseye to Bookworm. WRCP mandated the up-version of redfish tool to 1.1.8. There is no publically available version of redfishtool 1.1.8 in Bookworm or otherwise. This update introduces a new package ; redfishtool version 1.1.8 Test Plan: PEND: Verify build after manually removing the already downloaded version of redfishtool 1.1.5 PROG: Verify build of newly created build env after manually cherry-picking this update following the repo init/sync Story: TBD Task: TBD Change-Id: I7bf2469033d2be3f0b693153b3b8ee86a63cc3b5 Signed-off-by: Eric MacDonald --- bmc/redfishtool/debian/deb_folder/changelog | 5 + bmc/redfishtool/debian/deb_folder/control | 19 + bmc/redfishtool/debian/deb_folder/copyright | 34 + .../debian/deb_folder/redfishtool.install | 8 + bmc/redfishtool/debian/deb_folder/rules | 17 + bmc/redfishtool/debian/deb_folder/source.tgz | Bin 0 -> 247 bytes bmc/redfishtool/debian/meta_data.yaml | 7 + bmc/redfishtool/files/v1.1.8/AUTHORS.md | 3 + bmc/redfishtool/files/v1.1.8/CHANGELOG.md | 82 ++ bmc/redfishtool/files/v1.1.8/CONTRIBUTING.md | 64 + bmc/redfishtool/files/v1.1.8/LICENSE.md | 57 + bmc/redfishtool/files/v1.1.8/README.md | 607 ++++++++ bmc/redfishtool/files/v1.1.8/redfishtool.py | 21 + .../v1.1.8/redfishtoollib/AccountService.py | 725 +++++++++ .../files/v1.1.8/redfishtoollib/Chassis.py | 843 +++++++++++ .../files/v1.1.8/redfishtoollib/Managers.py | 728 ++++++++++ .../v1.1.8/redfishtoollib/ServiceRoot.py | 97 ++ .../v1.1.8/redfishtoollib/SessionService.py | 406 ++++++ .../files/v1.1.8/redfishtoollib/Systems.py | 990 +++++++++++++ .../files/v1.1.8/redfishtoollib/__init__.py | 8 + .../files/v1.1.8/redfishtoollib/raw.py | 392 +++++ .../v1.1.8/redfishtoollib/redfishtoolMain.py | 508 +++++++ .../redfishtoollib/redfishtoolTransport.py | 1292 +++++++++++++++++ bmc/redfishtool/files/v1.1.8/requirements.txt | 2 + .../files/v1.1.8/scripts/redfishtool | 18 + .../files/v1.1.8/scripts/redfishtool.py | 18 + bmc/redfishtool/files/v1.1.8/setup.cfg | 2 + bmc/redfishtool/files/v1.1.8/setup.py | 28 + .../upstream/redfishtool_1.1.8.orig.tar.gz | Bin 0 -> 426473 bytes debian_iso_image.inc | 3 + debian_pkg_dirs | 1 + 31 files changed, 6985 insertions(+) create mode 100644 bmc/redfishtool/debian/deb_folder/changelog create mode 100644 bmc/redfishtool/debian/deb_folder/control create mode 100644 bmc/redfishtool/debian/deb_folder/copyright create mode 100644 bmc/redfishtool/debian/deb_folder/redfishtool.install create mode 100755 bmc/redfishtool/debian/deb_folder/rules create mode 100644 bmc/redfishtool/debian/deb_folder/source.tgz create mode 100644 bmc/redfishtool/debian/meta_data.yaml create mode 100644 bmc/redfishtool/files/v1.1.8/AUTHORS.md create mode 100644 bmc/redfishtool/files/v1.1.8/CHANGELOG.md create mode 100644 bmc/redfishtool/files/v1.1.8/CONTRIBUTING.md create mode 100644 bmc/redfishtool/files/v1.1.8/LICENSE.md create mode 100644 bmc/redfishtool/files/v1.1.8/README.md create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtool.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/AccountService.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/Chassis.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/Managers.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/ServiceRoot.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/SessionService.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/Systems.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/__init__.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/raw.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/redfishtoolMain.py create mode 100644 bmc/redfishtool/files/v1.1.8/redfishtoollib/redfishtoolTransport.py create mode 100644 bmc/redfishtool/files/v1.1.8/requirements.txt create mode 100644 bmc/redfishtool/files/v1.1.8/scripts/redfishtool create mode 100644 bmc/redfishtool/files/v1.1.8/scripts/redfishtool.py create mode 100644 bmc/redfishtool/files/v1.1.8/setup.cfg create mode 100644 bmc/redfishtool/files/v1.1.8/setup.py create mode 100644 bmc/redfishtool/upstream/redfishtool_1.1.8.orig.tar.gz diff --git a/bmc/redfishtool/debian/deb_folder/changelog b/bmc/redfishtool/debian/deb_folder/changelog new file mode 100644 index 000000000..66893a486 --- /dev/null +++ b/bmc/redfishtool/debian/deb_folder/changelog @@ -0,0 +1,5 @@ +redfishtool (1.1.8-1) unstable; urgency=medium + + * Initial release. + + -- Eric Macdonald Wed, 10 Apr 2024 16:12:19 +0000 diff --git a/bmc/redfishtool/debian/deb_folder/control b/bmc/redfishtool/debian/deb_folder/control new file mode 100644 index 000000000..75bf11934 --- /dev/null +++ b/bmc/redfishtool/debian/deb_folder/control @@ -0,0 +1,19 @@ +Source: redfishtool +Section: tools +Priority: optional +Maintainer: StarlingX Developers +Build-Depends: debhelper-compat (= 13), + dh-python, + python3-all, + python3-setuptools, + python3-wheel, +Standards-Version: 4.1.2 +Homepage: https://www.starlingx.io + +Package: redfishtool +Architecture: all +Depends: ${python3:Depends}, ${misc:Depends}, +Description: Access BMC Redfish service + Provides command line interface to Board Management Controllers + that support the Redfish protocol. + This is the python3 version of the package. diff --git a/bmc/redfishtool/debian/deb_folder/copyright b/bmc/redfishtool/debian/deb_folder/copyright new file mode 100644 index 000000000..203554c59 --- /dev/null +++ b/bmc/redfishtool/debian/deb_folder/copyright @@ -0,0 +1,34 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: * +Copyright: + (c) 2013-2021 Wind River Systems, Inc + (c) Copyright (c) 2016, Contributing Member(s) of Distributed Management Task Force, Inc.. All rights reserved. +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + * Neither the name of Kitware, Inc. nor the names of Contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/bmc/redfishtool/debian/deb_folder/redfishtool.install b/bmc/redfishtool/debian/deb_folder/redfishtool.install new file mode 100644 index 000000000..7aa269e93 --- /dev/null +++ b/bmc/redfishtool/debian/deb_folder/redfishtool.install @@ -0,0 +1,8 @@ +usr/bin/redfishtool +usr/bin/redfishtool.py +usr/lib/python3/dist-packages/redfishtoollib/* +usr/lib/python3/dist-packages/redfishtool-1.1.8.egg-info/PKG-INFO +usr/lib/python3/dist-packages/redfishtool-1.1.8.egg-info/SOURCES.txt +usr/lib/python3/dist-packages/redfishtool-1.1.8.egg-info/dependency_links.txt +usr/lib/python3/dist-packages/redfishtool-1.1.8.egg-info/requires.txt +usr/lib/python3/dist-packages/redfishtool-1.1.8.egg-info/top_level.txt \ No newline at end of file diff --git a/bmc/redfishtool/debian/deb_folder/rules b/bmc/redfishtool/debian/deb_folder/rules new file mode 100755 index 000000000..c30960d07 --- /dev/null +++ b/bmc/redfishtool/debian/deb_folder/rules @@ -0,0 +1,17 @@ +#!/usr/bin/make -f +# export DH_VERBOSE=1 + +export PYBUILD_NAME=redfishtool +# export PYBUILD_SYSTEM=python3 + +export PBR_VERSION=1.0.0 +ROOT := $(CURDIR)/debian/tmp + +%: + dh $@ --with python3 --buildsystem=pybuild + +override_dh_install: + python3 setup.py install -f --install-layout=deb --root=$(CURDIR)/debian/tmp + python3 setup.py bdist_wheel --universal -d $(CURDIR)/debian/redfishtool-wheels/usr/share/python3-wheels + dh_install + diff --git a/bmc/redfishtool/debian/deb_folder/source.tgz b/bmc/redfishtool/debian/deb_folder/source.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1cda3794dd6f6efde0730554e0158be40651032b GIT binary patch literal 247 zcmb2|=3oE==C@ZaW*t@#VM|#5UF6d0(g|*_FUGv}^x|G<=oqopnC;h(vzrWVEO>Kj z%Z2~D1p_}EcyC!N@#x|-9d*N1Tjau(Qdec_cP=YErXsn})YG|%bEjyV#P!J4vv_M) zDL+3m?^UvJP3`Y_y?=#RbyxpCx1aZ8$a0_j8rGa;d-9*?zf`Y}?R_4kxllrU=BuAi zPks)UJ{WJ${(a&#{qO5orL{{Po+=l@)vA;bLQKkE(eP5U&Zu5W97 uUDGjb|L3cT from "Latest" to "v1" since Latest will require the check and add an additional Get /Redfish query +- Added elapsed execution time output if -ss or -sss is specified +- Updated usage for the above changes + +## [0.9.1] - 2016-09-06 +- Initial Public Release; supports most Redfish 1.0 features diff --git a/bmc/redfishtool/files/v1.1.8/CONTRIBUTING.md b/bmc/redfishtool/files/v1.1.8/CONTRIBUTING.md new file mode 100644 index 000000000..5f605c43e --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Contributing + +## Overview + +This repository is maintained by the [DMTF](https://www.dmtf.org/ "https://www.dmtf.org/"). All contributions are reviewed and approved by members of the organization. + +## Submitting Issues + +Bugs, feature requests, and questions are all submitted in the "Issues" section for the project. DMTF members are responsible for triaging and addressing issues. + +## Contribution Process + +1. Fork the repository. +2. Make and commit changes. +3. Make a pull request. + +All contributions must adhere to the BSD 3-Clause License described in the LICENSE.md file, and the [Developer Certificate of Origin](#developer-certificate-of-origin). + +Pull requests are reviewed and approved by DMTF members. + +## Developer Certificate of Origin + +All contributions must adhere to the [Developer Certificate of Origin (DCO)](http://developercertificate.org "http://developercertificate.org"). + +The DCO is an attestation attached to every contribution made by every developer. In the commit message of the contribution, the developer adds a "Signed-off-by" statement and thereby agrees to the DCO. This can be added by using the `--signoff` parameter with `git commit`. + +Full text of the DCO: + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/bmc/redfishtool/files/v1.1.8/LICENSE.md b/bmc/redfishtool/files/v1.1.8/LICENSE.md new file mode 100644 index 000000000..5bd979f24 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/LICENSE.md @@ -0,0 +1,57 @@ +The Distributed Management Task Force (DMTF) grants rights under copyright in +this software on the terms of the BSD 3-Clause License as set forth below; no +other rights are granted by DMTF. This software might be subject to other rights +(such as patent rights) of other parties. + + +### Copyrights. + +Copyright (c) 2016, Contributing Member(s) of Distributed Management Task Force, +Inc.. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. +* Neither the name of the Distributed Management Task Force (DMTF) nor the names +of its contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +### Patents. + +This software may be subject to third party patent rights, including provisional +patent rights ("patent rights"). DMTF makes no representations to users of the +standard as to the existence of such rights, and is not responsible to +recognize, disclose, or identify any or all such third party patent right, +owners or claimants, nor for any incomplete or inaccurate identification or +disclosure of such rights, owners or claimants. DMTF shall have no liability to +any party, in any manner or circumstance, under any legal theory whatsoever, for +failure to recognize, disclose, or identify any such third party patent rights, +or for such party's reliance on the software or incorporation thereof in its +product, protocols or testing procedures. DMTF shall have no liability to any +party using such software, whether such use is foreseeable or not, nor to any +patent owner or claimant, and shall have no liability or responsibility for +costs or losses incurred if software is withdrawn or modified after publication, +and shall be indemnified and held harmless by any party using the software from +any and all claims of infringement by a patent owner for such use. + +DMTF Members that contributed to this software source code might have made +patent licensing commitments in connection with their participation in the DMTF. +For details, see http://dmtf.org/sites/default/files/patent-10-18-01.pdf and +http://www.dmtf.org/about/policies/disclosures. \ No newline at end of file diff --git a/bmc/redfishtool/files/v1.1.8/README.md b/bmc/redfishtool/files/v1.1.8/README.md new file mode 100644 index 000000000..34da3888c --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/README.md @@ -0,0 +1,607 @@ +Copyright 2016-2018 DMTF. All rights reserved. + +# redfishtool + +## About + +***redfishtool*** is a commandline tool that implements the client side of the Redfish RESTful API for Data Center Hardware Management. + +**Redfish** is the new RESTful API for hardware management defined by the DMTF Scalable Platform Management Forum (SPMF). It provides a modern, secure, multi-node, extendable interface for doing hardware management. The initial release included hardware inventory, server power-on/off/reset, reading power draw, setting power limits, reading sensors such as fans, read/write of ID LEDs, asset tags, and went beyond IPMI in functionality to include inventory of processors, storage, Ethernet controllers, and total memory. New Redfish extensions have now been added to the spec and include firmware update, BIOS config, memory inventory, direct attached storage control, and the list grows. + +***redfishtool*** makes it simple to use the Redfish API from a BASH script or interactively from a client command shell. + +While other generic HTTP clients such as Linux curl can send and receive Redfish requests, ***redfishtool*** goes well beyond these generic HTTP clients by automatically handling many of the hypermedia and Redfish-specific protocol aspects of the Redfish API that require a client to often execute multiple queries to a redfish service to walk the hypermedia links from the redfish root down to the detailed URI of a specific resource (eg Processor-2 of Blade-4 in a computer blade system). Specifically, redfishtool provides the following functions over curl: + +* implements Redfish Session Authentication as well as HTTP Basic Auth +* walks the Redfish schema following strict interoperpbility processors...] to find find the targeted instance based on Id, UUID, URL or other attributes +* handles GETs for collections that are returned in multiple pieces--requiring client to read in a loop until the full collection is returned +* handles ETag and If-Match headers when PATCHing a resource to write properties +* implements many common set or action operations with simple commandline syntax (eg server reset, setting LEDs, assetTag, powerLimits, etc) +* negotiates the latest redfish protocol version between client and service (demonstrating the proper way to do this) +* can read specific properties of a resource, or expand collections to include all members of the collection expanded +* supports adding and deleting users, and common Redfish account service operations +* For debug, provides multiple levels of verbose output to add descriptive headers, and show what HTTP requests are being executed +* For debug, includes multiple levels of status display showing HTTP status codes and headers returned and sent +* For easy parsing, outputs all responses in JSON format unless verbose or status debug options were specified + + +## Why redfishtool? + +1. ***redfishtool*** was originally written during the development of the Redfish specification to help find ambiguities in the spec. +1. ***redfishtool*** is now also being used to test interoperability between redfish service implementations. +1. In addition, ***redfishtool*** provides an example implementation for how a client can execute common server management functions like inventory; power-on/off/reset; setting power limits, indicator LEDs, and AssetTags, and searching a multi-node redfish service to find a specific node (with specific UUID, redfish Id, etc). redfishtool follows strict rules of interoperability. To support this goal, liberal comments are added throughout code to explain why each step is being executed. +1. As described above, it makes it easy to use the Redfish API from a BASH script, or as an easy-to-use interactive CLI -- but WITHOUIT creating a 'new API'. All (rather most) of the responses from ***redfishtool*** are Redfish-defined responses. The properties and resources are defined in the redfish spec. ***redfishtool*** is just a tool to access the Redfish API-not a new interface itself. + * The exception is that a 'list' operation was added for all collections to display the key properties for each of the members--rather than just the URIs to the members. + + +## Installation +`redfishtool` can be installed via [pip](https://pip.pypa.io/en/stable/). + +``` +pip install redfishtool +``` + + +## Requirements + +***redfishtool*** is based on Python 3 and the client system is required to have the Python framework installed before the tool can be installed and executed on the system. + +If cloning the tool from Github, as opposed to performing the installation via pip, the following packages are required to be installed and accessible from the python environment: + +* requests - [https://github.com/psf/requests]() +* python-dateutil - [https://github.com/dateutil/dateutil]() + +You may install the required packages by running: + + pip install -r requirements.txt + + +## Usage + +***python*** ***redfishtool*** [ ***Options*** ] [ ***SubCommands*** ] [ ***Operation*** ] [ ***OtherArgs*** ] + +* ***redfishtool*** is a python3.4+ program. It uses the python3 "requests" lib for sending HTTP requests, and a host of other standard libs in python3.4+ +* The ***redfishtool*** option/optarg parsing strictly follows the well established linux/GNU getopt syntax where arguments and options can be specified in any order, and both short (eg -r ) or long (--rhost=) syntax is supported. +* ***options*** are used to pass usernames, passwords, Host:port, authentication options, verbose/status flags, and also to specify how to search to find specific collection members (-I , -a (all), -M : ). +* ***subCommands*** indicate the general area of the API (following ipmitool convention), and align with Redfish navigation property names like "Chassis", "Systems", "AccountService", etc. +* ***Operations*** are specify an action or operation you want to perform like S`ystems setBootOverride` ..., or `Systems reset`. +* ***OtherArgs*** are any other arguments after the Operation that are sometimes required--like: `Systems ` ` + +### Common OPTIONS: + + -V, --version -- show redfishtool version, and exit + -h, --help -- show Usage, Options, and list of subCommands, and exit + -v, --verbose -- verbose level, can repeat up to 5 times for more verbose output + -v(header), -vv(+addl info), -vvv(Request trace), -vvvv(+subCmd dbg), -vvvvv(max dbg) + -s, --status -- status level, can repeat up to 5 times for more status output + -s(http_status), + -ss(+r.url, +r.elapsed executionTime ), + -sss(+request hdrs,data,authType, +response status_code, +response executionTime, + +login auth token/sessId/sessUri) + -ssss(+response headers), -sssss(+response data + -u , --user= -- username used for remote redfish authentication + -p , --password= -- password used for remote redfish authentication + -r , --rhost= -- remote redfish service hostname or IP:port + -t , --token= -- redfish auth session token-for sessions across multiple calls + -q, --quiet -- quiet mode--suppress error, warning, and diagnostic messages + -c ,--config= -- read options (including credentials) from file + -T ,--Timeout= -- timeout in seconds for each http request. Default=10 + + -P , --Prop= -- return only the specified property. Applies only to all "get" operations + -E, --Entries -- Fetch the Logs entries. Applies to Logs sub-command of Systems, Chassis and Managers + + +###### Options used by "raw" subcommand: + + -d --data= -- the http request "data" to send on PATCH,POST,or PUT requests + + +###### Options to specify top-level collection members: eg: `Systems -I ` +For `Systems`, `Managers`, and `Chassis` commands that require specifying a top-level collection member, if no option is specified the default is `--One`. + + -I , --Id= -- Use to specify the collection member + -M : --Match=:-- Use = search to find the collection member + -F, --First -- Use the 1st link returned in the collection or 1st "matching" link if used with -M + -1, --One -- Use the single link returned in the collection. Return error if more than one member exists + -a, --all -- Returns all members if the operation is a Get on a top-level collection like Systems + -L , --Link= -- Use (eg /redfish/v1/Systems/1) to reference the collection member. + -- If is not one of the links in the collection, and error is returned. + + +###### Options to specify 2nd-level collection members: eg: `Systems -I Processors -i` + + -i , --id= -- use to specify the 2nd-level collection member + -m : --match=:val>--use = search of 2nd-level collection to specify member + -l --link= -- Use (eg /redfish/v1/SYstems/1/Processors/1) to reference a 2nd level resource + -- A -I|M|F|1|L option is still required to specify the link to the top-lvl collection + -a, --all -- Returns all members of the 2nd level collection if the operation is a Get on the + -- 2nd level collection (eg Processors). -I|M|F|1|L still specifies the top-lvl collection. + + +###### Additional OPTIONS: + + -W :, -- Send up to {GET /redfish} requests with TCP connection timeout + --Wait=: -- before sending subcommand to rhost. Default is -W 1:3 + -A , --Auth -- Authentication type to use: Authn={None|Basic|Session} Default is Basic + -S , --Secure= -- When to use https: (Note: doesn't stop rhost from redirect http to https) + ={Always | IfSendingCredentials | IfLoginOrAuthenticatedApi(default) } + -R , --RedfishVersion=-- The Major Redfish Protocol version to use: ver={v1(dflt), v, Latest} + -C --CheckRedfishVersion -- tells Redfishtool to execute GET /redfish to verify that the rhost supports + the specified redfish protocol version before executing a sub-command. + The -C flag is auto-set if the -R Latest or -W ... options are selected + -N, --NonBlocking -- Do not wait for asynchronous requests to complete. + -n, --no-proxy -- Ignore any PROXY environment variables. + -H , --Headers= -- Specify the request header list--overrides defaults. Format "{ A:B, C:D...}" + -D , --Debug= -- Flag for dev debug. is a 32-bit uint: 0x or format + + +### Subcommands: + + hello -- redfishtool hello world subcommand for dev testing + about -- display version and other information about this version of redfishtool + versions -- get redfishProtocol versions supported by rhost: GET ^/redfish + root | serviceRoot -- get serviceRoot resource: GET ^/redfish/v1/ + Systems -- operations on Computer Systems in the /Systems collection + Chassis -- operations on Chassis in the /Chassis collection + Managers -- operations on Managers in the /Managers collection + AccountService -- operations on AccountService including user administration + SessionService -- operations on SessionService including Session login/logout + odata -- get the Odata Service document: GET ^/redfish/v1/odata + metadata -- get the CSDL metadata document: GET ^/redfish/v1/$metadata + raw -- subcommand to execute raw http methods(GET,PATCH,POST...) and URIs + +For Subcommand usage, including subcommand Operations and OtherArgs, execute: + + redfishtool -h -- usage and options for specific subCommand + +### Subcommand Operations and Addl Args + +###### Systems Operations + + python redfishtool.py -r -u -p Systems -h + Usage: + redfishtool [OPTNS] Systems [] -- perform on the system specified + : + [collection] -- get the main Systems collection. (Default operation if no member specified) + [get] -- get the computerSystem object. (Default operation if collection member specified) + list -- list information about the Systems collection members("Id", URI, and AssetTag) + patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object + reset -- reset a system. = On, GracefulShutdown, GracefulRestart, + ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle + setAssetTag -- set the system's asset tag + setIndicatorLed -- set the indicator LED. =redfish defined values: Off, Lit, Blinking + setBootOverride -- set Boot Override properties. =Disabled|Once|Continuous + -- =None|Pxe|Floppy|Cd|Usb|Hdd|BiosSetup|Utilities|Diags|UefiTarget| + Processors [list] -- get the "Processors" collection, or list "id" and URI of members. + Processors [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + Inventory [list] -- get the "Inventory" collection, or list "id" and URI of members. + + EthernetInterfaces [list] -- get the "EthernetInterfaces" collection, or list "id" and URI of members. + EthernetInterfaces [IDOPTN]-- get the member specified by IDOPTN: -i, -m:, -l, -a #all + + SimpleStorage [list] -- get the ComputerSystem "SimpleStorage" collection, or list "id" and URI of members. + SimpleStorage [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + + Logs [list] -- get the ComputerSystem "LogServices" collection , or list "id" and URI of members. + Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + clearLog -- clears the log defined by + examples -- example commands with syntax + hello -- Systems hello -- debug command + + +###### Chassis Operations + + python redfishtool.py -r -u -p Chassis -h + Usage: + redfishtool [OPTNS] Chassis [] -- perform on the Chassis specified + : + [collection] -- get the main Chassis collection. (Default operation if no member specified) + [get] -- get the Chassis object. (Default operation if collection member specified) + list -- list information about the Chassis collection members("Id", URI, and AssetTag) + patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object + setAssetTag -- set the Chassis's asset tag + setIndicatorLed -- set the indicator LED. =redfish defined values: Off, Lit, Blinking + Power -- get the full Power resource under a specified Chassis instance. + Thermal -- get the full Thermal resource under a specified Chassis instance. + Sensors -- get all sensors + + getPowerReading [-i] [consumed]-- get powerControl resource w/ power capacity, PowerConsumed, and power limits + if "consumed" keyword is added, then only current usage of powerControl[indx] is returned + is the powerControl array index. default is 0. normally, 0 is the only entry + setPowerLimit [-i] [ []] -- set powerLimit control properties + =null disables power limiting. is the powerControl array indx (dflt=0) + + Logs [list] -- get the Chassis "LogServices" collection , or list "id" and URI of members. + Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + clearLog -- clears the log defined by + examples -- example commands with syntax + hello -- Chassis hello -- debug command + + +###### Managers Operations + + python redfishtool.py -r -u -p Managers -h + Usage: + redfishtool [OPTNS] Managers [] -- perform on the Managers specified + : + [collection] -- get the main Managers collection. (Default operation if no member specified) + [get] -- get the specified Manager object. (Default operation if collection member specified) + list -- list information about the Managers collection members("Id", URI, and UUID) + patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object + reset -- reset a Manager. = On, GracefulShutdown, GracefulRestart, + ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle + setDateTime --set the date and time + setTimeOffset offset= --set the time offset w/o changing time setting + is of form "[+/-]mm:ss". Ex: "-10:01" + NetworkProtocol -- get the "NetworkProtocol" resource under the specified manager. + setIpAddress [-i]... -- set the Manager IP address -NOT IMPLEMENTED YET + + EthernetInterfaces [list] -- get the managers "EthernetInterfaces" collection, or list "id",URI, Name of members. + EthernetInterfaces [IDOPTN]-- get the member specified by IDOPTN: -i, -m:, -a #all + + SerialInterfaces [list] -- get the managers "SerialInterfaces" collection, or list "id",URI, Name of members. + SerialInterfaces [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + + Logs [list] -- get the Managers "LogServices" collection , or list "id",URI, Name of members. + Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + clearLog -- clears the log defined by + examples -- example commands with syntax + hello -- Systems hello -- debug command + + +###### AccountService Operations + + python redfishtool.py -r -u -p AccountService -h + Usage: + redfishtool [OPTNS] AccountService [] -- perform on the AccountService + : + [get] -- get the AccountService object. + patch {A: B,C: D,...} -- patch the AccountService w/ json-formatted {prop: value...} + Accounts [list] -- get the "Accounts" collection, or list "Id", username, and Url + Accounts [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + Roles [list] -- get the "Roles" collection, or list "Id", IsPredefined, and Url + Roles [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + adduser [] -- add a new user to the Accounts collection + -- :{Administrator | Operator | ReadOnlyUser | -- delete an existing user from Accouts collection + setpassword -- set (change) the password of an existing user account + useradmin [enable|disable|unlock|[setRoleId ]] -- enable|disable|unlock.. a user account + setusername -- set UserName for account with given Id + examples -- example commands with syntax + hello -- AccountService hello -- debug command + + +###### SessionService Operations + + python redfishtool.py -r -u -p SessionService -h + Usage: + redfishtool [OPTNS] SessionService [] -- perform on the SessionService + : + [get] -- get the sessionService object. + patch {A: B,C: D,...} -- patch the sessionService w/ json-formatted {prop: value...} + setSessionTimeout -- patches the SessionTimeout property w/ etag support + Sessions [list] -- get the "Sessions" collection, or list "Id", username, and Url + Sessions [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all + login -- sessionLogin. post to Sessions collection to create a session + the user is -u, password is -p + logout -- logout or delete the session by identified by -i or -l + where is the session path returned in Location from login + examples -- example commands with syntax + hello -- Systems hello -- debug command + + +###### raw Operations + + python redfishtool.py -r -u -p raw -h + Usage: + redfishtool [OPTNS] raw + + redfishtool raw -h # for help + redfishtool raw examples #for example commands + + is one of: GET, PATCH, POST, DELETE, HEAD, PUT + is full URI path to a redfish resource--the full path following , starting with forward slash / + + Common OPTNS: + -u , --user= -- username used for remote redfish authentication + -p , --password= -- password used for remote redfish authentication + -t , --token= - redfish auth session token-for sessions across multiple calls + + -r , --rhost= -- remote redfish service hostname or IP:port + -X --request= -- the http method to use. ={GET,PATCH,POST,DELETE,HEAD,PUT}. Default=GET + -d --data= -- the http request "data" to send on PATCH,POST,or PUT requests + -H , --Headers= -- Specify the request header list--overrides defaults. Format "{ A:B, C:D...}" + -S , --Secure= -- When to use https: (Note: doesn't stop rhost from redirect http to https) + : + GET -- HTTP GET method + PATCH -- HTTP PATCH method + POST -- HTTP POST method + DELETE -- HTTP DELETE method + HEAD -- HTTP HEAD method + PUT -- HTTP PUT method + examples -- example raw commands with syntax + hello -- raw hello -- debug command + + +# Example Usage + +### System subcommand Examples + + $ python redfishtool.py -r -u -p Systems examples + # Shows the Systems Collection + redfishtool -r -u -p Systems + + # Lists Id, Uri, AssetTag for all systems + redfishtool -r -u -p Systems list + + # Gets the system with Id= + redfishtool -r -u -p Systems -I + + # Gets the system with AssetTag=12345 + redfishtool -r -u -p Systems -M AssetTag:12345 + + # Gets the system at URI= + redfishtool -r -u -p Systems -L + + # Gets the first system returned (for debug) + redfishtool -r -u -p Systems -F + + # Gets the first system and verify that there is only one system + redfishtool -r -u -p Systems -1 + + # Patches the json-formated {prop: value...} data to the specified system + redfishtool -r -u -p Systems -I patch {A: B,C: D,...} + + # Patches the json-formated {prop: value...} data to all systems + redfishtool -r -u -p Systems --all patch {A: B,C: D,...} + + # Resets a system. =the redfish-defined values: On, Off, gracefulOff... + redfishtool -r -u -p Systems -I reset + + # Resets all systems. =the redfish-defined values: On, Off, gracefulOff... + redfishtool -r -u -p Systems --all reset + + # Sets the system's asset tag to + redfishtool -r -u -p Systems -I setAssetTag + + # Sets all system's asset tags to + redfishtool -r -u -p Systems --all setAssetTag + + # Sets the indicator LED. =redfish defined values: Off, Lit, Blinking + redfishtool -r -u -p Systems -I setIndicatorLed + + # Sets the indicator LED on all systems. =redfish defined values: Off, Lit, Blinking + redfishtool -r -u -p Systems --all setIndicatorLed + + # Sets Boot Override properties. =Disabled|Once|Continuous + redfishtool -r -u -p Systems -I setBootOverride + + # Sets Boot Override properties on all systems. =Disabled|Once|Continuous + redfishtool -r -u -p Systems --all setBootOverride + + # Gets the Processor Collection + redfishtool -r -u -p Systems -I Processors + + # Lists Id, Uri, & Socket for all processors in system w/ Id= + redfishtool -r -u -p Systems -I Processors list + + # Gets the processor with id=1 in system with Id= + redfishtool -r -u -p Systems -I Processors -i 1 + + # Gets processor with property Socket=CPU_1, on system at url + redfishtool -r -u -p Systems -L Processors -m Socket:CPU_1 + + # Gets log member with Id=SEL from the first System + redfishtool -r -u -p Systems -1 Logs -i SEL + + # Gets log entries with Id=SEL from the first System + redfishtool -r -u -p Systems -1 Logs -E -i SEL + + # Gets System inventory + redfishtool -r -u -p Systems Inventory + + +### Chassis subcommand Examples + + $ python redfishtool.py -r -u -p Chassis examples + # Shows the Chassis Collection + redfishtool -r -u -p Chassis + + # Lists Id, Uri, AssetTag for all Chassis + redfishtool -r -u -p Chassis list + + # Gets the Chassis with Id= + redfishtool -r -u -p Chassis -I + + # Gets the Chassis with AssetTag=12345 + redfishtool -r -u -p Chassis -M AssetTag:12345 + + # Gets the Chassis at URI= + redfishtool -r -u -p Chassis -L + + # Gets the first Chassis returned (for debug) + redfishtool -r -u -p Chassis -F + + # Gets the first Chassis and verify that there is only one system + redfishtool -r -u -p Chassis -1 + + # Patches the json-formated {prop: value...} data to the specified chassis + redfishtool -r -u -p Chassis -I patch {A: B,C: D,...} + + # Patches the json-formated {prop: value...} data to all chassis + redfishtool -r -u -p Chassis --all patch {A: B,C: D,...} + + # Sets the chassis's asset tag + redfishtool -r -u -p Chassis -I setAssetTag + + # Sets all chassis's asset tags + redfishtool -r -u -p Chassis --all setAssetTag + + # Sets the indicator LED. =redfish defined values: Off, Lit, Blinking + redfishtool -r -u -p Chassis -I setIndicatorLed + + # Sets the indicator LED on all chassis. =redfish defined values: Off, Lit, Blinking + redfishtool -r -u -p Chassis --all setIndicatorLed + + # Gets the full chassis Power resource + redfishtool -r -u -p Chassis -I Power + + # Gets the full chassis Thermal resource + redfishtool -r -u -p Chassis -I Thermal + + # Gets chassis/Power powerControl[] resource if optional "consumed" arg, then return only the PowerConsumedWatts prop + redfishtool -r -u -p Chassis -I getPowerReading[-i [consumed] + + # Sets the power limit + redfishtool -r -u -p Chassis -L setPowerLimit [-i] [ []] + + # Sets the power limit on all chassis + redfishtool -r -u -p Chassis --all setPowerLimit [-i] [ []] + + # Gets log member with Id=SEL from the first Chassis + redfishtool -r -u -p Chassis -1 Logs -i SEL + + # Gets log entries with Id=SEL from the first Chassis + redfishtool -r -u -p Chassis -1 Logs -E -i SEL + + # Gets all Sensors + redfishtool -r -u -p Chassis Sensors + + +### Managers subcommand Examples + + $ python redfishtool.py -r -u -p Managers examples + # Shows the Managers Collection + redfishtool -r -u -p + + # Lists Id, Uri, AssetTag for all Managers + redfishtool -r -u -p Managers list + + # Gets the Manager with Id= + redfishtool -r -u -p Managers -I + + # Gets the Manager with AssetTag=12345 + redfishtool -r -u -p Managers -M AssetTag:12345 + + # Gets the Manager at URI= + redfishtool -r -u -p Managers -L + + # Gets the first Manager returned (for debug) + redfishtool -r -u -p Managers -F + + # Gets the first Manager and verify that there is only one Manager + redfishtool -r -u -p Managers -1 + + # Patches the json-formated {prop: value...} data to the object + redfishtool -r -u -p Managers -I patch {A: B,C: D,...} + + # Resets a Manager. =the redfish-defined values: On, Off, gracefulOff... + redfishtool -r -u -p Managers -I reset + + # Gets the NetworkProtocol resource under the specified manager + redfishtool -r -u -p Managers -I NetworkProtocol + + # Lists Id, Uri, and Name for all of the NICs for Manager w/ Id= + redfishtool -r -u -p Managers -I EthernetInterfaces list + + # Gets the NIC with id=1 in manager with Id= + redfishtool -r -u -p Managers -I EthernetInterfaces -i 1 + + # Gets the NIC with MAC AA:BB:CC:DD:EE:FF for manager at url + redfishtool -r -u -p Managers -L EthernetInterfaces -m MACAddress:AA:BB:CC:DD:EE:FF + + # Gets log member with Id=SEL from the first Manager + redfishtool -r -u -p Managers -1 Logs -i SEL + + # Gets log entries with Id=SEL from the first Manager + redfishtool -r -u -p Managers -1 Logs -E -i SEL + + +### AccountService subcommand Examples + + $ python redfishtool.py -r -u -p AccountService examples + # Gets the AccountService + redfishtool -r -u -p AccountService + + # Sets the failed login lockout threshold + redfishtool -r -u -p AccountService patch { "AccountLockoutThreshold": 5 } ] + + # Gets the Accounts collection + redfishtool -r -u -p AccountService Accounts + + # List Accounts to get Id, username, url for each account + redfishtool -r -u -p AccountService Accounts list + + # Gets the Accounts member with username: john + redfishtool -r -u -p AccountService Accounts -m UserName:john + + # Lists the Roles collection to get RoleId, IsPredefined, & url for each role + redfishtool -r -u -p AccountService Roles list + + # Gets the Roles member with RoleId=Admin + redfishtool -r -u -p AccountService Roles -i Admin + + # Adds the new user (john) w/ passwd "12345" and role: Admin + redfishtool -r -u -p AccountService adduser john 12345 Admin + + # Deletes the account with the username "john" + redfishtool -r -u -p AccountService deleteuser john + + # Disables the account with the username "john" + redfishtool -r -u -p AccountService useradmin john disable + + # Unlocks the account with the username "john" + redfishtool -r -u -p AccountService useradmin john unlock + + # Sets the username for account with id=3 to "alice" + redfishtool -r -u -p AccountService setusername 3 alice + + +### SessionService subcommand Examples + + $ python redfishtool.py -r -u -p SessionService examples + # Gets the sessionService + redfishtool -r -u -p SessionService + + # Sets the session timeout property + redfishtool -r -u -p SessionService setSessionTimeout + + # Gets Sessions collection + redfishtool -r -u -p SessionService Sessions + + # Gets the session at URI= + redfishtool -r -u -p SessionService Sessions -l + + # Gets the session with session Id + redfishtool -r -u -p SessionService Sessions -i + + # Patches the json-formated {prop: value...} data to the sessionService object + redfishtool -r -u -p SessionService patch {A: B,C: D,...} + + # Login (create session) + redfishtool -r -u -p SessionService login -u -p + + # Logout (delete session ) + redfishtool -r -u -p SessionService logout -i + + +## Running in Windows + +In order for executables to resolve if using Windows, ensure both the "Python" and "Scripts" folder are included in the PATH environment variable. For example, if Python is installed to "C:\Python", the PATH environment variable should include "C:\Python" and "C:\Python\scripts". + + +## Known Issues, and ToDo Enhancements + +1. modifications to make PATCH commands work better with Windows cmd shell quoting +2. support clearlog +3. add additional APIs that have been added to Redfish after 1.0---this version supports only 1.0 APIs +4. add custom role create and delete + + +## Release Process + +1. Go to the "Actions" page +2. Select the "Release and Publish" workflow +3. Click "Run workflow" +4. Fill out the form +5. Click "Run workflow" diff --git a/bmc/redfishtool/files/v1.1.8/redfishtool.py b/bmc/redfishtool/files/v1.1.8/redfishtool.py new file mode 100644 index 000000000..3ed0ac806 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtool.py @@ -0,0 +1,21 @@ +#!/usr/bin/python +# Copyright Notice: +# Copyright 2016, 2020 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: redfishtool.py +# +# contents: +# -CLI wrapper: calls main() routine in ./redfishtool package +# +# Note: structure supports a future lib interface to the routines +# where the libWrapper is the interface +# +from redfishtoollib import main +import sys + +if __name__ == "__main__": + main(sys.argv) + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/AccountService.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/AccountService.py new file mode 100644 index 000000000..caec77e38 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/AccountService.py @@ -0,0 +1,725 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: AccountService.py +# +# contains AccountService related subCommands and access functions +# +# Class RfAccountServiceMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - AccountService command table, dispatch of operation eg get, reset +# - AccountServiceMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run AccountService operation (sub-sub-command) +# +# Class RfAccountServiceOperations +# All of the AccountService sub-command operations eg: get AccountService,Accounts, Sessions, adduser... etc +# - hello - test cmd +# - get - get the account service +# - patch - raw subcommand to patch a accountService, with etag support +# - Accounts - get Accounts collection, Accounts instance, list Accounts, get all Accounts +# - Roles - get Roles collection, Roles instance, list Roles, get all Roles +# - adduser - add a new user to Accounts collection +# - setpassword - update the password of an existing user account +# - deleteuser - delete an existin user from Accouts collection +# - userAdmin - enable, disable, or unlock a user account +# - addRole - add a new custom role to the Roles collection +# - deleteRole - delete an existing role from Roles collection +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin + +class RfAccountServiceMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] AccountService [] -- perform on the AccountService ".format(rft.program)) + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print("") + + def displayOperations(self,rft): + print(" :") + print(" [get] -- get the AccountService object. ") + print(" patch {A: B,C: D,...} -- patch the AccountService w/ json-formatted {prop: value...} ") + print(" Accounts [list] -- get the \"Accounts\" collection, or list \"Id\", username, and Url ") + print(" Accounts [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" Roles [list] -- get the \"Roles\" collection, or list \"Id\", IsPredefined, and Url ") + print(" Roles [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" adduser [] -- add a new user to the Accounts collection") + print(" -- :{Administrator | Operator | ReadOnlyUser | -- delete an existing user from Accouts collection") + print(" setpassword -- set (change) the password of an existing user account") + print(" useradmin [enable|disable|unlock|[setRoleId ]] -- enable|disable|unlock.. a user account") + print(" setusername -- set UserName for account with given Id") + #print(" addrole -- add a new custom role to the Roles collection") + #print(" -- is string of form: Login,ConfigeUsers,ConfigureSelf...") + #print(" deleterole -- delete an existing role from Roles collection") + print(" examples -- example commands with syntax") + print(" hello -- AccountService hello -- debug command") + return(0) + +# - changePassword - update the password of an existing user account +# - deleteUser - delete an existin user from Accouts collection +# - userAdmin - enable, disable, or unlock a user account +# - addRole - add a new custom role to the Roles collection +# - deleteRole - delete an existing role from Roles collection +# - examples --prints some example apis + + def runOperation(self,rft): + # instantiate AccountService class + op=RfAccountServiceOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "get": op.get, + "patch": op.patch, + "Accounts": op.getAccounts, + "Roles": op.getRoles, + "adduser": op.addUser, + "deleteuser": op.deleteUser, + "setpassword": op.setPassword, + "useradmin": op.userAdmin, + "setusername": op.setUsername, + #"addrole": op.addRole, + #"deleterole": op.deleteRole, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"AccountService:runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"AccountService:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"AccountService:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("AccountService: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def AccountServiceMain(self,rft,cmdTop=False): + rft.printVerbose(4,"AccountServiceMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(0,None,False,None) + + # we will validate usage of -P and -a in action processing + # actually, if a non 'get' action is specified, -P and -a are just ignored :) + + args=rft.subcommandArgv[0:] + + #if no args, this is a getAccountService command + if( len(args) < 2 ): + self.operation="get" + self.args= None + else: + self.operation=args[1] + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"AccountService: operation={}, args={}".format(self.operation,self.args)) + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"AccountService: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"AccountService: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the AccountService subCommand +# +class RfAccountServiceOperations(): + def __init__(self): + self.AccountServicePath=None + self.AccountServiceCollectionDict=None + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from AccountService") + return(0,None,False,None) + + + + def get(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("get AccountService: Error getting service root, aborting") + return(rc,r,False,None) + + # get the link to the AccountService + # need to test we got good data + if (("AccountService" in d) and ("@odata.id" in d["AccountService"])): + accountServiceLink=d["AccountService"]["@odata.id"] + else: + rft.printErr("Error: root does not have a AccountService link") + return(4) + + rft.printVerbose(4,"AccountService: get AccountService: link is: {}".format(accountServiceLink)) + + if cmdTop is True: prop=rft.prop + + # do a GET to get the AccountService, if -P show property, else show full response + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=accountServiceLink, prop=prop) + + if(rc==0): rft.printVerbose(1," AccountService Resource:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + + def patch(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + # verify we have got an argument which is the patch structure + # its in form '{ "AssetTag": , "IndicatorLed": }' + ##print("patchData: {}".format(patchData)) + if( len( sc.args) == 2): + ##print("data:{}".format(sc.args[1])) + try: + patchData=json.loads(sc.args[1]) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1])) + return(5,None,False,None) + ##print("patchData: {}".format(patchData)) + else: + rft.printErr("Patch: error: invalid argument format") + rft.printErr(" : args={}".format(sc.args)) + rft.printErr(" : expect: SessionService patch \"{ : }\"") + return(4,None,False,None) + + # read the AccountService resource + # this is used by the generic rft.patchResource() function to see if there is an etag in the response + # if an etag is in response hdr, then we must include the etag on the patch, so this get is required + # note: the format of etag header for redfish is: ETag: W/"" or " 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=accountsLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="UserName") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, UserName".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no account was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=accountsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the session specific by -i or -m or -l + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the Accounts collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=accountsLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the account, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the Accounts members + else: + rft.printVerbose(4,"getting expanded Accounts Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=accountsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getRoles(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation: getRoles collection".format(rft.subcommand,sc.operation)) + + # get the AccountService resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="Roles" + # get the link to the Roles collection + if ((collName in d) and ("@odata.id" in d[collName])): + rolesLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: AccountsService resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=rolesLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="IsPredefined") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, IsPredefined".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no account was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=rolesLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the session specific by -i or -m or -l + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the Accounts collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=rolesLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the account, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the Accounts members + else: + rft.printVerbose(4,"getting expanded Roles Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=rolesLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + + # adduser [] ---add a new user + def addUser(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # verify we got 2 args and + # if we got 3rd arg , use it, otherwise use builtin Operator role + # get the Accounts collection + # then verify we don't already have a user with that username + # then get Roles collection, and verify it is a valid role for that system + # create patch string + # POST new entry to Accounts with: user, passwd, roleId to create new user + + # verify we have two addl arg, + if(len(sc.args) < 3 ): + rft.printErr("Error, adduser: invalid number of arguments") + rft.printErr("Syntax: {} AccountService adduser []".format(rft.program)) + rft.printErr(" default is Operator") + return(8,None,False,None) + + # get the and from args + userName=sc.args[1] + password=sc.args[2] + + # if a roleId was specified (arg3), then use it, otherwise use default operator + if(len(sc.args) > 3 ): + roleVal=sc.args[3] + else: + roleVal="Operator" + + # get Accounts collection + rc,r,j,d=op.getAccounts(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + accountsUrl=r.url + + # check if we already have a user with this username + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="UserName" + rft.matchLevel2Value=userName + path,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc == 0): # if we found the user with this name + rft.printErr("Error: username {} already exists".format(userName)) + return(9,None,False,None) + + # verify that the specified RoleId is valid for this system + # get Roles collection + rc,r,j,d=op.getRoles(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + # check if the roleId specified is supported on this platform + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="Id" + rft.matchLevel2Value=roleVal + path,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc != 0): + rft.printErr("Error: roleId: {} is not supported on remote service".format(roleVal)) + return(rc,None,False,None) + + # create POST request data + postData={"UserName": userName, "Password": password, "RoleId": roleVal} + + # POST new entry to Accounts with: user, passwd, roleId to create new user + reqPostData=json.dumps(postData) + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'POST', accountsUrl, reqData=reqPostData) + if(rc==0): + rft.printVerbose(1," Add User: {} (POST to Accounts collection): ".format(userName), skip1=True, printV12=cmdTop) + respData=d + return(rc,r,j,respData) + else: return(rc,r,False,None) + + + + + # deleteuser + def deleteUser(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # verify one addl arg username + # get Accounts collection + # then find the Account entry for username, and verify we have that user + # sent DELETE to Accounts collection to remove the account + + # verify we have one addl arg: username + if(len(sc.args) < 2 ): + rft.printErr("Error, deleteuser: invalid number of arguments") + rft.printErr("Syntax: {} AccountService deleteuser ".format(rft.program)) + return(8,None,False,None) + + # get the and from args + userName=sc.args[1] + + # get Accounts collection + rc,r,j,d=op.getAccounts(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + accountsUrl=r.url + + # check if we indeed have a user with this username + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="UserName" + rft.matchLevel2Value=userName + accountPath,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc != 0): # if we found the user with this name + rft.printErr("Error: username {} does not exists".format(userName)) + return(9,None,False,None) + + # send DELETE to this account Url to remove it from the Accounts collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'DELETE', accountsUrl, relPath=accountPath) + if(rc==0): + rft.printVerbose(1," Delete User: {} (DELETE to Account entry): ".format(userName), skip1=True, printV12=cmdTop) + return(rc,r,False,None) + else: return(rc,r,False,None) + + + + + # useradmin {enable|disable|unlock|{setRoleId }} + def userAdmin(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # verify we have two addl arg, and it is a valid keyword: enable,disable,unlock,setRoleId=... + # create the patch strint + # get Accounts collection + # then find the Accounts entry for username, and verify we have that user + # verify the service supports the properties we need to set + # send patch to set the properties + #print("arglen:{}, arg0:{}, args:{}".format(len(sc.args),sc.args[0], sc.args)) + + # verify we have two addl arg, + if(len(sc.args) < 3 ): + rft.printErr("Error, userAdmin: invalid number of arguments") + rc=self.userAdminUsage(rft) + return(rc,None,False,None) + + # get the and from args + userName=sc.args[1] + action=sc.args[2] + roleVal="" + + # create the patch string, and identify property to set so that we can check that the resource has the property later + if( action=="enable"): + prop="Enabled" + patchData={prop: True} + elif( action=="disable"): + prop="Enabled" + patchData={prop: False} + elif( action=="unlock"): + prop="Locked" + patchData={prop: False} + elif( action=="setRoleId"): + if(len(sc.args) < 4): + rft.printErr("Error, userAdmin setRoleId: no argument") + rc=self.userAdminUsage(rft) + return(rc,None,False,None) + else: + prop="RoleId" + roleVal=sc.args[3] + patchData={prop: roleVal} + else: + rft.printErr("Error: userAdmin: specified not valid") + rc=self.userAdminUsage(rft) + return(rc,None,False,None) + + # get Accounts collection + rc,r,j,d=op.getAccounts(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + # find the Accounts entry for username, and verify we have that user + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="UserName" + rft.matchLevel2Value=userName + path,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc != 0): + rft.printErr("Error: username {} is not valid user".format(userName)) + return(rc,None,False,None) + accountResponse=r + + # verify the service supports the properties we need to set + if( not prop in d ): + rft.printErr("Error: userAdmin: property {} not in the remote service user account resource".format(prop)) + return(8,None,False,None) + + # if action=setRoleId, verify the service has that role + if( action=="setRoleId"): + # get Roles collection + rc,r,j,d=op.getRoles(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + # check if the roleId specified is supported on this platform + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="Id" + rft.matchLevel2Value=roleVal + path,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc != 0): + rft.printErr("Error: roleId: {} is not supported on remote service".format(roleVal)) + return(rc,None,False,None) + + # send patch to set the properties + rc,r,j,d=rft.patchResource(rft, accountResponse, patchData) + if(rc==0): + rft.printVerbose(1," AccountService userAdmin {} {} :".format(action,roleVal),skip1=True, printV12=cmdTop) + respData={prop: d[prop]} + return(rc,r,j,respData) + else: return(rc,r,False,None) + + def userAdminUsage(self,rft): + rft.printErr("Syntax: {} useradmin {{enable|disable|unlock|{{setRoleId }} }}".format(rft.program)) + return(8) + + + # setusername + def setUsername(self, sc, op, rft, cmdTop=False, prop=None): + rft.printVerbose(4, "{}:{}: in operation".format(rft.subcommand, sc.operation)) + + # verify we have two addl args ( and ) + # create the patch string + # get Accounts collection + # then find the Accounts entry for the id, and verify we have that id + # verify the service supports the properties we need to set + # send patch to set the properties + # print("arglen:{}, arg0:{}, args:{}".format(len(sc.args),sc.args[0], sc.args)) + + # verify we have two additional args, + if (len(sc.args) != 3): + rft.printErr("Error, setusername: invalid number of arguments") + rc = self.setUsernameUsage(rft) + return (rc, None, False, None) + + # get the and from args + idVal = sc.args[1] + userName = sc.args[2] + + # create the patch string, and identify property to set so that we can check that the resource has the property later + prop = "UserName" + patchData = {prop: userName} + + # get Accounts collection + rc, r, j, d = op.getAccounts(sc, op, rft) + if (rc != 0): return (rc, r, False, None) + + # find the Accounts entry for id, and verify we have that id + rft.gotMatchLevel2Optn = True + rft.matchLevel2Prop = "Id" + rft.matchLevel2Value = idVal + path, rc, r, j, d = rft.getLevel2ResourceById(rft, r, + d) # this will return path to the acct as well as current resource in d + if (rc != 0): + rft.printErr("Error: Id {} is not a valid account Id".format(idVal)) + return (rc, None, False, None) + accountResponse = r + + # verify the service supports the properties we need to set + if (not prop in d): + rft.printErr("Error: setusername: property {} not in the remote service user account resource".format(prop)) + return (8, None, False, None) + + # send patch to set the properties + rc, r, j, d = rft.patchResource(rft, accountResponse, patchData) + if (rc == 0): + rft.printVerbose(1, " AccountService setusername {} {} :".format(idVal, userName), skip1=True, + printV12=cmdTop) + respData = {prop: d[prop]} + return (rc, r, j, respData) + else: + return (rc, r, False, None) + + def setUsernameUsage(self, rft): + rft.printErr( + "Syntax: {} setusername ".format(rft.program)) + return (8) + + + # setpassword + def setPassword(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # verify we have two addl args: . + # create the patch string + # get Accounts collection + # then find the Accounts entry for username, and verify we have that user + # send patch to set the password + + # verify we have two addl arg, + if(len(sc.args) < 3 ): + rft.printErr("Error, setPassword: invalid number of arguments") + rft.printErr("Syntax: {} AccountService setPassword setpassword ".format(rft.program)) + return(8,None,False,None) + + # get the and from args, and for the patchData + prop="Password" + userName=sc.args[1] + password=sc.args[2] + patchData={prop: password} + + # get Accounts collection + rc,r,j,d=op.getAccounts(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + # find the Accounts entry for username, and verify we have that user + rft.gotMatchLevel2Optn=True + rft.matchLevel2Prop="UserName" + rft.matchLevel2Value=userName + path,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) # this will return path to the acct as well as current resource in d + if( rc != 0): + rft.printErr("Error: username {} is not valid user".format(userName)) + return(rc,None,False,None) + + # send patch to set the properties + # but set flag not to read the account back + # since the password has changed, this would fail for Basic Auth + rc,r,j,d=rft.patchResource(rft, r, patchData, getResponseAfterPatch=False ) + if(rc==0): + rft.printVerbose(1," AccountService setPassword: successful",skip1=True, printV12=cmdTop) + respData=d + return(rc,r,j,respData) + else: return(rc,r,False,None) + + + + + # addRole -- operation flow similar to addUser + #def setPassword(self,sc,op,rft,cmdTop=False, prop=None): + # rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + + + # deleteRole -- operation flow similar to deleteUser + #def setPassword(self,sc,op,rft,cmdTop=False, prop=None): + # rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r AccountService # gets the AccountService".format(rft.program)) + print(" {} -r AccountService patch {{ \"AccountLockoutThreshold\": 5 }} ]# set failed login lockout threshold".format(rft.program)) + print(" {} -r AccountService Accounts # gets Accounts collection".format(rft.program)) + print(" {} -r AccountService Accounts list # list Accounts to get Id, username, url for each account".format(rft.program)) + print(" {} -r AccountService Accounts -mUserName:john # gets the Accounts member with username: john".format(rft.program)) + print(" {} -r AccountService Roles list # list Roles collection to get RoleId, IsPredefined, & url for each role".format(rft.program)) + print(" {} -r AccountService Roles -iAdministrator # gets the Roles member with RoleId=Administrator".format(rft.program)) + print(" {} -r AccountService adduser john 12345 Administrator # add new user (john) w/ passwd \"12345\" and role: Administrator".format(rft.program)) + print(" {} -r AccountService deleteuser john # delete user \"john\"s account".format(rft.program)) + print(" {} -r AccountService useradmin john disable # disable user \"john\"s account".format(rft.program)) + print(" {} -r AccountService useradmin john unlock # unlock user \"john\"s account".format(rft.program)) + print(" {} -r AccountService setusername 3 alice # set username for account with id=3 to \"alice\"".format(rft.program)) + return(0,None,False,None) + + +''' +TODO: +1. add "addRole" and "deleteRole" commands +2. + + +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/Chassis.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Chassis.py new file mode 100644 index 000000000..4eea25d7c --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Chassis.py @@ -0,0 +1,843 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: Chassis.py +# +# contains Chassis subCommands and access functions +# +# Class RfSystemsMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - Chassis command table, dispatch of operation eg get, reset +# - ChassisMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run Chassis operations (sub-sub-command) +# +# Class RfChassisOperations +# All of the Chassis sub-command operations eg: get, setIndicatorLed, etc +# - hello - test cmd +# - getCollection - return the Chassis collection +# - get - get a member of a collection -or property of the member +# - list - show of list of collection members and key idetifying properties +# (Id, AssetTag, UriPath) +# - patch - raw subcommand to patch a Chassis Member, with etag support +# - setAssetTag -- patches the assetTag of Chassis instance w/ etag support +# - setIndicatorLed --sets id LED to a specified value w/ etag support +# - getPower - get Processors collection, processor instance, or all +# - getSensors - get all sensors +# - getThermal - get Ethernet collection, instance, all +# - setPowerLimit - Set the PowerLimit for a member of the powerControl array +# - getPowerReading [consumed] - get the powerControl array, or ConsumedWatts from pwrCntl[0] +# - getLogService - get LogService collection, instance, all +# - clearLog -- clears a specified log (not implemented yet) +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin + +class RfChassisMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] Chassis [] -- perform on the Chassis specified ".format(rft.program)) + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print(" ") + + def displayOperations(self,rft): + print(" :") + print(" [collection] -- get the main Chassis collection. (Default operation if no member specified)") + print(" [get] -- get the Chassis object. (Default operation if collection member specified)") + print(" list -- list information about the Chassis collection members(\"Id\", URI, and AssetTag)") + print(" patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object") + print(" setAssetTag -- set the Chassis's asset tag ") + print(" setIndicatorLed -- set the indicator LED. =redfish defined values: Off, Lit, Blinking") + print(" Power -- get the full Power resource under a specified Chassis instance.") + print(" Thermal -- get the full Thermal resource under a specified Chassis instance.") + print(" Sensors -- get all sensors.") + print("") + print(" getPowerReading [-i] [consumed]-- get powerControl resource w/ power capacity, PowerConsumed, and power limits") + print(" if \"consumed\" keyword is added, then only current usage of powerControl[indx] is returned") + print(" is the powerControl array index. default is 0. normally, 0 is the only entry") + print(" setPowerLimit [-i] [ []] -- set powerLimit control properties") + print(" =null disables power limiting. is the powerControl array indx (dflt=0)") + print("") + print(" Logs [list] -- get the Chassis \"LogServices\" collection , or list \"id\" and URI of members.") + print(" Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" clearLog -- clears the log defined by ") + print(" examples -- example commands with syntax") + print(" hello -- Chassis hello -- debug command") + return(0) + + + + def runOperation(self,rft): + # instantiate ChassisOperations class + op=RfChassisOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "collection": op.getCollection, + "get": op.get, + "list": op.clist, + "patch": op.patch, + "setAssetTag": op.setAssetTag, + "setIndicatorLed": op.setIndicatorLed, + "Power": op.getPower, + "Thermal": op.getThermal, + "Sensors": op.getSensors, + "setPowerLimit": op.setPowerLimit, + "getPowerReading": op.getPowerReading, + "Logs": op.getLogService, + "-clearLog": op.clearLog, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"Chassis:runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"Chassis:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"Chassis:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("Chassis: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def ChassisMain(self,rft,cmdTop=False): + rft.printVerbose(4,"ChassisMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(0,None,False,None) + + # we will validate usage of -P and -a in action processing + # actually, if a non 'get' action is specified, -P and -a are just ignored :) + + args=rft.subcommandArgv[0:] + + #if no args, then if no member Id was specified (with -I|-M|-1|-F) then assume it is a "collection" operation + # if a -IM1F was specified, then assume it is a "get" operation for that member + if( len(args) < 2 ): + if( rft.IdOptnCount==0 ): + self.operation="collection" + else: + self.operation="get" + self.args= None + else: + self.operation=args[1] + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"Chassis: operation={}, args={}".format(self.operation,self.args)) + + # check if the command requires a collection member target -I|-M|-L|-1|-F eg sysIdoptn + nonIdCommands = ["collection", "list", "examples", "hello"] + if( ( not self.operation in nonIdCommands ) and (rft.IdOptnCount==0) ): + # default to --One if no Id option specified + rft.oneOptn = True + rft.IdOptnCount += 1 + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"Chassis: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"Chassis: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the Chassis subCommand +# +class RfChassisOperations(): + def __init__(self): + self.chassisPath=None + self.chassisCollectionDict=None + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from Chassis") + return(0,None,False,None) + + def getCollection(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in getCollection".format(rft.subcommand,sc.operation)) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("getCollection: Error getting service root, aborting") + return(rc,r,False,None) + + # get the link to the Systems collection + # need to test we got good data + if (("Chassis" in d) and ("@odata.id" in d["Chassis"])): + systemsLink=d["Chassis"]["@odata.id"] + else: + rft.printErr("Error: service root does not have a Chassis link") + return(4) + + rft.printVerbose(4,"Chassis:getCollection: link is: {}".format(systemsLink)) + + + # if a -a option was entered with "Chassis" or "Chassis collection" operation, + # then return all members of the Chassis collection expanded + if((cmdTop is True) and (rft.allOptn is True) ): + collName="Chassis" + rft.printVerbose(4,"Expand Chassis collection to return ALL Chassis collection members fully expanded in response") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=systemsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + # otherwise, just return the collection + # now read the /Chassis collection + # use the returned url as the base url to read the Chassis collection + else: + if cmdTop is True: prop=rft.prop + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=systemsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," Chassis Collection:",skip1=True, printV12=cmdTop) + + return(rc,r,j,d) + + + def get(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + collUrl=r.url + + # search collection to find path to system + sysPath,rc,r,j,d=rft.getPathBy(rft, r, d, prop) + if( rc !=0 ): #if a path was not found, its an error + return(rc,r,j,d) + + rft.printVerbose(4,"ChassisOperations:get: got a path, now get entries") + + if cmdTop is True: prop=rft.prop + + #if here, rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the Chassis, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=sysPath, prop=prop) + + if(rc==0): rft.printVerbose(1," Chassis Resource:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def clist(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + collName="Chassis" + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="AssetTag") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, AssetTag".format(collName,skip1=True, printV12=cmdTop)) + return(rc,r,j,d) + + + def iterate_op(self, run_single, sc, op, rft, cmdTop=False, prop=None): + # Wrapper method to handle issuing commands to a single chassis or to all chassis in the collection + if rft.allOptn: + # Issue command to all chassis in collection + + # get the list of chassis + rc, r, j, d = op.clist(sc, op, rft, cmdTop=cmdTop, prop=prop) + if rc != 0 or not j or d is None or not isinstance(d, dict) or 'Members' not in d: + rft.printErr("Unable to get list of Chassis; return code = {}, list data = {}".format(rc, d)) + return rc, r, j, d + + # save existing rft options + saved_allOptn = rft.allOptn + saved_Link = rft.Link + saved_gotIdOptn = rft.gotIdOptn + saved_IdOptnCount = rft.IdOptnCount + + # set rft options to process a single item + rft.allOptn = False + rft.gotIdOptn = True + rft.IdOptnCount = 1 + + # iterate through chassis and run operation on each based on the link (@odata.id value) + members = d.get('Members') + rc, r, j, d = 8, None, False, None + for member in members: + if '@odata.id' in member: + link = member.get('@odata.id') + # set rft.Link to point to the target chassis + rft.Link = link + # perform the operation + rc, r, j, d = run_single(sc, op, rft, cmdTop=cmdTop, prop=prop) + else: + rft.printErr("No '@odata.id' found in chassis member: {}".format(member)) + + # restore existing rft options + rft.allOptn = saved_allOptn + rft.Link = saved_Link + rft.gotIdOptn = saved_gotIdOptn + rft.IdOptnCount = saved_IdOptnCount + return rc, r, j, d + else: + # Issue command to single specified chassis + return run_single(sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def patch_single(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + # verify we have got an argument which is the patch structure + # its in form '{ "AssetTag": , "IndicatorLed": }' + ##print("patchData: {}".format(patchData)) + if( len( sc.args) == 2): + ##print("data:{}".format(sc.args[1])) + try: + patchData=json.loads(sc.args[1]) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1])) + return(5,None,False,None) + ##print("patchData: {}".format(patchData)) + else: + rft.printErr("Patch: error: invalid argument format") + rft.printErr(" : args={}".format(sc.args)) + rft.printErr(" : expect: Systems patch \"{ : }\"") + return(4,None,False,None) + + # read the Chassis resource + # this is used by the generic rft.patchResource() function to see if there is an etag in the response + # if an etag is in response hdr, then we must include the etag on the patch, so this get is required + # note: the format of etag header for redfish is: ETag: W/"" or "".format(rft.program)) + return(8,None,False,None) + targLedState=sc.args[1] + if not targLedState in validLedStates: + rft.printErr("Error, invalid LED state specified") + return(8,None,False,None) + patchData={propName: targLedState} + + # get the resource to verify it includes IndicatorLED, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("System resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," Chassis setIndicatorLed:",skip1=True, printV12=cmdTop) + ledState={"IndicatorLED": d["IndicatorLED"]} + return(rc,r,j,ledState) + else: return(rc,r,False,None) + + + def setIndicatorLed(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.setIndicatorLed_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def getPower(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + resName="Power" + + # get the Chassis resource first + rc,r,j,d=op.get(sc, op, rft, cmdTop, resName) + if( rc != 0): return(rc,r,False,None) + + # get the link to the Power resource under Chassis + if ((resName in d) and ("@odata.id" in d[resName])): + resLink=d[resName]["@odata.id"] + else: + rft.printErr("Error: Chassis resource does not have a {} link".format(resName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getThermal(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + resName="Thermal" + # get the Chassis resource first + rc,r,j,d=op.get(sc, op, rft, cmdTop, resName) + if( rc != 0): return(rc,r,False,None) + + # get the link to the Thermal resource under Chassis + if ((resName in d) and ("@odata.id" in d[resName])): + resLink=d[resName]["@odata.id"] + else: + rft.printErr("Error: Chassis resource does not have a {} link".format(resName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getPowerReading(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + if cmdTop is True: prop=rft.prop + + # check if there is an arg for the operation + if( sc.argnum > 1 ): + if( sc.args[1] == 'consumed' ): + prop="PowerConsumedWatts" + else: + rft.printErr("Error: getPowerReading: invalid argument: {}".format(sc.args[1])) + rft.printErr("Syntax: getPowerReading [-i] [consumed]") + return(4,None,False,None) + + # get the Chassis Power resource + rc,r,j,d=op.getPower(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + # get the Power Control Object + if ("PowerControl" in d ): + powerControl=d["PowerControl"] + powerControlMembers=len(powerControl) + else: + rft.printErr("Error: Chassis resource does not have a PowerControl resource") + return(4,None,False,None) + if(powerControlMembers == 0 ): + rft.printErr("Error: Chassis PowerControl array is empty") + return(4,None,False,None) + + # get the powerControl array index + if( rft.IdLevel2 is None ): + indx=0 + else: + indxPattern="(^0$)|(^([1-9][0-9]{,2})$)" # : 33... + indxMatch=re.search(indxPattern,rft.IdLevel2) + if( indxMatch ): + indx=int(rft.IdLevel2) # convert base10 to integer + rft.printVerbose(4,"getPowerReading: Indx={}".format(indx)) + else: + rft.printErr("Error: getPowerReading: invalid PowerControl index value: {}".format(rft.IdLevel2)) + rft.printErr("Chassis getPowerReading [current] [-i] -- is integer 0 - 99 (default is 0)") + + if( (indx+1) > powerControlMembers ): + rft.printErr("Error: specified PowerControl index does not exist: indx={}, members={}".format(indx,powerControlMembers)) + return(4,None,False,None) + + #if here, we have a valid indx + if(prop is not None): + if( prop in powerControl[indx] ): + respDataVal=powerControl[indx][prop] + respData={prop: respDataVal} + rft.printVerbose(1," Get Current Power consumption (PowerConsumedWatts) of PowerControl[{}] resource".format(indx,skip1=True, printV12=cmdTop)) + else: + rft.printErr("Error: Property {} not not returned in PowerControl[{}] resource".format(prop,indx)) + return(4,r,j,d) + else: + respData=powerControl[indx] #return the full powerControl array + rft.printVerbose(1," Chassis PowerControl[{}] array:".format(indx,skip1=True, printV12=cmdTop)) + + return(rc,r,j,respData) + + + + + #setPowerLimit [-i] [ []] -- set powerLimit control properties") + def setPowerLimit_single(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + if cmdTop is True: prop=rft.prop + + # check if there is a required 2nd arg + if( sc.argnum > 1 ): + limitPattern="(^null$)|(^0$)|(^([1-9][0-9]{,5})$)" # null | 0 | 8 | 33 |344 | 34333 + limitMatch=re.search(limitPattern,sc.args[1]) # sc.args[1]= + if( limitMatch ): + if(sc.args[1] == "null"): + limit=None + else: + limit=int(sc.args[1]) # keep it a string + rft.printVerbose(4,"setPowerLimit: limit={}".format(limit)) + powerLimitData={"LimitInWatts": limit } + else: + rft.printErr("Error: setPowerLimit: invalid value specified: {}".format(sc.args[1])) + rft.printErr("Chassis setPowerLimit [-i] [ []] --limit=null | 0-99999") + return(8,None,False,None) + else: + rft.printErr("Error: setPowerLimit: no value specified: {}") + rft.printErr("Chassis setPowerLimit [-i] [ []] --limit=null | 0-99999") + return(8,None,False,None) + # check if there is an optional 3rd arg + includeException=False + validExceptionVals=("NoAction", "HardPowerOff", "LogEventOnly", "Oem") + if (sc.argnum > 2 ): + exceptionVal=sc.args[2] # sc.args[2]= + if( not exceptionVal in validExceptionVals ): + rft.printErr("Error: setPowerLimit: invalid value specified: {}".format(sc.args[2])) + rft.printErr("Chassis setPowerLimit [-i] [ []] --limit=null | 0-99999") + return(8,None,False,None) + else: + rft.printVerbose(4,"setPowerLimit: exception={}".format(exceptionVal)) + powerLimitData["LimitException"]=exceptionVal + includeException=True + + # check if there is an optional 4th arg in milliseconds + includeCorrectionTime=False + if (sc.argnum > 3 ): + correctionPattern="(^([1-9][0-9]{,6})$)" # 1-999999 ms + correctionMatch=re.search(correctionPattern,sc.args[3]) # sc.args[3]= + if( correctionMatch ): + correctionTime=int(sc.args[3]) # keep it a string + rft.printVerbose(4,"setPowerLimit: correctionInMs={}".format(correctionTime)) + powerLimitData["CorrectionInMs"]=correctionTime + includeCorrectionTime=True + else: + rft.printErr("Error: setPowerLimit: invalid value specified: {}".format(sc.args[3])) + rft.printErr("Chassis setPowerLimit [-i] [ []] --correctionTime=1-999999 ms") + return(8,None,False,None) + + # get the Chassis Power resource + rc,r,j,d=op.getPower(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + # get the Power Control Object + if ("PowerControl" in d ): + powerControl=d["PowerControl"] + powerControlMembers=len(powerControl) + else: + rft.printErr("Error: Chassis resource does not have a PowerControl resource") + return(4,None,False,None) + if(powerControlMembers == 0 ): + rft.printErr("Error: Chassis PowerControl array is empty") + return(4,None,False,None) + + # get the powerControl array index + if( rft.IdLevel2 is None ): + indx=0 + else: + indxPattern="(^0$)|(^([1-9][0-9]*)$)" # : 33... + indxMatch=re.search(indxPattern,rft.IdLevel2) + if( indxMatch ): + indx=int(rft.IdLevel2) # convert base10 to integer + rft.printVerbose(4,"setPowerLimit: Indx={}".format(indx)) + else: + rft.printErr("Error: setPowerLimit: invalid PowerControl index: {}".format(rft.IdLevel2)) + rft.printErr("Chassis setPowerLimit [current] [-i] -- is integer 0 - 99 (default is 0)") + return(8,None,False,None) + + if( (indx+1) > powerControlMembers ): + rft.printErr("Error: specified PowerControl index does not exist: indx={}, members={}".format(indx,powerControlMembers)) + return(8,None,False,None) + + # check that this PowerControl index member has the properties + if( not "LimitInWatts" in powerControl[indx]["PowerLimit"] ): + rft.printErr("Error: setPowerLimit: LimitInWatts property is not in rhost PowerControl[{}]".format(indx)) + return(8,None,False,None) + if( ( includeException is True ) and ( not "LimitException" in powerControl[indx]["PowerLimit"] ) ): + rft.printErr("Error: setPowerLimit: LimitException property is not in rhost PowerControl[{}]".format(indx)) + return(8,None,False,None) + if( ( includeCorrectionTime is True ) and ( not "CorrectionInMs" in powerControl[indx]["PowerLimit"] ) ): + rft.printErr("Error: setPowerLimit: CorrectionInMs property is not in rhost PowerControl[{}]".format(indx)) + return(8,None,False,None) + + # now build the final full patch data + #patchData={"PowerControl"[indx]: { "PowerLimit": powerLimitData } } + powerLimitRes={ "PowerLimit": powerLimitData } + patchData={ "PowerControl": [ {} ] } + empty={} + powerControlArray=list() + for i in range (0,powerControlMembers): + powerControlArray.append(empty) + powerControlArray[indx]=powerLimitRes + patchData["PowerControl"]=powerControlArray + + rft.printVerbose(4,"setPowerLimit: patchData: {}".format(json.dumps(patchData))) + + #call the generic patch command to send the patch. This takes care of etag support + rc,r,j,d=rft.patchResource(rft, r, patchData) + + if(rc==0): + rft.printVerbose(1," SetPowerLimit [{}]:",indx, skip1=True, printV12=cmdTop) + powerLimit={"PowerLimit": d["PowerControl"][indx]["PowerLimit"]} + return(rc,r,j,powerLimit) + + else: return(rc,r,False,None) + + + def setPowerLimit(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.setPowerLimit_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def getLogService(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,":{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the Chassis resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="LogServices" + # get the link to the LogServices collection + if ((collName in d) and ("@odata.id" in d[collName])): + logLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: Chassis resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no Log was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the LogServices collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the LogServices, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # If '--Entries' specified, get "Entries" nav link and read it + if rc == 0 and rft.gotEntriesOptn: + if r is not None and j and isinstance(d, dict): + rft.printVerbose(1, 'getLogService: attempting to get Entries for Logs') + entries = d.get('Entries') + if entries is not None and isinstance(entries, dict): + entries_uri = entries.get('@odata.id') + if entries_uri is not None: + rc, r, j, d = rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, + relPath=entries_uri, prop=prop) + else: + rft.printErr('getLogService: @odata.id not found in "Entries" property') + else: + rft.printErr('getLogService: "Entries" property not found in JSON payload') + else: + rft.printErr( + 'Unable to fetch Entries property from previous response: response = {}, is_json = {}, type(json) = {}' + .format(r, j, type(d))) + + # else, client specified the -a option requesting ALL of the LogServices members + # for logs, we will not support this. Its too much data. + else: + rft.printErr("Error: -a option not supported for LogServices") + return(6,None,False,None) + + return(rc,r,j,d) + + + def clearLog(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + return(8,None,False,None) + + + def getSensors(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + sys.stdout.write("%-50s %-10s %-10s\n" % ("Sensor", "Reading", "UpperCritical")) + resName="Thermal" + # get the Chassis resource first + rc,r,j,d=op.get(sc, op, rft, cmdTop, resName) + if( rc != 0): return(rc,r,False,None) + + # get the link to the Thermal resource under Chassis + if ((resName in d) and ("@odata.id" in d[resName])): + resLink=d[resName]["@odata.id"] + else: + rft.printErr("Error: Chassis resource does not have a {} link".format(resName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop)) + + collection = ['Temperatures','Fans'] + for collName in collection: + numOfLinks=len(d[collName]) + for i in range (0,numOfLinks): + if collName== 'Fans': + sys.stdout.write("%-50s %-10s %-10s\n" % (d[collName][i]["MemberId"], d[collName][i].get("Reading","N/A"),"N/A")) + else: + sys.stdout.write("%-50s %-10s %-10s\n" % (d[collName][i]["MemberId"], d[collName][i].get("ReadingCelsius","N/A"), d[collName][i].get("UpperThresholdCritical","N/A)"))) + + + resName="Sensors" + # get the Chassis resource first + rc,r,j,d=op.get(sc, op, rft, cmdTop, resName) + if( rc != 0): return(rc,r,False,None) + + # get the link to the Thermal resource under Chassis + if ((resName in d) and ("@odata.id" in d[resName])): + resLink=d[resName]["@odata.id"] + else: + rft.printErr("Error: Chassis resource does not have a {} link".format(resName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop)) + + numOfLinks=len(d["Members"]) + for i in range (0,numOfLinks): + collName="Members" + rc1,r1,j1,d1=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=d[collName][i]["@odata.id"], prop=prop) + output=json.dumps(d1,indent=4) + sys.stdout.write("%-50s %-10s %-10s\n" % (d1["Id"], d1.get("Reading", "N/A"), d1.get("Thresholds", {}).get("UpperCritical",{}).get("Reading","N/A"))) + + + return(rc,r,False,None) + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r Chassis # shows the Chassis collection".format(rft.program)) + print(" {} -r Chassis list # lists Id, Uri, AssetTag for all Chassis".format(rft.program)) + print(" {} -r Chassis -I # gets the Chassis with Id=".format(rft.program)) + print(" {} -r Chassis -M AssetTag:12345 # gets the Chassis with AssetTag=12345".format(rft.program)) + print(" {} -r Chassis -L # gets the Chassis at URI= Chassis -F # get the First Chassis returned (for debug)".format(rft.program)) + print(" {} -r Chassis -1 # get the first Chassis and verify that there is only one system".format(rft.program)) + print(" {} -r Chassis -I patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the object".format(rft.program)) + print(" {} -r Chassis -I setAssetTag # set the system's asset tag ".format(rft.program)) + print(" {} -r Chassis -I setIndicatorLed # set the indicator LED. =redfish defined values: Off, Lit, Blinking".format(rft.program)) + print(" {} -r Chassis -I Power # get the full chassis Power resource".format(rft.program)) + print(" {} -r Chassis -I Thermal # get the full chassis Thermal resource".format(rft.program)) + print(" {} -r Chassis -I getPowerReading[-i [consumed] # get chassis/Power powerControl[] resource".format(rft.program)) + print(" # if optional \"consumed\" arg, then return only the PowerConsumedWatts prop") + print(" {} -r Chassis -L setPowerLimit [-i] [ []] # set power limit".format(rft.program)) + return(0,None,False,None) + + + + +''' +TODO: +1. clearlog not implemented + +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/Managers.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Managers.py new file mode 100644 index 000000000..5a8965965 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Managers.py @@ -0,0 +1,728 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: Managers.py +# +# contains Managers related subCommands and access functions +# +# Class RfManagersMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - Managers command table, dispatch of operation eg get, reset +# - ManagersMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run Managers operation (sub-sub-command) +# +# Class RfManagersOperations +# All of the Managers sub-command operations eg: Managers reset, setTime etc +# - hello - test cmd +# - getCollection - return the Managers collection +# - get - get a member of a collection -or property of the member +# - list - show of list of collection members and key idetifying properties +# (Id, UUID, UriPath) +# - patch - raw subcommand to patch a Managers Member, with etag support +# - reset --reset a system instance +# - setDateTime -- set the manager dataTime setting +# - setTimeOffset -- set the local time offset w/o changing time +# - getNetworkProtocol - get Network Protocol sub-resource +# - getEnetInterfaces - get Ethernet collection, instance, all +# - getSerialInterfaces - get SerialInterfaces collection, instance, all +# - getLogService - get LogService collection, instance, all +# - clearLog -- clears a specified log (not implemented yet) +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin + +class RfManagersMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] Managers [] -- perform on the Managers specified ".format(rft.program)) + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print("") + + def displayOperations(self,rft): + print(" :") + print(" [collection] -- get the main Managers collection. (Default operation if no member specified)") + print(" [get] -- get the specified Manager object. (Default operation if collection member specified)") + print(" list -- list information about the Managers collection members(\"Id\", URI, and UUID)") + print(" patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object") + print(" reset -- reset a Manager. = On, GracefulShutdown, GracefulRestart, ") + print(" ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle") + print(" setDateTime --set the date and time") + print(" setTimeOffset offset= --set the time offset w/o changing time setting") + print(" is of form \"[+/-]mm:ss\". Ex: \"-10:01\" ") + print(" NetworkProtocol -- get the \"NetworkProtocol\" resource under the specified manager.") + print(" setIpAddress [-i]... -- set the Manager IP address -NOT IMPLEMENTED YET") + print("") + print(" EthernetInterfaces [list] -- get the managers \"EthernetInterfaces\" collection, or list \"id\",URI, Name of members.") + print(" EthernetInterfaces [IDOPTN]-- get the member specified by IDOPTN: -i, -m:, -a #all") + print("") + print(" SerialInterfaces [list] -- get the managers \"SerialInterfaces\" collection, or list \"id\",URI, Name of members.") + print(" SerialInterfaces [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print("") + print(" Logs [list] -- get the Managers \"LogServices\" collection , or list \"id\",URI, Name of members.") + print(" Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" clearLog -- clears the log defined by ") + print(" examples -- example commands with syntax") + print(" hello -- Systems hello -- debug command") + return(0) + + + def runOperation(self,rft): + # instantiate SystemsOperations class + op=RfManagersOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "collection": op.getCollection, + "get": op.get, + "list": op.list, + "patch": op.patch, + "reset": op.reset, + "setDateTime": op.setDateTime, + "setTimeOffset": op.setTimeOffset, + "setIpAddress": op.setIpAddress, + "NetworkProtocol": op.getNetworkProtocol, + "EthernetInterfaces": op.getEnetInterfaces, + "SerialInterfaces": op.getSerialInterfaces, + "Logs": op.getLogService, + "clearLog": op.clearLog, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"Managers:runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"Managers:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"Managers:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("Managers: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def ManagersMain(self,rft,cmdTop=False): + rft.printVerbose(4,"ManagersMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(0,None,False,None) + + # we will validate usage of -P and -a in action processing + # actually, if a non 'get' action is specified, -P and -a are just ignored :) + + args=rft.subcommandArgv[0:] + + #if no args, then if no member Id was specified (with -I|-M|-1|-F) then assume it is a "collection" operation + # if a -IM1F was specified, then assume it is a "get" operation for that member + if( len(args) < 2 ): + if( rft.IdOptnCount==0 ): + self.operation="collection" + else: + self.operation="get" + self.args= None + else: + self.operation=args[1] + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"Managers: operation={}, args={}".format(self.operation,self.args)) + + # check if the command requires a collection member target -I|-M|-L|-1|-F eg sysIdoptn + nonIdCommands=["collection", "list", "examples", "hello"] + if( ( not self.operation in nonIdCommands ) and (rft.IdOptnCount==0) ): + # default to --One if no Id option specified + rft.oneOptn = True + rft.IdOptnCount += 1 + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"Managers: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"Managers: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the Managers subCommand +# +class RfManagersOperations(): + def __init__(self): + self.managersPath=None + self.managersollectionDict=None + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from Managers") + return(0,None,False,None) + + def getCollection(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in getCollection".format(rft.subcommand,sc.operation)) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("getCollection: Error getting service root, aborting") + return(rc,r,False,None) + + # get the link to the Managers collection + # need to test we got good data + if (("Managers" in d) and ("@odata.id" in d["Managers"])): + systemsLink=d["Managers"]["@odata.id"] + else: + rft.printErr("Error: service root does not have a Managers link") + return(4) + + rft.printVerbose(4,"Managers:getCollection: link is: {}".format(systemsLink)) + + + # if a -a option was entered with "Managers" or "Managers collection" operation, + # then return all members of the Managers collection expanded + if((cmdTop is True) and (rft.allOptn is True) ): + collName="Managers" + rft.printVerbose(4,"Expand Managers collection to return ALL Managers collection members fully expanded in response") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=systemsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + # otherwise, just return the collection + # now read the /Managers collection + # use the returned url as the base url to read the Managers collection + else: + if cmdTop is True: prop=rft.prop + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=systemsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," Managers Collection:",skip1=True, printV12=cmdTop) + + return(rc,r,j,d) + + + def get(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + collUrl=r.url + + # search collection to find path to the Manager + sysPath,rc,r,j,d=rft.getPathBy(rft, r, d) + if( rc !=0 ): #if a path was not found, its an error + return(rc,r,j,d) + + rft.printVerbose(4,"ManagersOperations:get: got a path, now get entries") + + if cmdTop is True: prop=rft.prop + + #if here, rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the Manager, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=sysPath, prop=prop) + + if(rc==0): rft.printVerbose(1," Managers Resource:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def list(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + collName="Managers" + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="UUID") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, UUID".format(collName,skip1=True, printV12=cmdTop)) + return(rc,r,j,d) + + + def patch(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + # verify we have got an argument which is the patch structure + # its in form '{ "AssetTag": , "IndicatorLed": }' + ##print("patchData: {}".format(patchData)) + if( len( sc.args) == 2): + try: + patchData=json.loads(sc.args[1]) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1])) + return(5,None,False,None) + else: + rft.printErr("Patch: error: invalid argument format") + rft.printErr(" : args={}".format(sc.args)) + rft.printErr(" : expect: Systems patch \"{ : }\"") + return(4,None,False,None) + + # read the Managers resource + # this is used by the generic rft.patchResource() function to see if there is an etag in the response + # if an etag is in response hdr, then we must include the etag on the patch, so this get is required + # note: the format of etag header for redfish is: ETag: W/"" or " + # where is a subset of Redfish defined redfish resetType values + # and will be validated against the allowable values read from the remote service + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the resetType from args + validResetTypes=["On","ForceOff","GracefulShutdown","ForceRestart","Nmi","GracefulRestart", + "ForceOn","PushPowerButton","PowerCycle"] + if(len(sc.args) < 2 ): + rft.printErr("Error, no resetType value specified") + return(8,None,False,None) + + resetType=sc.args[1] + if not resetType in validResetTypes: + rft.printErr("Error, Invalid value specified: {}".format(resetType)) + return(8,None,False,None) + + #now read remote service to find out if specified resetType is one of the allowable values for this rhost + rc,r,j,d=op.get(sc,op,rft,prop="Actions") + if(rc != 0): + print("Error, can't read Actions properties from remote service") + return(8,None,False,None) + + if( (j is True) and ("Actions" in d) and ("#Manager.Reset" in d["Actions"])): + resetProps=d["Actions"]["#Manager.Reset"] + if( "ResetType@Redfish.AllowableValues" in resetProps ): + supportedResetTypes=resetProps["ResetType@Redfish.AllowableValues"] + if not resetType in supportedResetTypes: + rft.printErr("Error, the resetType specified is not supported by the remote service (via @Redfish.AllowableValues)") + return(8,None,False,None) + elif "@Redfish.ActionInfo" in resetProps: + action_info_path = resetProps["@Redfish.ActionInfo"] + supportedResetTypes = rft.getActionInfoAllowableValues(rft, r, action_info_path, "ResetType") + if supportedResetTypes is not None and resetType not in supportedResetTypes: + rft.printErr("Error, the resetType specified is not supported by the remote service (via @Redfish.ActionInfo)") + return(8,None,False,None) + else: # rhost didn't return any AllowableValues, but it isn't required, so allow the action + rft.printVerbose(2, "The remote service does not have a ResetType@Redfish.AllowableValues or @Redfish.ActionInfo prop") + else: + rft.printErr("Error, the remote service does not have an Actions: Manager.Reset property") + return(8,None,False,None) + + # now get the target URI from the remote host + if( not "target" in resetProps ): + rft.printErr("Error, the remote service doesnt have a Reset Target property (the reset path)") + return(8,None,False,None) + resetPath=resetProps["target"] + + resetData={"ResetType": resetType } + + #since we already did a get, we have the path and can just execute the post directly. + #output the post data in json to send over the network + reqPostData=json.dumps(resetData) + # send post to rhost. Use the resetPath as the relative path + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'POST', r.url, relPath=resetPath, + reqData=reqPostData) + + if(rc==0): + rft.printVerbose(1," Managers reset: ", resetType, skip1=True, printV12=cmdTop) + resetd=None + return(rc,r,False,resetd) + else: return(rc,r,False,None) + + + # setDateTime -- command to set the Managers time--and timezone + def setDateTime(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + propName="DateTime" + # get the target dateTime from args + if(len(sc.args) < 2 ): + rft.printErr("Error, no dateTime argument specified") + rft.printErr("Syntax: {} [options] Managers setDateTime ".format(rft.program)) + rft.printErr(" in form: \"YYYY-MM-DDThh:mm:ss[+/-]hh:ss\"") + return(8,None,False,None) + else: #we have an arg[1] + datePattern="(^[2][0]\d{2}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[-+][0-1][0-9]:[0-5][0-9]$)" # YYYY-MM-DDTHH:mm:ss[+/-]hh:ss + dateMatch=re.search(datePattern,sc.args[1]) # sc.args[1]= + if( dateMatch ): + dateTimeString=(sc.args[1]) # keep it a string + rft.printVerbose(4,"setDateTime: dateTime={}".format(dateTimeString)) + else: + rft.printErr("Error: setDateTime: invalid value specified: {}".format(sc.args[1])) + rft.printErr("Managers setDateTime # eg: YYYY-MM-DDThh:mm:ss[+/-]hh:ss") + return(8,None,False,None) + + patchData={propName: dateTimeString} + + # get the resource to verify it includes dateTime property, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("Managers resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," setDateTime:",skip1=True, printV12=cmdTop) + dateTimeReturned={propName: d[propName]} + return(rc,r,j,dateTimeReturned) + else: return(rc,r,False,None) + + + + # setTimeOffset -- command to change the Managers timezone offset (w/o changing time) + def setTimeOffset(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + propName="DateTimeLocalOffset" + # get the target offset from args + if(len(sc.args) < 2 ): + rft.printErr("Error, no timeOffset argument specified") + rft.printErr("Syntax: {} [options] Managers setTimeOffset offset=".format(rft.program)) + rft.printErr(" where in form: \"[+/-]hh:ss\", ex: -06:00 ") + return(8,None,False,None) + else: #we have an arg[1] + datePattern="^offset=([-+][0-1][0-9]:[0-5][0-9])$" # [+/-]hh:ss + dateMatch=re.search(datePattern,sc.args[1]) # sc.args[1]= + if( dateMatch ): + timeOffsetString=dateMatch.group(1) # keep it a string. + rft.printVerbose(4,"setDateTime: timeOffset={}".format(timeOffsetString)) + else: + rft.printErr("Error: setTimeOffset: invalid value specified: {}".format(sc.args[1])) + rft.printErr("Managers setTimeOffset offset= # eg: where =[+/-]hh:ss") + return(8,None,False,None) + + patchData={propName: timeOffsetString} + + # get the resource to verify it includes DateTimeLocalOffset property, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("Managers resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," setTimeOffset:",skip1=True, printV12=cmdTop) + timeOffsetReturned={propName: d[propName]} + return(rc,r,j,timeOffsetReturned) + else: return(rc,r,False,None) + + + def getNetworkProtocol(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the Manager resource first + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + resName="NetworkProtocol" + # get the link to the NetworkProtocol resource under Manager + if ((resName in d) and ("@odata.id" in d[resName])): + resLink=d[resName]["@odata.id"] + else: + rft.printErr("Error: Manager resource does not have a {} link".format(resName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + # setIpAddress -- command to set the IP address of the MC + def setIpAddress(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print("Not Implemented Yet") + return(6,None,False,None) + + + def getEnetInterfaces(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the Manager resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="EthernetInterfaces" + # get the link to the EthernetInterfaces collection + if ((collName in d) and ("@odata.id" in d[collName])): + nicLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: Manager resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no NIC was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the EthernetInterfaces collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the EthernetInterfaces, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the EthernetInterfaces members + else: + rft.printVerbose(4,"getting expanded EthernetInterfaces Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=nicLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getSerialInterfaces(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the Manager resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="SerialInterfaces" + # get the link to the SerialInterfaces collection + if ((collName in d) and ("@odata.id" in d[collName])): + cntlrLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: Manager resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name" ) + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no SerialInterfaces controller was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the SerialInterfaces collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the SerialInterfaces, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the SerialInterfaces members + else: + rft.printVerbose(4,"getting expanded SerialInterfaces Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=cntlrLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getLogService(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,":{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the Manager resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="LogServices" + # get the link to the LogServices collection + if ((collName in d) and ("@odata.id" in d[collName])): + logLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: Manager resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no Log was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the LogServices collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the LogServices, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # If '--Entries' specified, get "Entries" nav link and read it + if rc == 0 and rft.gotEntriesOptn: + if r is not None and j and isinstance(d, dict): + rft.printVerbose(1, 'getLogService: attempting to get Entries for Logs') + entries = d.get('Entries') + if entries is not None and isinstance(entries, dict): + entries_uri = entries.get('@odata.id') + if entries_uri is not None: + rc, r, j, d = rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, + relPath=entries_uri, prop=prop) + else: + rft.printErr('getLogService: @odata.id not found in "Entries" property') + else: + rft.printErr('getLogService: "Entries" property not found in JSON payload') + else: + rft.printErr( + 'Unable to fetch Entries property from previous response: response = {}, is_json = {}, type(json) = {}' + .format(r, j, type(d))) + + # else, client specified the -a option requesting ALL of the LogServices members + # for logs, we will not support this. Its too much data. + else: + rft.printErr("Error: -a option not supported for LogServices") + return(6,None,False,None) + + return(rc,r,j,d) + + + def clearLog(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print("NOT IMPLEMENTED YET") + return(8,None,False,None) + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r # shows the Managers collection".format(rft.program)) + print(" {} -r Managers list # lists Id, Uri, AssetTag for all Managers".format(rft.program)) + print(" {} -r Managers -I # gets the Manager with Id=".format(rft.program)) + print(" {} -r Managers -M AssetTag:12345 # gets the Manager with AssetTag=12345".format(rft.program)) + print(" {} -r Managers -L # gets the Manager at URI= Managers -F # get the First Manager returned (for debug)".format(rft.program)) + print(" {} -r Managers -1 # get the first Manager and verify that there is only one Manager".format(rft.program)) + print(" {} -r Managers -I patch {{A: B,C: D,...}}# patch the json-formatted {{prop: value...}} data to the object".format(rft.program)) + print(" {} -r Managers -I reset # reset a Manager. =the redfish-defined values: On, Off, gracefulOff...".format(rft.program)) + print(" {} -r Managers -I NetworkProtocol # get the NetworkProtocol resource under the specified manager".format(rft.program)) + print(" {} -r Managers -I EthernetInterfaces list # lists Id, Uri, and Name for all of the NICs for Manager w/ Id=".format(rft.program)) + print(" {} -r Managers -I EthernetInterfaces -i 1 # get the NIC with id=1 in manager with Id=".format(rft.program)) + print(" {} -r Managers -L EthernetInterfaces -m MACAddress:AA:BB:CC:DD:EE:FF # get NIC with MAC AA:BB... for manager at url ".format(rft.program)) + return(0,None,False,None) + + + +''' +TODO: +1. clearlog not implemented +2. setIpAddress not implemented + +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/ServiceRoot.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/ServiceRoot.py new file mode 100644 index 000000000..991399657 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/ServiceRoot.py @@ -0,0 +1,97 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: ServiceRoot.py +# +# contains serviceRoot related subCommands and access functions +# Class RfServiceRoot +# - getServiceRoot GET /redfish/v1 +# - getOdataServiceDocument GET /redfish/v1/odata +# - getOdataMetadataDocument GET /redfish/v1/$metadata +# +import requests +import json +from urllib.parse import urljoin, urlparse, urlunparse + +class RfServiceRoot: + def __init__(self): + # option parsing variables + self.serviceRootDict=None + self.url=None + + def getServiceRoot(self,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"ServiceRoot: in getServiceRoot") + + if(rft.help): + print(" {} {} -r [-vh] -- get serviceRoot resource".format(rft.program,rft.subcommand)) + return(0,None,False,None) + + # execute GET /redfish to negotiate protocol version and get root path + # rootPath is stored in rft.rootPath + rc,r,j,d=rft.getVersionsAndSetRootPath(rft) + if( rc!=0): return(rc,r,j,d) + # + if cmdTop is True: prop=rft.prop + + # get root service. if -P prop, showproperty + rc,r,j,d=rft.rftSendRecvRequest(rft.UNAUTHENTICATED_API, 'GET', rft.rootUri, relPath=rft.rootPath,prop=prop) + + #save the rootService response. The transport may need it later to get link to session and login + rft.rootResponseDict=d + + if(rc==0 and cmdTop is True): + rft.printVerbose(1," Service Root:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def getOdataServiceDocument(self,rft,cmdTop=False): + rft.printVerbose(4,"ServiceRoot: in getOdataServiceDocument") + + if(rft.help): + print(" {} {} -r [-vh] -- get the Odata Service Document".format(rft.program,rft.subcommand)) + return(0,None,False,None) + + #get root path, scheme, and create URL + rc,r,j,d=rft.getVersionsAndSetRootPath(rft) + if( rc!=0): return(rc,r,j,d) + + #calculate relative path = rootPath / odata + rpath=urljoin(rft.rootPath, "odata") + + rc,r,j,d=rft.rftSendRecvRequest(rft.UNAUTHENTICATED_API, 'GET', rft.rootUri, relPath=rpath) + if(rc==0 ): + rft.printVerbose(1," Odata Service Document:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + + def getOdataMetadataDocument(self,rft,cmdTop=False): + rft.printVerbose(4,"ServiceRoot: in getOdataMetadataDocument") + + if(rft.help): + print(" {} {} -r [-vh] -- get the CSDL metadata document".format(rft.program,rft.subcommand)) + return(0,None,False,None) + + #get root path, scheme, and create URL + rc,r,j,d=rft.getVersionsAndSetRootPath(rft) + if( rc!=0): return(rc,r,j,d) + + #calculate relative path = rootPath / $metadata + rpath=urljoin(rft.rootPath, "$metadata") + + # set content-type to xml (dflt is application/json) + hdrs = {"Accept": "application/xml", "OData-Version": "4.0" } + + + rc,r,j,d=rft.rftSendRecvRequest(rft.UNAUTHENTICATED_API, 'GET', rft.rootUri, relPath=rpath, jsonData=False, headersInput=hdrs ) + if(rc==0): + rft.printVerbose(1," Odata Metadata Document:",skip1=True,printV12=cmdTop) + return(rc,r,j,d) + +''' +TODO: +1. + + +''' diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/SessionService.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/SessionService.py new file mode 100644 index 000000000..4a37be8b2 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/SessionService.py @@ -0,0 +1,406 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: SessionService.py +# +# contains SessionService related subCommands and access functions +# +# Class RfSessionServiceMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - SessionService command table, dispatch of operation eg get, reset +# - SessionServiceMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run SessionService operation (sub-sub-command) +# +# Class RfSessionServiceOperations +# All of the SessionService sub-command operations eg: login, logout, get sessionService, etc +# - hello - test cmd +# - get - get the session service +# - patch - raw subcommand to patch a sessionService, with etag support +# - setSessionTimeout -- patches the SessionTimeout property w/ etag support +# - Sessions - get Sessions collection, Session instance, list Sessions, get all Sessions +# - login - Session login (post to add a new session) +# - logout - Session logout (delete to delete a session) +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin + +class RfSessionServiceMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] SessionService [] -- perform on the SessionService ".format(rft.program)) + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print("") + + def displayOperations(self,rft): + print(" :") + print(" [get] -- get the sessionService object. ") + print(" patch {A: B,C: D,...} -- patch the sessionService w/ json-formatted {prop: value...} ") + print(" setSessionTimeout -- patches the SessionTimeout property w/ etag support ") + print(" Sessions [list] -- get the \"Sessions\" collection, or list \"Id\", username, and Url ") + print(" Sessions [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" login -- sessionLogin. post to Sessions collection to create a session") + print(" the user is -u, password is -p") + print(" logout -- logout or delete the session by identified by -i or -l") + print(" where is the session path returned in Location from login") + print(" examples -- example commands with syntax") + print(" hello -- Systems hello -- debug command") + return(0) + + + def runOperation(self,rft): + # instantiate SessionServiceOperations class + op=RfSessionServiceOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "get": op.get, + "patch": op.patch, + "setSessionTimeout": op.setSessionTimeout, + "Sessions": op.getSessions, + "login": op.sessionLogin, + "logout": op.sessionLogout, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"SessionService:runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"SessionService:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"SessionService:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("SessionService: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def SessionServiceMain(self,rft,cmdTop=False): + rft.printVerbose(4,"SessionServiceMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(0,None,False,None) + + # we will validate usage of -P and -a in action processing + # actually, if a non 'get' action is specified, -P and -a are just ignored :) + + args=rft.subcommandArgv[0:] + + #if no args, this is a getSessionService command + if( len(args) < 2 ): + self.operation="get" + self.args= None + else: + self.operation=args[1] + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"SessionService: operation={}, args={}".format(self.operation,self.args)) + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"SessionService: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"SessionService: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the SessionService subCommand +# +class RfSessionServiceOperations(): + def __init__(self): + self.SessionServicePath=None + self.SessionServiceCollectionDict=None + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from SessionService") + return(0,None,False,None) + + + + def get(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("get SessionService: Error getting service root, aborting") + return(rc,r,False,None) + + # get the link to the SessionService + # need to test we got good data + if (("SessionService" in d) and ("@odata.id" in d["SessionService"])): + sessionServiceLink=d["SessionService"]["@odata.id"] + else: + rft.printErr("Error: root does not have a SessionService link") + return(4) + + rft.printVerbose(4,"SessionService: get SessionService: link is: {}".format(sessionServiceLink)) + + if cmdTop is True: prop=rft.prop + + # do a GET to get the SessionService, if -P show property, else show full response + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=sessionServiceLink, prop=prop) + + if(rc==0): rft.printVerbose(1," SessionService Resource:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + + def patch(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + # verify we have got an argument which is the patch structure + # its in form '{ "AssetTag": , "IndicatorLed": }' + ##print("patchData: {}".format(patchData)) + if( len( sc.args) == 2): + ##print("data:{}".format(sc.args[1])) + try: + patchData=json.loads(sc.args[1]) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1])) + return(5,None,False,None) + ##print("patchData: {}".format(patchData)) + else: + rft.printErr("Patch: error: invalid argument format") + rft.printErr(" : args={}".format(sc.args)) + rft.printErr(" : expect: SessionService patch \"{ : }\"") + return(4,None,False,None) + + # read the sessionServiceLink resource + # this is used by the generic rft.patchResource() function to see if there is an etag in the response + # if an etag is in response hdr, then we must include the etag on the patch, so this get is required + # note: the format of etag header for redfish is: ETag: W/"" or " ".format(rft.program)) + return(8,None,False,None) + sessTimeout=int(sc.args[1],0) # base 0 means int() will interpret 0x as hex and as decimal + patchData={propName: sessTimeout} + + # get the resource to verify it includes AssetTag, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("SessionService resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," SessionService setSessionTimeout:",skip1=True, printV12=cmdTop) + assetTag={propName: d[propName]} + return(rc,r,j,assetTag) + else: return(rc,r,False,None) + + + + def getSessions(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation: getSessions collection".format(rft.subcommand,sc.operation)) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="Sessions" + # get the link to the Sessions collection + if ((collName in d) and ("@odata.id" in d[collName])): + sessionsLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: SessionService resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=sessionsLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="UserName") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Socket".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no session was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=sessionsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the session specific by -i or -m or -l + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the Sessions collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=sessionsLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the session, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the Sessions members + else: + rft.printVerbose(4,"getting expanded Sessions Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=sessionsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + + #SessionService -u -p login, returns authToken and sessionId + def sessionLogin(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + #note that and were passed-in in -u and -p options and are stores in + # rft.user and rft.password + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("SessionService: login: Error getting service root, aborting") + return(rc,r,False,None) + + #call rfSessionLogin in transport class to login + rc,r,j,d=rft.rfSessionLogin(rft,cleanupOnExit=False) + if(rc == 0): + # return sessionId and authToken + respData={"SessionId": rft.sessionId, "SessionLocation": rft.sessionLink, "X-Auth-Token": rft.authToken} + rft.printVerbose(1," SessionLogin: {}".format(respData, skip1=True, printV12=cmdTop)) + return(rc,r,j,respData) + else: + return(rc,r,j,d) + + + #SessionService -t logout [-i|-l], returns 204 no content + def sessionLogout(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + if( (rft.linkLevel2 is None) and( rft.IdLevel2 is None) ): + rft.printErr("Error, logout: no sessionId or sessionLink specified") + rft.printErr("Syntax: {} [options] [SessionService] logout [-i | -l ]".format(rft.program)) + return(8,None,False,None) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("SessionService: login: Error getting service root, aborting") + return(rc,r,False,None) + + #find Sessions collection + if( ("Links" in d) and ("Sessions" in d["Links"]) and ("@odata.id" in d["Links"]["Sessions"]) ): + sessionsLink=rft.rootResponseDict["Links"]["Sessions"]["@odata.id"] + #print("loginUri:{}".format(loginUri)) + else: + rft.printErr("Error: the rootService response does not have a login link: \"Links\":\"Sessions\":{{\"@odata.id\": }") + return(4,None,False,None) + + # get the Sessions collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=sessionsLink, prop=prop) + if(rc != 0): + rft.printErr("Error: logout: can't read sessions collection") + return(6,None,False,None) + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + rft.printErr("Error: the specified sessionId or sessionLink was not a valid Sessions collection entry") + return(rc,r,j,d) + + #path2 is the path the the session we want to delete + rc,r,j,d=rft.rfSessionDelete(rft,sessionLink=path2) + if(rc!=0): + rft.printErr("Error: Logout: the session delete failed") + return(rc,r,j,d) + else: + rft.printVerbose(1," SessionLogout successful".format(skip1=True, printV12=cmdTop)) + return(rc,r,j,d) + + + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r SessionService # gets the sessionService".format(rft.program)) + print(" {} -r SessionService setSessionTimeout # sets the session timeout property".format(rft.program)) + print(" {} -r SessionService Sessions # gets Sessions collection".format(rft.program)) + print(" {} -r SessionService Sessions -l # gets the session at URI= SessionService Sessions -i # gets the session with session Id ".format(rft.program)) + print(" {} -r SessionService patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the sessionService object".format(rft.program)) + print(" {} -r SessionService login # login (create session)".format(rft.program)) + print(" {} -r SessionService logout # logout (delete session ".format(rft.program)) + return(0,None,False,None) + + + +''' +TODO: +1. e + +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/Systems.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Systems.py new file mode 100644 index 000000000..a0be4ad0d --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/Systems.py @@ -0,0 +1,990 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: Systems.py +# +# contains Systems related subCommands and access functions +# +# Class RfSystemsMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - Systems command table, dispatch of operation eg get, reset +# - SystemsMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run System operation (sub-sub-command) +# +# Class RfSystemsOperations +# All of the Systems sub-command operations eg: Systems reset, setIndicatorLed, etc +# - hello - test cmd +# - getCollection - return the Systems collection +# - get - get a member of a collection -or property of the member +# - list - show of list of collection members and key idetifying properties +# (Id, AssetTag, UriPath) +# - patch - raw subcommand to patch a System Member, with etag support +# - reset --reset a system instance +# - setAssetTag -- patches the assetTag of system instance w/ etag support +# - setIndicatorLed --sets id LED to a specified value w/ etag support +# - setBootOverride -set boot override enable, and target to valid values +# with proper checking for valid values... +# - getProcessors - get Processors collection, processor instance, or all +# - getInventory - get inventory of Processors, Memory, Fans, Power Supplies +# - getEnetInterfaces - get Ethernet collection, instance, all +# - getSimpleStorage - get SimpleStorage collection, instance, all +# - getLogService - get LogService collection, instance, all +# - clearLog -- clears a specified log (not implemented yet) +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin + +class RfSystemsMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] Systems [] -- perform on the system specified ".format(rft.program)) + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print("") + + def displayOperations(self,rft): + print(" :") + print(" [collection] -- get the main Systems collection. (Default operation if no member specified)") + print(" [get] -- get the computerSystem object. (Default operation if collection member specified)") + print(" list -- list information about the Systems collection members(\"Id\", URI, and AssetTag)") + print(" patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object") + print(" reset -- reset a system. = On, GracefulShutdown, GracefulRestart, ") + print(" ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle") + print(" setAssetTag -- set the system's asset tag ") + print(" setIndicatorLed -- set the indicator LED. =redfish defined values: Off, Lit, Blinking") + print(" setBootOverride -- set Boot Override properties. =Disabled|Once|Continuous") + print(" -- =None|Pxe|Floppy|Cd|Usb|Hdd|BiosSetup|Utilities|Diags|UefiTarget|") + print(" Processors [list] -- get the \"Processors\" collection, or list \"id\" and URI of members.") + print(" Processors [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" Inventory [list] -- get the \"Inventory\" collection, or list \"id\" and URI of members.") + print("") + print(" EthernetInterfaces [list] -- get the \"EthernetInterfaces\" collection, or list \"id\" and URI of members.") + print(" EthernetInterfaces [IDOPTN]-- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print("") + print(" SimpleStorage [list] -- get the ComputerSystem \"SimpleStorage\" collection, or list \"id\" and URI of members.") + print(" SimpleStorage [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print("") + print(" Logs [list] -- get the ComputerSystem \"LogServices\" collection , or list \"id\" and URI of members.") + print(" Logs [IDOPTN] -- get the member specified by IDOPTN: -i, -m:, -l, -a #all") + print(" clearLog -- clears the log defined by ") + print(" examples -- example commands with syntax") + print(" hello -- Systems hello -- debug command") + return(0) + + + + def runOperation(self,rft): + # instantiate SystemsOperations class + op=RfSystemsOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "collection": op.getCollection, + "get": op.get, + "list": op.list, + "patch": op.patch, + "reset": op.reset, + "setAssetTag": op.setAssetTag, + "setIndicatorLed": op.setIndicatorLed, + "setBootOverride": op.setBootOverride, + "Processors": op.getProcessors, + "Inventory": op.getInventory, + "EthernetInterfaces": op.getEnetInterfaces, + "SimpleStorage": op.getSimpleStorage, + "Logs": op.getLogService, + "-clearLog": op.clearLog, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"Systems:runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"Systems:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"Systems:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("Systems: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def SystemsMain(self,rft,cmdTop=False): + rft.printVerbose(4,"SystemsMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(0,None,False,None) + + # we will validate usage of -P and -a in action processing + # actually, if a non 'get' action is specified, -P and -a are just ignored :) + + args=rft.subcommandArgv[0:] + + #if no args, then if no member Id was specified (with -I|-M|-1|-F) then assume it is a "collection" operation + # if a -IM1F was specified, then assume it is a "get" operation for that member + if( len(args) < 2 ): + if( rft.IdOptnCount==0 ): + self.operation="collection" + else: + self.operation="get" + self.args= None + else: + self.operation=args[1] + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"Systems: operation={}, args={}".format(self.operation,self.args)) + + # check if the command requires a collection member target -I|-M|-L|-1|-F eg sysIdoptn + nonIdCommands = ["collection", "list", "examples", "hello"] + if( ( not self.operation in nonIdCommands ) and (rft.IdOptnCount==0) ): + # default to --One if no Id option specified + rft.oneOptn = True + rft.IdOptnCount += 1 + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"Systems: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"Systems: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the Systems subCommand +# +class RfSystemsOperations(): + def __init__(self): + self.systemsPath=None + self.systemsCollectionDict=None + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from Systems") + return(0,None,False,None) + + def getCollection(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in getCollection".format(rft.subcommand,sc.operation)) + + # 1st get serviceRoot + svcRoot=RfServiceRoot() + rc,r,j,d = svcRoot.getServiceRoot(rft) + if( rc != 0 ): + rft.printErr("getCollection: Error getting service root, aborting") + return(rc,r,False,None) + + # get the link to the Systems collection + # need to test we got good data + if (("Systems" in d) and ("@odata.id" in d["Systems"])): + systemsLink=d["Systems"]["@odata.id"] + else: + + rft.printErr("Error: service root does not have a Systems link") + return(4) + + rft.printVerbose(4,"Systems:getCollection: link is: {}".format(systemsLink)) + + + # if a -a option was entered with "Systems" or "Systems collection" operation, + # then return all members of the Systems collection expanded + if((cmdTop is True) and (rft.allOptn is True) ): + collName="Systems" + rft.printVerbose(4,"Expand Systems collection to return ALL Systems collection members fully expanded in response") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=systemsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + # otherwise, just return the collection + # now read the /Systems collection + # use the returned url as the base url to read the systems collection + else: + if cmdTop is True: prop=rft.prop + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=systemsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," Systems Collection:",skip1=True, printV12=cmdTop) + + return(rc,r,j,d) + + + def get(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + collUrl=r.url + + # search collection to find path to system + sysPath,rc,r,j,d=rft.getPathBy(rft, r, d) + if( rc !=0 ): #if a path was not found, its an error + return(rc,r,j,d) + + rft.printVerbose(4,"SystemsOperations:get: got a path, now get entries") + + if cmdTop is True: prop=rft.prop + + #if here, rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the system, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=sysPath, prop=prop) + + if(rc==0): rft.printVerbose(1," Systems Resource:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def list(self,sc,op,rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # getCollection + collName="Systems" + rc,r,j,d=op.getCollection(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="AssetTag") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, AssetTag".format(collName,skip1=True, printV12=cmdTop)) + return(rc,r,j,d) + + + def iterate_op(self, run_single, sc, op, rft, cmdTop=False, prop=None): + # Wrapper method to handle issuing commands to a single system or to all systems in the collection + if rft.allOptn: + # Issue command to all systems in collection + + # get the list of systems + rc, r, j, d = op.list(sc, op, rft, cmdTop=cmdTop, prop=prop) + if rc != 0 or not j or d is None or not isinstance(d, dict) or 'Members' not in d: + rft.printErr("Unable to get list of Systems; return code = {}, list data = {}".format(rc, d)) + return rc, r, j, d + + # save existing rft options + saved_allOptn = rft.allOptn + saved_Link = rft.Link + saved_gotIdOptn = rft.gotIdOptn + saved_IdOptnCount = rft.IdOptnCount + + # set rft options to process a single item + rft.allOptn = False + rft.gotIdOptn = True + rft.IdOptnCount = 1 + + # iterate through systems and run operation on each based on the link (@odata.id value) + members = d.get('Members') + rc, r, j, d = 8, None, False, None + for member in members: + if '@odata.id' in member: + link = member.get('@odata.id') + # set rft.Link to point to the target system + rft.Link = link + # perform the operation + rc, r, j, d = run_single(sc, op, rft, cmdTop=cmdTop, prop=prop) + else: + rft.printErr("No '@odata.id' found in system member: {}".format(member)) + + # restore existing rft options + rft.allOptn = saved_allOptn + rft.Link = saved_Link + rft.gotIdOptn = saved_gotIdOptn + rft.IdOptnCount = saved_IdOptnCount + return rc, r, j, d + else: + # Issue command to single specified system + return run_single(sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def patch_single(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + # verify we have got an argument which is the patch structure + # its in form '{ "AssetTag": , "IndicatorLed": }' + ##print("patchData: {}".format(patchData)) + if( len( sc.args) == 2): + ##print("data:{}".format(sc.args[1])) + try: + patchData=json.loads(sc.args[1]) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1])) + return(5,None,False,None) + ##print("patchData: {}".format(patchData)) + else: + rft.printErr("Patch: error: invalid argument format") + rft.printErr(" : args={}".format(sc.args)) + rft.printErr(" : expect: Systems patch \"{ : }\"") + return(4,None,False,None) + + # read the system resource + # this is used by the generic rft.patchResource() function to see if there is an etag in the response + # if an etag is in response hdr, then we must include the etag on the patch, so this get is required + # note: the format of etag header for redfish is: ETag: W/"" or " + # where is a subset of Redfish defined redfish resetType values + # and will be validated against the allowable values read from the remote service + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the resetType from args + validResetTypes=["On","ForceOff","GracefulShutdown","ForceRestart","Nmi","GracefulRestart", + "ForceOn","PushPowerButton","PowerCycle"] + if(len(sc.args) < 2 ): + rft.printErr("Error, no resetType value specified") + return(8,None,False,None) + + resetType=sc.args[1] + if not resetType in validResetTypes: + rft.printErr("Error, Invalid value specified: {}".format(resetType)) + return(8,None,False,None) + + #now read remote service to find out if specified resetType is one of the allowable values for this rhost + rc,r,j,d=op.get(sc,op,rft,prop="Actions") + if(rc != 0): + print("Error, can't read Actions properties from remote service") + return(8,None,False,None) + + if( (j is True) and ("Actions" in d) and ("#ComputerSystem.Reset" in d["Actions"])): + resetProps=d["Actions"]["#ComputerSystem.Reset"] + if( "ResetType@Redfish.AllowableValues" in resetProps ): + supportedResetTypes=resetProps["ResetType@Redfish.AllowableValues"] + if not resetType in supportedResetTypes: + rft.printErr("Error, the resetType specified is not supported by the remote service (via @Redfish.AllowableValues)") + return(8,None,False,None) + elif "@Redfish.ActionInfo" in resetProps: + action_info_path = resetProps["@Redfish.ActionInfo"] + supportedResetTypes = rft.getActionInfoAllowableValues(rft, r, action_info_path, "ResetType") + if supportedResetTypes is not None and resetType not in supportedResetTypes: + rft.printErr("Error, the resetType specified is not supported by the remote service (via @Redfish.ActionInfo)") + return(8,None,False,None) + else: # rhost didn't return any AllowableValues, but it isn't required, so allow the action + rft.printVerbose(2, "The remote service does not have a ResetType@Redfish.AllowableValues or @Redfish.ActionInfo prop") + else: + rft.printErr("Error, the remote service does not have an Actions: ComputerSystem.Reset property") + return(8,None,False,None) + + # now get the target URI from the remote host + if( not "target" in resetProps ): + rft.printErr("Error, the remote service doesnt have a Reset Target property (the reset path)") + return(8,None,False,None) + resetPath=resetProps["target"] + + resetData={"ResetType": resetType } + + #since we already did a get, we have the path and can just execute the post directly. + #output the post data in json to send over the network + reqPostData=json.dumps(resetData) + # send post to rhost. Use the resetPath as the relative path + rft.printVerbose(1, 'Posting reset command to target {}'.format(resetPath)) + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'POST', r.url, relPath=resetPath, + reqData=reqPostData) + + if(rc==0): + rft.printVerbose(1," Systems reset: ", resetType, skip1=True, printV12=cmdTop) + resetd=None + return(rc,r,False,resetd) + else: return(rc,r,False,None) + + + def reset(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.reset_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def setAssetTag_single(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + propName="AssetTag" + + # get the asset tag from args + if(len(sc.args) < 2 ): + rft.printErr("Error, no assetTag value specified") + rft.printErr("Syntax: {} [options] Systems setAssetTag \"string\" ".format(rft.program)) + return(8,None,False,None) + assetTag=sc.args[1] + patchData={propName: assetTag} + + # get the resource to verify it includes AssetTag, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("System resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," Systems setAssetTag:",skip1=True, printV12=cmdTop) + assetTag={propName: d[propName]} + return(rc,r,j,assetTag) + else: return(rc,r,False,None) + + + def setAssetTag(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.setAssetTag_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def setIndicatorLed_single(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + propName="IndicatorLED" + + # get the asset tag from args + validLedStates=["Lit","Blinking","Off"] + if(len(sc.args) < 2 ): + rft.printErr("Error, no ledState value specified") + rft.printErr("Syntax: {} [options] Systems setIndicatorLed ".format(rft.program)) + return(8,None,False,None) + targLedState=sc.args[1] + if not targLedState in validLedStates: + rft.printErr("Error, invalid LED state specified") + return(8,None,False,None) + patchData={propName: targLedState} + + # get the resource to verify it includes IndicatorLED, + # the response will also be used by Patch() to check for etag + rc,r,j,d=op.get(sc,op,rft) + if( rc != 0): return(rc,r,False,None) + if( not propName in d ): + rft.printErr("System resource does not have a {} property.".format(propName)) + return(8,r,False,None) + + #ststststst rc,r,j,d=op.patch(sc,op, rft, patchData=patchData, r=r) + rc,r,j,d=rft.patchResource(rft, r, patchData) + if(rc==0): + rft.printVerbose(1," Systems setIndicatorLed:",skip1=True, printV12=cmdTop) + ledState={"IndicatorLED": d["IndicatorLED"]} + return(rc,r,j,ledState) + else: return(rc,r,False,None) + + + def setIndicatorLed(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.setIndicatorLed_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def setBootOverride_single(self,sc,op,rft,cmdTop=False, prop=None): + # this operation has argument syntaxes below: + # ...setBootOverride [] + # where is not required if enabledVal==Disabled + # ...setBootOverride Once + # ...setBootOverride Continuous + # ...setBootOverride Disabled + # where TargetValue is subset of Redfish defined targets supported by rhost + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the next boot value from args + validTargetVals=("None","Pxe","Floppy","Cd","Usb","Hdd", + "BiosSetup","Utilities","Diags","UefiTarget") + validEnabledVals=("Once","Disabled","Continuous") + if(len(sc.args) < 2 ): + rft.printErr("Error, no bootSourceOverrideEnabled value specified") + rft.printErr("Syntax: {} [options] Systems setBootOverride []".format(rft.program)) + rft.printErr(" =Disabled|Once|Continuous, =None,Pxe,BiosSetup...") + return(8,None,False,None) + + enabledVal=sc.args[1] + if not enabledVal in validEnabledVals: + rft.printErr("Error, Invalid value specified: {}".format(enabledVal)) + rft.printErr("Syntax: {} [options] Systems setBootOverride []".format(rft.program)) + rft.printErr(" =Disabled|Once|Continuous, =None,Pxe,BiosSetup...") + return(8,None,False,None) + + #now read target, + # we will need to check that the properties we are patching are there, and chk for etag hdr + # and to see if the value specified is one of the allowable values for this rhost + rc,r,j,d=op.get(sc,op,rft,prop="Boot") + if(rc != 0): + print("Error, can't read boot properties from remote service") + return(8,None,False,None) + + # verify that they have a BootSourceOverrideEnabled prop + bootRes=d["Boot"] + if( not "BootSourceOverrideEnabled" in bootRes ): + rft.printErr("Error, the service does not have BootSourceOverrideEnabled property") + return(8,None,False,None) + + if( enabledVal=="Disabled"): + #just patch rhost to set OverrideEnabled=Disabled + patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal} } + rc,r,j,d=rft.patchResource(rft, r, patchData) + + else: # we are enabling bootOverride and also need to set the target + if(len(sc.args) < 3): + rft.printErr("Error, no bootSourceOverrideTarget specified") + return(8,None,False,None) + targetVal=sc.args[2] + if not targetVal in validTargetVals: + rft.printErr("Error, invalid BootSourceOverrideTarget value specified: {}".format(targetVal)) + rft.printErr("Syntax: {} [options] Systems setBootOverride []".format(rft.program)) + rft.printErr(" =Disabled|Once|Continuous, =None,Pxe,BiosSetup...") + return(8,None,False,None) + + if( (j is True) and ("Boot" in d) ): + if "BootSourceOverrideTarget@Redfish.AllowableValues" in d["Boot"]: + rhostSupportedTargets=d["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] + if not targetVal in rhostSupportedTargets: + rft.printErr("Error, the boot target specified is not supported by the remote service (via @Redfish.AllowableValues)") + return(8,None,False,None) + elif "@Redfish.ActionInfo" in d["Boot"]: + action_info_path = d["Boot"]["@Redfish.ActionInfo"] + rhostSupportedTargets = rft.getActionInfoAllowableValues(rft, r, action_info_path, "BootSourceOverrideTarget") + if rhostSupportedTargets is not None and targetVal not in rhostSupportedTargets: + rft.printErr("Error, the boot target specified is not supported by the remote service (via @Redfish.ActionInfo)") + return (8, None, False, None) + else: # rhost didn't return any AllowableValues, but it isn't required, so allow the action + rft.printVerbose(2, "The remote service does not have a BootSourceOverrideTarget@Redfish.AllowableValues or @Redfish.ActionInfo prop") + else: + rft.printErr("Error, the remote service does not have a Boot prop") + return (8, None, False, None) + + # verify that they have a BootSourceOverrideEnabled and BootSourceOverrideTarget prop + if( not "BootSourceOverrideTarget" in bootRes ): + rft.printErr("Error, the service does not have oneOf BootSourceOverride..Enabled or ..Target property") + return(8,None,False,None) + + #form the patch data + + # Get the value of "BootSourceOverrideTarget" property and pass it in the patch request. + # Some HW vendors need this property to be passed explicitly. + if "BootSourceOverrideMode" in d["Boot"]: + patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal, "BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"] } } + else: + patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal } } + + #call the generic patch command to send the patch. This takes care of etag support + rc,r,j,d=rft.patchResource(rft, r, patchData) + + + if(rc==0): + rft.printVerbose(1," Systems setBootOverride:",skip1=True, printV12=cmdTop) + bootd={"Boot": d["Boot"]} + return(rc,r,j,bootd) + + else: return(rc,r,False,None) + + + def setBootOverride(self, sc, op, rft, cmdTop=False, prop=None): + return op.iterate_op(op.setBootOverride_single, sc, op, rft, cmdTop=cmdTop, prop=prop) + + + def getProcessors(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation: getProcessorColl".format(rft.subcommand,sc.operation)) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="Processors" + # get the link to the Processors collection + if ((collName in d) and ("@odata.id" in d[collName])): + procsLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: computer system resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=procsLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Socket") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Socket".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no proc was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=procsLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the processor collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=procsLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the processor, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the processor members + else: + rft.printVerbose(4,"getting expanded Processor Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=procsLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + def getInventory(self,sc,op, rft, cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation: getInventory".format(rft.subcommand,sc.operation)) + + sys.stdout.write("%-20s %2s %-20s %2s %-10s\n" % ("Component","|","Present","|","Functional")) + # Get Processor and Memory Inventory + collection = ['Processors','Memory'] + for collName in collection: + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + # get the link to the Processors collection + if ((collName in d) and ("@odata.id" in d[collName])): + Link=d[collName]["@odata.id"] + else: + rft.printErr("Error: computer system resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=Link) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + output=json.dumps(d,indent=4) + print(output) + else: + numOfLinks=len(d["Members"]) + for i in range (0,numOfLinks): + collName="Members" + sys.stdout.write("%-20s %2s %-20s %2s %-10s\n" % (d[collName][i]["Id"], "|", d[collName][i].get("Status", {}).get("State","N/A"), "|", d[collName][i].get("Status",{}).get("Health","N/A"))) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + # get Fan inventory + jsonData=True + rc1,r1,j1,d1=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET',r.url, relPath=d["Links"]["Chassis"][0]["@odata.id"]+"/Thermal", prop=prop,jsonData=jsonData) + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + output=json.dumps(d1,indent=4) + print(output) + else: + numOfLinks=len(d1["Fans"]) + for i in range (0,numOfLinks): + collName="Fans" + sys.stdout.write("%-20s %2s %-20s %2s %-10s\n" % (d1[collName][i]["Name"], "|", d1[collName][i].get("Status",{}).get("State","N/A"), "|", d1[collName][i].get("Status",{}).get("Health", "N/A"))) + + # get Power Supply inventory + rc1,r1,j1,d1=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET',r.url, relPath=d["Links"]["Chassis"][0]["@odata.id"]+"/Power", prop=prop,jsonData=jsonData) + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + output=json.dumps(d1,indent=4) + print(output) + else: + numOfLinks=len(d1["PowerSupplies"]) + for i in range (0,numOfLinks): + collName="PowerSupplies" + sys.stdout.write("%-20s %2s %-20s %2s %-10s\n" % (d1[collName][i]["Name"], "|", d1[collName][i].get("Status",{}).get("State", "N/A"), "|", d1[collName][i].get("Status",{}).get("Health","N/A"))) + + return(0,None,False,None) + + + + def getEnetInterfaces(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="EthernetInterfaces" + # get the link to the EthernetInterfaces collection + if ((collName in d) and ("@odata.id" in d[collName])): + nicLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: EthernetInterfaces resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no NIC was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the EthernetInterfaces collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=nicLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the EthernetInterfaces, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the EthernetInterfaces members + else: + rft.printVerbose(4,"getting expanded EthernetInterfaces Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=nicLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getSimpleStorage(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="SimpleStorage" + # get the link to the SimpleStorage collection + if ((collName in d) and ("@odata.id" in d[collName])): + cntlrLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: computer system resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name" ) + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no SimpleStorage controller was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the SimpleStorage collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=cntlrLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the SimpleStorage, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # else, return ALL of the SimpleStorage members + else: + rft.printVerbose(4,"getting expanded SimpleStorage Collection") + rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=cntlrLink) + if(rc==0): + rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop)) + + return(rc,r,j,d) + + + + def getLogService(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,":{}:{}: in operation".format(rft.subcommand,sc.operation)) + + # get the system resource + rc,r,j,d=op.get(sc,op, rft) + if( rc != 0): return(rc,r,False,None) + + collName="LogServices" + # get the link to the LogServices collection + if ((collName in d) and ("@odata.id" in d[collName])): + logLink=d[collName]["@odata.id"] + else: + rft.printErr("Error: computer system resource does not have a {} link".format(collName)) + return(6,None,False,None) + + if cmdTop is True: prop=rft.prop + + # check if there is a list arg for the operation + if( sc.argnum > 1 and sc.args[1] == 'list' ): + #get the collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink) + #loop through the members and create the list sub-operation response + rc,r,j,d=rft.listCollection(rft, r, d, prop="Name") + if(rc==0): + rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if no Log was specified. If not, return the collection + elif(rft.IdLevel2OptnCount==0): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop)) + + # else: check if the -a (all) option is set. If not, return the proc specific by -i or -m + # search collection to find path using getPath2 + elif( rft.allOptn is not True ): + # get the LogServices collection + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop) + collUrl=r.url + + # now search for 2nd level resource and return + path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d) + if(rc!=0): + return(rc,r,j,d) + # so rc=0 + #if sysPath returned a response but we need to extract the property do it here + if( (r is not None) and (prop is not None) ): + rc,r,j,d=rft.getPropFromDict(rft,r,d,prop) + + # otherwise, we need to do a GET to get the LogServices, if -P show property, else show full response + elif( r is None ): + rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop) + if(rc==0): + rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop)) + + # If '--Entries' specified, get "Entries" nav link and read it + if rc == 0 and rft.gotEntriesOptn: + if r is not None and j and isinstance(d, dict): + rft.printVerbose(1, 'getLogService: attempting to get Entries for Logs') + entries = d.get('Entries') + if entries is not None and isinstance(entries, dict): + entries_uri = entries.get('@odata.id') + if entries_uri is not None: + rc, r, j, d = rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, + relPath=entries_uri, prop=prop) + else: + rft.printErr('getLogService: @odata.id not found in "Entries" property') + else: + rft.printErr('getLogService: "Entries" property not found in JSON payload') + else: + rft.printErr( + 'Unable to fetch Entries property from previous response: response = {}, is_json = {}, type(json) = {}' + .format(r, j, type(d))) + + # else, client specified the -a option requesting ALL of the LogServices members + # for logs, we will not support this. Its too much data. + else: + rft.printErr("Error: -a option not supported for LogServices") + return(6,None,False,None) + + return(rc,r,j,d) + + + def clearLog(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + return(8,None,False,None) + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r Systems # shows the systems collection".format(rft.program)) + print(" {} -r Systems list # lists Id, Uri, AssetTag for all systems".format(rft.program)) + print(" {} -r Systems -I # gets the system with Id=".format(rft.program)) + print(" {} -r Systems -M AssetTag:12345 # gets the system with AssetTag=12345".format(rft.program)) + print(" {} -r Systems -L # gets the system at URI= Systems -F # get the First system returned (for debug)".format(rft.program)) + print(" {} -r Systems -1 # get the first system and verify that there is only one system".format(rft.program)) + print(" {} -r Systems -I patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the object".format(rft.program)) + print(" {} -r Systems -I reset # reset a system. =the redfish-defined values: On, Off, gracefulOff...".format(rft.program)) + print(" {} -r Systems -I setAssetTag # set the system's asset tag ".format(rft.program)) + print(" {} -r Systems -I setIndicatorLed # set the indicator LED. =redfish defined values: Off, Lit, Blinking".format(rft.program)) + print(" {} -r Systems -I setBootOverride #-- set Boot Override properties. =Disabled|Once|Continuous".format(rft.program)) + print(" {} -r Systems -I Processors # get the processors Collection".format(rft.program)) + print(" {} -r Systems -I Processors list # lists Id, Uri, and Socket for all processors in system with Id=".format(rft.program)) + print(" {} -r Systems -I Processors -i 1 # get the processor with id=1 in system with Id=".format(rft.program)) + print(" {} -r Systems -L Processors -m Socket:CPU_1 # get processor with property Socket=CPU_1, on system at url ".format(rft.program)) + return(0,None,False,None) + + + + +''' +TODO: +1. clearlog not implemented +2. handling segmented response to collections not tested (but code is there) + +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/__init__.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/__init__.py new file mode 100644 index 000000000..d4dd4254c --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/__init__.py @@ -0,0 +1,8 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +from .redfishtoolMain import main + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/raw.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/raw.py new file mode 100644 index 000000000..e7a7455d1 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/raw.py @@ -0,0 +1,392 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: rawMain.py +# +# contains raw subCommands and access functions +# +# Class RfRawMain +# - functions init, displayUsage, displayHelp, displayOperations, +# - runOperation - raw subcommand table, dispatch of operations: get, patch, post.. +# - RfRawMain - called from redfishMain, enforce legal option combinations, +# and call runOperation to run System operation (sub-sub-command) +# +# Class RfRawOperations +# All of the Systems sub-command operations eg: Systems reset, setIndicatorLed, etc +# - hello - test cmd +# - httpGet -- send GET method +# - httpPatch -- send GET method +# - httpPost -- send GET method +# - httpDelete -- send GET method +# - httpHead -- send GET method +# - httpPut -- send GET method (not implemented in 0.9) +# - examples --prints some example apis +# +from .redfishtoolTransport import RfTransport +import requests +import json +import getopt +import re +import sys +#from .ServiceRoot import RfServiceRoot +from urllib.parse import urljoin, urlparse, urlunparse + +class RfRawMain(): + def __init__(self): + # operation string and remaining args + self.operation=None + self.args=None + self.argnum=0 + self.nonIdCommands=None + + def displayUsage(self,rft): + if(rft.quiet): return(0) + print(" Usage:") + print(" {} [OPTNS] raw ".format(rft.program)) + print("") + print(" {} raw -h # for help".format(rft.program)) + print(" {} raw examples #for example commands".format(rft.program)) + print("") + print(" is one of: GET, PATCH, POST, DELETE, HEAD, PUT") + print(" is full URI path to a redfish resource--the full path following , starting with forward slash /") + print("") + print(" Common OPTNS:") + print(" -u , --user= -- username used for remote redfish authentication") + print(" -p , --password= -- password used for remote redfish authentication") + print(" -t , --token= - redfish auth session token-for sessions across multiple calls") + print("") + print(" -r , --rhost= -- remote redfish service hostname or IP:port") + print(" -X --request= -- the http method to use. ={GET,PATCH,POST,DELETE,HEAD,PUT}. Default=GET") + print(" -d --data= -- the http request \"data\" to send on PATCH,POST,or PUT requests") + print(" -H , --Headers= -- Specify the request header list--overrides defaults. Format \"{ A:B, C:D...}\" ") + print(" -S , --Secure= -- When to use https: (Note: doesn't stop rhost from redirect http to https)") + + + def displayHelp(self,rft): + self.displayUsage(rft) + self.displayOperations(rft) + print("") + + def displayOperations(self,rft): + print(" :") + print(" GET -- HTTP GET method") + print(" PATCH -- HTTP PATCH method") + print(" POST -- HTTP POST method") + print(" DELETE -- HTTP DELETE method") + print(" HEAD -- HTTP HEAD method") + print(" PUT -- HTTP PUT method") + print(" examples -- example raw commands with syntax") + print(" hello -- raw hello -- debug command") + return(0) + + + def runOperation(self,rft): + # instantiate SystemsOperations class + op=RfRawOperations() + + # dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction" + operationTable = { + "GET": op.httpGet, + "PATCH": op.httpPatch, + "POST": op.httpPost, + "DELETE": op.httpDelete, + "HEAD": op.httpHead, + "PUT": op.httpPut, + "hello": op.hello, + "examples": op.examples + } + + rft.printVerbose(5,"raw: runOperation: operation: {}".format(self.operation)) + rft.printVerbose(5,"raw:runOperation: args: {}".format(self.args)) + + if self.operation in operationTable: + rft.printVerbose(5,"raw:runOperation: found Oper: {} in table. executing".format(rft.subcommand)) + rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True) + return(rc,r,j,d) + + else: # invalid operation + rft.printErr("raw: Invalid operation: {}".format(self.operation)) + return(2,None,False,None) + + + + def RawMain(self,rft,cmdTop=False): + rft.printVerbose(4,"RawMain: subcommand: {}".format(rft.subcommand)) + + if( rft.help ): + self.displayHelp(rft) + return(2,None,False,None) + + args=rft.subcommandArgv[0:] + + # if 2 args, only operations are: hello and examples + nonMethodOperations=("hello","examples") + if( len(args)==2): + nonMethodOp=args[1] + if( not nonMethodOp in nonMethodOperations): + rft.printErr("Syntax error: \"raw\" subcommands require 2 arguments\n") + rft.printErr("") + self.displayHelp(rft) + return(2,None,False,None) + # else all operations require two args + elif( len(args) < 3 ): + rft.printErr("Syntax error: \"raw\" subcommands require 2 arguments\n") + self.displayHelp(rft) + return(2,None,False,None) + + self.operation=args[1] # for raw subcommand, this is the http method (eg GET) + self.args = args[1:] # now args points to the 1st argument + self.argnum =len(self.args) + + rft.printVerbose(5,"raw: operation={}, args={}".format(self.operation,self.args)) + + + # now execute the operation. + rc,r,j,d = self.runOperation(rft) + + if(rc !=0 ): + rft.printVerbose(5,"raw: operation returned with error: rc={}".format(rc)) + return(rc,r,False,None) + + #else, if here, the subcommand executed without error. Return with 0 exit code + rft.printVerbose(5,"raw: operation exited OK") + return(rc,r,j,d) + + +# +# contains operations related to the Systems subCommand +# +class RfRawOperations(): + def __init__(self): + self.systemsPath=None + self.systemsCollectionDict=None + + # function to return API type: authenticated or unauthenticated + # authenticated APIs require authentication + # unauthenticated APIs are /redfish, /redfish/v1, /redfish/v1/odata, /redfish/v1/$metadata + def getApiType(self,rft,uri): + unauthenticatedAPIs=("/redfish", "/redfish/v1", "/redfish/v1/", "/redfish/v1/odata", "/redfish/v1/$metadata") + if not uri in unauthenticatedAPIs: + return( rft.AUTHENTICATED_API ) + else: + return( rft.UNAUTHENTICATED_API ) + + + def hello(self,sc,op,rft,cmdTop=False): + rft.printVerbose(4,"in hello") + rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args)) + print("hello world from raw subcommand") + return(0,None,False,None) + + + def httpGet(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="GET" + rft.printVerbose(4,"raw: GET: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + if cmdTop is True: prop=rft.prop + jsonData=True + if (path=="/redfish/v1/$metadata"): jsonData=False + rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, prop=prop,jsonData=jsonData) + if(rc==0): + rft.printVerbose(1," raw GET:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + else: + rft.printErr("raw: Error getting response") + return(rc,r,False,None) + + def httpHead(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="HEAD" + rft.printVerbose(4,"raw: HEAD: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path) + if(rc==0): + rft.printVerbose(1," raw HEAD:",skip1=True, printV12=cmdTop) + return(rc,r,False,None) + else: + rft.printErr("raw: Error getting response") + return(rc,r,False,None) + + + + + def httpPatch(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # load patch data--verify its good json + # get the patchData from rft.requestData readin on commandline via: -d + try: + patchData=json.loads(rft.requestData) + except ValueError: + rft.printErr("Patch: invalid Json input data:{}".format(rft.requestData)) + return(5,None,False,None) + ##print("patchData: {}".format(patchData)) + + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="PATCH" + rft.printVerbose(4,"raw: PATCH: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + #read the resource--the generic patch command needs it to get the etag + rc,r,j,d=rft.rftSendRecvRequest(apiType, "GET", rootUrl, relPath=path) + if(rc!=0): + rft.printErr("raw: Error getting resource prior to patching it, aborting") + return(rc,r,False,None) + + # now call the generic patch function to send the patch + rc,r,j,d=rft.patchResource(rft, r, patchData) + + if(rc==0): rft.printVerbose(1," Systems Patch:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def httpPost(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # load post data--verify its good json + # get the postData from rft.requestData readin on commandline via: -d + try: + postData=json.loads(rft.requestData) + except ValueError: + rft.printErr("Post: invalid Json input data:{}".format(rft.requestData)) + return(5,None,False,None) + ##print("postData: {}".format(postData)) + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="POST" + rft.printVerbose(4,"raw: POST: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + #output the post data in json to send over the network + reqPostData=json.dumps(postData) + #Post the data + rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, reqData=reqPostData) + if(rc!=0): + rft.printErr("raw: Error sending POST to resource, aborting") + return(rc,r,False,None) + + + if(rc==0): rft.printVerbose(1," raw POST:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def httpPut(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # load put data--verify its good json + # get the postData from rft.requestData readin on commandline via: -d + try: + putData=json.loads(rft.requestData) + except ValueError: + rft.printErr("Put: invalid Json input data:{}".format(rft.requestData)) + return(5,None,False,None) + ##print("postData: {}".format(postData)) + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="PUT" + rft.printVerbose(4,"raw: POST: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + #output the post data in json to send over the network + reqPutData=json.dumps(putData) + #Put the data + rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, reqData=reqPutData) + if(rc!=0): + rft.printErr("raw: Error sending PUT to resource, aborting") + return(rc,r,False,None) + + + if(rc==0): rft.printVerbose(1," raw PUT:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def httpDelete(self,sc,op,rft,cmdTop=False, prop=None): + rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation)) + + # we verified that we had two args in RawMain(), so we can just read the arg here + path=sc.args[1] + method="DELETE" + rft.printVerbose(4,"raw: DELETE: method:{} path:{}".format(method,path)) + + apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API + + # calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost) + scheme=rft.getApiScheme(apiType) + scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""] + rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://[:]/redfish" + + #send DELETE to the resource path specified + rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path) + if(rc!=0): + rft.printErr("raw: Error sending DELETE to resource, aborting") + return(rc,r,False,None) + + + if(rc==0): rft.printVerbose(1," raw DELETE:",skip1=True, printV12=cmdTop) + return(rc,r,j,d) + + + def examples(self,sc,op,rft,cmdTop=False,prop=None): + rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation)) + print(" {} -r raw GET /redfish/v1/ # returns the root collection".format(rft.program)) + + return(0,None,False,None) + + + + +''' +TODO: +1. need to handle case where no patch or post data (no -d) +2. add raw PUT +CHANGES: +0.9.2: no longer call /redfish and /redfish/v1 before executing the raw api +''' + + + + diff --git a/bmc/redfishtool/files/v1.1.8/redfishtoollib/redfishtoolMain.py b/bmc/redfishtool/files/v1.1.8/redfishtoollib/redfishtoolMain.py new file mode 100644 index 000000000..6faf3a675 --- /dev/null +++ b/bmc/redfishtool/files/v1.1.8/redfishtoollib/redfishtoolMain.py @@ -0,0 +1,508 @@ +# Copyright Notice: +# Copyright 2016 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md + +# redfishtool: redfishtool.py Main +# +# contains: +# - functions called for usage: +# -- displayUsage, displayOptions, listSubcommands, displayHelp +# - Main routine, with argc/argv option parsing, and valid argument/option checking +# after parsing options, runs cmd with runSubCmd function, outputs result, cleansup sessions created +# - runSubCmd -- Main subcommand command table and subcommand execution +# - hello subcommand for testing +# - help subcommand +# - about subcommand for info about this version of redfishtool +# +import sys +import getopt +import re +import json +import os +from .redfishtoolTransport import RfTransport +from .ServiceRoot import RfServiceRoot +from .Systems import RfSystemsMain +from .Chassis import RfChassisMain +from .Managers import RfManagersMain +from .SessionService import RfSessionServiceMain +from .AccountService import RfAccountServiceMain +from .raw import RfRawMain + +def displayUsage(rft,*argv,**kwargs): + rft.printErr(" Usage:",noprog=True) + rft.printErr(" {} [OPTIONS] []... ",prepend=" ") + rft.printErr(" {} [OPTIONS] hmraw []", prepend=" ") + +def displayOptions(rft): + print("") + print(" Common OPTIONS:") + print(" -V, --version -- show {} version, and exit".format(rft.program)) + print(" -h, --help -- show Usage, Options, and list of subCommands, and exit".format(rft.program)) + print(" -v, --verbose -- verbose level, can repeat up to 5 times for more verbose output") + print(" -v(header), -vv(+addl info), -vvv(Request trace), -vvvv(+subCmd dbg), -vvvvv(max dbg)") + print(" -s, --status -- status level, can repeat up to 5 times for more status output") + print(" -s(http_status), ") + print(" -ss(+r.url, +r.elapsed executionTime ), ") + print(" -sss(+request hdrs,data,authType, +response status_code, +response executionTime, ") + print(" +login auth token/sessId/sessUri)") + print(" -ssss(+response headers), -sssss(+response data") + print(" -u , --user= -- username used for remote redfish authentication") + print(" -p , --password= -- password used for remote redfish authentication") + print(" -r , --rhost= -- remote redfish service hostname or IP:port") + print(" -t , --token= -- redfish auth session token-for sessions across multiple calls") + print(" -q, --quiet -- quiet mode--suppress error, warning, and diagnostic messages") + print(" -c ,--config= -- read options (including credentials) from file ") + print(" -T ,--Timeout= -- timeout in seconds for each http request. Default=10") + print("") + print(" -P , --Prop= -- return only the specified property. Applies only to all \"get\" operations") + print(" -E, --Entries -- Fetch the Logs entries. Applies to Logs sub-command of Systems, Chassis and Managers") + print("") + print(" Options used by \"raw\" subcommand:") + print(" -d --data= -- the http request \"data\" to send on PATCH,POST,or PUT requests") + print("") + print(" Options to specify top-level collection members: eg: Systems -I ") + print(" -I , --Id= -- Use to specify the collection member") + print(" -M : --Match=:-- Use = search to find the collection member") + print(" -F, --First -- Use the 1st link returned in the collection or 1st \"matching\" link if used with -M") + print(" -1, --One -- Use the single link returned in the collection. Return error if more than one member exists") + print(" -a, --all -- Returns all members if the operation is a Get on a top-level collection like Systems") + print(" -L , --Link= -- Use (eg /redfish/v1/Systems/1) to reference the collection member. ") + print(" -- If is not one of the links in the collection, and error is returned.") + print(" Options to specify 2nd-level collection members: eg: Systems -I Processors -i") + print(" -i , --id= -- use to specify the 2nd-level collection member") + print(" -m : --match=:val>--use = search of 2nd-level collection to specify member") + print(" -l --link= -- Use (eg /redfish/v1/SYstems/1/Processors/1) to reference a 2nd level resource") + print(" -- A -I|M|F|1|L option is still required to specify the link to the top-lvl collection") + print(" -a, --all -- Returns all members of the 2nd level collection if the operation is a Get on the ") + print(" -- 2nd level collection (eg Processors). -I|M|F|1|L still specifies the top-lvl collection.") + print("") + print(" Additional OPTIONS:") + print(" -W :, -- Send up to {GET /redfish} requests with TCP connection timeout") + print(" --Wait=: -- before sending subcommand to rhost. Default is -W 1:3") + print(" -A , --Auth -- Authentication type to use: Authn={None|Basic|Session} Default is Basic") + print(" -S , --Secure= -- When to use https: (Note: doesn't stop rhost from redirect http to https)") + print(" ={Always | IfSendingCredentials | IfLoginOrAuthenticatedApi(default) }") + print(" -R , --RedfishVersion=-- The Major Redfish Protocol version to use: ver={v1(dflt), v, Latest}") + print(" -C --CheckRedfishVersion -- tells Redfishtool to execute GET /redfish to verify that the rhost supports") + print(" the specified redfish protocol version before executing a sub-command. ") + print(" The -C flag is auto-set if the -R Latest or -W ... options are selected") + print(" -N, --NonBlocking -- Do not wait for asynchronous requests to complete.") + print(" -n, --no-proxy -- Ignore any PROXY environment variables.") + print(" -H , --Headers= -- Specify the request header list--overrides defaults. Format \"{ A:B, C:D...}\" ") + print(" -D , --Debug= -- Flag for dev debug. is a 32-bit uint: 0x or format") + print("") + +def listSubcommands(rft): + # print a list of Main subcommands in order to show them in lc subcommand + print(" Subcommands:") + print(" hello -- redfishtool hello world subcommand for dev testing") + print(" about -- display version and other information about this version of {}".format(rft.program)) + print(" versions -- get redfishProtocol versions supported by rhost: GET ^/redfish") + print(" root | serviceRoot -- get serviceRoot resource: GET ^/redfish/v1/") + print(" Systems -- operations on Computer Systems in the /Systems collection ") + print(" Chassis -- operations on Chassis in the /Chassis collection") + print(" Managers -- operations on Managers in the /Managers collection") + print(" AccountService -- operations on AccountService including user administration") + print(" SessionService -- operations on SessionService including Session login/logout") + print(" odata -- get the Odata Service document: GET ^/redfish/v1/odata") + print(" metadata -- get the CSDL metadata document: GET ^/redfish/v1/$metadata") + print(" raw -- subcommand to execute raw http methods(GET,PATCH,POST...) and URIs") + + return(0) + +def displayHelp(rft): + displayUsage(rft,file=sys.stdout) + displayOptions(rft) + listSubcommands(rft) + print("") + print(" For Subcommand usage, options, operations, help:") + print(" {} -h -- usage and options for specific subCommand".format(rft.program)) + print("") + +def main(argv): + #instantiate transport object which initializes default options + rft=RfTransport() + + try: + opts, args = getopt.gnu_getopt(argv[1:],"Vhvsqu:p:r:t:c:T:P:d:EI:M:F1L:i:m:l:aW:A:S:R:H:D:CNn", + ["Version", "help", "verbose", "status", "quiet", + "user=", "password=", "rhost=", "token=", "config=", "Timeout=", + "Prop=", "data=", "Entries", "Id=", "Match=", "First", "One", "Link=", + "id=", "match=", "link", "all", + "Wait=", "Auth=","Secure=", "RedfishVersion=", "Headers=", "Debug=", + "CheckRedfishVersion", "NonBlocking", "no-proxy"]) + except getopt.GetoptError: + rft.printErr("Error parsing options") + displayUsage(rft) + sys.exit(1) + + for opt, arg in opts: + if opt in ("-h", "--help"): + rft.help=True + elif opt in ("-V", "--Version", "--version"): + print("{} Version: {}".format(rft.program, rft.version)) + sys.exit(0) + elif opt in ("-v", "--verbose"): + rft.verbose = min((rft.verbose+1), 5) + elif opt in ("-s", "--status"): + rft.status = min((rft.status+1), 5) + elif opt in ("-q", "--quiet"): + rft.quiet=True + elif opt in ("-u", "--user"): + rft.user=arg + elif opt in ("-p", "--password"): + rft.password=arg + elif opt in ("-r", "--rhost"): + rft.rhost=arg + elif opt in ("-t", "--token"): #Redfish Session authentication token + rft.token=arg + rft.authToken=rft.token + elif opt in ("-c", "--config"): #read additional options from a file + rft.configFile=arg + try: + with open(arg, 'r') as f: + configdata = json.load(f) + if 'user' in configdata and 'password' in configdata: + rft.user = configdata['user'] + rft.password = configdata['password'] + rft.printVerbose(1,"Main: get pw from configfile, usr:{}, pw:{}".format(rft.user, rft.password)) + else: + rft.printErr("Invalid --config= filedata: {}".format(configdata)) + except IOError: + rft.printErr("Invalid: Failed to read configfile") + + elif opt in ("-T", "--Timeout"): #Specify http timeout in seconds + timePattern="^([1-9][0-9]*)$" + timeMatch=re.search(timePattern,arg) + if( timeMatch ): + rft.timeout=int(arg) + else: + rft.printErr("Invalid -Timeout value: {}".format(arg)) + rft.printErr(" Expect: -T where is a decimal int",noprog=True) + sys.exit(1) + # options that reference a specific property for Get operations + elif opt in ("-P", "--Prop"): + rft.prop=arg + rft.gotPropOptn=True + # options used by raw subcommand to specify http method (-X, --request) and request data (-d, --data) + elif opt in ("-d", "--data"): + rft.requestData=arg + # options related to the Logs sub-command of Systems, Chassis and Managers + elif opt in ("-E", "--Entries"): + rft.gotEntriesOptn = True + # options to find the Id or member of a collection in a subcommand + elif opt in ("-I", "--Id"): + rft.Id=arg + rft.gotIdOptn=True + rft.IdOptnCount+=1 + elif opt in ("-M", "--Match"): + # arg is of the form: ":" + pair = arg.split(':', 1) + if len(pair) == 2: + rft.matchProp = pair[0] + rft.matchValue = pair[1] + rft.IdOptnCount += 1 + rft.gotMatchOptn = True + else: + rft.printErr("Invalid --Match= option format: {}".format(arg)) + rft.printErr(" Expect --Match=: Ex -M AssetTag:5555, -Match=AssetTag:5555",noprog=True) + sys.exit(1) + elif opt in ("-F", "--First"): + rft.firstOptn=True + rft.IdOptnCount+=1 + elif opt in ("-1", "--One"): + rft.oneOptn=True + rft.IdOptnCount+=1 + elif opt in ("-L", "--Link"): + rft.Link=arg + rft.gotIdOptn=True + rft.IdOptnCount+=1 + elif opt in ("-i", "--id"): + rft.IdLevel2=arg + rft.gotIdLevel2Optn=True + rft.IdLevel2OptnCount+=1 + elif opt in ("-m", "--match"): + # arg is of the form: ":" + pair = arg.split(':', 1) + if len(pair) == 2: + rft.matchLevel2Prop = pair[0] + rft.matchLevel2Value = pair[1] + rft.IdLevel2OptnCount += 1 + rft.gotMatchLevel2Optn = True + else: + rft.printErr("Invalid level2 --match= option format: {}".format(arg)) + rft.printErr(" Expect --match=: Ex -m ProcessorType:CPU, -match=ProcessorType:CPU",noprog=True) + sys.exit(1) + elif opt in ("-l", "--link"): + rft.linkLevel2=arg + rft.gotIdLevel2Optn=True + rft.IdLevel2OptnCount+=1 + elif opt in ("-a", "--all"): + rft.allOptn=True # additional options + rft.IdLevel2OptnCount+=1 + elif opt in ("-W", "--Wait"): #specify how long to ping rhost before sending http requests 0=no ping + # arg is of the form ":