From 76b5777e6b789d59f6e77d680dccb808b112e1b8 Mon Sep 17 00:00:00 2001 From: Mike Raineri Date: Fri, 9 Jul 2021 08:52:57 -0400 Subject: [PATCH 1/2] Added tests for verifying contents of Ethernet interfaces for managers --- .../manager_ethernet_interface_check.py | 170 ++++++++++++++++++ manager_ethernet_interface/test_conf.json | 5 + manager_ethernet_interface/toolspath.py | 11 ++ requirements.txt | 2 +- 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 manager_ethernet_interface/manager_ethernet_interface_check.py create mode 100644 manager_ethernet_interface/test_conf.json create mode 100644 manager_ethernet_interface/toolspath.py diff --git a/manager_ethernet_interface/manager_ethernet_interface_check.py b/manager_ethernet_interface/manager_ethernet_interface_check.py new file mode 100644 index 0000000..2cb58f2 --- /dev/null +++ b/manager_ethernet_interface/manager_ethernet_interface_check.py @@ -0,0 +1,170 @@ +#! /usr/bin/python3 +# Copyright Notice: +# Copyright 2021 DMTF. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Usecase-Checkers/blob/master/LICENSE.md + +""" +Manager Ethernet Interface Usecase Test + +File : manager_ethernet_interface_check.py + +Brief : This file contains the definitions and functionalities for performing + the usecase test for verifying Ethernet interfaces of the managers +""" + +import argparse +import sys +import time + +import redfish +import redfish_utilities + +import toolspath +from usecase.results import Results + +def dummy_address_check( address ): + """ + Determines if values contain dummy addresses + + Args: + address: Dictionary, list, or string containing addresses + + Returns: + True if any of the data contains a dummy address; False otherwise + """ + + dummy_addresses = [ "", "0.0.0.0", "::" ] + + if isinstance( address, dict ): + # Go through each property and check the value + for property in address: + if dummy_address_check( address[property] ): + return True + elif isinstance( address, list ): + # Go through each index and check the value + for value in address: + if dummy_address_check( value ): + return True + elif isinstance( address, str ): + if address in dummy_addresses: + return True + + return False + +if __name__ == '__main__': + + # Get the input arguments + argget = argparse.ArgumentParser( description = "Usecase checker for one time boot" ) + 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" ) + argget.add_argument( "--Secure", "-S", type = str, default = "Always", help = "When to use HTTPS (Always, IfSendingCredentials, IfLoginOrAuthenticatedApi, Never)" ) + argget.add_argument( "--directory", "-d", type = str, default = None, help = "Output directory for results.json" ) + args = argget.parse_args() + + # Set up the Redfish object + base_url = "https://" + args.rhost + if args.Secure == "Never": + base_url = "http://" + args.rhost + with redfish.redfish_client( base_url = base_url, username = args.user, password = args.password ) as redfish_obj: + # Create the results object + service_root = redfish_obj.get( "/redfish/v1/" ) + results = Results( "Manager Ethernet Interface", service_root.dict ) + if args.directory is not None: + results.set_output_dir( args.directory ) + + # Get the available managers + test_managers = redfish_utilities.get_manager_ids( redfish_obj ) + manager_count = len( test_managers ) + print( "Found {} manager instances".format( manager_count ) ) + if manager_count == 0: + results.update_test_results( "Manager Count", 1, "No manager instances were found" ) + else: + results.update_test_results( "Manager Count", 0, None ) + + # Go through each manager and test each of its Ethernet interfaces + for manager in test_managers: + # Get the available Ethernet interfaces + test_interfaces = redfish_utilities.get_manager_ethernet_interface_ids( redfish_obj, manager ) + interface_count = len( test_interfaces ) + print( "Found {} Ethernet interface instances in manager '{}'".format( interface_count, manager ) ) + if interface_count == 0: + results.update_test_results( "Ethernet Interface Count", 1, "No Ethernet interface instances were found in manager '{}'".format( manager ) ) + else: + results.update_test_results( "Ethernet Interface Count", 0, None ) + + # Go through each Ethernet interface and test the response payloads + for interface in test_interfaces: + print( "Testing interface '{}'".format( interface ) ) + interface_resp = redfish_utilities.get_manager_ethernet_interface( redfish_obj, manager, interface ) + + # Check VLAN properties + if "VLAN" in interface_resp.dict: + property_check_list = [ "VLANEnable", "VLANId", "VLANPriority", "Tagged" ] + req_property_check_list = [ "VLANEnable", "VLANId" ] + for property in property_check_list: + # Check if the property is null + if property in interface_resp.dict["VLAN"]: + if interface_resp.dict["VLAN"][property] is None: + results.update_test_results( "Null Usage", 1, "'{}' contains null values in manager '{}' interface '{}'".format( property, manager, interface ) ) + else: + results.update_test_results( "Null Usage", 0, None ) + + # Check if the property is expected + if property in req_property_check_list: + if property in interface_resp.dict["VLAN"]: + results.update_test_results( "Expected Properties", 0, None ) + else: + results.update_test_results( "Expected Properties", 1, None, "VLAN does not contain {} in manager '{}' interface '{}'".format( property, manager, interface ) ) + + # Check usage of name servers + property_check_list = [ "NameServers", "StaticNameServers", "IPv4Addresses", "IPv4StaticAddresses", "IPv6Addresses", "IPv6StaticAddresses", "IPv6DefaultGateway", "IPv6StaticDefaultGateways" ] + property_status_list = [ "NameServers", "IPv4Addresses", "IPv6Addresses" ] + property_ip_list = [ "IPv4Addresses", "IPv4StaticAddresses", "IPv6Addresses", "IPv6StaticAddresses", "IPv6StaticDefaultGateways" ] + for property in property_check_list: + if property in interface_resp.dict: + # Status properties have an additional check to ensure null is not used; the array grows and shrinks based on what's active + if property in property_status_list: + if None in interface_resp.dict[property]: + results.update_test_results( "Null Usage", 1, "'{}' contains null values in manager '{}' interface '{}'".format( property, manager, interface ) ) + else: + results.update_test_results( "Null Usage", 0, None ) + + # Check that dummy addresses are not used + if dummy_address_check( interface_resp.dict[property] ): + results.update_test_results( "Dummy Value Usage", 1, "'{}' contains an empty string, 0.0.0.0, or :: rather than null in manager '{}' interface '{}'".format( property, manager, interface ) ) + else: + results.update_test_results( "Dummy Value Usage", 0, None ) + + # Check for expected IPv4 properties + if property in property_ip_list: + for i, address in enumerate( interface_resp.dict[property] ): + # Check that there is only a Gateway for index 0 + if "IPv4" in property: + if "Gateway" in address and i != 0: + results.update_test_results( "IPv4 Gateway", 1, "IPv4 gateway property found at non-first array index in manager '{}' interface '{}'".format( manager, interface ) ) + else: + results.update_test_results( "IPv4 Gateway", 0, None ) + + # Check for presence of properties + if "IPv4" in property: + ip_properties = [ "Gateway", "Address", "SubnetMask" ] + if "Static" not in property: + ip_properties.append( "AddressOrigin" ) + else: + ip_properties = [ "Address", "PrefixLength" ] + if "Static" not in property: + ip_properties.append( "AddressOrigin" ) + ip_properties.append( "AddressState" ) + for ip_property in ip_properties: + if ip_property == "Gateway" and i == 0: + continue + if ip_property not in address: + results.update_test_results( "Expected Properties", 1, None, "{} index {} does not contain {} in manager '{}' interface '{}'".format( property, i, ip_property, manager, interface ) ) + else: + results.update_test_results( "Expected Properties", 0, None ) + + # Save the results + results.write_results() + + sys.exit( results.get_return_code() ) diff --git a/manager_ethernet_interface/test_conf.json b/manager_ethernet_interface/test_conf.json new file mode 100644 index 0000000..10f334a --- /dev/null +++ b/manager_ethernet_interface/test_conf.json @@ -0,0 +1,5 @@ +{ + "test": { + "command": "$interpreter manager_ethernet_interface_check.py -r $target_system -u $username -p $password -S $https -d $output_subdir" + } +} diff --git a/manager_ethernet_interface/toolspath.py b/manager_ethernet_interface/toolspath.py new file mode 100644 index 0000000..5e19028 --- /dev/null +++ b/manager_ethernet_interface/toolspath.py @@ -0,0 +1,11 @@ +# Copyright Notice: +# Copyright 2021 Distributed Management Task Force, Inc. All rights reserved. +# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Usecase-Checkers/blob/master/LICENSE.md + +import os +import sys + +cur_dir = os.path.dirname( __file__ ) +path_dir = os.path.abspath( os.path.join( cur_dir, os.path.pardir ) ) +if path_dir not in sys.path: + sys.path.insert( 0, path_dir ) diff --git a/requirements.txt b/requirements.txt index 7453fd0..fb622d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ jsonschema redfish>=3.0.0 -redfish_utilities>=1.0.7 +redfish_utilities>=1.1.4 From 750374e90abf20255492276938055c99f95a1be5 Mon Sep 17 00:00:00 2001 From: Mike Raineri Date: Fri, 9 Jul 2021 08:59:44 -0400 Subject: [PATCH 2/2] Updated README to include new checker documentation --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f995a7..f543462 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ $ python3 power_thermal_test.py -r 127.0.0.1:8000 -u -p -S Always ### Power Control Checker -This checker logs into a specified service and traverses the systems collection. +This checker logs into a specified service and traverses the system collection. It will perform the following operations on all systems: * Reads the allowable `ResetType` parameter values * Performs a reset using each of the allowable `ResetType` values @@ -96,3 +96,18 @@ Example: ``` $ python3 query_parameters_check.py --r 127.0.0.1:8000 -u -p -S Always ``` + + +### Manager Ethernet Interface Checker + +This checker logs into a specified service and traverses the Ethernet interface collection in each manager found in the manager collection. +It will perform the following operations on all Ethernet interfaces: +* Inspects array properties to ensure `null` is used to show empty slots that a client is allowed to configure +* Inspects string properties containing IP addresses to ensure invalid addresses, such as `0.0.0.0`, are not used +* Inspects IPv4 address properties to ensure `Gateway` is only present in the first array index +* Ensures the minimum number of expected properties for configuring VLANs and IP addresses are present + +Example: +``` +$ python3 manager_ethernet_interface_check.py --r 127.0.0.1:8000 -u -p -S Always +``` \ No newline at end of file