diff options
Diffstat (limited to 'tools/hdv')
-rw-r--r-- | tools/hdv/redfish/__init__.py | 0 | ||||
-rw-r--r-- | tools/hdv/redfish/conf/cases.xlsx | bin | 0 -> 17263 bytes | |||
-rw-r--r-- | tools/hdv/redfish/conf/cases.yaml | 517 | ||||
-rw-r--r-- | tools/hdv/redfish/conf/config.yaml | 17 | ||||
-rw-r--r-- | tools/hdv/redfish/conf/depends.yaml | 33 | ||||
-rw-r--r-- | tools/hdv/redfish/conf/report.yaml | 831 | ||||
-rw-r--r-- | tools/hdv/redfish/docs/readme.md | 129 | ||||
-rw-r--r-- | tools/hdv/redfish/errors.py | 47 | ||||
-rw-r--r-- | tools/hdv/redfish/excel_2_yaml.py | 61 | ||||
-rw-r--r-- | tools/hdv/redfish/hdv.py | 59 | ||||
-rw-r--r-- | tools/hdv/redfish/hdv_redfish.py | 676 | ||||
-rw-r--r-- | tools/hdv/redfish/http_handler.py | 129 | ||||
-rw-r--r-- | tools/hdv/redfish/log_utils.py | 33 | ||||
-rw-r--r-- | tools/hdv/redfish/yaml_utils.py | 28 |
14 files changed, 2560 insertions, 0 deletions
diff --git a/tools/hdv/redfish/__init__.py b/tools/hdv/redfish/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/hdv/redfish/__init__.py diff --git a/tools/hdv/redfish/conf/cases.xlsx b/tools/hdv/redfish/conf/cases.xlsx Binary files differnew file mode 100644 index 0000000..e7fc61d --- /dev/null +++ b/tools/hdv/redfish/conf/cases.xlsx diff --git a/tools/hdv/redfish/conf/cases.yaml b/tools/hdv/redfish/conf/cases.yaml new file mode 100644 index 0000000..5609708 --- /dev/null +++ b/tools/hdv/redfish/conf/cases.yaml @@ -0,0 +1,517 @@ +--- +- case_name: set asset code + case_sn: 1 + expected_code: 200 + expected_result: '{"AssetTag": "CM_cc@1234"}' + group: asset managment + header: null + method: PATCH + request_body: '{"AssetTag": "CM_cc@1234"}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: get asset code + case_sn: 2 + expected_code: 200 + expected_result: '{"AssetTag": "CM_cc@1234"}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: set host name + case_sn: 3 + expected_code: 200 + expected_result: '{"HostName": "NFV-RPZJHZ-01B"}' + group: asset managment + header: null + method: PATCH + request_body: '{"HostName": "NFV-RPZJHZ-01B"}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check host name + case_sn: 4 + expected_code: 200 + expected_result: '{"HostName": "NFV-RPZJHZ-01B"}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check manufacturer + case_sn: 5 + expected_code: 200 + expected_result: '{"Manufacturer": "New H3C Technologies Co., Ltd."}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check model + case_sn: 6 + expected_code: 200 + expected_result: '{"Model": "UniServer R4900 G3"}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check serial number + case_sn: 7 + expected_code: 200 + expected_result: '{"SerialNumber": "N/A"}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check main board name + case_sn: 8 + expected_code: 200 + expected_result: '{"Oem":{"Mainboard": {"BoardName": "RS33M2C9S"}}}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check main board serial number + case_sn: 9 + expected_code: 200 + expected_result: '{"Oem": {"Mainboard": {"SerialNumber": "N/A"}}}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check BIOS version + case_sn: 10 + expected_code: 200 + expected_result: '{"BiosVersion": "2.00.35P01 V100R001B02D035SP01"}' + group: asset managment + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check CPU amount + case_sn: 11 + expected_code: 200 + expected_result: '{"Members@odata.count": 2}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Processors +- case_name: check CPU info + case_sn: 12 + expected_code: 200 + expected_result: '{ "count": 2, "Manufacturer": "Intel(R) Corporation", "MaxSpeedMHz": + 2300, "Model": "Intel(R) Xeon(R) Gold 5218N CPU @ 2.30GHz", "ProcessorArchitecture": + ["x86", "IA-64", "ARM", "MIPS", "OEM"], "Socket": [1, 2], "Status": { "Health": + "OK", "State": "Enabled" }, "TotalCores": 16, "TotalThreads": + 32}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{cpu_id} +- case_name: check memory mount + case_sn: 13 + expected_code: 200 + expected_result: '{"Members@odata.count": 12}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Memory +- case_name: check memory information + case_sn: 14 + expected_code: 200 + expected_result: '{ "count": 12, "BaseModuleType": "RDIMM", "CapacityMiB": + 32768, "DeviceLocator": "N/A", "Manufacturer": ["Hynix Semiconductor", "Micron"], "MemoryDeviceType": + "DDR4", "OperatingSpeedMhz": 2666, "PartNumber": ["HMA84GR7AFR4N-VK","36ASF4G72PZ-2G6D1"], "Status": + { "Health": "OK", "State": "Enabled" }}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{memory_id} +- case_name: check raid card amount + case_sn: 15 + expected_code: 200 + expected_result: '{"Members@odata.count": 1}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Storages +- case_name: check raid card information + case_sn: 16 + expected_code: 200 + expected_result: '{ "count": 1, "StorageControllers": [ { "FirmwareVersion": + "2.62", "Manufacturer": "H3C", "Model": "N/A", "Status": + { "Health": "OK", "State": "Enabled" } } ]}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{storage_id} +- case_name: check harddisk information + case_sn: 17 + expected_code: 200 + expected_result: '{ "count": 4, "CapacityBytes": [480102187008, 960193626112], "Location": + { "Info": "N/A", "InfoFormat": "DeviceName" }, "Manufacturer": + "ATA", "MediaType": "SSD", "Model": ["INTEL SSDSC2KB48", "INTEL SSDSC2KB96"], "Protocol": + "SATA", "Status": { "Health": "OK", "State": "Enabled" }}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{drives_id} +- case_name: check network interface adapter information + case_sn: 18 + expected_code: 200 + expected_result: '{ "count": 3, "Manufacturer": "Mellanox", "Model": "NIC-620F-B2-25Gb-2P-1-X", "Name": + ["PCIeSlot2", "PCIeSlot3", "PCIeSlot6"], "Oem": { "Public": { "CardModel": + "2*25GE", "RootBDF": ["0000:17:00.0", "0000:17:02.0", "0000:AE:02.0"], } }, "Status": + { "Health": "OK", "State": "Enabled" }}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{networkadapters_id} +- case_name: check network interface adapter port information + case_sn: 19 + expected_code: 200 + expected_result: '{ "count": 6, "AssociatedNetworkAddresses": [ "N/A" ], "Oem": + { "Public": { "BDF": "N/A", "PortType": "OpticalPort" } }, "PhysicalPortNumber": + ["1", "2"]}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{networkports_id} +- case_name: check fans information + case_sn: 20 + expected_code: 200 + expected_result: '{ "FanSummary": { "Count": 6 }, "Fans": [ { "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } }, { "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } } ],}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check power amount + case_sn: 21 + expected_code: 200 + expected_result: '{ "DeviceMaxNum": { "PowerSupplyNum": 2},}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check power detail info + case_sn: 22 + expected_code: 200 + expected_result: '{ "PowerControl": [ { "PowerConsumedWatts": + "N/A","Status":{ "Health": "OK", "State": "Enabled" } }, ], "PowerSupplies": + [ { "LineInputVoltage": "N/A", "MemberId": "1", "PowerCapacityWatts": + 800,"Status": { "Health": "OK", "State": "Enabled" } }, { "LineInputVoltage": + "N/A", "MemberId": "2", "PowerCapacityWatts": 800,"Status": + { "Health": "OK", "State": "Enabled" } } ],}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Power +- case_name: check logical dirve health status + case_sn: 23 + expected_code: 200 + expected_result: '{ "count": 2, "Name": "N/A", "Status": { "Health": + ["OK", "Critical"], "State": "Enabled" }}' + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{volume_id} +- case_name: check server temperature air intake + case_sn: 24 + expected_code: 200 + expected_result: '{ "Temperatures": [ { "Name": "INPUT_TEMP", "ReadingCelsius": + "N/A", } ]}' + group: sensor management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check cpu temperature + case_sn: 25 + expected_code: 200 + expected_result: '{ "Temperatures": [ { "Name": "INPUT_TEMP", "ReadingCelsius": + "N/A", }, { "Name": "CPU1_TEMP", "ReadingCelsius": + "N/A", }, { "Name": "CPU2_TEMP", "ReadingCelsius": + "N/A", }, ]}' + group: sensor management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check server power state + case_sn: 26 + expected_code: 200 + expected_result: '{"PowerState": "On"}' + group: power management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: remote power on server + case_sn: 27 + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + group: power management + header: null + method: POST + request_body: '{"ResetType": "On"}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote power off server + case_sn: 28 + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + group: power management + header: null + method: POST + request_body: '{"ResetType": "GracefulShutdown"}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote reset server + case_sn: 29 + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + group: power management + header: null + method: POST + request_body: '{"ResetType": "ForceRestart"}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote configure CPU in hyperthreading disabled + case_sn: 30 + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Disabled"}}' + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "ProcessorHyperThreading": "Disabled" }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: remote get CPU hyperthreading in disabled + case_sn: 31 + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Disabled"}}' + group: remote configure + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: remote configure CPU in hyperthreading enabled + case_sn: 32 + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Enabled"}}' + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "ProcessorHyperThreading": "Enabled" }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: remote get CPU hyperthreading in enabled + case_sn: 33 + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Enabled"}}' + group: remote configure + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: disable PXE mode + case_sn: 34 + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check IPV4 PXE mode in disabled + case_sn: 35 + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + group: remote configure + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: enable PXE mode + case_sn: 36 + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check ipv4 PXE mode in enabled + case_sn: 37 + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + group: remote configure + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: set boot type order + case_sn: 38 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order + case_sn: 39 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + group: remote interface management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: configure boot order + case_sn: 40 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order + case_sn: 41 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + group: remote interface management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: configure new boot PXE order first + case_sn: 42 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order PEX order first + case_sn: 43 + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + group: remote interface management + header: null + method: GET + request_body: null + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: check BMC Firmware version + case_sn: 44 + expected_code: 200 + expected_result: '{"count": 1, "FirmwareVersion": "1.30.11P01 HDM V100R001B03D011SP01"}' + group: remote interface management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{manager_id} +- case_name: change BMC account + case_sn: 45 + expected_code: 200 + expected_result: '{"UserName": "CM_cc@1234","RoleId": "Administrator",}' + group: remote interface management + header: null + method: PATCH + request_body: '{"UserName": "CM_cc@1234","Password": "1234@CM_cc","RoleId": "Administrator",}' + url: https://{bmc_ip}/redfish/v1/AccountService/Accounts/3 +- case_name: configure BMC ip in static, ipv4 + case_sn: 46 + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "Address": + "192.168.66.120", "AddressOrigin": "Static", "Gateway": + "192.168.66.1", "SubnetMask": "255.255.255.128" } ]}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "Address": "192.168.66.120", "AddressOrigin": + "Static", "Gateway": "192.168.66.1", "SubnetMask": "255.255.255.128" } ]}' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in DHCP, gateway and subnet mask ipv4 + case_sn: 47 + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "Address": + "192.168.66.120", "AddressOrigin": "DHCP", "Gateway": "192.168.66.1", "SubnetMask": + "255.255.255.128" } ]}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "AddressOrigin": "DHCP" } ]}' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in static, ipv4 + case_sn: 48 + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "AddressOrigin": + "DHCP", } ]}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "AddressOrigin": "DHCP" } ]}' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in static, ipv6 + case_sn: 49 + expected_code: 200 + expected_result: '{"count": 1, "IPv6Addresses": [ { "Address": + "N/A", "AddressOrigin": "N/A", "PrefixLength": 64 }, { "Address": + "2019::11", "AddressOrigin": "Static", "PrefixLength": 64 } ]}' + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv6Addresses": [ { "Address": "2019::11", "AddressOrigin": + "Static", "PrefixLength": 64 } ]}' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 diff --git a/tools/hdv/redfish/conf/config.yaml b/tools/hdv/redfish/conf/config.yaml new file mode 100644 index 0000000..b57b71c --- /dev/null +++ b/tools/hdv/redfish/conf/config.yaml @@ -0,0 +1,17 @@ +--- +bmc_ip: 172.29.160.22 +bmc_user: root +bmc_pwd: Huawei12#$ +system_id: 1 +chassis_id: 1 +attr_name: 3 +pro_seq: 4 +url_seq: 5 +req_header_seq: 6 +req_body_seq: 7 +expect_return_code_seq: 8 +expect_return_value_seq: 9 +return_code_seq: 10 +return_value_seq: 11 +detail_result: 12 +final_result: 13 diff --git a/tools/hdv/redfish/conf/depends.yaml b/tools/hdv/redfish/conf/depends.yaml new file mode 100644 index 0000000..eecdcd4 --- /dev/null +++ b/tools/hdv/redfish/conf/depends.yaml @@ -0,0 +1,33 @@ +--- +- component_id: cpu_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Processors +- component_id: memory_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Memory +- component_id: storage_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Storages +- component_id: drives_id + key_flags: Drives + pro_value: GET + url_value: https://{bmc_ip}{storage_id} +- component_id: networkadapters_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/NetworkAdapters +- component_id: networkports_id + key_flags: Controllers:Link:NetworkPorts + pro_value: GET + url_value: https://{bmc_ip}{networkadapters_id} +- component_id: volume_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}{storage_id}/Volumes +- component_id: manager_id + key_flags: Members + pro_value: GET + url_value: https://{bmc_ip}/redfish/v1/Managers diff --git a/tools/hdv/redfish/conf/report.yaml b/tools/hdv/redfish/conf/report.yaml new file mode 100644 index 0000000..fc15f65 --- /dev/null +++ b/tools/hdv/redfish/conf/report.yaml @@ -0,0 +1,831 @@ +- case_name: set asset code + case_sn: 1 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''AssetTag'': "Failure, expect value: CM_cc@1234, return value: Can''t + find the key AssetTag in return value"}]}' + expected_code: 200 + expected_result: '{"AssetTag": "CM_cc@1234"}' + final_rst: Failure + group: asset managment + header: null + method: PATCH + request_body: '{"AssetTag": "CM_cc@1234"}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: get asset code + case_sn: 2 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''AssetTag'': ''Success''}]}' + expected_code: 200 + expected_result: '{"AssetTag": "CM_cc@1234"}' + final_rst: Success + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: set host name + case_sn: 3 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''HostName'': "Failure, expect value: NFV-RPZJHZ-01B, return value: + Can''t find the key HostName in return value"}]}' + expected_code: 200 + expected_result: '{"HostName": "NFV-RPZJHZ-01B"}' + final_rst: Failure + group: asset managment + header: null + method: PATCH + request_body: '{"HostName": "NFV-RPZJHZ-01B"}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check host name + case_sn: 4 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''HostName'': "Failure, expect value: NFV-RPZJHZ-01B, return value: + Can''t find the key HostName in return value"}]}' + expected_code: 200 + expected_result: '{"HostName": "NFV-RPZJHZ-01B"}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check manufacturer + case_sn: 5 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Manufacturer'': ''Failure, expect value: New H3C Technologies Co., + Ltd., return value: Huawei''}]}' + expected_code: 200 + expected_result: '{"Manufacturer": "New H3C Technologies Co., Ltd."}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check model + case_sn: 6 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Model'': ''Failure, expect value: UniServer R4900 G3, return value: + RH2288H V3''}]}' + expected_code: 200 + expected_result: '{"Model": "UniServer R4900 G3"}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check serial number + case_sn: 7 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''SerialNumber'': ''Success''}]}' + expected_code: 200 + expected_result: '{"SerialNumber": "N/A"}' + final_rst: Success + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check main board name + case_sn: 8 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Oem'': "Failure, expect value: {''Mainboard'': {''BoardName'': + ''RS33M2C9S''}}, return value: Can''t find the key Oem in return value"}]}' + expected_code: 200 + expected_result: '{"Oem":{"Mainboard": {"BoardName": "RS33M2C9S"}}}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check main board serial number + case_sn: 9 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Oem'': "Failure, expect value: {''Mainboard'': {''SerialNumber'': + ''N/A''}}, return value: Can''t find the key Oem in return value"}]}' + expected_code: 200 + expected_result: '{"Oem": {"Mainboard": {"SerialNumber": "N/A"}}}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check BIOS version + case_sn: 10 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''BiosVersion'': ''Failure, expect value: 2.00.35P01 V100R001B02D035SP01, + return value: 3.63''}]}' + expected_code: 200 + expected_result: '{"BiosVersion": "2.00.35P01 V100R001B02D035SP01"}' + final_rst: Failure + group: asset managment + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: check CPU amount + case_sn: 11 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Members@odata.count'': ''Success''}]}' + expected_code: 200 + expected_result: '{"Members@odata.count": 2}' + final_rst: Success + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Processors +- case_name: check CPU info + case_sn: 12 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Success'', + ''Manufacturer'': ''Success'', ''MaxSpeedMHz'': ''Failure, expect value: 2300, + return value: 3500'', ''Model'': ''Failure, expect value: Intel(R) Xeon(R) Gold + 5218N CPU @ 2.30GHz, return value: Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz'', + ''ProcessorArchitecture'': ''Success'', ''Socket'': ''Failure, expect value: [1, + 2], return value: 0'', ''Status'': {''Health'': ''Success'', ''State'': ''Success''}, + ''TotalCores'': ''Failure, expect value: 16, return value: 8'', ''TotalThreads'': + ''Failure, expect value: 32, return value: 16''}, {''return_code'': ''Success'', + ''Manufacturer'': ''Success'', ''MaxSpeedMHz'': ''Failure, expect value: 2300, + return value: 3500'', ''Model'': ''Failure, expect value: Intel(R) Xeon(R) Gold + 5218N CPU @ 2.30GHz, return value: Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz'', + ''ProcessorArchitecture'': ''Success'', ''Socket'': ''Success'', ''Status'': {''Health'': + ''Success'', ''State'': ''Success''}, ''TotalCores'': ''Failure, expect value: + 16, return value: 8'', ''TotalThreads'': ''Failure, expect value: 32, return value: + 16''}]}' + expected_code: 200 + expected_result: '{ "count": 2, "Manufacturer": "Intel(R) Corporation", "MaxSpeedMHz": + 2300, "Model": "Intel(R) Xeon(R) Gold 5218N CPU @ 2.30GHz", "ProcessorArchitecture": + ["x86", "IA-64", "ARM", "MIPS", "OEM"], "Socket": [1, 2], "Status": { "Health": + "OK", "State": "Enabled" }, "TotalCores": 16, "TotalThreads": + 32}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200, 200]' + url: https://{bmc_ip}{cpu_id} +- case_name: check memory mount + case_sn: 13 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Members@odata.count'': ''Failure, expect value: 12, return value: + 4''}]}' + expected_code: 200 + expected_result: '{"Members@odata.count": 12}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Memory +- case_name: check memory information + case_sn: 14 + details_result: '{''count'': ''Failure, the actual num is 4'', ''info'': [{''return_code'': + ''Success'', ''BaseModuleType'': "Failure, expect value: RDIMM, return value: + Can''t find the key BaseModuleType in return value", ''CapacityMiB'': ''Success'', + ''DeviceLocator'': ''Success'', ''Manufacturer'': ''Success'', ''MemoryDeviceType'': + ''Success'', ''OperatingSpeedMhz'': ''Failure, expect value: 2666, return value: + 2400'', ''PartNumber'': "Failure, expect value: [''HMA84GR7AFR4N-VK'', ''36ASF4G72PZ-2G6D1''], + return value: Can''t find the key PartNumber in return value", ''Status'': {''Health'': + ''Success'', ''State'': ''Success''}}, {''return_code'': ''Success'', ''BaseModuleType'': + "Failure, expect value: RDIMM, return value: Can''t find the key BaseModuleType + in return value", ''CapacityMiB'': ''Success'', ''DeviceLocator'': ''Success'', + ''Manufacturer'': ''Success'', ''MemoryDeviceType'': ''Success'', ''OperatingSpeedMhz'': + ''Failure, expect value: 2666, return value: 2400'', ''PartNumber'': "Failure, + expect value: [''HMA84GR7AFR4N-VK'', ''36ASF4G72PZ-2G6D1''], return value: Can''t + find the key PartNumber in return value", ''Status'': {''Health'': ''Success'', + ''State'': ''Success''}}, {''return_code'': ''Success'', ''BaseModuleType'': "Failure, + expect value: RDIMM, return value: Can''t find the key BaseModuleType in return + value", ''CapacityMiB'': ''Success'', ''DeviceLocator'': ''Success'', ''Manufacturer'': + ''Success'', ''MemoryDeviceType'': ''Success'', ''OperatingSpeedMhz'': ''Failure, + expect value: 2666, return value: 2400'', ''PartNumber'': "Failure, expect value: + [''HMA84GR7AFR4N-VK'', ''36ASF4G72PZ-2G6D1''], return value: Can''t find the key + PartNumber in return value", ''Status'': {''Health'': ''Success'', ''State'': + ''Success''}}, {''return_code'': ''Success'', ''BaseModuleType'': "Failure, expect + value: RDIMM, return value: Can''t find the key BaseModuleType in return value", + ''CapacityMiB'': ''Success'', ''DeviceLocator'': ''Success'', ''Manufacturer'': + ''Success'', ''MemoryDeviceType'': ''Success'', ''OperatingSpeedMhz'': ''Failure, + expect value: 2666, return value: 2400'', ''PartNumber'': "Failure, expect value: + [''HMA84GR7AFR4N-VK'', ''36ASF4G72PZ-2G6D1''], return value: Can''t find the key + PartNumber in return value", ''Status'': {''Health'': ''Success'', ''State'': + ''Success''}}]}' + expected_code: 200 + expected_result: '{ "count": 12, "BaseModuleType": "RDIMM", "CapacityMiB": + 32768, "DeviceLocator": "N/A", "Manufacturer": ["Hynix Semiconductor", "Micron"], "MemoryDeviceType": + "DDR4", "OperatingSpeedMhz": 2666, "PartNumber": ["HMA84GR7AFR4N-VK","36ASF4G72PZ-2G6D1"], "Status": + { "Health": "OK", "State": "Enabled" }}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200, 200, 200, 200]' + url: https://{bmc_ip}{memory_id} +- case_name: check raid card amount + case_sn: 15 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Members@odata.count'': ''Success''}]}' + expected_code: 200 + expected_result: '{"Members@odata.count": 1}' + final_rst: Success + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Storages +- case_name: check raid card information + case_sn: 16 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Success'', + ''StorageControllers'': [{''FirmwareVersion'': ''2.62'', ''Manufacturer'': ''H3C'', + ''Model'': ''N/A'', ''Status'': {''Health'': ''Success'', ''State'': ''Success''}}]}]}' + expected_code: 200 + expected_result: '{ "count": 1, "StorageControllers": [ { "FirmwareVersion": + "2.62", "Manufacturer": "H3C", "Model": "N/A", "Status": + { "Health": "OK", "State": "Enabled" } } ]}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}{storage_id} +- case_name: check harddisk information + case_sn: 17 + details_result: '{''count'': ''Failure, the actual num is 6'', ''info'': [{''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}, {''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}, {''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}, {''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}, {''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}, {''return_code'': + ''Success'', ''CapacityBytes'': ''Failure, expect value: [480102187008, 960193626112], + return value: None'', ''Location'': {''Info'': ''N/A'', ''InfoFormat'': ''DeviceName''}, + ''Manufacturer'': ''ATA'', ''MediaType'': ''SSD'', ''Model'': "Failure, expect + value: [''INTEL SSDSC2KB48'', ''INTEL SSDSC2KB96''], return value: None", ''Protocol'': + ''Success'', ''Status'': {''Health'': ''Success'', ''State'': ''Enabled''}}]}' + expected_code: 200 + expected_result: '{ "count": 4, "CapacityBytes": [480102187008, 960193626112], "Location": + { "Info": "N/A", "InfoFormat": "DeviceName" }, "Manufacturer": + "ATA", "MediaType": "SSD", "Model": ["INTEL SSDSC2KB48", "INTEL SSDSC2KB96"], "Protocol": + "SATA", "Status": { "Health": "OK", "State": "Enabled" }}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200, 200, 200, 200, 200, 200]' + url: https://{bmc_ip}{drives_id} +- case_name: check network interface adapter information + case_sn: 18 + details_result: N/A + expected_code: 200 + expected_result: '{ "count": 3, "Manufacturer": "Mellanox", "Model": "NIC-620F-B2-25Gb-2P-1-X", "Name": + ["PCIeSlot2", "PCIeSlot3", "PCIeSlot6"], "Oem": { "Public": { "CardModel": + "2*25GE", "RootBDF": ["0000:17:00.0", "0000:17:02.0", "0000:AE:02.0"], } }, "Status": + { "Health": "OK", "State": "Enabled" }}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{networkadapters_id} +- case_name: check network interface adapter port information + case_sn: 19 + details_result: N/A + expected_code: 200 + expected_result: '{ "count": 6, "AssociatedNetworkAddresses": [ "N/A" ], "Oem": + { "Public": { "BDF": "N/A", "PortType": "OpticalPort" } }, "PhysicalPortNumber": + ["1", "2"]}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{networkports_id} +- case_name: check fans information + case_sn: 20 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''FanSummary'': "Failure, expect value: {''Count'': 6}, return value: + Can''t find the key FanSummary in return value", ''Fans'': [{''MemberId'': ''Success'', + ''Oem'': {''Public'': {''SpeedRatio'': ''N/A''}}, ''Status'': {''Health'': ''Success'', + ''State'': ''Success''}}, {''MemberId'': ''Success'', ''Oem'': {''Public'': {''SpeedRatio'': + ''N/A''}}, ''Status'': {''Health'': ''Success'', ''State'': ''Success''}}, {''MemberId'': + ''Success'', ''Oem'': {''Public'': {''SpeedRatio'': ''N/A''}}, ''Status'': {''Health'': + ''Success'', ''State'': ''Success''}}, {''MemberId'': ''Success'', ''Oem'': {''Public'': + {''SpeedRatio'': ''N/A''}}, ''Status'': {''Health'': ''Success'', ''State'': ''Success''}}, + {''MemberId'': ''Success'', ''Oem'': {''Public'': {''SpeedRatio'': ''N/A''}}, + ''Status'': {''Health'': ''Success'', ''State'': ''Success''}}, {''MemberId'': + ''Success'', ''Oem'': {''Public'': {''SpeedRatio'': ''N/A''}}, ''Status'': {''Health'': + ''Success'', ''State'': ''Success''}}]}]}' + expected_code: 200 + expected_result: '{ "FanSummary": { "Count": 6 }, "Fans": [ { "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } }, { "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } },{ "MemberId": + "N/A", "Oem": { "Public": { "SpeedRatio": + "N/A" } }, "Status": { "Health": + "OK", "State": "Enabled" } } ],}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check power amount + case_sn: 21 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''DeviceMaxNum'': "Failure, expect value: {''PowerSupplyNum'': 2}, + return value: Can''t find the key DeviceMaxNum in return value"}]}' + expected_code: 200 + expected_result: '{ "DeviceMaxNum": { "PowerSupplyNum": 2},}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id} +- case_name: check power detail info + case_sn: 22 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''PowerControl'': [{''PowerConsumedWatts'': ''Success'', ''Status'': + {''Health'': ''OK'', ''State'': ''Enabled''}}], ''PowerSupplies'': [{''LineInputVoltage'': + ''Success'', ''MemberId'': ''Failure, expect value: 1, return value: 0'', ''PowerCapacityWatts'': + ''Failure, expect value: 800, return value: 460'', ''Status'': {''Health'': ''Success'', + ''State'': ''Success''}}, {''LineInputVoltage'': ''Success'', ''MemberId'': ''Failure, + expect value: 2, return value: 1'', ''PowerCapacityWatts'': ''Failure, expect + value: 800, return value: 460'', ''Status'': {''Health'': ''Failure, expect value: + OK, return value: Critical'', ''State'': ''Success''}}]}]}' + expected_code: 200 + expected_result: '{ "PowerControl": [ { "PowerConsumedWatts": + "N/A","Status":{ "Health": "OK", "State": "Enabled" } }, ], "PowerSupplies": + [ { "LineInputVoltage": "N/A", "MemberId": "1", "PowerCapacityWatts": + 800,"Status": { "Health": "OK", "State": "Enabled" } }, { "LineInputVoltage": + "N/A", "MemberId": "2", "PowerCapacityWatts": 800,"Status": + { "Health": "OK", "State": "Enabled" } } ],}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Power +- case_name: check logical dirve health status + case_sn: 23 + details_result: N/A + expected_code: 200 + expected_result: '{ "count": 2, "Name": "N/A", "Status": { "Health": + ["OK", "Critical"], "State": "Enabled" }}' + final_rst: Failure + group: compoment management + header: null + method: GET + request_body: null + url: https://{bmc_ip}{volume_id} +- case_name: check server temperature air intake + case_sn: 24 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Temperatures'': [{''Name'': ''Failure, expect value: INPUT_TEMP, + return value: Inlet Temp'', ''ReadingCelsius'': ''Success''}]}]}' + expected_code: 200 + expected_result: '{ "Temperatures": [ { "Name": "INPUT_TEMP", "ReadingCelsius": + "N/A", } ]}' + final_rst: Failure + group: sensor management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check cpu temperature + case_sn: 25 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''Temperatures'': [{''Name'': ''Failure, expect value: INPUT_TEMP, + return value: Inlet Temp'', ''ReadingCelsius'': ''Success''}, {''Name'': ''Failure, + expect value: CPU1_TEMP, return value: Outlet Temp'', ''ReadingCelsius'': ''Success''}, + {''Name'': ''Failure, expect value: CPU2_TEMP, return value: PCH Temp'', ''ReadingCelsius'': + ''N/A''}]}]}' + expected_code: 200 + expected_result: '{ "Temperatures": [ { "Name": "INPUT_TEMP", "ReadingCelsius": + "N/A", }, { "Name": "CPU1_TEMP", "ReadingCelsius": + "N/A", }, { "Name": "CPU2_TEMP", "ReadingCelsius": + "N/A", }, ]}' + final_rst: Failure + group: sensor management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Chassis/{chassis_id}/Thermal +- case_name: check server power state + case_sn: 26 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Success'', ''PowerState'': ''Failure, expect value: On, return value: Off''}]}' + expected_code: 200 + expected_result: '{"PowerState": "On"}' + final_rst: Failure + group: power management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id} +- case_name: remote power on server + case_sn: 27 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + final_rst: Failure + group: power management + header: null + method: POST + request_body: '{"ResetType": "On"}' + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote power off server + case_sn: 28 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + final_rst: Failure + group: power management + header: null + method: POST + request_body: '{"ResetType": "GracefulShutdown"}' + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote reset server + case_sn: 29 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{"error":{"@Message.ExtendedInfo": [{"Message": "Successfully + Completed Request", "Severity":"OK"}]}}' + final_rst: Failure + group: power management + header: null + method: POST + request_body: '{"ResetType": "ForceRestart"}' + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset +- case_name: remote configure CPU in hyperthreading disabled + case_sn: 30 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''ProcessorHyperThreading'': + ''Disabled''}, return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Disabled"}}' + final_rst: Failure + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "ProcessorHyperThreading": "Disabled" }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: remote get CPU hyperthreading in disabled + case_sn: 31 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Disabled"}}' + final_rst: Failure + group: remote configure + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: remote configure CPU in hyperthreading enabled + case_sn: 32 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''ProcessorHyperThreading'': + ''Enabled''}, return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Enabled"}}' + final_rst: Failure + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "ProcessorHyperThreading": "Enabled" }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: remote get CPU hyperthreading in enabled + case_sn: 33 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{"Attributes": {"ProcessorHyperThreading": "Enabled"}}' + final_rst: Failure + group: remote configure + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: disable PXE mode + case_sn: 34 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''IPv4PXESupport'': ''Disabled''}, + return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + final_rst: Failure + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check IPV4 PXE mode in disabled + case_sn: 35 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Disabled" }}' + final_rst: Failure + group: remote configure + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: enable PXE mode + case_sn: 36 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''IPv4PXESupport'': ''Enabled''}, + return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + final_rst: Failure + group: remote configure + header: null + method: PATCH + request_body: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check ipv4 PXE mode in enabled + case_sn: 37 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "IPv4PXESupport": "Enabled" }}' + final_rst: Failure + group: remote configure + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: set boot type order + case_sn: 38 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''BootTypeOrder0'': ''HardDiskDrive'', + ''BootTypeOrder1'': ''DVDROMDrive'', ''BootTypeOrder2'': ''PXE'', ''BootTypeOrder3'': + ''Others''}, return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order + case_sn: 39 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "HardDiskDrive", "BootTypeOrder1": + "DVDROMDrive", "BootTypeOrder2": "PXE", "BootTypeOrder3": + "Others", }}' + final_rst: Failure + group: remote interface management + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: configure boot order + case_sn: 40 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''BootTypeOrder0'': ''DVDROMDrive'', + ''BootTypeOrder1'': ''HardDiskDrive'', ''BootTypeOrder2'': ''Others'', ''BootTypeOrder3'': + ''PXE''}, return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order + case_sn: 41 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "DVDROMDrive", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "Others", "BootTypeOrder3": + "PXE", }}' + final_rst: Failure + group: remote interface management + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: configure new boot PXE order first + case_sn: 42 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''Attributes'': "Failure, expect value: {''BootTypeOrder0'': ''PXE'', + ''BootTypeOrder1'': ''HardDiskDrive'', ''BootTypeOrder2'': ''DVDROMDrive'', ''BootTypeOrder3'': + ''Others''}, return value: Can''t find the key Attributes in return value"}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios/Settings +- case_name: check boot order PEX order first + case_sn: 43 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure''}]}' + expected_code: 200 + expected_result: '{ "Attributes": { "BootTypeOrder0": "PXE", "BootTypeOrder1": + "HardDiskDrive", "BootTypeOrder2": "DVDROMDrive", "BootTypeOrder3": + "Others", }}' + final_rst: Failure + group: remote interface management + header: null + method: GET + request_body: null + return_code_seq: '[]' + url: https://{bmc_ip}/redfish/v1/Systems/{system_id}/Bios +- case_name: check BMC Firmware version + case_sn: 44 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Success'', + ''FirmwareVersion'': ''Failure, expect value: 1.30.11P01 HDM V100R001B03D011SP01, + return value: 2.43''}]}' + expected_code: 200 + expected_result: '{"count": 1, "FirmwareVersion": "1.30.11P01 HDM V100R001B03D011SP01"}' + final_rst: Failure + group: remote interface management + header: null + method: GET + request_body: null + return_code_seq: '[200]' + url: https://{bmc_ip}{manager_id} +- case_name: change BMC account + case_sn: 45 + details_result: '{''count'': ''N/A for this case'', ''info'': [{''return_code'': + ''Failure'', ''UserName'': "Failure, expect value: CM_cc@1234, return value: Can''t + find the key UserName in return value", ''RoleId'': "Failure, expect value: Administrator, + return value: Can''t find the key RoleId in return value"}]}' + expected_code: 200 + expected_result: '{"UserName": "CM_cc@1234","RoleId": "Administrator",}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{"UserName": "CM_cc@1234","Password": "1234@CM_cc","RoleId": "Administrator",}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}/redfish/v1/AccountService/Accounts/3 +- case_name: configure BMC ip in static, ipv4 + case_sn: 46 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Failure'', + ''IPv4Addresses'': "Failure, expect value: [{''Address'': ''192.168.66.120'', + ''AddressOrigin'': ''Static'', ''Gateway'': ''192.168.66.1'', ''SubnetMask'': + ''255.255.255.128''}], return value: Can''t find the key IPv4Addresses in return + value"}]}' + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "Address": + "192.168.66.120", "AddressOrigin": "Static", "Gateway": + "192.168.66.1", "SubnetMask": "255.255.255.128" } ]}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "Address": "192.168.66.120", "AddressOrigin": + "Static", "Gateway": "192.168.66.1", "SubnetMask": "255.255.255.128" } ]}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in DHCP, gateway and subnet mask ipv4 + case_sn: 47 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Failure'', + ''IPv4Addresses'': "Failure, expect value: [{''Address'': ''192.168.66.120'', + ''AddressOrigin'': ''DHCP'', ''Gateway'': ''192.168.66.1'', ''SubnetMask'': ''255.255.255.128''}], + return value: Can''t find the key IPv4Addresses in return value"}]}' + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "Address": + "192.168.66.120", "AddressOrigin": "DHCP", "Gateway": "192.168.66.1", "SubnetMask": + "255.255.255.128" } ]}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "AddressOrigin": "DHCP" } ]}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in static, ipv4 + case_sn: 48 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Failure'', + ''IPv4Addresses'': "Failure, expect value: [{''AddressOrigin'': ''DHCP''}], return + value: Can''t find the key IPv4Addresses in return value"}]}' + expected_code: 200 + expected_result: '{"count": 1, "IPv4Addresses": [ { "AddressOrigin": + "DHCP", } ]}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv4Addresses": [ { "AddressOrigin": "DHCP" } ]}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 +- case_name: configure BMC ip in static, ipv6 + case_sn: 49 + details_result: '{''count'': ''Success'', ''info'': [{''return_code'': ''Failure'', + ''IPv6Addresses'': "Failure, expect value: [{''Address'': ''N/A'', ''AddressOrigin'': + ''N/A'', ''PrefixLength'': 64}, {''Address'': ''2019::11'', ''AddressOrigin'': + ''Static'', ''PrefixLength'': 64}], return value: Can''t find the key IPv6Addresses + in return value"}]}' + expected_code: 200 + expected_result: '{"count": 1, "IPv6Addresses": [ { "Address": + "N/A", "AddressOrigin": "N/A", "PrefixLength": 64 }, { "Address": + "2019::11", "AddressOrigin": "Static", "PrefixLength": 64 } ]}' + final_rst: Failure + group: remote interface management + header: null + method: PATCH + request_body: '{ "IPv6Addresses": [ { "Address": "2019::11", "AddressOrigin": + "Static", "PrefixLength": 64 } ]}' + return_code_seq: '[''N/A'']' + url: https://{bmc_ip}{manager_id}/EthernetInterfaces/eth1 diff --git a/tools/hdv/redfish/docs/readme.md b/tools/hdv/redfish/docs/readme.md new file mode 100644 index 0000000..2113913 --- /dev/null +++ b/tools/hdv/redfish/docs/readme.md @@ -0,0 +1,129 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +This is a prototype of hardware validation implementation in redfish interface for a certain hardware vendor. +which originally is contributed by China Mobile. +>>> Usage: +usage: hdv.py [-h] [--version] [--config CONFIG] [--file_type FILE_TYPE] + [--case_yaml CASE_YAML] [--depends_yaml DEPENDS_YAML] + [--case_excel CASE_EXCEL] + +hdv tool by redfish, it works in two mode + +optional arguments: + -h, --help show this help message and exit + --version show tool version + --config CONFIG given global config.yaml file + --file_type FILE_TYPE + config file type, [yaml|excel] + --case_yaml CASE_YAML + case yaml file, uesd if file_type = yaml + --depends_yaml DEPENDS_YAML + depends yaml file,uesd if file_type = yaml + --case_excel CASE_EXCEL + excel case file used if file_type = excel +example: +#default case +1>python .\hdv.py +following config used + - ./conf/config.yaml + - file_type=excel + - ./conf/cases.xlsx +# use file_type=yaml +2>python .\hdv.py --file_type=yaml + +example1. default conf/config.yaml, file_type=excel, cases.xlsx used +python .\hdv.py +example2. use yaml file type config, default conf/cases.yaml conf/depends.yaml used +python .\hdv.py --file_type=yaml +example3. user input config file +python .\hdv.py --file_type=yaml --case_yaml=./conf-new/cases.yaml --depends_yaml=./conf-new/depends.yaml + +>>> tools directory: + +./redfish +├─conf # config directory +├─docs # readme +├─logs # hdv.log would be generated here. + +$ ls -lR . +$ ls redfish/*.py +redfish/__init__.py +redfish/excel_2_yaml.py #tool script to convert excel cases.xlsx sheets content to yaml format cases.yaml and depends.yaml +redfish/hdv_redfish.py #the code implementation by parsing config.yaml and cases.xlsx or cases.yaml and depends.yaml +redfish/log_utils.py #log utils +redfish/errors.py #error code definition for the tool during parse. +redfish/hdv.py #hdv portal +redfish/http_handler.py #http_handler +redfish/yaml_utils.py #yaml utils for test. + +$ ls redfish/conf +config.yaml #global config yaml where define BMC settings, static value, and some position definition in the cases.xlsx excel +cases.xlsx #two sheet defined (cases and depend_id), input case file if file_type=excel, default way. + #sheet cases - define all test case redfish url, expected value, etc + #sheet dependent_id - define all dependent_id url which is used to get parent resource id for the url in the cases. +cases.yaml #test cases yaml file,where the same set test case with cases.xlsx, it is used if file_type=yaml +depends.yaml #depends.yaml where the same content with sheet dependent_id, it is used if file_type=yaml +report.yaml #final test report, it is used if file_type=yaml + +$ ls redfish/docs +readme.md #readme + +$ ls redfish/logs +hdv.log # test log file + +>>> Principle +The hdv tool gets the global config from conf/config.yaml, e.g bmc settings, and +global variable definitions, and some excel column position used in case file_type=excel +User can select eiter file_type yaml or excel as the configure file type, +default type is excel at present. However the principle is similar. + +If file_type is excel, it will parse two sheets of excel workbook, cases and dependent_id. +The dependent_id sheet is used to define how to get the parents before checking a final redfish url, +thinking about checking a port should get the adapter at first. +The cases sheet is the test cases template, where the variable will be replaced +by global static value from config yaml or dependent_id + +By running a final redfish url request, it will get response result from the test server. +Then tool will compare the response value with expected value defined in <expected_result> column of cases sheet to decide if the case status. + +test report of each case <details,case_status> will write back to the same excel in the last two columns. + +Meanwhile, yaml file_type is supported also, it processes similarly as excel, except +- reading depends.yaml to get the dependent_id +- reading cases.yaml to run the test case +- report.yaml will be created as the final report. +cases.xlsx will not be used anymore in yaml case. + +Besides, excel_2_yaml.py script can be used to convert the cases.xlsx to yaml file accordingly. +If you want to update the cases content, you can update the excel at first, then convert by the script. + +>>> FAQ: +1. how to customize expected result? +you need put a json format value in it, the hierachy should be exactly the same with actual returned value, +as the comparing implementation relies on it. + => a simple example: '{"AssetTag": "CM_cc@1234"}' + => a complex example: +'{ "count": 2, "Manufacturer": "Intel(R) Corporation", "MaxSpeedMHz": + 2300, "Model": "Intel(R) Xeon(R) Gold 5218N CPU @ 2.30GHz", "ProcessorArchitecture": + ["x86", "IA-64", "ARM", "MIPS", "OEM"], "Socket": [1, 2], "Status": { "Health": + "OK", "State": "Enabled" }, "TotalCores": 16, "TotalThreads": + 32}' + +in the above data, a specific "count" attribute defined to check components quantity returned, e.g How many cpus expected. +generally it can be a subset attributes definition, comparing with actual return value +also it can support list of all expected value for list of objects. +example: "Socket:[1,2]", expecting return "Socket:1" and "Socket:2" from returned response + +>>>Perspective: +- there are differences between vendors's implementation, or even versions for the same vendor. +- define more test case or update existing case in the cases.yaml and depends.yaml or cases.xlsx file to support much more checks. +- more implementation could be contributed from community so that it can grow bigger to support more types and checkpoints test case. + +#https://gerrit.opnfv.org/gerrit/admin/repos/cirv diff --git a/tools/hdv/redfish/errors.py b/tools/hdv/redfish/errors.py new file mode 100644 index 0000000..e7ea8e8 --- /dev/null +++ b/tools/hdv/redfish/errors.py @@ -0,0 +1,47 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +""" +ERROR CODE instruction +""" +ERROR_CODE = { + # E100XXX: Connection + "E100001": "E100001:fail to get response from the url", + "E100002": "E100002:unexpected request url", + "E100003": "E100003:failed to setup connection", + # E200XXX: options - tools arguments. + "E200001": "E200001:unsupported input file_mode, \ + should be one of [yaml,excel]", + # E300XXX: resource issue - depended resource is not existing... + "E300001": "E300001:invalid token", + "E300002": "E300002:fail to get dependency parent id, Action: check if the \ + resource support by server", + "E300003": "E300003:fail to get expected id list for component_id, \ + Action: check if the resource support by server", + # E400XXX: configuration error + "E400001": "E400001:fail to find configure file", + "E400002": "E400002:parse config.yaml exception", + "E400003": "E400003: key_list is null for key_flags", + "E400004": "E400004: unexpected response body type", + "E400005": "E400005: customized expected value format error, \ + Action:check input expected value type with actual returned value type", + "E400006": "E400006: unexpected expected value type, \ + expected[str,list,dict]", + "E400007": "E400007: unexpected expected value type while comparing", + # E500XXX: application - find no value from cache + "E500001": "E500001: fail find key from actual value, \ + Action: check if the attribute support by server", + # E600XXX: restful interface + "E600001": "E600001: unsupported redfish api?", + } + +WARN_CODE = { + "W100001": "W100001: fail to the response from a request", + "W100002": "W100002: unexpected type of return_value type", + "W100003": "W100003: NoneType value", +} diff --git a/tools/hdv/redfish/excel_2_yaml.py b/tools/hdv/redfish/excel_2_yaml.py new file mode 100644 index 0000000..3bda3d5 --- /dev/null +++ b/tools/hdv/redfish/excel_2_yaml.py @@ -0,0 +1,61 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +''' +excel 2 yaml tools +convert excel config to yaml format config: depends.yaml and cases.yaml. +''' +import os +import yaml +from openpyxl.reader.excel import load_workbook +#pylint: disable=E0611 +from log_utils import LOGGER + + +def load_sheet(excel_file, sheet_index, start_col, end_col): + ''' + load sheet + ''' + if not os.path.exists(excel_file): + LOGGER.error("excel file not existing") + return None + input_file = load_workbook(excel_file) + input_ws = input_file[input_file.sheetnames[sheet_index]] + cell_key = [] + rows_list = [] + for i in range(start_col, end_col): + cell_key.append(input_ws.cell(row=1, column=i).value) + row = 2 + while input_ws.cell(row=row, column=1).value: + cell_value = [] + for i in range(start_col, end_col): + value = input_ws.cell(row=row, column=i).value + if isinstance(value, str): + value = value.strip().replace('\n', '') + cell_value.append(value) + cell_dict = dict(zip(cell_key, cell_value)) + row += 1 + rows_list.append(cell_dict) + + LOGGER.info(rows_list) + return rows_list + +def create_yaml(id_dict, yaml_file): + ''' + create yaml + ''' + with open(yaml_file, 'w') as y_file: + yaml.dump(id_dict, y_file, explicit_start=True) + + +DEPEND_FILE_NAME = "./conf/depends.yaml" +LOGGER.info("create %s ", DEPEND_FILE_NAME) +create_yaml(load_sheet("./conf/cases.xlsx", 1, 1, 5), DEPEND_FILE_NAME) + +CASE_FILE_NAME = "./conf/cases.yaml" +create_yaml(load_sheet("./conf/cases.xlsx", 0, 1, 10), CASE_FILE_NAME) diff --git a/tools/hdv/redfish/hdv.py b/tools/hdv/redfish/hdv.py new file mode 100644 index 0000000..80db3c9 --- /dev/null +++ b/tools/hdv/redfish/hdv.py @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +''' +hdv tools + all config files are put under conf/ + config.yaml is the global configuration + additional config for supporting two modes + - excel: tools will parse the depend_id sheet and cases sheet and \ + execute test case and write report back to excel + - yaml: tools will parse depends.yaml and cases.yaml and execute test case and\ + write a report.yaml + theory: + either test case can be finished by one restful request, + or an additional request needed to get dependency parent resource. + e.g a case for checking port, should get networkadaptor_id before that. +''' +import argparse +from hdv_redfish import run as run_case + +def parse_args(): + ''' + parse arguments + ''' + parser = argparse.ArgumentParser(description="hdv tool by redfish, \ + check readme under ./docs") + parser.add_argument('--version', action='version', \ + version='%(prog)s 0.1', help="show tool version") + parser.add_argument('--config', type=str, default="./conf/config.yaml", \ + help="given global config.yaml file") + parser.add_argument('--file_type', type=str, default="excel", \ + help="config file type, [yaml|excel],default is excel") + parser.add_argument('--case_yaml', type=str, default="./conf/cases.yaml", \ + help="case yaml file, uesd if file_type = yaml") + parser.add_argument('--depends_yaml', type=str, \ + default="./conf/depends.yaml",\ + help="depends yaml file,uesd if file_type = yaml") + parser.add_argument('--case_excel', type=str, default="./conf/cases.xlsx", \ + help="excel case file used if file_type = excel") + args = parser.parse_args() + return args + + +def main(): + ''' + main function + ''' + args = parse_args() + run_case(args.config, args.case_excel, args.depends_yaml, args.case_yaml,\ + args.file_type) + + +if __name__ == "__main__": + main() diff --git a/tools/hdv/redfish/hdv_redfish.py b/tools/hdv/redfish/hdv_redfish.py new file mode 100644 index 0000000..6d325f6 --- /dev/null +++ b/tools/hdv/redfish/hdv_redfish.py @@ -0,0 +1,676 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +''' +an implementation of hardware delivery validation based on redfish interface. +''' +import time +import os +import re +from re import DOTALL as DT +import json +import copy +from ast import literal_eval +import yaml +from openpyxl.reader.excel import load_workbook +from http_handler import UrllibHttpHandler, HEADERS +# pylint: disable=E0611 +from log_utils import BASE_DIR, LOG_FILE, LOGGER +from errors import ERROR_CODE, WARN_CODE + +LOGGER.info(BASE_DIR) + +ACCOUNT_INFO = {} +WAIT_INTERVAL = 5 + + + +def parse_config(config_yaml): + """ + parse setting from config.yaml + :return: + """ + try: + if not os.path.exists(config_yaml): + LOGGER.error(" %s, %s", ERROR_CODE['E400001'], config_yaml) + with open(config_yaml, 'r') as conf_file: + config = yaml.load(conf_file.read(), Loader=yaml.FullLoader) + except FileNotFoundError as fnfe: + LOGGER.error(fnfe) + LOGGER.error(u"%s", ERROR_CODE['E400002']) + return None + else: + return config + + +def get_token(http_handler, url): + """ + :return: x_auth_token + """ + retry_num = 3 + x_auth_token = None + while retry_num: + retry_num -= 1 + res = http_handler.post(url, ACCOUNT_INFO) + if res is None: + LOGGER.error("%s, %s", WARN_CODE['W100001'], url) + LOGGER.info("wait %s seconds to try again", WAIT_INTERVAL) + time.sleep(WAIT_INTERVAL) + continue + data = res.info() + if "X-Auth-Token" in data: + x_auth_token = data.get("X-Auth-Token") + return x_auth_token + else: + time.sleep(WAIT_INTERVAL) + return None + + +def get_etag(http_handler, url): + """ + :return: ETag + """ + etag = None + res = http_handler.get(url) + data = None + if res is not None: + data = res.info() + if data is not None and "ETag" in data: + etag = data.get("ETag") + return etag + + +def parse_data(exp_value, act_value): + ''' + parse the expected value and actual value: + @return: case 1: exp_value and actual value is str or int, + then return tuple (exp_value,act_value) + case 2: list,dict type, then return updated exp_value + ERROR_CODE for unexpected case. + ''' + if isinstance(exp_value, (str, int)) and isinstance(act_value, (str, int)): + return (exp_value, act_value) + if isinstance(exp_value, list): + if not isinstance(act_value, list): + return (exp_value, act_value) + else: + for exp in enumerate(exp_value, start=0): + index = exp[0] + exp_value[index] = parse_data( + exp_value[index], act_value[index]) + + elif isinstance(exp_value, dict): + if isinstance(act_value, dict): + for key, val in exp_value.items(): + if key in act_value: + exp_value[key] = parse_data(val, act_value[key]) + else: + LOGGER.error("%s,%s", ERROR_CODE['E500001'], key) + else: + LOGGER.error("%s,expected: %s , actual: %s", + ERROR_CODE['E400005'], exp_value, act_value) + else: + LOGGER.error("%s, expected type:%s, actual type %s", + ERROR_CODE['E400006'], type(exp_value), type(act_value)) + return exp_value + + +def compare_data(value, flag): + ''' + compare value content + ''' + if isinstance(value, tuple): + if value[1] is not None or value[1]: + if value[0] == 'N/A': + return "Success", flag + elif isinstance(value[0], (bool, int, str)): + if value[0] == value[1]: + return "Success", flag + else: + flag += 1 + return "Failure, expect value: " + str(value[0]) + \ + ", return value: " + str(value[1]), flag + elif value[1] in value[0] or value[0] == ['N/A']: + return "Success", flag + else: + flag += 1 + return "Failure, expect value: " + str(value[0]) + \ + ", return value: " + str(value[1]), flag + else: + flag += 1 + return "Failure, expect value: " + str(value[0]) + \ + ", return value: " + str(value[1]), flag + + elif isinstance(value, list): + for elem in enumerate(value, start=0): + index = elem[0] + value[index], flag = compare_data(value[index], flag) + elif isinstance(value, dict): + for key, val in value.items(): + value[key], flag = compare_data(val, flag) + else: + LOGGER.error("%s", ERROR_CODE['E400007']) + flag += 1 + return value, flag + + +def get_component_ids_yaml(file): + ''' + get component ids from yaml file + ''' + if not os.path.exists(file): + LOGGER.info("%s, %s", ERROR_CODE['E400001'], file) + return None + return yaml.load(open(file, "r")) + + +def get_component_ids_excel(excel_file): + ''' + get the component_id settings from the excel sheet2 + the componnet_id is the parent id of the hardware resource of sheet1 + ''' + input_file = load_workbook(excel_file) + input_ws = input_file[input_file.sheetnames[1]] + cell_key = [] + id_info_list = [] + for i in range(1, 5): + cell_key.append(input_ws.cell(row=1, column=i).value) + row = 2 + while input_ws.cell(row=row, column=1).value: + cell_value = [] + for i in range(1, 5): + + cell_value.append(input_ws.cell(row=row, column=i).value. + encode("utf8").decode("utf8").replace('\n', '')) + cell_dict = dict(zip(cell_key, cell_value)) + row += 1 + id_info_list.append(cell_dict) + return id_info_list + + +def create_real_url(url_value, id_dict, config_file): + ''' + create the real url + either a static url, or a replaced url by depended_id + ''' + url_list = [] + replaced = 0 + regexp = r'[^{]*{(?P<var>[a-zA-Z_]*)}' + # pattern = re.compile(regexp, re.S) + pattern = re.compile(regexp, DT) + LOGGER.info("url_value %s", url_value) + matches = list(pattern.finditer(url_value)) + for match in matches: + value = match.groupdict() + if value['var'] in config_file: + url_value = url_value.replace('{' + str(value['var']) + '}', + str(config_file[value['var']])) + + elif value['var'] in id_dict: + replaced = 1 + instance_list = id_dict[value['var']] + for instance in instance_list: + sgl_url = url_value.replace('{' + str(value['var']) + '}', + str(instance)) + LOGGER.debug("replaced url value %s", sgl_url) + url_list.append(sgl_url) + else: + replaced = 2 + LOGGER.error("%s for parameter %s", + ERROR_CODE['E300002'], value['var']) + # combine single case with list case together. + if replaced == 0: + LOGGER.info("adding static url %s into list", url_value) + url_list.append(url_value) + return url_list + + +def execute_get_url(url, http_handler): + """ + execute the url + """ + LOGGER.debug("execute url %s", url) + rsp = http_handler.get(url) + if rsp is None: + LOGGER.error("return None for url %s", url) + return None + ret_dict = {} + ret_dict.update({"return_code": rsp.code}) + return_value = json.loads(rsp.read()) + ret_dict.update({"return_value": return_value}) + LOGGER.info("ret_dict is %s", ret_dict) + LOGGER.debug("ret_dict type is %s", type(ret_dict)) + return ret_dict + + +def handle_depend_url(method, url_list, http_handler): + ''' + run request url in url_list and collect the response as list + ''' + response_list = [] + if method == 'GET': + for url_case in url_list: + response = execute_get_url(url_case, http_handler) + response_list.append(response) + elif method == 'POST': + pass + elif method == 'PATCH': + pass + elif method == 'DELETE': + pass + return response_list + + +def create_obj_id_list(key_flags, response_list): + ''' + create object id list + ''' + if response_list is None or response_list.__len__() == 0: + LOGGER.debug("response list is None") + return None + if key_flags is not None: + key_list = key_flags.split(':') + end_id_list = [] + for response in response_list: + if response is None: + LOGGER.warning("response is None") + continue + return_value = response['return_value'] + if len(key_list) == 1 and key_list[0] in return_value: + for i in return_value[key_list[0]]: + end_id_list.append(i['@odata.id']) + elif len(key_list) > 1: + for elem in enumerate(key_list, start=0): + index = elem[0] + if index == len(key_list) - 1: + for case in return_value[key_list[index]]: + end_id_list.append(case['@odata.id']) + else: + if isinstance(return_value, list): + return_value = return_value[0] + elif isinstance(return_value, dict): + return_value = return_value[key_list[index]] + else: + LOGGER.warning("%s, %s", WARN_CODE['W100002'], + type(return_value)) + + else: + LOGGER.error("%s %s", ERROR_CODE['E400003'], key_flags) + return end_id_list + + +def get_depend_id(config_file, http_handler, depend_ids): + ''' + @param mode: yaml or excel,default value "excel" + parse the component id list + build up the id resource for each component_id + return: id_dict like {component_id:[obj_list]} + ''' + id_dict = {} + for case in depend_ids: + component_name = case.get('component_id') + LOGGER.info("parsing component %s", component_name) + pro_value = case.get('pro_value') + url_value = case.get('url_value') + key_flags = case.get('key_flags') + # url_list = [] + url_list = create_real_url(url_value, id_dict, config_file) + # response_list = [] + response_list = handle_depend_url(pro_value, url_list, http_handler) + # end_id_list = [] + end_id_list = create_obj_id_list(key_flags, response_list) + if end_id_list is None or end_id_list.__len__() == 0: + LOGGER.error("%s,%s", ERROR_CODE['E300003'], component_name) + continue + id_dict.update({component_name: end_id_list}) + LOGGER.debug("id_dict content is %s", id_dict) + return id_dict + + +def read_row(input_ws, row, config_file): + ''' + read a row value + ''' + pro_value = input_ws.cell(row=row, column=config_file["pro_seq"]).value + url_value = input_ws.cell(row=row, column=config_file["url_seq"]).value + req_body_value = input_ws.cell( + row=row, column=config_file["req_body_seq"]).value + expect_return_code = \ + input_ws.cell( + row=row, column=config_file["expect_return_code_seq"]).value + expect_return_value = \ + input_ws.cell( + row=row, column=config_file["expect_return_value_seq"]).value + attr_name = input_ws.cell(row=row, column=config_file["attr_name"]).value + + if req_body_value is not None: + req_body_value = literal_eval(req_body_value) + if expect_return_code is not None: + expect_return_code = int(expect_return_code) + if expect_return_value is not None: + expect_return_value = literal_eval(expect_return_value) + return pro_value, url_value, req_body_value, expect_return_code,\ + expect_return_value, attr_name + + +def execute_post_url(body, handler, url): + ''' + execute post url + ''' + LOGGER.debug("execute url %s", url) + rsp = handler.post(url, body) + LOGGER.debug("post response %s", rsp) + if not isinstance(rsp, dict): + LOGGER.error("%s,%s, expected type %s", + ERROR_CODE["E400004"], type(rsp), dict) + return None + return rsp + + +def execute_patch_url(body, http_handler, url): + ''' + execute patch url + ''' + etag = get_etag(http_handler, url) + LOGGER.info("etag %s", etag) + rsp = http_handler.patch(url, body, etag) + LOGGER.debug("patch response %s", rsp) + LOGGER.debug("type response is %s", type(rsp)) + ret_dict = {} + if rsp is None: + LOGGER.error("%s %s", ERROR_CODE['E100001'], url) + ret_dict.update({"return_code": "N/A"}) + ret_dict.update({"return_value": "Failure"}) + return ret_dict + ret_dict.update({"return_code": rsp.code}) + return_value = json.loads(rsp.read()) + ret_dict.update({"return_value": return_value}) + return ret_dict + + +def handle_final_url(method, url_list, req_body=None, http_handler=None): + '''execute the requested url to get the response + ''' + response_list = [] + if method == 'GET': + for url_case in url_list: + rsp = execute_get_url(url_case, http_handler) + response_list.append(rsp) + elif method == 'POST': + if len(url_list) > 1: + LOGGER.error(ERROR_CODE['E100002']) + return None + url_value = url_list[0] + rsp = execute_post_url(req_body, http_handler, url_value) + response_list.append(rsp) + elif method == 'PATCH': + for url_case in url_list: + LOGGER.info(url_case) + temp = execute_patch_url(req_body, http_handler, url_case) + if temp is not None: + response_list.append(temp) + elif method == 'DELETE': + pass + LOGGER.info("response_list %s", response_list) + return response_list + + +def check_component_cnt(expect_return_value, res_list, result): + ''' + #check if the component count meet the required. + ''' + if expect_return_value.__contains__('count'): + if expect_return_value['count'] == len(res_list): + result.update({"count": "Success"}) + else: + result.update({"count": \ + "Failure, the actual num is "+ str(len(res_list))}) + else: + result.update({"count": "N/A for this case"}) + return result + + +def parse_test_result(expect_return_value, expect_return_code, + actual_result_list, final_result): + ''' + @param expected_return_value expected value set in input excel + @param expected_return_code expected return code + @param actual_result_list: actual result run by each url list checking + @param final_result: returned final result + parsing the test final_result by comparing expected_value with + real test final_result value. + ''' + return_code_list = [] + return_value_list = [] + flag = 0 + final_result = check_component_cnt(expect_return_value, + actual_result_list, final_result) + + for each_result in actual_result_list: + temp_result = {} + if each_result is not None: + LOGGER.debug("current result is %s,result_list is %s", + each_result, actual_result_list) + return_code = each_result["return_code"] + return_code_list.append(return_code) + return_value = each_result["return_value"] + if return_code == expect_return_code: + code_result = 'Success' + else: + code_result = 'Failure' + temp_result.update({'return_code': code_result}) + else: + LOGGER.warning("%s ,set failure", WARN_CODE['W100003']) + temp_result.update({'return_code': 'Failure'}) + return_value_list.append(temp_result) + flag += 1 + continue + + #parse the actual result according to the expected value hierachy. + ex_value = copy.deepcopy(expect_return_value) + exp_act_pairs = {} + for key, value in ex_value.items(): + if key in return_value: + exp_act_pairs[key] = parse_data(value, return_value[key]) + elif key == 'count': + pass + else: + LOGGER.error("%s, %s", ERROR_CODE['E500001'], key) + exp_act_pairs[key] = \ + (value, "Can't find the key {} in return value".format(key)) + LOGGER.debug("real_result:%s", exp_act_pairs) + + # comparing expected result with real result. + if exp_act_pairs: + for key, value in exp_act_pairs.items(): + temp_result[key], flag = compare_data(value, flag) + return_value_list.append(temp_result) + return return_value_list, return_code_list, final_result, flag + + +def write_result_2_excel(config_file, input_ws, row, flag, result): + ''' + write the result back to excel + ''' + if not result: + input_ws.cell(row=row, column=config_file["detail_result"], + value=str('N/A')) + else: + input_ws.cell(row=row, column=config_file["detail_result"], + value=str(result)) + if flag == 0: + input_ws.cell(row=row, column=config_file["final_result"], + value=str("Success")) + else: + input_ws.cell(row=row, column=config_file["final_result"], + value=str("Failure")) + return row + + +def execute_final_url(config_file, depends_id, http_handler, + method, url, req_body): + ''' + execute final url to get the request result + ''' + url_list = create_real_url(url, depends_id, config_file) + rsp_list = handle_final_url(method, url_list, req_body, http_handler) + return rsp_list + + +def run_test_case_yaml(config_file, case_file, depends_id, http_handler): + '''run test case from cases.yaml + ''' + LOGGER.info("################# start perform test case ###################") + cases_result = [] + cases = read_yaml(case_file) + for case in cases: + method, url, req_body, expected_code, expected_value, tc_name \ + = case['method'], case['url'], case['request_body'], \ + case['expected_code'], case['expected_result'], case['case_name'] + + expected_value = literal_eval(expected_value) + flag = 0 + final_rst = {} + rsp_list = execute_final_url(config_file, depends_id, + http_handler, method, url, req_body) + if rsp_list is not None and len(rsp_list) > 0: + return_value_list, return_code_list, final_rst, flag = \ + parse_test_result( + expected_value, expected_code, rsp_list, final_rst) + final_rst.update({'info': return_value_list}) + LOGGER.debug("return_code_list:%s", return_code_list) + case['return_code_seq'] = str(return_code_list) + else: + LOGGER.error("%s", ERROR_CODE['E600001']) + flag += 1 + case['final_rst'] = "Success" if flag == 0 else "Failure" + case['details_result'] = \ + str(final_rst) if len(final_rst) > 0 else "N/A" + cases_result.append(case) + LOGGER.info("writing test final_rst for case %s", tc_name) + + write_result_2_yaml(cases_result) + + LOGGER.info("################# end perform test case #####################") + + +def read_yaml(file): + '''read a yaml file + ''' + if not os.path.exists(file): + LOGGER.info("%s %s", ERROR_CODE['E400001'], file) + return None + return yaml.load(open(file, "r")) + + +def write_result_2_yaml(result): + ''' + write test result to new report.yaml + ''' + LOGGER.info("writing to yaml file") + yaml.dump(result, open("./conf/report.yaml", "w")) + + +def run_test_case_excel(config_file, case_file, depends_id, http_handler): + ''' + perform the test case one by one, + and write test final_result back to the excel. + ''' + LOGGER.info("################# start perform test case ###################") + input_file = load_workbook(case_file) + input_ws = input_file[input_file.sheetnames[0]] + + row = 2 + while input_ws.cell(row=row, column=1).value: + method, url, req_body, expected_code, expected_value, tc_name \ + = read_row(input_ws, row, config_file) + + LOGGER.info("run test case ##%s##", tc_name) + if tc_name == "configure BMC ip in static, ipv4": + LOGGER.debug("debug") + flag = 0 + final_result = {} + rsp_list = [] + rsp_list = execute_final_url(config_file, depends_id, http_handler, + method, url, req_body) + if rsp_list is not None and len(rsp_list) > 0: + return_value_list, return_code_list, final_result, flag = \ + parse_test_result(expected_value, expected_code, + rsp_list, final_result) + final_result.update({'info': return_value_list}) + LOGGER.debug("return_code_list:%s", return_code_list) + input_ws.cell(row=row, column=config_file["return_code_seq"], + value=str(return_code_list)) + else: + LOGGER.error("%s", ERROR_CODE['E600001']) + flag += 1 + + LOGGER.info("writing test final_result for row %s", row) + row = write_result_2_excel( + config_file, input_ws, row, flag, final_result) + row += 1 + input_file.save(case_file) + LOGGER.info("################# end perform test case #####################") + + +def run(conf_file, case_excel_file=None, depend_yaml_file=None, + case_yaml_file=None, file_mode=None): + ''' + @param conf_file: config.yaml + @param case_excel_file: excel case file + @param depend_yaml_file: depends yaml file used if file_mode=yaml + @param case_yaml_file: case yaml file, used if file_mode=yaml + @param file_mode: "excel" or "yaml" + access function + ''' + # parse config.yaml + LOGGER.info("start engine ...") + config_file = parse_config(conf_file) + http_handler = UrllibHttpHandler() + + # get bmc info + bmc_ip, bmc_user, bmc_pwd = \ + config_file["bmc_ip"], config_file["bmc_user"], config_file["bmc_pwd"] + ACCOUNT_INFO.update({"UserName": bmc_user}) + ACCOUNT_INFO.update({"Password": bmc_pwd}) + + url = "https://{0}/redfish/v1/SessionService/Sessions".format(bmc_ip) + x_auth_token = get_token(http_handler, url) + LOGGER.info("x_auth_token: %s", x_auth_token) + + if x_auth_token is None: + LOGGER.error("%s token is None", ERROR_CODE['E300001']) + return None + + HEADERS.update({"X-Auth-Token": x_auth_token}) + id_info_list = None + if file_mode == "excel": + id_info_list = get_component_ids_excel(case_excel_file) + elif file_mode == "yaml": + id_info_list = get_component_ids_yaml(depend_yaml_file) + else: + LOGGER.error("%s,%s", ERROR_CODE['E200001'], file_mode) + return None + + # get dependent id + depends_id = get_depend_id(config_file, http_handler, id_info_list) + + # read the test case sheet and perform test + if file_mode == "excel": + run_test_case_excel(config_file, + case_excel_file, depends_id, http_handler) + elif file_mode == "yaml": + run_test_case_yaml(config_file, + case_yaml_file, depends_id, http_handler) + else: + LOGGER.error("%s,%s", ERROR_CODE['E200001'], file_mode) + return None + + LOGGER.info("done,checking the log %s", LOG_FILE) + + return True diff --git a/tools/hdv/redfish/http_handler.py b/tools/hdv/redfish/http_handler.py new file mode 100644 index 0000000..259bcaf --- /dev/null +++ b/tools/hdv/redfish/http_handler.py @@ -0,0 +1,129 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +''' +a common http_handler +''' +import urllib.request +import json +import ssl +from http.client import HTTPException +from urllib.error import HTTPError, URLError +# pylint: disable=E0611 +from log_utils import LOGGER +from errors import ERROR_CODE + +# pylint: disable=W0212 +ssl._create_default_https_context = ssl._create_unverified_context + +HEADERS = { + 'Connection': 'keep-alive', + 'User-Agent': \ + 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 \ + (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36', +} + +TIME_OUT = 3000 + + +class UrllibHttpHandler: + """ + http handler based on urllib of python2.7 + """ + + def __init__(self): + self.__header = HEADERS + + def get(self, url): + """ + run the get request + """ + try: + req = urllib.request.Request(url, headers=self.__header) + res = urllib.request.urlopen(req, timeout=TIME_OUT) + except HTTPException as http_exp: + LOGGER.error(http_exp) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + except HTTPError as http_err: + LOGGER.error(http_err) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + LOGGER.error(u"%s %s", ERROR_CODE['E600001'], url) + else: + return res + + def post(self, url, parameter=None): + """ + run the post request, parameter must to encode to bytes + """ + try: + data = json.dumps(parameter).encode(encoding="utf-8") + LOGGER.debug("data is %s", data) + req = urllib.request.Request(url, data=data, headers=self.__header) + req.add_header("Content-Type", "application/json") + res = urllib.request.urlopen(req, timeout=TIME_OUT) + except HTTPException as http_exp: + LOGGER.error(http_exp) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + except TimeoutError as timeout_error: + LOGGER.error(timeout_error) + LOGGER.error(u"%s", ERROR_CODE['E100003']) + except HTTPError as http_err: + LOGGER.error(http_err) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + LOGGER.error(u"%s %s", ERROR_CODE['E600001'], url) + except URLError as url_err: + LOGGER.error(url_err) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + else: + return res + + def put(self, url, parameter=None): + """ + run the put request, parameter must to encode to bytes + """ +# parameter_data = urllib.parse.urlencode(parameter) #?? + data = json.dumps(parameter).encode(encoding="utf-8") + LOGGER.debug("data is %s", data) + req = urllib.request.Request(url, data=data, headers=self.__header) + req.get_method = lambda: 'PUT' + res = urllib.request.urlopen(req) + return res + + def patch(self, url, parameter=None, etag=None): + """ + run the patch request, parameter must to encode to bytes + """ + data = json.dumps(parameter).encode(encoding="utf-8") + LOGGER.debug("data is %s", data) + req = urllib.request.Request(url, data=data, headers=self.__header) + req.add_header("Content-Type", "application/json") + req.add_header("If-Match", etag) + req.get_method = lambda: 'PATCH' + res = None + try: + res = urllib.request.urlopen(req, timeout=TIME_OUT) + except HTTPException as http_exp: + LOGGER.error(http_exp) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + except HTTPError as http_err: + LOGGER.error(http_err) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + LOGGER.error(u"%s %s", ERROR_CODE['E600001'], url) + except TypeError as type_err: + LOGGER.error(type_err) + LOGGER.error(u"%s %s", ERROR_CODE['E100001'], url) + return res + + def delete(self, url): + ''' + run the delete request, + ''' + req = urllib.request.Request(url, headers=self.__header) + req.get_method = lambda: 'DELETE' + res = urllib.request.urlopen(req) + return res diff --git a/tools/hdv/redfish/log_utils.py b/tools/hdv/redfish/log_utils.py new file mode 100644 index 0000000..996a1d1 --- /dev/null +++ b/tools/hdv/redfish/log_utils.py @@ -0,0 +1,33 @@ +############################################################################## +# Copyright (c) 2020 China Mobile Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +""" +define the LOGGER settings +""" +import logging +import sys + +BASE_DIR = sys.path[0] +LOG_FILE = BASE_DIR + "/" + "logs" + "/" + 'hdv.log' + +LOGGER = logging.getLogger("redfish") +LOGGER.setLevel(logging.DEBUG) + +FORMATTER = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] \ + - %(funcName)s - %(levelname)s: %(message)s') + +FILE = logging.FileHandler(filename=LOG_FILE, mode='w') +FILE.setLevel(logging.DEBUG) +FILE.setFormatter(FORMATTER) + +CONSOLE = logging.StreamHandler() +CONSOLE.setLevel(logging.DEBUG) +CONSOLE.setFormatter(FORMATTER) + +LOGGER.addHandler(CONSOLE) +LOGGER.addHandler(FILE) diff --git a/tools/hdv/redfish/yaml_utils.py b/tools/hdv/redfish/yaml_utils.py new file mode 100644 index 0000000..21e4fb6 --- /dev/null +++ b/tools/hdv/redfish/yaml_utils.py @@ -0,0 +1,28 @@ +''' +@author: cmcc +''' +import os +import yaml +# pylint: disable=E0611 +from log_utils import LOGGER + +def read_yaml(file): + '''read a yaml file + ''' + if not os.path.exists(file): + LOGGER.info("%s not found", file) + return None + return yaml.load(open(file, "r")) + + +def write_yaml(file, dict_data): + '''write a yaml file + ''' + #yaml.dump(dict_data, open(file, "w"), width = 80) + yaml.safe_dump(dict_data, open(file, "w"), explicit_start=True) + + +print(read_yaml("./conf/depends.yaml")) +print(read_yaml("./conf/cases.yaml")) + +write_yaml("./conf/report.yaml", read_yaml("./conf/cases.yaml")) |