Skip to content

Commit

Permalink
Merge pull request #104 from DMTF/Manager-ResetToDefaults
Browse files Browse the repository at this point in the history
Added resettodefaults subcommand to rf_manager_config.py
  • Loading branch information
mraineri authored Jul 7, 2023
2 parents be64ca6 + 55101b2 commit 8c0c285
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 13 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,12 @@ usage: rf_manager_config.py [-h] --user USER --password PASSWORD --rhost RHOST
A tool to manage managers in a service
positional arguments:
{info,reset,getnet,setnet}
{info,reset,getnet,setnet,resettodefaults}
info Displays information about a manager
reset Resets a manager
getnet Displays information about an Ethernet interface
setnet Configures an Ethernet interface
resettodefaults Resets a manager to default setting
required arguments:
--user USER, -u USER The user name for authentication
Expand Down Expand Up @@ -301,7 +302,6 @@ optional arguments:
--type {On,ForceOff,GracefulShutdown,GracefulRestart,ForceRestart,Nmi,ForceOn,PushPowerButton,PowerCycle}, -t {On,ForceOff,GracefulShutdown,GracefulRestart,ForceRestart,Nmi,ForceOn,PushPowerButton,PowerCycle}
The type of power/reset operation to perform
--info, -info Indicates if reset information should be reported
```

Example: `rf_manager_config.py -u root -p root -r https://192.168.1.100 reset -t GracefulRestart`
Expand Down Expand Up @@ -378,6 +378,30 @@ It will then locate the manager specified by the *manager* argument, locate the
* If *id* is not specified, and if the manager has exactly one Ethernet interface, it will perform the operation on the one interface.


#### Reset to Defaults

```
usage: rf_manager_config.py resettodefaults [-h]
[--type {ResetAll,PreserveNetworkAndUsers,PreserveNetwork}]
[--info]
optional arguments:
-h, --help show this help message and exit
--type {ResetAll,PreserveNetworkAndUsers,PreserveNetwork}, -t {ResetAll,PreserveNetworkAndUsers,PreserveNetwork}
The type of reset-to-defaults operation to perform
--info, -info Indicates if reset-to-defaults information should be
reported
```

Example: `rf_manager_config.py -u root -p root -r https://192.168.1.100 resettodefaults -t ResetAll`

The tool will log into the service specified by the *rhost* argument using the credentials provided by the *user* and *password* arguments.
It then traverses the manager collection for the service to find the matching system specified by the *manager* argument.
It will perform the `ResetToDefaults` action with the specified reset type from the *type* argument.
* If *manager* is not specified, and if the service has exactly one manager, it will perform the operation on the one manager.
* If *type* is not specified, it will attempt to perform the action with `PreserveNetworkAndUsers`.


### BIOS Settings

