-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #142 from DMTF/Assembly-Tool
Added 'rf_assembly.py' to manage Assembly resources on a service
- Loading branch information
Showing
6 changed files
with
348 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# Assembly (rf_assembly.py) | ||
|
||
Copyright 2019-2024 DMTF. All rights reserved. | ||
|
||
## About | ||
|
||
A tool to manage assemblies on a Redfish service. | ||
|
||
## Usage | ||
|
||
``` | ||
usage: rf_assembly.py [-h] --user USER --password PASSWORD --rhost RHOST | ||
--assembly ASSEMBLY [--index INDEX] [--debug] | ||
{info,download,upload} ... | ||
A tool to manage assemblies on a Redfish service | ||
positional arguments: | ||
{info,download,upload} | ||
info Displays information about the an assembly | ||
download Downloads assembly data to a file | ||
upload Uploads assembly data from a file | ||
required arguments: | ||
--user USER, -u USER The user name for authentication | ||
--password PASSWORD, -p PASSWORD | ||
The password for authentication | ||
--rhost RHOST, -r RHOST | ||
The address of the Redfish service (with scheme) | ||
--assembly ASSEMBLY, -a ASSEMBLY | ||
The URI of the target assembly | ||
optional arguments: | ||
-h, --help show this help message and exit | ||
--index INDEX, -i INDEX | ||
The target assembly index | ||
--debug Creates debug file showing HTTP traces and exceptions | ||
``` | ||
|
||
### Info | ||
|
||
Displays information about the an assembly. | ||
|
||
``` | ||
usage: rf_assembly.py info [-h] | ||
optional arguments: | ||
-h, --help show this help message and exit | ||
``` | ||
|
||
The tool will log into the service specified by the *rhost* argument using the credentials provided by the *user* and *password* arguments. | ||
It will then get the assembly information from the URI specified by the *assembly* argument and displays its information. | ||
|
||
Example: | ||
|
||
``` | ||
$ rf_assembly.py -u root -p root -r https://192.168.1.100 -a /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Assembly info | ||
0 | Contoso Power Supply | ||
| Model: 345TTT | ||
| PartNumber: 923943 | ||
| SerialNumber: 345394834 | ||
| Producer: Contoso Supply Co. | ||
| Vendor: Contoso | ||
| ProductionDate: 2017-04-01T14:55:33+03:00 | ||
``` | ||
|
||
### Download | ||
|
||
Downloads assembly data to a file. | ||
|
||
``` | ||
usage: rf_assembly.py download [-h] --file FILE | ||
required arguments: | ||
--file FILE, -f FILE The file, and optional path, to save the assembly data | ||
optional arguments: | ||
-h, --help show this help message and exit | ||
``` | ||
|
||
The tool will log into the service specified by the *rhost* argument using the credentials provided by the *user* and *password* arguments. | ||
It will then get the assembly information from the URI specified by the *assembly* argument and download the binary data contents to the file specified by the *file* argument. | ||
|
||
``` | ||
$ rf_assembly.py -u root -p root -r https://192.168.1.100 -a /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Assembly download -f data.bin | ||
Saving data to 'data.bin'... | ||
``` | ||
|
||
### Upload | ||
|
||
Uploads assembly data from a file. | ||
|
||
``` | ||
usage: rf_assembly.py upload [-h] --file FILE | ||
required arguments: | ||
--file FILE, -f FILE The file, and optional path, containing the assembly | ||
data to upload | ||
optional arguments: | ||
-h, --help show this help message and exit | ||
``` | ||
|
||
The tool will log into the service specified by the *rhost* argument using the credentials provided by the *user* and *password* arguments. | ||
It will then get the assembly information from the URI specified by the *assembly* argument and upload the contents of the file specified by the *file* argument to the binary data. | ||
|
||
``` | ||
$ rf_assembly.py -u root -p root -r https://192.168.1.100 -a /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Assembly upload -f data.bin | ||
Writing data from 'data.bin'... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
#! /usr/bin/python | ||
# Copyright Notice: | ||
# Copyright 2019-2024 DMTF. All rights reserved. | ||
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Tacklebox/blob/main/LICENSE.md | ||
|
||
""" | ||
Assembly Module | ||
File : assembly.py | ||
Brief : This file contains the definitions and functionalities for managing | ||
assemblies on a Redfish service | ||
""" | ||
|
||
from .messages import verify_response | ||
|
||
class RedfishAssemblyNotFoundError( Exception ): | ||
""" | ||
Raised when an assembly index cannot be found | ||
""" | ||
pass | ||
|
||
class RedfishAssemblyNoBinaryDataError( Exception ): | ||
""" | ||
Raised when an assembly index does not contain binary data | ||
""" | ||
pass | ||
|
||
def get_assembly( context, uri ): | ||
""" | ||
Collects assembly information from a Redfish service | ||
Args: | ||
context: The Redfish client object with an open session | ||
uri: The URI of the assembly to get | ||
Returns: | ||
A list containing all assemblies from the URI | ||
""" | ||
|
||
# Get the assembly | ||
assembly = context.get( uri ) | ||
verify_response( assembly ) | ||
return assembly.dict.get( "Assemblies", [] ) | ||
|
||
def print_assembly( assemblies, index = None ): | ||
""" | ||
Prints assembly information into a table | ||
Args: | ||
assemblies: An array of assembly information to print | ||
index: If specified, prints only the desired index | ||
""" | ||
|
||
assembly_format_header = " {:5s} | {} {}" | ||
assembly_format = " {:5s} | {}: {}" | ||
assembly_properties = [ "Model", "PartNumber", "SparePartNumber", "SKU", "SerialNumber", "Producer", "Vendor", | ||
"ProductionDate", "Version", "EngineeringChangeLevel" ] | ||
|
||
# If an index is specified, isolate to the one index | ||
if index is not None: | ||
if index < 0 or index >= len( assemblies ): | ||
raise RedfishAssemblyNotFoundError( "Assembly contains {} entries; index {} is not valid".format( len( assemblies ), index ) ) | ||
assemblies = [ assemblies[index] ] | ||
|
||
# Go through each assembly | ||
for assembly in assemblies: | ||
# Print the heading | ||
heading_details = [] | ||
state = assembly.get( "Status", {} ).get( "State" ) | ||
if state: | ||
heading_details.append( state ) | ||
health = assembly.get( "Status", {} ).get( "Health" ) | ||
if health: | ||
heading_details.append( health ) | ||
heading_details = ", ".join( heading_details ) | ||
if len( heading_details ) != 0: | ||
heading_details = "(" + heading_details + ")" | ||
print( assembly_format_header.format( assembly["MemberId"], assembly["Name"], heading_details ) ) | ||
|
||
# Print any of the found properties | ||
for property in assembly_properties: | ||
if property in assembly: | ||
print( assembly_format.format( "", property, assembly[property] ) ) | ||
|
||
def download_assembly( context, assemblies, filepath, index = None ): | ||
""" | ||
Downloads the binary data of an assembly to a file | ||
Args: | ||
context: The Redfish client object with an open session | ||
assemblies: An array of assembly information | ||
filepath: The filepath to download the binary data | ||
index: The index into the assemblies array to download; if None, perform on index 0 if there's only 1 assembly | ||
""" | ||
|
||
# Get the binary data URI | ||
binary_data_uri = get_assembly_binary_data_uri( assemblies, index ) | ||
|
||
# Download the data and save it | ||
response = context.get( binary_data_uri ) | ||
verify_response( response ) | ||
with open( filepath, "wb" ) as binary_file: | ||
binary_file.write( response.read ) | ||
|
||
def upload_assembly( context, assemblies, filepath, index = None ): | ||
""" | ||
Uploads the binary data of a file to an assembly | ||
Args: | ||
context: The Redfish client object with an open session | ||
assemblies: An array of assembly information | ||
filepath: The filepath of the binary data to upload | ||
index: The index into the assemblies array to upload; if None, perform on index 0 if there's only 1 assembly | ||
""" | ||
|
||
# Get the binary data URI | ||
binary_data_uri = get_assembly_binary_data_uri( assemblies, index ) | ||
|
||
# Upload the binary data | ||
with open( filepath, "rb" ) as binary_file: | ||
data = binary_file.read() | ||
response = context.put( binary_data_uri, body = data ) | ||
verify_response( response ) | ||
|
||
def get_assembly_binary_data_uri( assemblies, index = None ): | ||
""" | ||
Locates the binary data URI for a target assembly | ||
Args: | ||
assemblies: An array of assembly information | ||
index: The index into the assemblies array to download; if None, perform on index 0 if there's only 1 assembly | ||
Returns: | ||
A string containing the binary data URI | ||
""" | ||
|
||
# If an index is specified, isolate to the one index | ||
if index is None: | ||
index = 0 | ||
if len( assemblies ) != 1: | ||
raise RedfishAssemblyNotFoundError( "Assembly contains {} entries; an index needs to be specified".format( len( assemblies ) ) ) | ||
else: | ||
if index < 0 or index >= len( assemblies ): | ||
raise RedfishAssemblyNotFoundError( "Assembly contains {} entries; index {} is not valid".format( len( assemblies ), index ) ) | ||
|
||
# Get the binary data URI | ||
binary_data_uri = assemblies[index].get( "BinaryDataURI" ) | ||
if binary_data_uri is None: | ||
# No binary data | ||
raise RedfishAssemblyNoBinaryDataError( "Assembly index {} does not contain binary data".format( index ) ) | ||
return binary_data_uri |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#! /usr/bin/python | ||
# Copyright Notice: | ||
# Copyright 2019-2024 DMTF. All rights reserved. | ||
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Tacklebox/blob/main/LICENSE.md | ||
|
||
""" | ||
Redfish Assembly | ||
File : rf_assembly.py | ||
Brief : This script uses the redfish_utilities module to manage assemblies | ||
""" | ||
|
||
import argparse | ||
import datetime | ||
import logging | ||
import redfish | ||
import redfish_utilities | ||
import traceback | ||
import sys | ||
from redfish.messages import RedfishPasswordChangeRequiredError | ||
|
||
# Get the input arguments | ||
argget = argparse.ArgumentParser( description = "A tool to manage assemblies on a Redfish service" ) | ||
argget.add_argument( "--user", "-u", type = str, required = True, help = "The user name for authentication" ) | ||
argget.add_argument( "--password", "-p", type = str, required = True, help = "The password for authentication" ) | ||
argget.add_argument( "--rhost", "-r", type = str, required = True, help = "The address of the Redfish service (with scheme)" ) | ||
argget.add_argument( "--assembly", "-a", type = str, required = True, help = "The URI of the target assembly" ) | ||
argget.add_argument( "--index", "-i", type = int, help = "The target assembly index" ) | ||
argget.add_argument( "--debug", action = "store_true", help = "Creates debug file showing HTTP traces and exceptions" ) | ||
subparsers = argget.add_subparsers( dest = "command" ) | ||
info_argget = subparsers.add_parser( "info", help = "Displays information about the an assembly" ) | ||
download_argget = subparsers.add_parser( "download", help = "Downloads assembly data to a file" ) | ||
download_argget.add_argument( "--file", "-f", type = str, required = True, help = "The file, and optional path, to save the assembly data" ) | ||
upload_argget = subparsers.add_parser( "upload", help = "Uploads assembly data from a file" ) | ||
upload_argget.add_argument( "--file", "-f", type = str, required = True, help = "The file, and optional path, containing the assembly data to upload" ) | ||
args = argget.parse_args() | ||
|
||
if args.index and args.index < 0: | ||
print( "rf_assembly.py: error: the assembly index cannot be negative" ) | ||
sys.exit( 1 ) | ||
|
||
if args.debug: | ||
log_file = "rf_assembly-{}.log".format( datetime.datetime.now().strftime( "%Y-%m-%d-%H%M%S" ) ) | ||
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" | ||
logger = redfish.redfish_logger( log_file, log_format, logging.DEBUG ) | ||
logger.info( "rf_assembly Trace" ) | ||
|
||
# Set up the Redfish object | ||
redfish_obj = None | ||
try: | ||
redfish_obj = redfish.redfish_client( base_url = args.rhost, username = args.user, password = args.password, timeout = 15, max_retry = 3 ) | ||
redfish_obj.login( auth = "session" ) | ||
except RedfishPasswordChangeRequiredError as e: | ||
redfish_utilities.print_password_change_required_and_logout( redfish_obj, args ) | ||
sys.exit( 1 ) | ||
except Exception as e: | ||
raise | ||
|
||
exit_code = 0 | ||
try: | ||
assembly_info = redfish_utilities.get_assembly( redfish_obj, args.assembly ) | ||
if args.command == "download": | ||
print( "Saving data to '{}'...".format( args.file ) ) | ||
redfish_utilities.download_assembly( redfish_obj, assembly_info, args.file, args.index ) | ||
elif args.command == "upload": | ||
print( "Writing data from '{}'...".format( args.file) ) | ||
redfish_utilities.upload_assembly( redfish_obj, assembly_info, args.file, args.index ) | ||
else: | ||
redfish_utilities.print_assembly( assembly_info, args.index ) | ||
except Exception as e: | ||
if args.debug: | ||
logger.error( "Caught exception:\n\n{}\n".format( traceback.format_exc() ) ) | ||
exit_code = 1 | ||
print( e ) | ||
finally: | ||
# Log out | ||
redfish_utilities.logout( redfish_obj ) | ||
sys.exit( exit_code ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters