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 <eric.macdonald@windriver.com>
This commit is contained in:
Eric MacDonald 2024-04-22 18:58:38 +00:00
parent 80e8d0206d
commit 48d1d9b9dd
31 changed files with 6985 additions and 0 deletions

View File

@ -0,0 +1,5 @@
redfishtool (1.1.8-1) unstable; urgency=medium
* Initial release.
-- Eric Macdonald <eric.macdonald@windriver.com> Wed, 10 Apr 2024 16:12:19 +0000

View File

@ -0,0 +1,19 @@
Source: redfishtool
Section: tools
Priority: optional
Maintainer: StarlingX Developers <starlingx-discuss@lists.starlingx.io>
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.

View File

@ -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.

View File

@ -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

View File

@ -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

Binary file not shown.

View File

@ -0,0 +1,7 @@
---
debname: redfishtool
debver: 1.0-1
src_path: files/v1.1.8
revision:
dist: $STX_DIST
PKG_GITREVCOUNT: true

View File

@ -0,0 +1,3 @@
# Original Contribution:
* Paul Vancil - Dell Inc. -- Dell Extreme Scale Infrastructure (ESI) Architecture Team

View File

@ -0,0 +1,82 @@
# Change Log
## [1.1.8] - 2023-08-16
- Corrected matching logic to return the matched resource if found
## [1.1.7] - 2023-06-30
- Added workaround to specify the 'BootSourceOverrideMode' property when modifying the boot override properties even if the mode is not changing
## [1.1.6] - 2022-06-20
- Minor spelling fixes
## [1.1.5] - 2021-03-02
- Added --no-proxy option
- Fixed missing documentation to show PowerCycle is a valid reset option
## [1.1.4] - 2020-07-24
- Renamed the package to not conflict with the name of the script
- Added additional redfishtool.py script for Windows compatibility
## [1.1.3] - 2020-05-01
- Added inventory and sensor options
## [1.1.2] - 2020-04-10
- Made fix to collection listing to show members when optional properties are not present
## [1.1.1] - 2020-01-10
- Made fixes to properly format IPv6 URLs
## [1.1.0] - 2019-08-09
- Added support for getting Power and Thermal resources with a specified chassis, or leaving it unspecified if there is exactly one chassis
## [1.0.9] - 2019-07-12
- Added the ability to get credentials from the config file rather than specifying them on the command line
## [1.0.8] - 2018-11-30
- Made the "One" option enabled by default for Systems, Managers, and Chassis collections
## [1.0.7] - 2018-10-19
- Made fix to allow for 202 responses on operations
## [1.0.6] - 2018-10-12
- Fixed help output for the raw command
## [1.0.5] - 2018-09-21
- Fixed bug with nextLink handling
- Fixed parsing of `@odata.type` properties when they use an unversioned namespace
## [1.0.4] - 2018-02-02
- Fixed parsing of match argument when there are colons in the match data
## [1.0.3] - 2018-01-02
- Added support for PUT with raw commands
- Added support for getting log entries via the -E argument
## [1.0.2] - 2017-10-27
- Added support for leveraging the `@Redfish.ActionInfo` annotation if `@Redfish.AllowableValues` for Reset actions
- Added support for `--all` option to Systems and Chassis commands that perform update operations
- Fixed handling of the `setTimeOffset` argument
## [1.0.1] - 2017-06-15
- Created a script called `redfishtool` to be installed via `pip install redfishtool`
## [1.0.0] - 2017-06-01
- Added AccountService setusername operation to modify the UserName property in an existing account
## [0.9.3] - 2017-04-27
- Updated spelling in various usage print statements
- Forrected usage statement for SessionService login and logout subcommands
- Fixed error in collection list subcommand to show the path correctly
- Fixed password update so that it won't fail if using basicAuth to change user's own password. It was failing if using basic auth of self user since the final get used the old credentials.
- Changed elapsed time calculation to measure execution time around request lib instead of using the internal request r.elapsed property. 0.9.2 was measuring exec time short in some cases as the internal elapsed prop measures execution time until 1st header is returned--not until all data is returned.
## [0.9.2] - 2016-12-05
- Modified "raw" subcommand so that it does not execute /redfish or /redfish/v1 before executing the raw api
- Changed default behavior to NOT always send a /redfish query and verify that the remote service supports the specified redfishProtocol before executing the API
- Added a new -C option to invoke the "Check remote RedfishProtocol Version" before executing function. If -R "Latest" is set by the user, then the -C flat is auto-set since "Latest" means use the latest mutually supported version
- Changed the default -R <redfishVersion> 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

View File