```
Expand Down
3 changes: 3 additions & 0 deletions redfish_utilities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@
from .managers import print_manager
from .managers import get_manager_reset_info
from .managers import manager_reset
from .managers import get_manager_reset_to_defaults_info
from .managers import manager_reset_to_defaults
from .managers import get_manager_ethernet_interface_ids
from .managers import get_manager_ethernet_interface
from .managers import set_manager_ethernet_interface
from .managers import print_manager_ethernet_interface
from .messages import print_error_payload
from .messages import verify_response
from .resets import reset_types
from .resets import reset_to_defaults_types
from .sensors import get_sensors
from .sensors import print_sensors
from .systems import get_system_ids
Expand Down
143 changes: 132 additions & 11 deletions redfish_utilities/managers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/python
# Copyright Notice:
# Copyright 2019-2021 DMTF. All rights reserved.
# Copyright 2019-2023 DMTF. All rights reserved.
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Tacklebox/blob/main/LICENSE.md

"""
Expand All @@ -12,9 +12,11 @@
with the managers collection for a given Redfish service
"""

import sys
from .collections import get_collection_ids
from .messages import verify_response
from .resets import reset_types
from .resets import reset_to_defaults_types

class RedfishManagerNotFoundError( Exception ):
"""
Expand All @@ -34,6 +36,12 @@ class RedfishManagerResetNotFoundError( Exception ):
"""
pass

class RedfishManagerResetToDefaultsNotFoundError( Exception ):
"""
Raised when the ResetToDefaults action cannot be found
"""
pass

def get_manager_ids( context ):
"""
Finds the manager collection and returns all of the member's identifiers
Expand All @@ -49,7 +57,7 @@ def get_manager_ids( context ):
service_root = context.get( "/redfish/v1/" )
if "Managers" not in service_root.dict:
# No manager collection
raise RedfishManagerNotFoundError( "Service does not contain a manager collection" )
raise RedfishManagerNotFoundError( "The service does not contain a manager collection" )

# Get the manager collection and iterate through its collection
return get_collection_ids( context, service_root.dict["Managers"]["@odata.id"] )
Expand Down Expand Up @@ -78,13 +86,13 @@ def get_manager( context, manager_id = None ):
if len( avail_managers ) == 1:
manager = context.get( manager_uri_pattern.format( avail_managers[0] ) )
else:
raise RedfishManagerNotFoundError( "Service does not contain exactly one manager; a target manager needs to be specified: {}".format( ", ".join( avail_managers ) ) )
raise RedfishManagerNotFoundError( "The service does not contain exactly one manager; a target manager needs to be specified: {}".format( ", ".join( avail_managers ) ) )

# Check the response and return the manager if the response is good
if manager.status == 404:
if avail_managers is None:
avail_managers = get_manager_ids( context )
raise RedfishManagerNotFoundError( "Service does not contain a manager called {}; valid managers: {}".format( manager_id, ", ".join( avail_managers ) ) )
raise RedfishManagerNotFoundError( "The service does not contain a manager called {}; valid managers: {}".format( manager_id, ", ".join( avail_managers ) ) )
verify_response( manager )
return manager

Expand Down Expand Up @@ -121,24 +129,24 @@ def get_manager_reset_info( context, manager_id = None, manager = None ):
Returns:
The URI of the Reset action
A list of parameter requirements from the Action Info
A list of parameter requirements from the action info
"""

if manager is None:
manager = get_manager( context, manager_id )

# Check that there is a Reset action
if "Actions" not in manager.dict:
raise RedfishManagerResetNotFoundError( "Manager does not support Reset" )
raise RedfishManagerResetNotFoundError( "Manager {} does not support the Reset action".format( manager["Id"] ) )
if "#Manager.Reset" not in manager.dict["Actions"]:
raise RedfishManagerResetNotFoundError( "Manager does not support Reset" )
raise RedfishManagerResetNotFoundError( "Manager {} does not support the Reset action".format( manager["Id"] ) )

# Extract the info about the SimpleUpdate action
# Extract the info about the Reset action
reset_action = manager.dict["Actions"]["#Manager.Reset"]
reset_uri = reset_action["target"]

if "@Redfish.ActionInfo" not in reset_action:
# No Action Info; need to build this manually based on other annotations
# No action info; need to build this manually based on other annotations

# Default parameter requirements
reset_parameters = [
Expand All @@ -155,7 +163,7 @@ def get_manager_reset_info( context, manager_id = None, manager = None ):
if param["Name"] + "@Redfish.AllowableValues" in reset_action:
param["AllowableValues"] = reset_action[param["Name"] + "@Redfish.AllowableValues"]
else:
# Get the Action Info and its parameter listing
# Get the action info and its parameter listing
action_info = context.get( reset_action["@Redfish.ActionInfo"] )
reset_parameters = action_info.dict["Parameters"]

Expand Down Expand Up @@ -200,7 +208,120 @@ def manager_reset( context, manager_id = None, reset_type = None ):

# Reset the manager
response = context.post( reset_uri, body = payload )
verify_response( response )
try:
verify_response( response )
except Exception as e:
additional_message = ""
if response.status == 400:
# Append the list of valid reset types to 400 Bad Request responses
additional_message = "\nNo supported reset types listed"
for param in reset_parameters:
if param["Name"] == "ResetType" and "AllowableValues" in param:
additional_message = "\nSupported reset types: {}".format( ", ".join( param["AllowableValues"] ) )
raise type( e )( str( e ) + additional_message ).with_traceback( sys.exc_info()[2] )
return response

def get_manager_reset_to_defaults_info( context, manager_id = None, manager = None ):
"""
Finds a manager matching the given ID and returns its reset-to-defaults info
Args:
context: The Redfish client object with an open session
manager_id: The manager to locate; if None, perform on the only manager
manager: Existing manager resource to inspect for reset info
Returns:
The URI of the Reset action
A list of parameter requirements from the action info
"""

if manager is None:
manager = get_manager( context, manager_id )

# Check that there is a Reset action
if "Actions" not in manager.dict:
raise RedfishManagerResetToDefaultsNotFoundError( "Manager {} does not support the ResetToDefaults action".format( manager["Id"] ) )
if "#Manager.ResetToDefaults" not in manager.dict["Actions"]:
raise RedfishManagerResetToDefaultsNotFoundError( "Manager {} does not support the ResetToDefaults action".format( manager["Id"] ) )

# Extract the info about the ResetToDefaults action
reset_action = manager.dict["Actions"]["#Manager.ResetToDefaults"]
reset_uri = reset_action["target"]

if "@Redfish.ActionInfo" not in reset_action:
# No action info; need to build this manually based on other annotations

# Default parameter requirements
reset_parameters = [
{
"Name": "ResetType",
"Required": True,
"DataType": "String",
"AllowableValues": reset_to_defaults_types
}
]

# Get the AllowableValues from annotations
for param in reset_parameters:
if param["Name"] + "@Redfish.AllowableValues" in reset_action:
param["AllowableValues"] = reset_action[param["Name"] + "@Redfish.AllowableValues"]
else:
# Get the action info and its parameter listing
action_info = context.get( reset_action["@Redfish.ActionInfo"] )
reset_parameters = action_info.dict["Parameters"]

return reset_uri, reset_parameters

def manager_reset_to_defaults( context, manager_id = None, reset_type = None ):
"""
Finds a manager matching the given ID and performs a reset-to-defaults
Args:
context: The Redfish client object with an open session
manager_id: The manager to locate; if None, perform on the only manager
reset_type: The type of reset to perform; if None, perform one of the common resets
Returns:
The response of the action
"""

# Check that the values themselves are supported by the schema
reset_to_default_type_values = reset_to_defaults_types
if reset_type is not None:
if reset_type not in reset_to_default_type_values:
raise ValueError( "{} is not an allowable reset type ({})".format( reset_type, ", ".join( reset_to_default_type_values ) ) )

# Locate the reset action
reset_uri, reset_parameters = get_manager_reset_to_defaults_info( context, manager_id )

# Build the payload
if reset_type is None:
for param in reset_parameters:
if param["Name"] == "ResetType":
if "PreserveNetworkAndUsers" in param["AllowableValues"]:
reset_type = "PreserveNetworkAndUsers"
elif "PreserveNetwork" in param["AllowableValues"]:
reset_type = "PreserveNetwork"
elif "ResetAll" in param["AllowableValues"]:
reset_type = "ResetAll"

payload = {}
if reset_type is not None:
payload["ResetType"] = reset_type

# Reset the manager to defaults
response = context.post( reset_uri, body = payload )
try:
verify_response( response )
except Exception as e:
additional_message = ""
if response.status == 400:
# Append the list of valid reset types to 400 Bad Request responses
additional_message = "\nNo supported reset types listed"
for param in reset_parameters:
if param["Name"] == "ResetType" and "AllowableValues" in param:
additional_message = "\nSupported reset types: {}".format( ", ".join( param["AllowableValues"] ) )
raise type( e )( str( e ) + additional_message ).with_traceback( sys.exc_info()[2] )
return response

def get_manager_ethernet_interface_ids( context, manager_id = None ):
Expand Down
2 changes: 2 additions & 0 deletions redfish_utilities/resets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@

reset_types = [ "On", "ForceOff", "GracefulShutdown", "GracefulRestart", "ForceRestart", "Nmi", "ForceOn",
"PushPowerButton", "PowerCycle", "Suspend", "Pause", "Resume" ]

reset_to_defaults_types = [ "ResetAll", "PreserveNetworkAndUsers", "PreserveNetwork" ]
18 changes: 18 additions & 0 deletions scripts/rf_manager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
setnet_argget.add_argument( "--vlan", "-vlan", type = str, help = "The VLAN enabled configuration to set", choices = [ "On", "Off" ] )
setnet_argget.add_argument( "--vlanid", "-vlanid", type = int, help = "The VLAN ID to set" )
setnet_argget.add_argument( "--vlanpriority", "-vlanpriority", type = int, help = "The VLAN priority to set" )
reset_to_defaults_argget = subparsers.add_parser( "resettodefaults", help = "Resets a manager to default settings" )
reset_to_defaults_argget.add_argument( "--type", "-t", type = str, help = "The type of reset-to-defaults operation to perform", choices = redfish_utilities.reset_to_defaults_types )
reset_to_defaults_argget.add_argument( "--info", "-info", action = "store_true", help = "Indicates if reset-to-defaults information should be reported" )
args = argget.parse_args()

if args.debug:
Expand Down Expand Up @@ -73,6 +76,21 @@
response = redfish_utilities.manager_reset( redfish_obj, args.manager, args.type )
response = redfish_utilities.poll_task_monitor( redfish_obj, response )
redfish_utilities.verify_response( response )
elif args.command == "resettodefaults":
if args.info:
reset_uri, reset_parameters = redfish_utilities.get_manager_reset_to_defaults_info( redfish_obj, args.manager )
printed_reset_types = False
for param in reset_parameters:
if param["Name"] == "ResetType" and "AllowableValues" in param:
print( "Supported reset types: {}".format( ", ".join( param["AllowableValues"] ) ) )
printed_reset_types = True
if not printed_reset_types:
print( "No reset information found" )
else:
print( "Resetting the manager to defaults..." )
response = redfish_utilities.manager_reset_to_defaults( redfish_obj, args.manager, args.type )
response = redfish_utilities.poll_task_monitor( redfish_obj, response )
redfish_utilities.verify_response( response )
elif args.command == "getnet":
interface = redfish_utilities.get_manager_ethernet_interface( redfish_obj, args.manager, args.id )
redfish_utilities.print_manager_ethernet_interface( interface )
Expand Down

0 comments on commit 8c0c285

Please sign in to comment.