@ -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.
```

View File

@ -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.

View File

@ -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 <host>) or long (--rhost=<host>) 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 <Id>, -a (all), -M <prop>:<val> ).
* ***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 <setBootOverride> <enableValue>` <targetValue>`
### 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>, --user=<usernm> -- username used for remote redfish authentication
-p <passwd>, --password=<passwd> -- password used for remote redfish authentication
-r <rhost>, --rhost=<rhost> -- remote redfish service hostname or IP:port
-t <token>, --token=<token> -- redfish auth session token-for sessions across multiple calls
-q, --quiet -- quiet mode--suppress error, warning, and diagnostic messages
-c <cfgFile>,--config=<cfgFile> -- read options (including credentials) from file <cfgFile>
-T <timeout>,--Timeout=<timeout> -- timeout in seconds for each http request. Default=10
-P <property>, --Prop=<property> -- 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> --data=<data> -- the http request "data" to send on PATCH,POST,or PUT requests
###### Options to specify top-level collection members: eg: `Systems -I <sysId>`
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>, --Id=<Id> -- Use <Id> to specify the collection member
-M <prop>:<val> --Match=<prop>:<val>-- Use <prop>=<val> 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>, --Link=<Link> -- Use <Link> (eg /redfish/v1/Systems/1) to reference the collection member.
-- If <Link> is not one of the links in the collection, and error is returned.
###### Options to specify 2nd-level collection members: eg: `Systems -I<sysId> Processors -i<procId>`
-i <id>, --id=<id> -- use <id> to specify the 2nd-level collection member
-m <prop>:<val> --match=<prop>:val>--use <prop>=<val> search of 2nd-level collection to specify member
-l <link> --link=<link> -- Use <link> (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 <num>:<connTimeout>, -- Send up to <num> {GET /redfish} requests with <connTimeout> TCP connection timeout
--Wait=<num>:<ConnTimeout> -- before sending subcommand to rhost. Default is -W 1:3
-A <Authn>, --Auth <Authn> -- Authentication type to use: Authn={None|Basic|Session} Default is Basic
-S <Secure>, --Secure=<Secure> -- When to use https: (Note: doesn't stop rhost from redirect http to https)
<Secure>={Always | IfSendingCredentials | IfLoginOrAuthenticatedApi(default) }
-R <ver>, --RedfishVersion=<ver>-- The Major Redfish Protocol version to use: ver={v1(dflt), v<n>, 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 <hdrs>, --Headers=<hdrs> -- Specify the request header list--overrides defaults. Format "{ A:B, C:D...}"
-D <flag>, --Debug=<flag> -- Flag for dev debug. <flag> is a 32-bit uint: 0x<hex> or <dec> 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 <SubCommand> -h -- usage and options for specific subCommand
### Subcommand Operations and Addl Args
###### Systems Operations
python redfishtool.py -r <rhost> -u <username> -p <password> Systems -h
Usage:
redfishtool [OPTNS] Systems <operation> [<args>] -- perform <operation> on the system specified
<operations>:
[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 <resetType> -- reset a system. <resetType>= On, GracefulShutdown, GracefulRestart,
ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle
setAssetTag <assetTag> -- set the system's asset tag
setIndicatorLed <state> -- set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking
setBootOverride <enabledVal> <targetVal> -- set Boot Override properties. <enabledVal>=Disabled|Once|Continuous
-- <targetVal> =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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -a #all
clearLog <id> -- clears the log defined by <id>
examples -- example commands with syntax
hello -- Systems hello -- debug command
###### Chassis Operations
python redfishtool.py -r <rhost> -u <username> -p <password> Chassis -h
Usage:
redfishtool [OPTNS] Chassis <operation> [<args>] -- perform <operation> on the Chassis specified
<operations>:
[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 <assetTag> -- set the Chassis's asset tag
setIndicatorLed <state> -- set the indicator LED. <state>=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<indx>] [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
<indx> is the powerControl array index. default is 0. normally, 0 is the only entry
setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] -- set powerLimit control properties
<limit>=null disables power limiting. <indx> 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<id>, -m<prop>:<val>, -l<link>, -a #all
clearLog <id> -- clears the log defined by <id>
examples -- example commands with syntax
hello -- Chassis hello -- debug command
###### Managers Operations
python redfishtool.py -r <rhost> -u <username> -p <password> Managers -h
Usage:
redfishtool [OPTNS] Managers <operation> [<args>] -- perform <operation> on the Managers specified
<operations>:
[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 <resetType> -- reset a Manager. <resetType>= On, GracefulShutdown, GracefulRestart,
ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle
setDateTime <dateTimeString>--set the date and time
setTimeOffset offset=<offsetString> --set the time offset w/o changing time setting
<offsetString> is of form "[+/-]mm:ss". Ex: "-10:01"
NetworkProtocol -- get the "NetworkProtocol" resource under the specified manager.
setIpAddress [-i<indx>]... -- 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<id>, -m<prop>:<val>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -a #all
clearLog <id> -- clears the log defined by <id>
examples -- example commands with syntax
hello -- Systems hello -- debug command
###### AccountService Operations
python redfishtool.py -r <rhost> -u <username> -p <password> AccountService -h
Usage:
redfishtool [OPTNS] AccountService <operation> [<args>] -- perform <operation> on the AccountService
<operations>:
[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<Id>, -m<prop>:<val>, -l<link>, -a #all
Roles [list] -- get the "Roles" collection, or list "Id", IsPredefined, and Url
Roles [IDOPTN] -- get the member specified by IDOPTN: -i<Id>, -m<prop>:<val>, -l<link>, -a #all
adduser <usernm> <passwd> [<roleId>] -- add a new user to the Accounts collection
-- <roleId>:{Administrator | Operator | ReadOnlyUser | <a custom roleId}, dflt=Operator
deleteuser <usernm> -- delete an existing user from Accouts collection
setpassword <usernm> <passwd> -- set (change) the password of an existing user account
useradmin <userName> [enable|disable|unlock|[setRoleId <roleId>]] -- enable|disable|unlock.. a user account
setusername <id> <userName> -- set UserName for account with given Id
examples -- example commands with syntax
hello -- AccountService hello -- debug command
###### SessionService Operations
python redfishtool.py -r <rhost> -u <username> -p <password> SessionService -h
Usage:
redfishtool [OPTNS] SessionService <operation> [<args>] -- perform <operation> on the SessionService
<operations>:
[get] -- get the sessionService object.
patch {A: B,C: D,...} -- patch the sessionService w/ json-formatted {prop: value...}
setSessionTimeout <timeout> -- 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<Id>, -m<prop>:<val>, -l<link>, -a #all
login -- sessionLogin. post to Sessions collection to create a session
the user is -u<user>, password is -p<password>
logout -- logout or delete the session by identified by -i<SessionId> or -l<link>
where <link> 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 <rhost> -u <username> -p <password> raw -h
Usage:
redfishtool [OPTNS] raw <method> <path>
redfishtool raw -h # for help
redfishtool raw examples #for example commands
<method> is one of: GET, PATCH, POST, DELETE, HEAD, PUT
<path> is full URI path to a redfish resource--the full path following <ipaddr:port>, starting with forward slash /
Common OPTNS:
-u <user>, --user=<usernm> -- username used for remote redfish authentication
-p <passwd>, --password=<passwd> -- password used for remote redfish authentication
-t <token>, --token=<token> - redfish auth session token-for sessions across multiple calls
-r <rhost>, --rhost=<rhost> -- remote redfish service hostname or IP:port
-X <method> --request=<method> -- the http method to use. <method>={GET,PATCH,POST,DELETE,HEAD,PUT}. Default=GET
-d <data> --data=<data> -- the http request "data" to send on PATCH,POST,or PUT requests
-H <hdrs>, --Headers=<hdrs> -- Specify the request header list--overrides defaults. Format "{ A:B, C:D...}"
-S <Secure>, --Secure=<Secure> -- When to use https: (Note: doesn't stop rhost from redirect http to https)
<operations / methods>:
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 <ip> -u <username> -p <password> Systems examples
# Shows the Systems Collection
redfishtool -r <ip> -u <username> -p <password> Systems
# Lists Id, Uri, AssetTag for all systems
redfishtool -r <ip> -u <username> -p <password> Systems list
# Gets the system with Id=<d>
redfishtool -r <ip> -u <username> -p <password> Systems -I <id>
# Gets the system with AssetTag=12345
redfishtool -r <ip> -u <username> -p <password> Systems -M AssetTag:12345
# Gets the system at URI=<systemUrl>
redfishtool -r <ip> -u <username> -p <password> Systems -L <systemUrl>
# Gets the first system returned (for debug)
redfishtool -r <ip> -u <username> -p <password> Systems -F
# Gets the first system and verify that there is only one system
redfishtool -r <ip> -u <username> -p <password> Systems -1
# Patches the json-formated {prop: value...} data to the specified system
redfishtool -r <ip> -u <username> -p <password> Systems -I <id> patch {A: B,C: D,...}
# Patches the json-formated {prop: value...} data to all systems
redfishtool -r <ip> -u <username> -p <password> Systems --all patch {A: B,C: D,...}
# Resets a system. <resetType>=the redfish-defined values: On, Off, gracefulOff...
redfishtool -r <ip> -u <username> -p <password> Systems -I <id> reset <resetType>
# Resets all systems. <resetType>=the redfish-defined values: On, Off, gracefulOff...
redfishtool -r <ip> -u <username> -p <password> Systems --all reset <resetType>
# Sets the system's asset tag to <assetTag>
redfishtool -r <ip> -u <username> -p <password> Systems -I <id> setAssetTag <assetTag>
# Sets all system's asset tags to <assetTag>
redfishtool -r <ip> -u <username> -p <password> Systems --all setAssetTag <assetTag>
# Sets the indicator LED. <state>=redfish defined values: Off, Lit, Blinking
redfishtool -r <ip> -u <username> -p <password> Systems -I <id> setIndicatorLed <state>
# Sets the indicator LED on all systems. <state>=redfish defined values: Off, Lit, Blinking
redfishtool -r <ip> -u <username> -p <password> Systems --all setIndicatorLed <state>
# Sets Boot Override properties. <enabledVal>=Disabled|Once|Continuous
redfishtool -r <ip> -u <username> -p <password> Systems -I <id> setBootOverride <enabledVal> <targetVal>
# Sets Boot Override properties on all systems. <enabledVal>=Disabled|Once|Continuous
redfishtool -r <ip> -u <username> -p <password> Systems --all setBootOverride <enabledVal> <targetVal>
# Gets the Processor Collection
redfishtool -r <ip> -u <username> -p <password> Systems -I <Id> Processors
# Lists Id, Uri, & Socket for all processors in system w/ Id=<Id>
redfishtool -r <ip> -u <username> -p <password> Systems -I <Id> Processors list
# Gets the processor with id=1 in system with Id=<Id>
redfishtool -r <ip> -u <username> -p <password> Systems -I <Id> Processors -i 1
# Gets processor with property Socket=CPU_1, on system at url <sysUrl>
redfishtool -r <ip> -u <username> -p <password> Systems -L <sysUrl> Processors -m Socket:CPU_1
# Gets log member with Id=SEL from the first System
redfishtool -r <ip> -u <username> -p <password> Systems -1 Logs -i SEL
# Gets log entries with Id=SEL from the first System
redfishtool -r <ip> -u <username> -p <password> Systems -1 Logs -E -i SEL
# Gets System inventory
redfishtool -r <ip> -u <username> -p <password> Systems Inventory
### Chassis subcommand Examples
$ python redfishtool.py -r <ip> -u <username> -p <password> Chassis examples
# Shows the Chassis Collection
redfishtool -r <ip> -u <username> -p <password> Chassis
# Lists Id, Uri, AssetTag for all Chassis
redfishtool -r <ip> -u <username> -p <password> Chassis list
# Gets the Chassis with Id=<d>
redfishtool -r <ip> -u <username> -p <password> Chassis -I <id>
# Gets the Chassis with AssetTag=12345
redfishtool -r <ip> -u <username> -p <password> Chassis -M AssetTag:12345
# Gets the Chassis at URI=<chassisUrl>
redfishtool -r <ip> -u <username> -p <password> Chassis -L <chassisUrl>
# Gets the first Chassis returned (for debug)
redfishtool -r <ip> -u <username> -p <password> Chassis -F
# Gets the first Chassis and verify that there is only one system
redfishtool -r <ip> -u <username> -p <password> Chassis -1
# Patches the json-formated {prop: value...} data to the specified chassis
redfishtool -r <ip> -u <username> -p <password> Chassis -I <id> patch {A: B,C: D,...}
# Patches the json-formated {prop: value...} data to all chassis
redfishtool -r <ip> -u <username> -p <password> Chassis --all patch {A: B,C: D,...}
# Sets the chassis's asset tag
redfishtool -r <ip> -u <username> -p <password> Chassis -I <id> setAssetTag <assetTag>
# Sets all chassis's asset tags
redfishtool -r <ip> -u <username> -p <password> Chassis --all setAssetTag <assetTag>
# Sets the indicator LED. <state>=redfish defined values: Off, Lit, Blinking
redfishtool -r <ip> -u <username> -p <password> Chassis -I <id> setIndicatorLed <state>
# Sets the indicator LED on all chassis. <state>=redfish defined values: Off, Lit, Blinking
redfishtool -r <ip> -u <username> -p <password> Chassis --all setIndicatorLed <state>
# Gets the full chassis Power resource
redfishtool -r <ip> -u <username> -p <password> Chassis -I <Id> Power
# Gets the full chassis Thermal resource
redfishtool -r <ip> -u <username> -p <password> Chassis -I <Id> Thermal
# Gets chassis/Power powerControl[<indx>] resource if optional "consumed" arg, then return only the PowerConsumedWatts prop
redfishtool -r <ip> -u <username> -p <password> Chassis -I <Id> getPowerReading[-i<indx> [consumed]
# Sets the power limit
redfishtool -r <ip> -u <username> -p <password> Chassis -L<Url> setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]]
# Sets the power limit on all chassis
redfishtool -r <ip> -u <username> -p <password> Chassis --all setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]]
# Gets log member with Id=SEL from the first Chassis
redfishtool -r <ip> -u <username> -p <password> Chassis -1 Logs -i SEL
# Gets log entries with Id=SEL from the first Chassis
redfishtool -r <ip> -u <username> -p <password> Chassis -1 Logs -E -i SEL
# Gets all Sensors
redfishtool -r <ip> -u <username> -p <password> Chassis Sensors
### Managers subcommand Examples
$ python redfishtool.py -r <ip> -u <username> -p <password> Managers examples
# Shows the Managers Collection
redfishtool -r <ip> -u <username> -p <password>
# Lists Id, Uri, AssetTag for all Managers
redfishtool -r <ip> -u <username> -p <password> Managers list
# Gets the Manager with Id=<d>
redfishtool -r <ip> -u <username> -p <password> Managers -I <id>
# Gets the Manager with AssetTag=12345
redfishtool -r <ip> -u <username> -p <password> Managers -M AssetTag:12345
# Gets the Manager at URI=<mgrUrl>
redfishtool -r <ip> -u <username> -p <password> Managers -L <mgrUrl>
# Gets the first Manager returned (for debug)
redfishtool -r <ip> -u <username> -p <password> Managers -F
# Gets the first Manager and verify that there is only one Manager
redfishtool -r <ip> -u <username> -p <password> Managers -1
# Patches the json-formated {prop: value...} data to the object
redfishtool -r <ip> -u <username> -p <password> Managers -I <id> patch {A: B,C: D,...}
# Resets a Manager. <resetType>=the redfish-defined values: On, Off, gracefulOff...
redfishtool -r <ip> -u <username> -p <password> Managers -I <id> reset <resetType>
# Gets the NetworkProtocol resource under the specified manager
redfishtool -r <ip> -u <username> -p <password> Managers -I <Id> NetworkProtocol
# Lists Id, Uri, and Name for all of the NICs for Manager w/ Id=<Id>
redfishtool -r <ip> -u <username> -p <password> Managers -I <Id> EthernetInterfaces list
# Gets the NIC with id=1 in manager with Id=<Id>
redfishtool -r <ip> -u <username> -p <password> Managers -I <Id> EthernetInterfaces -i 1
# Gets the NIC with MAC AA:BB:CC:DD:EE:FF for manager at url <Url>
redfishtool -r <ip> -u <username> -p <password> Managers -L <Url> EthernetInterfaces -m MACAddress:AA:BB:CC:DD:EE:FF
# Gets log member with Id=SEL from the first Manager
redfishtool -r <ip> -u <username> -p <password> Managers -1 Logs -i SEL
# Gets log entries with Id=SEL from the first Manager
redfishtool -r <ip> -u <username> -p <password> Managers -1 Logs -E -i SEL
### AccountService subcommand Examples
$ python redfishtool.py -r <ip> -u <username> -p <password> AccountService examples
# Gets the AccountService
redfishtool -r <ip> -u <username> -p <password> AccountService
# Sets the failed login lockout threshold
redfishtool -r <ip> -u <username> -p <password> AccountService patch { "AccountLockoutThreshold": 5 } ]
# Gets the Accounts collection
redfishtool -r <ip> -u <username> -p <password> AccountService Accounts
# List Accounts to get Id, username, url for each account
redfishtool -r <ip> -u <username> -p <password> AccountService Accounts list
# Gets the Accounts member with username: john
redfishtool -r <ip> -u <username> -p <password> AccountService Accounts -m UserName:john
# Lists the Roles collection to get RoleId, IsPredefined, & url for each role
redfishtool -r <ip> -u <username> -p <password> AccountService Roles list
# Gets the Roles member with RoleId=Admin
redfishtool -r <ip> -u <username> -p <password> AccountService Roles -i Admin
# Adds the new user (john) w/ passwd "12345" and role: Admin
redfishtool -r <ip> -u <username> -p <password> AccountService adduser john 12345 Admin
# Deletes the account with the username "john"
redfishtool -r <ip> -u <username> -p <password> AccountService deleteuser john
# Disables the account with the username "john"
redfishtool -r <ip> -u <username> -p <password> AccountService useradmin john disable
# Unlocks the account with the username "john"
redfishtool -r <ip> -u <username> -p <password> AccountService useradmin john unlock
# Sets the username for account with id=3 to "alice"
redfishtool -r <ip> -u <username> -p <password> AccountService setusername 3 alice
### SessionService subcommand Examples
$ python redfishtool.py -r <ip> -u <username> -p <password> SessionService examples
# Gets the sessionService
redfishtool -r <ip> -u <username> -p <password> SessionService
# Sets the session timeout property
redfishtool -r <ip> -u <username> -p <password> SessionService setSessionTimeout <timeout>
# Gets Sessions collection
redfishtool -r <ip> -u <username> -p <password> SessionService Sessions
# Gets the session at URI=<sessUrl>
redfishtool -r <ip> -u <username> -p <password> SessionService Sessions -l <sessUrl>
# Gets the session with session Id <sessId>
redfishtool -r <ip> -u <username> -p <password> SessionService Sessions -i <sessId>
# Patches the json-formated {prop: value...} data to the sessionService object
redfishtool -r <ip> -u <username> -p <password> SessionService patch {A: B,C: D,...}
# Login (create session)
redfishtool -r <ip> -u <username> -p <password> SessionService login -u <user> -p <password>
# Logout (delete session <sessId>)
redfishtool -r <ip> -u <username> -p <password> SessionService logout -i <sessionId>
## 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"

View File

@ -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)

View File

@ -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 <operation> [<args>] -- perform <operation> on the AccountService ".format(rft.program))
def displayHelp(self,rft):
self.displayUsage(rft)
self.displayOperations(rft)
print("")
def displayOperations(self,rft):
print(" <operations>:")
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<Id>, -m<prop>:<val>, -l<link>, -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<Id>, -m<prop>:<val>, -l<link>, -a #all")
print(" adduser <usernm> <passwd> [<roleId>] -- add a new user to the Accounts collection")
print(" -- <roleId>:{Administrator | Operator | ReadOnlyUser | <a custom roleId}, dflt=Operator")
print(" deleteuser <usernm> -- delete an existing user from Accouts collection")
print(" setpassword <usernm> <passwd> -- set (change) the password of an existing user account")
print(" useradmin <userName> [enable|disable|unlock|[setRoleId <roleId>]] -- enable|disable|unlock.. a user account")
print(" setusername <id> <userName> -- set UserName for account with given Id")
#print(" addrole <roleId> <listOfPrivileges> -- add a new custom role to the Roles collection")
#print(" -- <listOfPrivileges> is string of form: Login,ConfigeUsers,ConfigureSelf...")
#print(" deleterole <roleId> -- 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": <val>, "IndicatorLed": <val> }'
##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 \"{ <prop>: <value> }\"")
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/"<string>" or "<string"
rc,r,j,d=op.get(sc,op,rft)
if( rc != 0):
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," AccountService Patch:",skip1=True, printV12=cmdTop)
return(rc,r,j,d)
def getAccounts(self,sc,op, rft, cmdTop=False, prop=None):
rft.printVerbose(4,"{}:{}: in operation: getAccounts 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="Accounts"
# get the link to the Accounts collection
if ((collName in d) and ("@odata.id" in d[collName])):
accountsLink=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=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 <username> <passwd> [<roleId>] ---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 <username> and <passwd>
# if we got 3rd arg <roleId>, 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 <username> <passwd> [<roleId>]".format(rft.program))
rft.printErr(" default <roleId> is Operator")
return(8,None,False,None)
# get the <username> and <password> 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 <username>
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 <username>".format(rft.program))
return(8,None,False,None)
# get the <username> and <password> 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 <username> {enable|disable|unlock|{setRoleId <roleId>}}
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 <username> and <action> 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 <roleId> 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 <action> 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 <username> {{enable|disable|unlock|{{setRoleId <roleId>}} }}".format(rft.program))
return(8)
# setusername <id> <username>
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 (<id> and <username>)
# 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 <id> and <username> 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 <id> <username>".format(rft.program))
return (8)
# setpassword <username> <password>
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: <username> <passwd>.
# 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 <username> <password>".format(rft.program))
return(8,None,False,None)
# get the <username> and <action> 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 <RoleId> <priviletes string> -- 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 <RoleId> -- 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<ip> AccountService # gets the AccountService".format(rft.program))
print(" {} -r<ip> AccountService patch {{ \"AccountLockoutThreshold\": 5 }} ]# set failed login lockout threshold".format(rft.program))
print(" {} -r<ip> AccountService Accounts # gets Accounts collection".format(rft.program))
print(" {} -r<ip> AccountService Accounts list # list Accounts to get Id, username, url for each account".format(rft.program))
print(" {} -r<ip> AccountService Accounts -mUserName:john # gets the Accounts member with username: john".format(rft.program))
print(" {} -r<ip> AccountService Roles list # list Roles collection to get RoleId, IsPredefined, & url for each role".format(rft.program))
print(" {} -r<ip> AccountService Roles -iAdministrator # gets the Roles member with RoleId=Administrator".format(rft.program))
print(" {} -r<ip> AccountService adduser john 12345 Administrator # add new user (john) w/ passwd \"12345\" and role: Administrator".format(rft.program))
print(" {} -r<ip> AccountService deleteuser john # delete user \"john\"s account".format(rft.program))
print(" {} -r<ip> AccountService useradmin john disable # disable user \"john\"s account".format(rft.program))
print(" {} -r<ip> AccountService useradmin john unlock # unlock user \"john\"s account".format(rft.program))
print(" {} -r<ip> 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.
'''

View File

@ -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 <operation> [<args>] -- perform <operation> on the Chassis specified ".format(rft.program))
def displayHelp(self,rft):
self.displayUsage(rft)
self.displayOperations(rft)
print(" ")
def displayOperations(self,rft):
print(" <operations>:")
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 <assetTag> -- set the Chassis's asset tag ")
print(" setIndicatorLed <state> -- set the indicator LED. <state>=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<indx>] [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(" <indx> is the powerControl array index. default is 0. normally, 0 is the only entry")
print(" setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] -- set powerLimit control properties")
print(" <limit>=null disables power limiting. <indx> 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<id>, -m<prop>:<val>, -l<link>, -a #all")
print(" clearLog <id> -- clears the log defined by <id>")
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": <val>, "IndicatorLed": <val> }'
##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 \"{ <prop>: <value> }\"")
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/"<string>" or "<string"
rc,r,j,d=op.get(sc,op,rft)
if( rc != 0):
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," Chassis Patch:",skip1=True, printV12=cmdTop)
return(rc,r,j,d)
def patch(self, sc, op, rft, cmdTop=False, prop=None):
return op.iterate_op(op.patch_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] Chassis 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("Chassis 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 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] Chassis setIndicatorLed <Lit|Off|Blinking>".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<indx>] [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})$)" # <decimal>: 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<indx>] --<indx> 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<indx>] <limit> [<exception> [<correctionTime>]] -- 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 <limit>
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]=<limit>
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 <limit> value specified: {}".format(sc.args[1]))
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --limit=null | 0-99999")
return(8,None,False,None)
else:
rft.printErr("Error: setPowerLimit: no <limit> value specified: {}")
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --limit=null | 0-99999")
return(8,None,False,None)
# check if there is an optional 3rd arg <exception>
includeException=False
validExceptionVals=("NoAction", "HardPowerOff", "LogEventOnly", "Oem")
if (sc.argnum > 2 ):
exceptionVal=sc.args[2] # sc.args[2]=<exceptionEnum>
if( not exceptionVal in validExceptionVals ):
rft.printErr("Error: setPowerLimit: invalid <limit> value specified: {}".format(sc.args[2]))
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --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 <correctionTime> 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]=<correctionInMs>
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 <correctionTime> value specified: {}".format(sc.args[3]))
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --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]*)$)" # <decimal>: 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<indx>] --<indx> 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<ip> Chassis # shows the Chassis collection".format(rft.program))
print(" {} -r<ip> Chassis list # lists Id, Uri, AssetTag for all Chassis".format(rft.program))
print(" {} -r<ip> Chassis -I <id> # gets the Chassis with Id=<d>".format(rft.program))
print(" {} -r<ip> Chassis -M AssetTag:12345 # gets the Chassis with AssetTag=12345".format(rft.program))
print(" {} -r<ip> Chassis -L <sysUrl> # gets the Chassis at URI=<systemUrl".format(rft.program))
print(" {} -r<ip> Chassis -F # get the First Chassis returned (for debug)".format(rft.program))
print(" {} -r<ip> Chassis -1 # get the first Chassis and verify that there is only one system".format(rft.program))
print(" {} -r<ip> Chassis -I <id> patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the object".format(rft.program))
print(" {} -r<ip> Chassis -I <id> setAssetTag <assetTag> # set the system's asset tag ".format(rft.program))
print(" {} -r<ip> Chassis -I <id> setIndicatorLed <state> # set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking".format(rft.program))
print(" {} -r<ip> Chassis -I<Id> Power # get the full chassis Power resource".format(rft.program))
print(" {} -r<ip> Chassis -I<Id> Thermal # get the full chassis Thermal resource".format(rft.program))
print(" {} -r<ip> Chassis -I<Id> getPowerReading[-i<indx> [consumed] # get chassis/Power powerControl[<indx>] resource".format(rft.program))
print(" # if optional \"consumed\" arg, then return only the PowerConsumedWatts prop")
print(" {} -r<ip> Chassis -L<Url> setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] # set power limit".format(rft.program))
return(0,None,False,None)
'''
TODO:
1. clearlog not implemented
'''

View File

@ -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 <operation> [<args>] -- perform <operation> on the Managers specified ".format(rft.program))
def displayHelp(self,rft):
self.displayUsage(rft)
self.displayOperations(rft)
print("")
def displayOperations(self,rft):
print(" <operations>:")
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 <resetType> -- reset a Manager. <resetType>= On, GracefulShutdown, GracefulRestart, ")
print(" ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle")
print(" setDateTime <dateTimeString>--set the date and time")
print(" setTimeOffset offset=<offsetString> --set the time offset w/o changing time setting")
print(" <offsetString> is of form \"[+/-]mm:ss\". Ex: \"-10:01\" ")
print(" NetworkProtocol -- get the \"NetworkProtocol\" resource under the specified manager.")
print(" setIpAddress [-i<indx>]... -- 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<id>, -m<prop>:<val>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -a #all")
print(" clearLog <id> -- clears the log defined by <id>")
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": <val>, "IndicatorLed": <val> }'
##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 \"{ <prop>: <value> }\"")
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/"<string>" or "<string"
rc,r,j,d=op.get(sc,op,rft)
if( rc != 0):
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," Managers Patch:",skip1=True, printV12=cmdTop)
return(rc,r,j,d)
def reset(self,sc,op,rft,cmdTop=False, prop=None):
# this operation has argument syntaxes below:
# ...reset <resetType>
# where <resetType> 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 <resetType> 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 <dateTimeString>".format(rft.program))
rft.printErr(" <dateTimeString> 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]=<dateTimeString>
if( dateMatch ):
dateTimeString=(sc.args[1]) # keep it a string
rft.printVerbose(4,"setDateTime: dateTime={}".format(dateTimeString))
else:
rft.printErr("Error: setDateTime: invalid <dateTime> value specified: {}".format(sc.args[1]))
rft.printErr("Managers setDateTime <dateTimeString> # 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=<timeOffsetString>".format(rft.program))
rft.printErr(" where <timeOffsetString> 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]=<timeOffset>
if( dateMatch ):
timeOffsetString=dateMatch.group(1) # keep it a string. <timeOffsetString>
rft.printVerbose(4,"setDateTime: timeOffset={}".format(timeOffsetString))
else:
rft.printErr("Error: setTimeOffset: invalid <timeOffset> value specified: {}".format(sc.args[1]))
rft.printErr("Managers setTimeOffset offset=<timeOffset> # eg: where <timeOffset>=[+/-]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<ip> # shows the Managers collection".format(rft.program))
print(" {} -r<ip> Managers list # lists Id, Uri, AssetTag for all Managers".format(rft.program))
print(" {} -r<ip> Managers -I <id> # gets the Manager with Id=<d>".format(rft.program))
print(" {} -r<ip> Managers -M AssetTag:12345 # gets the Manager with AssetTag=12345".format(rft.program))
print(" {} -r<ip> Managers -L <mgrUrl> # gets the Manager at URI=<mgrUrl".format(rft.program))
print(" {} -r<ip> Managers -F # get the First Manager returned (for debug)".format(rft.program))
print(" {} -r<ip> Managers -1 # get the first Manager and verify that there is only one Manager".format(rft.program))
print(" {} -r<ip> Managers -I <id> patch {{A: B,C: D,...}}# patch the json-formatted {{prop: value...}} data to the object".format(rft.program))
print(" {} -r<ip> Managers -I <id> reset <resetType> # reset a Manager. <resetType>=the redfish-defined values: On, Off, gracefulOff...".format(rft.program))
print(" {} -r<ip> Managers -I<Id> NetworkProtocol # get the NetworkProtocol resource under the specified manager".format(rft.program))
print(" {} -r<ip> Managers -I<Id> EthernetInterfaces list # lists Id, Uri, and Name for all of the NICs for Manager w/ Id=<Id>".format(rft.program))
print(" {} -r<ip> Managers -I<Id> EthernetInterfaces -i 1 # get the NIC with id=1 in manager with Id=<Id>".format(rft.program))
print(" {} -r<ip> Managers -L <Url> EthernetInterfaces -m MACAddress:AA:BB:CC:DD:EE:FF # get NIC with MAC AA:BB... for manager at url <Url>".format(rft.program))
return(0,None,False,None)
'''
TODO:
1. clearlog not implemented
2. setIpAddress not implemented
'''

View File

@ -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<rhost> [-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<rhost> [-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<rhost> [-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.
'''

View File

@ -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 <operation> [<args>] -- perform <operation> on the SessionService ".format(rft.program))
def displayHelp(self,rft):
self.displayUsage(rft)
self.displayOperations(rft)
print("")
def displayOperations(self,rft):
print(" <operations>:")
print(" [get] -- get the sessionService object. ")
print(" patch {A: B,C: D,...} -- patch the sessionService w/ json-formatted {prop: value...} ")
print(" setSessionTimeout <timeout> -- 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<Id>, -m<prop>:<val>, -l<link>, -a #all")
print(" login -- sessionLogin. post to Sessions collection to create a session")
print(" the user is -u<user>, password is -p<password>")
print(" logout -- logout or delete the session by identified by -i<SessionId> or -l<link>")
print(" where <link> 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": <val>, "IndicatorLed": <val> }'
##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 \"{ <prop>: <value> }\"")
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/"<string>" or "<string"
rc,r,j,d=op.get(sc,op,rft)
if( rc != 0):
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," SessionService Patch:",skip1=True, printV12=cmdTop)
return(rc,r,j,d)
def setSessionTimeout(self,sc,op,rft,cmdTop=False, prop=None):
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
propName="SessionTimeout"
# get the SessionTimeout from args
if(len(sc.args) < 2 ):
rft.printErr("Error, no SessionTimeout value specified")
rft.printErr("Syntax: {} [options] SessionService setSessionTimeout <timeoutInt> ".format(rft.program))
return(8,None,False,None)
sessTimeout=int(sc.args[1],0) # base 0 means int() will interpret 0x<num> as hex and <num> 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 <username> -p <passwd> 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 <username> and <password> 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<token> logout [-i<sessionId>|-l<sessionLink>], 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<sessionId> | -l <sessionLink>]".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\": <uri>}")
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<ip> SessionService # gets the sessionService".format(rft.program))
print(" {} -r<ip> SessionService setSessionTimeout <timeout> # sets the session timeout property".format(rft.program))
print(" {} -r<ip> SessionService Sessions # gets Sessions collection".format(rft.program))
print(" {} -r<ip> SessionService Sessions -l<sessUrl> # gets the session at URI=<sessUrl".format(rft.program))
print(" {} -r<ip> SessionService Sessions -i<sessId> # gets the session with session Id <sessId>".format(rft.program))
print(" {} -r<ip> SessionService patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the sessionService object".format(rft.program))
print(" {} -r<ip> SessionService login <usernm> <passwd> # login (create session)".format(rft.program))
print(" {} -r<ip> SessionService logout <sessionId> # logout (delete session <sessId>".format(rft.program))
return(0,None,False,None)
'''
TODO:
1. e
'''

View File

@ -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 <operation> [<args>] -- perform <operation> on the system specified ".format(rft.program))
def displayHelp(self,rft):
self.displayUsage(rft)
self.displayOperations(rft)
print("")
def displayOperations(self,rft):
print(" <operations>:")
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 <resetType> -- reset a system. <resetType>= On, GracefulShutdown, GracefulRestart, ")
print(" ForceRestart, ForceOff, ForceOn, Nmi, PushPowerButton, PowerCycle")
print(" setAssetTag <assetTag> -- set the system's asset tag ")
print(" setIndicatorLed <state> -- set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking")
print(" setBootOverride <enabledVal> <targetVal> -- set Boot Override properties. <enabledVal>=Disabled|Once|Continuous")
print(" -- <targetVal> =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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -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<id>, -m<prop>:<val>, -l<link>, -a #all")
print(" clearLog <id> -- clears the log defined by <id>")
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": <val>, "IndicatorLed": <val> }'
##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 \"{ <prop>: <value> }\"")
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/"<string>" or "<string"
rc,r,j,d=op.get(sc,op,rft)
if( rc != 0):
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 patch(self, sc, op, rft, cmdTop=False, prop=None):
return op.iterate_op(op.patch_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
def reset_single(self,sc,op,rft,cmdTop=False, prop=None):
# this operation has argument syntaxes below:
# ...reset <resetType>
# where <resetType> 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 <resetType> 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 <Lit|Off|Blinking>".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 <enabledVal> [<targetVal>]
# where <targetVal> is not required if enabledVal==Disabled
# ...setBootOverride Once <targetVal>
# ...setBootOverride Continuous <targetVal>
# ...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 <enableVal> [<targetVal>]".format(rft.program))
rft.printErr(" <enableVal>=Disabled|Once|Continuous, <targetVal>=None,Pxe,BiosSetup...")
return(8,None,False,None)
enabledVal=sc.args[1]
if not enabledVal in validEnabledVals:
rft.printErr("Error, Invalid <Enabled> value specified: {}".format(enabledVal))
rft.printErr("Syntax: {} [options] Systems setBootOverride <enableVal> [<targetVal>]".format(rft.program))
rft.printErr(" <enableVal>=Disabled|Once|Continuous, <targetVal>=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 <enableVal> [<targetVal>]".format(rft.program))
rft.printErr(" <enableVal>=Disabled|Once|Continuous, <targetVal>=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<ip> Systems # shows the systems collection".format(rft.program))
print(" {} -r<ip> Systems list # lists Id, Uri, AssetTag for all systems".format(rft.program))
print(" {} -r<ip> Systems -I <id> # gets the system with Id=<d>".format(rft.program))
print(" {} -r<ip> Systems -M AssetTag:12345 # gets the system with AssetTag=12345".format(rft.program))
print(" {} -r<ip> Systems -L <sysUrl> # gets the system at URI=<systemUrl".format(rft.program))
print(" {} -r<ip> Systems -F # get the First system returned (for debug)".format(rft.program))
print(" {} -r<ip> Systems -1 # get the first system and verify that there is only one system".format(rft.program))
print(" {} -r<ip> Systems -I <id> patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the object".format(rft.program))
print(" {} -r<ip> Systems -I <id> reset <resetType> # reset a system. <resetType>=the redfish-defined values: On, Off, gracefulOff...".format(rft.program))
print(" {} -r<ip> Systems -I <id> setAssetTag <assetTag> # set the system's asset tag ".format(rft.program))
print(" {} -r<ip> Systems -I <id> setIndicatorLed <state> # set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking".format(rft.program))
print(" {} -r<ip> Systems -I <id> setBootOverride <enabledVal> <targetVal> #-- set Boot Override properties. <enabledVal>=Disabled|Once|Continuous".format(rft.program))
print(" {} -r<ip> Systems -I<Id> Processors # get the processors Collection".format(rft.program))
print(" {} -r<ip> Systems -I<Id> Processors list # lists Id, Uri, and Socket for all processors in system with Id=<Id>".format(rft.program))
print(" {} -r<ip> Systems -I<Id> Processors -i 1 # get the processor with id=1 in system with Id=<Id>".format(rft.program))
print(" {} -r<ip> Systems -L <sysUrl> Processors -m Socket:CPU_1 # get processor with property Socket=CPU_1, on system at url <sysUrl>".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)
'''

View File

@ -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

View File

@ -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 <method> <path> ".format(rft.program))
print("")
print(" {} raw -h # for help".format(rft.program))
print(" {} raw examples #for example commands".format(rft.program))
print("")
print(" <method> is one of: GET, PATCH, POST, DELETE, HEAD, PUT")
print(" <path> is full URI path to a redfish resource--the full path following <ipaddr:port>, starting with forward slash /")
print("")
print(" Common OPTNS:")
print(" -u <user>, --user=<usernm> -- username used for remote redfish authentication")
print(" -p <passwd>, --password=<passwd> -- password used for remote redfish authentication")
print(" -t <token>, --token=<token> - redfish auth session token-for sessions across multiple calls")
print("")
print(" -r <rhost>, --rhost=<rhost> -- remote redfish service hostname or IP:port")
print(" -X <method> --request=<method> -- the http method to use. <method>={GET,PATCH,POST,DELETE,HEAD,PUT}. Default=GET")
print(" -d <data> --data=<data> -- the http request \"data\" to send on PATCH,POST,or PUT requests")
print(" -H <hdrs>, --Headers=<hdrs> -- Specify the request header list--overrides defaults. Format \"{ A:B, C:D...}\" ")
print(" -S <Secure>, --Secure=<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(" <operations / methods>:")
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 <method> <path>
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 <uri> 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]://<rhost>[:<port>]/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 <uri> 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]://<rhost>[:<port>]/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 <patchData>
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 <uri> 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]://<rhost>[:<port>]/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 <patchData>
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 <uri> 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]://<rhost>[:<port>]/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 <patchData>
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 <uri> 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]://<rhost>[:<port>]/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 <uri> 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]://<rhost>[:<port>]/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<ip> 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
'''

View File

@ -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] <SubCommand> <operation> [<args>]... ",prepend=" ")
rft.printErr(" {} [OPTIONS] hmraw <method> <hmUrl> [<data>]", 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>, --user=<usernm> -- username used for remote redfish authentication")
print(" -p <passwd>, --password=<passwd> -- password used for remote redfish authentication")
print(" -r <rhost>, --rhost=<rhost> -- remote redfish service hostname or IP:port")
print(" -t <token>, --token=<token> -- redfish auth session token-for sessions across multiple calls")
print(" -q, --quiet -- quiet mode--suppress error, warning, and diagnostic messages")
print(" -c <cfgFile>,--config=<cfgFile> -- read options (including credentials) from file <cfgFile>")
print(" -T <timeout>,--Timeout=<timeout> -- timeout in seconds for each http request. Default=10")
print("")
print(" -P <property>, --Prop=<property> -- 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> --data=<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 <sysId>")
print(" -I <Id>, --Id=<Id> -- Use <Id> to specify the collection member")
print(" -M <prop>:<val> --Match=<prop>:<val>-- Use <prop>=<val> 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>, --Link=<Link> -- Use <Link> (eg /redfish/v1/Systems/1) to reference the collection member. ")
print(" -- If <Link> is not one of the links in the collection, and error is returned.")
print(" Options to specify 2nd-level collection members: eg: Systems -I<sysId> Processors -i<procId>")
print(" -i <id>, --id=<id> -- use <id> to specify the 2nd-level collection member")
print(" -m <prop>:<val> --match=<prop>:val>--use <prop>=<val> search of 2nd-level collection to specify member")
print(" -l <link> --link=<link> -- Use <link> (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 <num>:<connTimeout>, -- Send up to <num> {GET /redfish} requests with <connTimeout> TCP connection timeout")
print(" --Wait=<num>:<ConnTimeout> -- before sending subcommand to rhost. Default is -W 1:3")
print(" -A <Authn>, --Auth <Authn> -- Authentication type to use: Authn={None|Basic|Session} Default is Basic")
print(" -S <Secure>, --Secure=<Secure> -- When to use https: (Note: doesn't stop rhost from redirect http to https)")
print(" <Secure>={Always | IfSendingCredentials | IfLoginOrAuthenticatedApi(default) }")
print(" -R <ver>, --RedfishVersion=<ver>-- The Major Redfish Protocol version to use: ver={v1(dflt), v<n>, 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 <hdrs>, --Headers=<hdrs> -- Specify the request header list--overrides defaults. Format \"{ A:B, C:D...}\" ")
print(" -D <flag>, --Debug=<flag> -- Flag for dev debug. <flag> is a 32-bit uint: 0x<hex> or <dec> 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(" {} <SubCommand> -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 <timeout> where <timeout> 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: "<prop>:<value>"
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=<prop>:<value> 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: "<prop>:<value>"
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=<prop>:<value> 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 "<num>:<time>
rft.checkProtocolVer=True # if -W ..., set flag to force running GET /redfish first
waitPattern="^([1-9][0-9]*):([1-9][0-9]*)$"
waitMatch=re.search(waitPattern,arg)
if( waitMatch ):
rft.waitNum=int(waitMatch.group(1))
rft.waitTime=int(waitMatch.group(2))
print("num: {}, time: {}".format(rft.waitNum, rft.waitTime))
else:
rft.printErr("Invalid --Wait= option format: {}".format(arg))
rft.printErr(" Expect --Wait=<num>:<time> where num and time are decimals. Ex -W 10:5",noprog=True)
sys.exit(1)
elif opt in ("-A", "--Auth"): # specify authentication type
rft.auth=arg
if not rft.auth in rft.authValidValues:
rft.printErr("Invalid --Auth option: {}".format(rft.auth))
rft.printErr(" Valid values: {}".format(rft.authValidValues),noprog=True)
sys.exit(1)
elif opt in ("-S", "--Secure"): #Specify when to use HTTPS
rft.secure=arg
if not rft.secure in rft.secureValidValues:
rft.printErr("Invalid --Secure option: {}".format(rft.secure))
rft.printErr(" Valid values: {}".format(rft.secureValidValues),noprog=True)
sys.exit(1)
elif opt in ("-R", "--RedfishVersion"): #specify redfish protocol version to use
rft.protocolVer=arg
if(rft.protocolVer=="Latest"):
rft.checkProtocolVer=True # if -R Latest, set flag to force running GET /redfish first
elif opt in ("-H", "--Headers"): #specify request headers -- overrides defaults
try:
rft.headers=json.loads(arg)
except ValueError:
rft.printErr("Invalid -H arg: invalid Json data format: {}".format(arg))
sys.exit(1)
elif opt in ("-D", "--Debug"): # --Debug=0x3334 or --Debug=233 regex:(^(0[xX]([0-9a-fA-F]){1,8}$))|(^0$)|(^([1-9][0-9]*)$)
dbgPattern="(^(0[xX]([0-9a-fA-F]){1,8}$))|(^0$)|(^([1-9][0-9]*)$)" # --Flag=0x<hex> or <decimal>: 0x3334 or 33...
dbgMatch=re.search(dbgPattern,arg)
if( dbgMatch ):
rft.dgbFlag=int(arg,0)
rft.printVerbose(4,"Main: Flag=0x{:08x}".format(rft.dbgFlag))
else:
rft.printErr("Invalid -Flag value: {}".format(arg))
rft.printErr(" Expect --Flag=<flag> where <flag> is a decimal int",noprog=True)
sys.exit(1)
elif opt in ("-C", "--CheckRedfishVersion"):
rft.checkProtocolVer=True
elif opt in ("-N", "--NonBlocking"):
rft.blocking=False
elif opt in ("-n", "--no-proxy"):
rft.no_proxy = True
else:
rft.printErr("Error: Unsupported option: {}".format(opt))
displayUsage(rft)
sys.exit(1)
# if no subcommand, check if -h --help option was entered, and print help if so
# if a subcommand was entered, then the subcommand will print the help
if( rft.help ):
if( not args ):
displayHelp(rft)
sys.exit(0)
# if -t <token> option was specified, the --Auth=<auth> must be Session (which is the default)
if (rft.token is not None) and (rft.auth != "Session"):
rft.printErr("Invalid mix of --Auth and --token options")
rft.printErr(" if --token=<authToken> is specified, --Auth must be Session", noprog=True)
sys.exit(1)
# check for invalid option combinations
if( rft.IdOptnCount > 1 ):
if( not (rft.firstOptn and rft.gotMatchOptn ) ):
rft.printErr("Syntax error: invalid combination of -I,-M,-1,-F options.")
rft.printErr(" Valid combinations: -I | -M[F] | -1 | -F ",noprog=True)
displayUsage(rft)
sys.exit(1)
# if -I <Id>, convert to -M <prop>:<val>
if( rft.gotIdOptn is True ):
rft.matchProp="Id"
rft.matchValue=rft.Id
rft.gotMatchOptn=True
rft.firstOptn=True
# if -i <id>, convert to -m <prop>:<val>
if( rft.gotIdLevel2Optn is True ):
rft.matchLevel2Prop="Id"
rft.matchLevel2Value=rft.IdLevel2
rft.gotMatchLevel2Optn=True
# check for invalid Level-2 collection member reference options
# there are 3 ways to specify the level-2 colltion member: -i<id> | -m<prop>:<val> | -l<link> | -a
# a command should only include one of these. each time one if found during option processing, IdLevel2OptnCount is incremented
if( rft.IdLevel2OptnCount > 1):
rft.printErr("Syntax error: invalid mix of options -i,-m,-a used to specify a 2nd-level collection member.")
rft.printErr(" Valid combinations: -i | -m | -l | -a ",noprog=True)
displayUsage(rft)
sys.exit(1)
# -P, -a will be validated against the operation in the Subcommand processing
# whether at least one -I|-M|-1|-F|-L or one -i|-m|-l|-a is required is validated in subcommand processing based on operation
#after parsing options (GNU style) args should now be a list of arguments starting with the subcommand
#if no subcommand at this point, it is a syntax error.
#otherwise, save the subcommand and subcommand argv array for the subcommand to parse
if( not args ):
rft.printErr("Syntax error. No subcommand specified.")
displayUsage(rft)
sys.exit(1)
else:
rft.subcommand=args[0]
rft.subcommandArgv=list(args)
# disable reading of proxy environment variables for --no-proxy
if rft.no_proxy:
os.environ['NO_PROXY'] = '*'
rft.printVerbose(5,"Main: subcmd: {}, subCmdArgs:{}".format(rft.subcommand,rft.subcommandArgv))
rft.printVerbose(5,"Main: verbose={}, status={}, user={}, password={}, rhost={}".format(rft.verbose, rft.status,
rft.user,rft.password,rft.rhost))
rft.printVerbose(5,"Main: token={}, RedfishVersion={}, Auth={}, Timeout={}, NonBlocking={}".format(rft.token,
rft.protocolVer, rft.auth, rft.timeout, not rft.blocking))
rft.printVerbose(5,"Main: prop={}, Id={}, Match={}:{}, First={}, -1={}, Link={}".format( rft.prop,
rft.Id, rft.matchProp,rft.matchValue, rft.firstOptn, rft.oneOptn, rft.Link))
rft.printVerbose(5,"Main: gotIdOptn={}, IdOptnCount={}, gotPropOptn={}, gotMatchOptn={}, gotEntriesOptn={}".format(
rft.gotIdOptn, rft.IdOptnCount, rft.gotPropOptn, rft.gotMatchOptn, rft.gotEntriesOptn))
rft.printVerbose(5,"Main: 2nd-Level Collection Member reference options: -i<id>={}, -m<match>={}:{}, -l<link>={} -all={}".format(
rft.IdLevel2, rft.matchLevel2Prop, rft.matchLevel2Value, rft.linkLevel2, rft.allOptn))
rft.printVerbose(5,"Main: 2nd-level Collection Member parsing: gotIdLevel2Optn={}, gotMatchLevel2Optn={}, IdLevel2OptnCount={}".format(
rft.gotIdLevel2Optn, rft.gotMatchLevel2Optn, rft.IdLevel2OptnCount ))
rft.printVerbose(5,"Main: configFile={}, Secure={}, waitNum:waitTime={}:{}, Degug={:08x}".format(
rft.configFile,rft.secure, rft.waitNum,rft.waitTime,rft.dbgFlag))
rft.printVerbose(5,"Main: Headers={}".format(rft.headers))
rft.printVerbose(5,"Main: options parsed. Now lookup subcommand and execute it")
# instantiate the SubCmd object, and run the specified subcommand
#rfCmds=RfSubCmds()
#rc=rfCmds.runSubCmd(rft)
rc,r,j,d=runSubCmd(rft)
if(rc !=0 ):
rft.printVerbose(5,"#DB4:Main: subcommand returned with error: rc={}".format(rc))
rft.printVerbose(1,"Main: Error: rc={}".format(rc))
if r is not None:
rft.printVerbose(5," Response status code:{}".format(r.status_code))
rft.printVerbose(5," Response headers: {}".format(r.headers))
#cleanup any sessions we opened
rft.rfCleanup(rft)
sys.exit(rc)
rft.printVerbose(5,"Main: subcommand exited OK.")
if( r is not None ):
rft.printVerbose(5," Status code:{}".format(r.status_code))
rft.printStatus(1,r=r)
rft.printStatus(2,r=r)
# print out result here.
if( j is True and d is not None):
output=json.dumps(d,indent=4)
print(output)
elif( j is False and d is not None):
output=r.text
print(output)
else:
pass
#cleanup any sessions we opened on the remote service
rft.rfCleanup(rft)
rft.printVerbose(5,"Main: Done")
#print("headers:{}".format(r.headers))
sys.exit(0)
# enter cmdClasses in other files here:
# lookup the function for the subcommand and execute it
# pass the transport class that includes argv/argc array and Main options
# returns tuple: rc(0=ok, >0=err), r (from request), dataType(0=none,1=dict,2=text) ,d (data)
# rc, r, dt, d=runSubCmd(rft)
def runSubCmd(rft):
# instantiate all cmdClasses (note: helloCmd and listSubcommands are in this class)
root=RfServiceRoot()
systems=RfSystemsMain()
chassis=RfChassisMain()
managers=RfManagersMain()
sessionService=RfSessionServiceMain()
accountService=RfAccountServiceMain()
raw=RfRawMain()
# dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction"
subCmdTable = {
"help": helpSubcmd,
"about": aboutSubcmd,
"versions": rft.getVersions,
"serviceRoot": root.getServiceRoot,
"root": root.getServiceRoot, #alias for serviceRoot
"odata": root.getOdataServiceDocument,
"metadata": root.getOdataMetadataDocument,
"Systems": systems.SystemsMain,
"Chassis": chassis.ChassisMain,
"Managers": managers.ManagersMain,
"AccountService": accountService.AccountServiceMain,
"SessionService": sessionService.SessionServiceMain,
"raw": raw.RawMain,
"hello": helloSubcmd
}
rft.printVerbose(5,"runSubCmd: subcmd: {}".format(rft.subcommand))
rft.printVerbose(5,"runSubCmd: argvs: {}".format(rft.subcommandArgv))
if rft.subcommand in subCmdTable:
rft.printVerbose(5,"runSubCmd: found SubCmd: {} in table. executing".format(rft.subcommand))
rc,r,j,d=subCmdTable[rft.subcommand](rft,cmdTop=True)
return(rc,r,j,d)
else: # invalit subcmd
rft.printErr("Invalid SubCommand: {}".format(rft.subcommand))
return(1,None,False,None)
def helloSubcmd(rft, cmdTop=False):
rft.printVerbose(5,"Main: in hello subcommand")
if(rft.help):
print("# {} [OPTIONS] hello ---prints hello world message for debug".format(rft.program))
return(0,None,False,None)
print("#\n# Hello World \n#")
return(0,None,False,None)
def helpSubcmd(rft, cmdTop=False):
rft.printVerbose(5,"Main: in help subcommand")
displayHelp(rft)
return(0,None,False,None)
def aboutSubcmd(rft, cmdTop=False):
rft.printVerbose(5,"Main: in about subcommand")
if(rft.help):
print("# {} [OPTIONS] about ---prints information about {}".format(rft.program,rft.program))
return(0,None,False,None)
print("#")
print("# {} {}:".format(rft.program,rft.subcommand))
print("# Version: {}".format(rft.version))
print("# Supports Redfish Protocol Versions: {}".format(rft.supportedVersions))
print("# Release date: {}".format(rft.releaseDate))
print("# Download from: {}".format(rft.downloadFrom))
print("#")
return(0,None,False,None)
if __name__ == "__main__":
main(sys.argv)
'''
TODO
1. implement -c cfgfile
2. implement -d <data> -d @<file> for raw subcommand POST or PATCH data
3.
'''

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
python-dateutil
requests

View File

@ -0,0 +1,18 @@
#!/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: scripts/redfishtool
#
# 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)

View File

@ -0,0 +1,18 @@
#!/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: scripts/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)

View File

@ -0,0 +1,2 @@
[metadata]
description-file = README.md

View File

@ -0,0 +1,28 @@
from setuptools import setup
from os import path
this_directory = path.abspath(path.dirname(__file__))
with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
long_description = f.read()
setup(name='redfishtool',
version='1.1.8',
description='Redfishtool package and command-line client',
long_description=long_description,
long_description_content_type='text/markdown',
author='DMTF, https://www.dmtf.org/standards/feedback',
license='BSD 3-clause "New" or "Revised License"',
classifiers=[
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3.4',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications'
],
keywords='Redfish',
url='https://github.com/DMTF/Redfishtool',
download_url='https://github.com/DMTF/Redfishtool/archive/1.1.5.tar.gz',
packages=['redfishtoollib'],
scripts=['scripts/redfishtool', 'scripts/redfishtool.py'],
install_requires=['python-dateutil', 'requests']
)

Binary file not shown.

View File

@ -384,6 +384,9 @@ openvswitch-common
openvswitch-switch-dpdk
openvswitch-switch
#redfishtool 1.1.8
redfishtool
#runc
#golang-github-opencontainers-runc-dev # not used
runc

View File

@ -13,6 +13,7 @@ base/synce4l
base/systemd
base/systemd-presets
base/watchdog
bmc/redfishtool
centos-debian-compat
ceph/ceph
config/facter