diff --git a/dockers/docker-pit/Dockerfile.j2 b/dockers/docker-pit/Dockerfile.j2 new file mode 100644 index 00000000000..b7ed5d775e5 --- /dev/null +++ b/dockers/docker-pit/Dockerfile.j2 @@ -0,0 +1,32 @@ +{% from "dockers/dockerfile-macros.j2" import install_debian_packages, install_python_wheels, copy_files %} +FROM docker-config-engine-buster + +ARG docker_container_name +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -f -y \ + dmidecode + +RUN apt-get install -f -y python-dev python-pip + +{% if docker_pit_whls.strip() -%} +# Copy locally-built Python wheel dependencies +{{ copy_files("python-wheels/", docker_pit_whls.split(' '), "/python-wheels/") }} + +# Install locally-built Python wheel dependencies +{{ install_python_wheels(docker_pit_whls.split(' ')) }} +{% endif %} + +RUN apt-get clean -y && \ + apt-get autoclean -y && \ + apt-get autoremove -y && \ + rm -rf /debs + + +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] + +ENTRYPOINT ["/usr/local/bin/supervisord"] diff --git a/dockers/docker-pit/critical_processes b/dockers/docker-pit/critical_processes new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dockers/docker-pit/supervisord.conf b/dockers/docker-pit/supervisord.conf new file mode 100644 index 00000000000..035866cb91b --- /dev/null +++ b/dockers/docker-pit/supervisord.conf @@ -0,0 +1,23 @@ +[supervisord] +logfile_maxbytes=20MB +logfile_backups=5 +nodaemon=true + +[eventlistener:dependent-startup] +command=python3 -m supervisord_dependent_startup +autostart=true +autorestart=unexpected +startretries=0 +exitcodes=0,3 +events=PROCESS_STATE +buffer_size=1024 + +[program:rsyslogd] +command=/usr/sbin/rsyslogd -n -iNONE +priority=1 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true + diff --git a/rules/docker-pit.mk b/rules/docker-pit.mk new file mode 100644 index 00000000000..89490ebdd9b --- /dev/null +++ b/rules/docker-pit.mk @@ -0,0 +1,34 @@ +# Docker image for SONiC platform monitoring tools + +DOCKER_PIT_STEM = docker-pit +DOCKER_PIT = $(DOCKER_PIT_STEM).gz +DOCKER_PIT_DBG = $(DOCKER_PIT_STEM)-$(DBG_IMAGE_MARK).gz + +$(DOCKER_PIT)_PATH = $(DOCKERS_PATH)/$(DOCKER_PIT_STEM) + + +#$(DOCKER_PIT)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY2) +#$(DOCKER_PIT)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY3) +#$(DOCKER_PIT)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY2) +#$(DOCKER_PIT)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY3) +$(DOCKER_PIT)_PYTHON_WHEELS += $(SONIC_PIT_PY2) + +$(DOCKER_PIT)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_DEPENDS) + +$(DOCKER_PIT)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES) + +$(DOCKER_PIT)_LOAD_DOCKERS = $(DOCKER_CONFIG_ENGINE_BUSTER) + +SONIC_DOCKER_IMAGES += $(DOCKER_PIT) +SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_PIT) + +SONIC_DOCKER_DBG_IMAGES += $(DOCKER_PIT_DBG) +SONIC_INSTALL_DOCKER_DBG_IMAGES += $(DOCKER_PIT_DBG) + +$(DOCKER_PIT)_CONTAINER_NAME = pit +$(DOCKER_PIT)_RUN_OPT += --privileged -t +$(DOCKER_PIT)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro +$(DOCKER_PIT)_RUN_OPT += -v /usr/share/sonic:/usr/share/sonic:ro +$(DOCKER_PIT)_RUN_OPT += -v /usr/lib/python2.7/dist-packages:/usr/lib/python2.7/dist-packages:ro + +$(DOCKER_PIT)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT) diff --git a/rules/sonic-pit.mk b/rules/sonic-pit.mk new file mode 100644 index 00000000000..917e8dee913 --- /dev/null +++ b/rules/sonic-pit.mk @@ -0,0 +1,12 @@ +# sonic-pit package + +SONIC_PIT_PY2 = sonic_pit-1.0-py2-none-any.whl +$(SONIC_PIT_PY2)_SRC_PATH = $(SRC_PATH)/sonic-pit +$(SONIC_PIT_PY2)_PYTHON_VERSION = 2 +SONIC_PYTHON_WHEELS += $(SONIC_PIT_PY2) + +SONIC_PIT_PY3 = sonic_pit-1.0-py3-none-any.whl +$(SONIC_PIT_PY3)_SRC_PATH = $(SRC_PATH)/sonic-pit +$(SONIC_PIT_PY3)_PYTHON_VERSION = 3 +#SONIC_PYTHON_WHEELS += $(SONIC_PIT_PY3) + diff --git a/src/sonic-pit/LICENSE b/src/sonic-pit/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/src/sonic-pit/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/sonic-pit/README.md b/src/sonic-pit/README.md new file mode 100644 index 00000000000..b7760278d54 --- /dev/null +++ b/src/sonic-pit/README.md @@ -0,0 +1,16 @@ +# PIT system of S3IP(Simplify Switch System Integration Program) + +Brief +PIT is a sub-system, dedicated for the verification of white-box switch hardware, firmware, and driver. +PIT provides: + *An automation test framework for platform hardware, firmware, and driver test; + *A set of test cases written in platform-independent (CPU, ASIC, product) manner; + *A set of platform APIs to provide hardware function abstraction (complementary to SONiC platform APIs), which help test case logic platform-independent. + +Use scenario + * Daily regression test in development stage(ODM + DC netowork users) + * Manufacturing test in manufacture stage(ODM side) + * Acceptance test in delivering stage(DC netowrk owner side) + +Detail +https://github.com/clarklee-guizhao/SONiC/blob/pit/doc/pit/Platform_Integration_Test_high_level_design.md diff --git a/src/sonic-pit/doc/Firmware_upgrading_porting_guide.md b/src/sonic-pit/doc/Firmware_upgrading_porting_guide.md new file mode 100755 index 00000000000..c6c3063d74d --- /dev/null +++ b/src/sonic-pit/doc/Firmware_upgrading_porting_guide.md @@ -0,0 +1,265 @@ +# Firmware upgrade test requirement + +#### 1. Firmware upgrade test cases support only manual mode, and it should be used under development test and manufacturing test stage. Test with param '-g manufacture -t manual' + +#### 2. When firmware upgrade test is run, select test item manually. Current supported tests are: Upgrade firmware on Master/Primary flash and verify; upgrade firmware on slave/secondary flash and verify. + +```shell +1.save master running firmware version and program test firmware +2.check master test firmware version and program baseline firmware +3.check master baseline firmware version +4.save slave running firmware version and program test firmware +5.check slave test firmware version and program baseline firmware +6.check slave baseline firmware version +``` + +#### 3. Example test case configuration + +```json +{ + "software_system":{ + "sysdiag_version":"1.1", + "timezone":"Asia/Shanghai", + "sonic_version":"SONiC.dev_202012_td4_100g.0-dirty-20220118.203718" + }, + "cpld_firmware_type":["LED_CPLD1","LED_CPLD2","FAN_CPLD","SYS_CPLD"], + "fpga_firmware_type":["FPGA"], + "bios_firmware_type":["BIOS"], + "switch_firmware_type":["SWITCH_PCIE"], + "cpld_firmware_baseline":{ + "SYS_CPLD": { + "absolute_path":true, + "file_path": "/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme" + }, + "LED_CPLD1": { + "absolute_path":false, + "file_path": "" + }, + "LED_CPLD2": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + }, + "FAN_CPLD": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + } + }, + "fpga_firmware_baseline":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + } + }, + "bios_firmware_baseline":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "switch_firmware_baseline":{ + "SWITCH_PCIE": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "cpld_firmware_test":{ + "SYS_CPLD": { + "absolute_path":true, + "file_path": "/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme" + }, + "LED_CPLD1": { + "absolute_path":false, + "file_path": "", + "version": "" + }, + "LED_CPLD2": { + "absolute_path":false, + "file_path": "", + "version": "" + }, + "FAN_CPLD": { + "absolute_path":false, + "file_path": "", + "version": "" + } + }, + "fpga_firmware_test":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "bios_firmware_test":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "switch_firmware_test":{ + "SWITCH_PCIE": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + } + +} + +``` + +##### 3.1 @firmware_type is list of firmware types to be upgraded, it'll be passed to FwMgrUtil (Platform plugins) class object and will invoke firmware_upgrade method to upgrde it. Firmware upgrade order is defined by list order. + +##### 3.2 firmware_baseline is baseline firmware version, before TEST, running version of all firmware will be saved, after TEST, all original version will be restore and check. + +##### 3.3 Each type of logical device, it has a set of params: + + +```json + { + "absolute_path":true, + "file_path": "/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.24h.vme", + "version": "b.24", + "slave_upgrade": true, + "same_with_master":true, + "slave_file_path":"", + "slave_version":"" +}, +``` + +###### 3.3.1 @absolute_path is used to indicate whether the firmware_file use absolute path, if not specified, the path of firmware file will be appended a prefix $(PIT_SRC_DIR)/config/platform/$(PLATFORM_NAME)/ + +###### 3.3.2 @version is target version of primary flash firmware after upgrade; @slave_version is target version of secondary firmware after upgrade. + +###### 3.3.3 @slave_upgrade indicates whether primary/secondary upgrqade is supported; @same_with_master indiates whether to use the same firmware file to test primary and secondary flash upgrade. + +##### 4. Mandatory method in fwmgrutil.py: + get_fw_version、firmware_upgrade、firmware_refresh, example implementation as follow + +```python + def get_fw_version(self, fw_type_list, fw_extr=None): + """ + @fw_type_list firmware type list, should be list of the strings:'bios', 'uboot', 'bmc', 'cpld', .etc. + @fw_extra OPTIONAL, extra information string, for fw_type 'BIOS'/'Uboot'/'BMC', value should be one of 'master'/'slave' + Retrieves all firmwares' version on the device + @return DICT, key=firmware name, value=DICT,include "version" and "description" + """ + result = {} + ret = run_command("show platform firmware status") + for item in fw_type_list: + for str in ret.splitlines(): + if item in str: + str_list = str.split() + str_len = len(str_list) + for i in range(0, str_len): + if item == str_list[i]: + result[item] = {} + result[item]['version'] = str_list[i + 1] + desc = '' + for j in range(i + 2, str_len): + desc += str_list[j] + ' ' + result[item]['description'] = desc + break + return result + +``` + +```python + def firmware_upgrade(self, fw_type, fw_path, fw_extr=None): + """ + @fw_type: firmware type, should be one of the strings:'bios', 'uboot', 'bmc', 'cpld', .etc. + @fw_path: target firmware file + @fw_extra OPTIONAL, extra information string, for fw_type 'BIOS'/'Uboot'/'BMC', value should be one of 'master'/'slave'/'both' + @return: Boolean + """ + cmd = "config platform firmware install chassis component %s fw %s -y" % (fw_type, fw_path) + ret = False + try: + retstr = run_command(cmd, True) + for line in retstr.splitlines(): + if ("%s upgrade success" % fw_type) in line: + print(line) + ret = True + break + except Exception as e: + print(str(e)) + return ret +``` + +```python + def firmware_refresh(self, fw_extra=None): + # os.system("sleep 10; shutdown now &") + return True +``` + +##### 5. @running_version_dict is used to store firmware versions before TEST, saved in file master_firmware_baseline.json; it will also be used to restore original firmware version and be check. + +```json +{ + "SYS_CPLD": { + "version": "b.25", + "description": "Mianboard System CPLD code version " + }, + "LED_CPLD1": { + "version": "1.5", + "description": "LED_CPLD1 code version " + }, + "LED_CPLD2": { + "version": "1.7", + "description": "LED_CPLD2 code version " + }, + "FAN_CPLD": { + "version": "a.a", + "description": "FAN_CPLD code version " + }, + "FPGA": { + "version": "4a.6", + "description": "Subcard borad FPGA code version " + }, + "BIOS": { + "version": "v09.0B.00.02", + "description": "Basic Input/Output System code version " + } +} +``` + +Note: +@get_fw_version MUST return dict in above format, otherwise version verification may failed. diff --git a/src/sonic-pit/pit-sysdiag/README.md b/src/sonic-pit/pit-sysdiag/README.md new file mode 100644 index 00000000000..0285ebd5790 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/README.md @@ -0,0 +1,1029 @@ +# `sysdiag` 设计说明 +`sysdiag`是一套交换机软硬件测试工具集,目的是对交换机进行健康检查,生成测试报告,并支持环境类测试、研发测试和生产测试等。 + +`sysdiag`的设计理念是: + + * 测试项通用化,不依赖某硬件平台或特殊场景; + * 测试项的具体测试流程需大家认可; + * 测试项测试过程中需要输入的内容均采用读配置文件的形式; + * 测试项中涉及平台数据获取的调用,均可通过`plugins`下的模块; + +由于每个平台硬件设计的差异,可以通过平台层的配置文件做隔离。 + +### 1 整体架构 + +`sysdiag`整体架构如下图。`sysdiag`包括两部分`test_cases`和`tools`,其中`test_cases`是测试项集合,`tools`是平台工具。 + +```json + ┌───────────────────────────────────────────────────────────┐ + │ sysdiag │ + │ ┌────────────────┬───────────────┐ ┌─────────────┐ │ + │ │ test_caseA │ fantool │ │ │ │ + │ │ test_caseB │ psutool │ │ traffic │ │ + │ │ test_caseZ │ ... │ │ │ │ + │ └────────────────┴───────────────┘ └─────────────┘ │ + │ ▲ ▲ ▲ │ + │ │ │ │ │ + └───────────┼───────────────┼────────────────┼──────────────┘ + │ │ │ + ┌────────┴────────┬──────┴──────────┬─────┴───────────┐ +middle │ │ │ │ +abstract │ plugins │ common_tools │ bcmcmd │ +layer │ │ │ ctc_shell │ + └─────────────────┴─────────────────┴─────────────────┘ + + + ┌─────────────────────────────────┐ ┌─────────────────┐ + │ │ │ │ +kernel │ hardware drivers │ │ asic │ +space │ │ │ │ + └─────────────────────────────────┘ └─────────────────┘ +``` + +### 2 `plugins`中间抽象层接口设计 +`plugins`是`SONiC`下的平台抽象层,`sysdiag`可以利用并扩充此模块提供的接口作为平台数据来源。 +下面列举的`plugins`模块属于硬件平台下的通用组件,如果`sysdiag`需要访问下面模块以外的信息,可以通过通用的第三方工具或者`nos`下的文件接口,比如: + +* `CPU`:`/proc/cpuinfo`或者`dmidecode` +* `memory`:`/proc/meminfo`或者`dmidecode` +* `SSD`:`mmc-util`或者`smartctl` +* `RTC`:`/sys/class/rtc/rtc0/*` + + + +#### 2.1 `sfputil.py` +`sfputil.py`用于光模块访问控制。测试过程中主要关注光模块`IO`状态、光模块`EEPROM`读写等。需要实现的接口如下: + +``` python +class SfpUtil(SfpUtilBase): + # 获取sfp端口列表 + @property + def sfp_port_list(self): + ''' + SFP Ports list + @return LIST [1, 2, 3, ...] + ''' + return LIST + + # 获取qsfp端口列表 + @property + def qsfp_ports(self): + ''' + QSFP Ports list + @return LIST [1, 2, 3, ...] + ''' + return LIST + + # 获取光模块端口与eeprom映射字典 + @property + def port_to_eeprom_mapping(self): + ''' + dictionary where key=physical port index(integer), value=path to SFP EEPROM file(string) + @return DICT { + 1: None, + 2: '/sys/bus/i2c/devices/10-0050/eeprom' + } + ''' + return DICT + + # 获取光模块presence + def get_presence(self, port_index): + return True/False + + # 获取光模块qsfp interrupt + def get_interrupt(self, port_index): + return True/False + + # 获取光模块qsfp low power mode + def get_low_power_mode(self, port_index): + return True/False + def set_low_power_mode(self, port_index, enable): + return True/False + + # 获取光模块qsfp reset + def get_reset(self, port_index): + return True/False + def reset(self, port_index): + return True/False + + # 获取光模块sfp tx fault + def get_tx_fault(self, port_index): + return True/False + + # 获取光模块sfp rx los + def get_rx_los(self, port_index): + return True/False + + # 获取光模块sfp tx disable + def get_tx_disable(self, port_index): + return True/False + def set_tx_disable(self, port_index, enable): + return True/False + + # Eload支持low speed signals monitor/control,需要实现set/get接口 + # For sfp + def get_eload_tx_disable(self, port_index): + return True/False + def set_eload_tx_fault(self, port_index, enable): + return True/False + def set_eload_rx_los(self, port_index, enable): + return True/False + def set_eload_presence(self, port_index, enable): # sfp set mod_abs, qsfp set modprs + return True/False + # For qsfp + def get_eload_low_power_mode(self, port_index): + return True/False + def get_eload_reset_changed(self, port_index): + ''' + when set eload modprstL high, port cpld will reset qsfp; + we need call below function to check wether reset changed or not + ''' + return True/False + def set_eload_interrupt(self, port_index, enable): + return True/False + # eload power setting + def set_eload_power(self, port_index, value): + return True/False + # eload insertion counter + def get_eload_insertion_counter(self, port_index): + return integer + + # 获取光模块在位状态记录 + def get_plug_record(self): + ''' + 由于cpld中光模块在位状态记录是读清的,所以该接口需要一次性把数值拿出来。 + 返回字典,key=physical port index(integer), value=True if plug happend else False, + @return DICT { + 1: False, # 端口没有发生在位状态变化 + 2: True, # 端口发生过在位状态变化 + } + ''' + return { + 1:True, + 2:False, + } + + # 光模块事件上报 + def get_transceiver_change_event(self, timeout=0): + return DICT +``` + +`sysdiag`测试项可以通过`sfputil.py`获取硬件平台下的端口形态配置,并对光模块进行诊断。 + + +#### 2.2 `fanutil.py` +`fanutil.py`主要实现风扇访问控制,支持转速查看、占空比设置等。需要实现接口如下: + +```python +class FanUtil(object): + def __init__(self): + pass + + # 获取设备支持的风扇数量 + def get_num_fans(self): + """ + Retrieves the number of FANs supported on the device + """ + return FAN_NUM + + # 获取风扇在位状态 + def get_fan_presence(self, index): + """ + Retrieves the presence status of FAN defined by 1-based index + @index: An integer, 1-based index of the FAN + @return: Boolean, True if FAN is plugged, False if not + """ + return True/False + + # 获取风扇信息 + def get_all(self): + """ + Retrieves the FANs information + @return DICT + """ + return { + "Number": 1, + "FAN1": { + "Present": True, + "Running": True, + "Speed": 12000.0, + "pwm": 124, + "LowThd": 5400.0, + "HighThd": 20000.0, + "SN": "serial_number_example", # 'N/A' + "PN": "part_number_exampple", # 'N/A' + "Status": True, + "AirFlow": "F2B", # 'N/A' + } + } + + # 修改风扇转速 + def set_speed(self, fan, pwm): + # @fan: fan index; + # @pwm: pwm value [0 ~ 100]; + return True/False + + # 设置风扇强制全速(最高优先级) + def set_full_speed(self, enable): + return True/False + + # 设置风扇控制模式(auto/manual) + def set_mode(self, mode): + return True/False + +``` + +#### 2.3 `psuutil.py` +`psuuti.py`主要实现`PSU`状态获取。 + +```python +class PsuUtil(PsuBase): + + # 获取设备支持的PSU数量 + def get_num_psus(self): + """ + Retrieves the number of PSUs supported on the device + """ + return PSU_NUM + + # 获取PSU状态 + def get_psu_status(self, index): + """ + Retrieves the operational status of PSU + @index: An integer, 1-based index of PSU of which to query status + @return: Boolean, True if PSU is operating properly, False if PSU is faulty + """ + return True/False + + # 获取PSU信息 + def get_all(self): + return { + "Number": PSU_NUM, + "PSU1": { + "Present": True, + "InputType": "AC" or "DC", + "InputStatus": True/False, + "OutputStatus": True/False, + "AirFlow": "F2B", # 'N/A' + "SN": "serial_number_example", # 'N/A' + "PN": "part_number_exampple", # 'N/A' + "Vendor": "Vendor_name", # 'N/A' + "HwVersion": "1.0", # 硬件版本信息,'N/A' + "FwVersion": "1.0", # 固件版本信息,'N/A' + "Fan": { + "Value": 1100, + "LowThd": 100, + "HighThd": 2000, + "Unit": "rpm" + }, + "Temperature": { + "Value": 1100, + "LowThd": 100, + "HighThd": 2000, + "Unit": "C" + }, + "InputsPower": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'W', + }, + "OutputsPower": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'W', + }, + "InputsVoltage": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'V', + }, + "OutputsVoltage": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'V', + }, + "InputsCurrent": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'A', + }, + "OutputsCurrent": { + "Value": 111.1, + "LowThd": 111.1, + "HighThd": 111.1, + "Unit": 'A', + } + } + } + + # 设置PSU风扇转速(PSU FAN的最低转速修改) + def set_psu_fan_speed(self, psu_index, pwm): + """ + set psu fan speed. + @psu_index: An integer, 1-based index of PSU + @pwm: An integer, rang is [0 ~ 100] + """ + return True/False + +``` + +#### 2.4 `sensorutil.py` +`sensorutil.py` 主要实现`sensor`的访问。接口如下: +```python +class SensorUtil(object): + def __init__(self): + pass + + # 获取设备支持的sensor数量 + def get_num_sensors(self): + """ + Retrieves the number of Sensors supported on the device + """ + return SENSOR_NUM + + # 获取所有sensor状态 + def get_all(self): + return { + "EMC1403-I2C-3-5C":{ + "Present":True, + "CPU_TEMP": { + "Value": 12.0, + "LowThd": 54.0, + "HighThd": 65.0, + "Unit": "C", + "Description":"CPU TEMP" + }, + "Inlet_Temp": { + "Value": 12.0, + "LowThd": 54.0, + "HighThd": 65.0, + "Unit": "C", + "Description":"SMB Inlet TEMP" + }, + }, + "ADC128D818-I2C-9-1F":{ + "Present":True, + "P3V3_SW": { + "Value": 3.2, + "LowThd": 3.0, + "HighThd": 3.4, + "Unit": 'V', + "Description":"" + }, + }, + "PSU-I2C-4-59":{ + "Present":True, + "PSU1_CIN": { + "Value": 23.0, + "LowThd": 0, + "HighThd": 25, + "Unit": 'A', + "Description":"" + }, + "PSU1_VIN": { + "Value": 220, + "LowThd": 182, + "HighThd": 290, + "Unit": 'V', + "Description":"" + }, + "PSU1_PIN": { + "Value": 550, + "LowThd": 0, + "HighThd": 600, + "Unit": 'W', + "Description":"" + }, + }, + } +``` + +#### 2.5 `fwmgrutil.py` +`fwmgrutil.py` 主要实现固件版本查看、固件升级刷新,`FRU`读写访问等功能。接口如下: + +```python +class FwMgrUtil(object): + def __init__(self): + pass + + # 固件版本查看接口 + def get_fw_version(self): + """ + Retrieves all firmwares' version on the device + @return DICT, key=firmware name, value=DICT,include "version" and "description" + """ + return + { + "BIOS":{ + "Desc":"Basic Input/Output System", + "Version":"2.0.1" + }, + "BSP":{ + "Desc":"BSP Driver", + "Version":"2.0.1" + }, + "CPU_CPLD":{ + "Desc":"COMe Board CPLD", + "Version":"2.0.2" + }, + "CPU_IR3584_1":{ + "Desc":"CPU PVDDQ VR", + "Version":"2.0.0" + }, + } + + # 获取下次启动Flash以及Flash主备切换 + # BIOS + def get_bios_current_boot_flash(self): + return "master"/"slave" + + def get_bios_next_boot_flash(self): + return "master"/"slave" + + def set_bios_next_boot_flash(self, flash): + return True/False + + # Uboot + def get_uboot_current_boot_flash(self): + return "master"/"slave" + + def get_uboot_next_boot_flash(self): + return "master"/"slave" + + def set_uboot_next_boot_flash(self, flash): + return True/False + + # BMC + def get_bmc_current_boot_flash(self): + return "master"/"slave" + + def get_bmc_next_boot_flash(self): + return "master"/"slave" + + def set_bmc_next_boot_flash(self, flash): + return True/False + + # 固件更新 + def firmware_upgrade(self, fw_type, fw_path, fw_extr=None): + """ + @fw_type: firmware type, should be one of the strings:'bios', 'uboot', 'bmc', 'cpld', .etc. + @fw_path: target firmware file + @fw_extra OPTIONAL, extra information string, for fw_type 'BIOS'/'Uboot'/'BMC', value should be one of 'master'/'slave'/'both' + @return: Boolean + """ + return True/False + + def firmware_refresh(self, fw_type, fw_path, fw_extra=None): + return True/False + + # 获取FRU列表 + def get_frus(self): + """ + Retrieves all FRU names on the device + @return: LIST + """ + return FRU_NAME_LIST # ['sys', 'smb', 'pdb'] + + # 获取FRU内容 + def read_fru(self, fru_name): + """ + Read the FRU content. + @fru_name: string, the name of FRU + @return: TUPLE,(status, output), status=True if FRU acceed success, + output=string of the FRU content. + """ + return statue, content + + # 烧录FRU + def program_fru(self, fru_name, fru_bin_file): + """ + program the FRU + @fru_name: the name of FRU + @fru_bin_file: the file of FRU raw binary. + @return: True if programed success. + """ + return True/False +``` + +#### 2.6 `led_control.py` +`led_control.py`主要实现前面板`LEDs`和端口`LEDs`的亮灭控制,具体判断需要肉眼观察。接口如下: + +```python +class LedControl(LedControlBase): + # 设置panel sys led + def set_sys_led(self, color): + """ + @color: string,should be one of "red"/'green'/'yellow' , hardware supported color + """ + return True/False + + # 设置panel power led + def set_power_led(self, color): + return True/False + + # 设置panel fan led + def set_fan_led(self, color): + return True/False + + # 设置panel bmc led + def set_bmc_led(self, color): + return True/False + + # 设置port leds + def set_port_led(self, color): + return True/False + + # 设置 fan module led + def set_fan_module_led(self, color): + return True/False + + # 设置 psu module led + def set_psu_module_led(self, color): + return True/False + + # 设置panel location led + def set_location_led(self, color): + return True/False + + # 获取支持的LEDs列表 + def get_leds(self): + return LED_NAME_LIST #['sys', 'power', 'fan', 'port'] + +``` + +#### 2.7 `mgmtport.py` +`mgmtport.py`主要实现管理网口`eth0`的访问控制,接口如下: + +```python +class MgmtPortUtil(object): + def __init__(self): + pass + + # 获取管理口状态信息 + def get_all(self): + """ + Retrieves the management port running status + @return: DICT + """ + return { + "Link": True/False, + "Firmware": "3.25", + "Speed": "1000" or "100" or "10", + "Duplex": "full" or "half", + "AN": True/False, + } + +``` +管理口的管理如果在`CPU`侧,`CPU`可以直接通过读写`PHY`寄存器的形式管理管理网口;如果在`BMC`侧,则需要通过`restful`接口控制。 + +#### 2.8 `bmcutil.py` +`bmcutil.py`主要实现`BMC`侧的访问控制以及`BMC`系统的`diag`测试。 + +```python +class BmcUtil(object): + def __init__(self): + pass + + # 执行BMC diag测试 + def run_bmc_diag(self, case=None): + """ + Run bmc test cases + @case: case name; if not specified case name, then run all cases. + @return: TUPLE (status, output), status=Boolean, output=string of case returned. + """ + if not case: + test_all() + else: + test_one(case) + return (status, output) + + # 重启BMC + def reboot_bmc(self): + return True/False + + # 执行BMC系统下的command + def exec_raw_cmd(self, command): + """ + Run command under BMC OS + @command: command string, + @return: TUPLE (status, output), status=Boolean, output=string of case returned. + """ + return (status, output) + +``` + +#### 2.9 `fruidutil.py` FRU读写工具 +`fruidutil.py`用于fru信息的读取和写入,重构.c 文件的output file给出的相关命令,fru文件需要先从 src 目录放到 /usr/local/bin 目录下: + +``` Shell + +""" + FRU Commands: list read write edit + fru list - list all fru + fru list - list specific fru data + fru read - store fru data to file + fru write - write fru data from file + fru edit field
- edit FRU string + + You should move fru file to /usr/local/bin first +""" + +""" +fruidutil get [NODE+NUM] + set [NODE+NUM] -f + +OPTIONS: + option fru information + node fru node name + -f Path of xxx.bin file + -h Print use guide +""" +``` + +#### 2.10 `syseeprom-write.py` +`syseeprom-write.py`用于syseeprom信息的写入: + +``` Shell + +''' + write tlv eeprom to eeprom + + Usage: + syseeprom-write.py + + e.g.: + syseeprom-write.py ../config/common/syseeprom_test.json +''' +``` + + +### 3 `sysdiag` 实现 +`sysdiag`目录结构如下: + +```shell +. +├── readme.md +├── cases +│ ├── cpu_tc +│ │ └── config.json +│ ├── memory_tc +│ │ └── config.json +│ ├── rtc_tc +│ │ └── config.json +│ ├── ssd_tc +│ ├── replaceable_eeprom_read_tc +│ │ └── config.json +│ ├── replaceable_eeprom_write_tc +│ │ └── config.json +│ ├── slot_eeprom_read_tc +│ │ └── config.json +│ ├── slot_eeprom_write_tc +│ │ └── config.json +│ └── ... +│ └── config.json +├── config +│ └── platform +│ ├── arm64-alibaba_as61_48e4t_lc-r0 +│ │ ├── case_config.json +│ │ └── platform_config.json +│ ├── arm64-alibaba_as61_48x4t_lc-r0 +│ ├── x86_64-h3c_s9825-64d-w1-r0 +│ │ ├── case_config.json +│ │ └── platform_config.json +│ └── ... +│ ├── case_config.json +│ └── platform_config.json +└── src + ├── cpu_tc.py + ├── tlv_eeprom_read_tc.py + ├── errcode.py + ├── sysdiag.py + ├── ... +``` + +其中,所有测试项的实现代码`**_tc.py`都在目录`src/`下。 +`test_case.py`为定义的基础类,实现基本的测试项功能定义。 +`sysdiag.py`是运行测试的主程序,主要实现输入参数解析、测试项的创建及执行、输出测试结果。 + +`cases/`目录是`sysdiag`的所有测试用例`**_tc`的通用配置。 +`config/`目录是`sysdiag`的平台`platform`测试配置。 +各配置文件说明如下: + +* 测试项配置: + + * `config.json`:此文件为测试项的描述,可参考Ali设计。 + + ```json + { + "name": "tlv-eeprom-test", //测试项名称 + "description": "Test system TLV EEPROM", //测试项功能描述 + "type": "auto", //测试类型,自动化测试 + "tags": ["production", "delivery"], //测试标签,属于生产测试和交付测试共同 + "expectation": { //期望的结果 + "override": "enable", + "Serial Number": { + "type": "string", + "prefix": "R1241FCL", + "subfix": "none", + "keywords": [], + "length": 19 + }, + "Part Number": { + "type": "regex", + "pattern": "R1241-F[A-Z]{4}-[0-9]{2}" + } + } + } + ``` + + + +* 平台配置: + + * `platform_config.json`:此文件描述了平台测试项内容,包括哪些测试项,如下 + + ```json + { + "test_cases": [ //待测试项列表,说明该平台需要测试的内容 + "i2c_tc", + "emmc_tc", + "tlv_eeprom_tc", + "tlv_eeprom_read_tc", + "tlv_eeprom_write_tc", + "replaceable_eeprom_read_tc", + "replaceable_eeprom_write_tc", + "slot_eeprom_read_tc", + "slot_eeprom_write_tc" + ] + } + ``` + + * `case_config.json`:此文件描述了平台测试项的具体配置详情,比如`i2c`设备拓扑,举例如下: + + ```json + { + "i2c":{ + "CTRL_CPLD" : { + "bus" : 2, + "address" : "0x0d", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT1_CPLD" : { + "bus" : 3, + "address" : "0x30", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT2_CPLD" : { + "bus" : 4, + "address" : "0x31", + "register" : "0x0F", + "flag" : "rw" + } + }, + } + ``` + + + + + +#### 3.1 `syslogger`日志类 +定义了日志类`SysLogger`,默认打印日志到`/var/log/sysdiag.log`中,文件大小`20M`,超过之后轮转,只保留5份日志。 + +提供`log_dbg`、`log_info`、`log_warn`、`log_err`、`log_crit` 5种级别打印。默认只输出到日志中,可以追加参数`True`同步打印到终端。 + + + +#### 3.2 主类`sysdiag` + +`CLI`界面使用说明如下: + +```shell +$ sysdiag -h +================================================================= + ____ ___ _____ ____ ____ _ + | _ \_ _|_ _| / ___| _ _ ___| _ \(_) __ _ __ _ + | |_) | | | | _____ \___ \| | | / __| | | | |/ _` |/ _` | + | __/| | | | |_____| ___) | |_| \__ \ |_| | | (_| | (_| | + |_| |___| |_| |____/ \__, |___/____/|_|\__,_|\__, | + |___/ |___/ +================================================================= + + 2021-01-15 17:56:33 + +sysdiag + + System diagnostic test utility + +Usage: + sysdiag [FUNCTION] [OPTIONS...] + +FUNCTION: + -v, --version Show sysdiag version + -d, --diag [-g ] [-t ] Run diagnostic test, default tag is delivery + -s, --single Run single test, test case file should be given + -l, --list [-g ] Show all possible cases, default tag is delivery + +OPTIONS: + -h, --help Print sysdiag user guide + -c, --chinese Test outputs in Chinese + -q, --quiet don't print status messages to stdout") + -t, --type Specify type of test cases, default type is auto + -g, --tag Specify tag of test cases, default tag is delivery + -i, --interval Set test interval when loop test, unit minute + -b, --break Break when error occured under performance test + -p, --pre handle pre handle + +``` + + + +首先定义了`SysDiag`运行类。该类接收用户输入`sys.argv`,根据输入参数,决定下一步测试动作。 + +第二步,解析输入参数`parse_opts_and_args()`;支持的输入参数有`-d/-l/-s`等。 + +第三步,根据参数生成测试类列表`generate_test_case_list()`;这一步使用`imp.load_source()`、`getattr()`来实现测试类的实例化,并把实例化的类加入测试列表`self.test_case_list`中。 + +第四步,执行测试`run()`。根据用户输入的参数,决定是执行生产测试项还是交付测试项,还是只执行单项测试。 + +第五步,生成测试总结果`generate_final_result()`。 + + + +#### 3.3 `test_case` 设计 +`test_case.py`文件定义了`class TCBase`基类,此类主要实现对测试项配置文件`config.json`的解析,执行具体测试过程,生成测试结果。 + +成员函数有: + +* `def __init__(self, index, case_cfg_file)`:构造函数,索引和配置文件路径,构造函数会根据传入的配置文件路径,解析出里面的`json`内容,并提供`get_tc_*()` `API`; + +* **`def run_test(self, *argv):`**:需要用户实现的具体测试过程,并返回测试结果状态码; + +* `def generate_result(self, code):`:已经设计好的格式化测试结果。 + + ```python + def generate_result(self, code): + result = "Pass" if code==E.OK else "Fail" + msg = "Test case {}: [{}] =======> {}".format(self.case_index, self.get_tc_name(), result) + err = "ErrorCode: {}\nFail reasons: {}".format(code.name, self.fail_reason) + return msg, err + ``` + +* `def generate_result_dict(self,code)`:已经设计好的`json`格式测试结果返回函数。 + +特别说明,`run_test()`函数,是测试过程的具体实现,额外还需要实现对测试结果的赋值,用于测试结果输出打印。 + +* `self.fail_reason = [*, *, *]`,测试失败的原因列表。 +* `return`:返回测试结果状态码,`E.OK`表示成功,其他值失败。 + + + +`test_case.py`文件还定义了测试类`class TestCaseCommon`,继承自`TCBase`,主要增加了对平台配置文件`case_config.json`的解析。`TestCaseCommon`是每个`test_case`实现时需要继承的基础类,用户需要根据构造函数需要传入指定参数。 + +* `TestCaseCommon`: + + 构造函数:`__init__(self, index, module_name, logger, platform_cfg_file, case_cfg_file=None)`,构造函数会自动加载平台配置文件中的`json`内容。 + + 参数说明: + + * `@index`: 测试项索引,每个测试项都会分配一个ID; + * `@module_name`: 测试项名称,**此名称和测试项配置目录名一致**; + * `@logger`: 日志打印类,可以直接传值第4章的`logger`; + * `@platform_cfg_file`:平台下的测试项详细配置文件,` config/platform/***/case_config.json` + * `@case_cfg_file`: 测试项的配置文件, 缺省状态下会直接访问`cases/${module_name}/config.json` + + + +文件命名和类名定义约束: + +1. 测试项`py`文件命名,请按格式`xxxx_tc.py`,`xxxx`指定测试项名称,例如风扇测试项`fan_tc.py`; + +2. 测试项中类名定义请按格式`class XXXXTC(TestCaseCommon)`,其中`TC`是`testcase`的缩写,`XXXX`是测试项文件名去掉中间连接线`_`之后的单词大写,例如风扇测试项`fan_tc.py`中类定义为`class FANTC(TestCaseCommon)`, + +3. 测试类构造函数传值时,`module_name`的赋值和测试项配置目录名一致,例如风扇测试项的构造函数实现如下,其中`MODULE_NAME="fan_tc"`,`fan_tc`即是`config/cases/fan_tc/case_config.json`中的测试项配置目录名称。 + + ``` + class FANTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "fan_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + ``` + + + +举个例子,如何实现一个`i2c`测试项。 + +首先分析需求,`i2c`测试,需要访问验证`i2c bus`下的从设备(涉及`cpld`、`sfp eerpom`、`fru`、`rtc`等)是否工作正常,对于`cpld`可以通过读写测试寄存器验证,其他设备可以读测试。 + +基于需要,`i2c`测试类继承`TestCaseCommon`,并需要实现接口`run_test()`。 + +并且,`i2c`测试还需要该平台下的`i2c`拓扑,故在`config/platform/*/case_config.json`中增加`i2c`配置如下: + +```json +{ + "i2c":{ + "CTRL_CPLD" : { + "bus" : 2, + "address" : "0x0d", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT1_CPLD" : { + "bus" : 3, + "address" : "0x30", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT2_CPLD" : { + "bus" : 4, + "address" : "0x31", + "register" : "0x0F", + "flag" : "rw" + } + } +} +``` + +定义`i2c`测试类如下: + +```python +class I2CTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "i2c_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + try: + if self.platform_cfg_json and 'i2c' in self.platform_cfg_json.keys(): + self.i2c_devices = self.platform_cfg_json['i2c_topo'] + except Exception as e: + self.logger.log_err(str(e)) + self.i2c_devices = None +``` + +并实现`i2c rw`函数如下,遍历访问设备,对于读写失败的设备,记录失败原因到`fail_reason`列表中。 + +```python +def i2c_device_rw_test(self, also_print_console=False): + ''' + i2c device read/write test + ''' +``` + +`run_test`函数中调用`i2c_device_rw_test`即可; + + + +#### 3.4 错误码定义 +文件`errorcode.py`,错误码定义采用枚举定义的方法,定义常见的错误类型。 + +如下: +```python +class E(Enum): + OK = 0 # pass + EFAIL = 1 # fail + EUSB1000 = 1000 # usb not found + EUSB1001 = 1001 # usb write error + ... +``` +测试项`xxxx_tc.py`直接引入该模块。 + +#### 3.5 测试项`xxxx_tc.py`实现参考 + +```python +from test_case import TestCaseCommon +from errcode import E + +class XXXXTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "xxxx_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + def test_a(self): + if fail: + self.fail_reason.append("usb not found") + return E.EUSB1000 + else: + return E.OK + + def test_b(self): + if fail: + self.fail_reason.append("usb write error") + return E.EUSB1001 + else: + return E.OK + + # 必须实现的函数 + def run_test(self, *argv): + ret = self.test_a() + if ret != E.OK: + return ret.value + ret = self.test_b() + return ret.value + +``` diff --git a/src/sonic-pit/pit-sysdiag/cases/bios_firmware_program_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/bios_firmware_program_tc/config.json new file mode 100755 index 00000000000..4766b0b92e4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/bios_firmware_program_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "bios-firmware-program-test", + "description": "bios firmware program test", + "type": "manual", + "tags": ["manufacture", "development"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/biosme_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/biosme_tc/config.json new file mode 100644 index 00000000000..8b73c5f1418 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/biosme_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "biosme-test", + "description": "bios me contrl test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/bmc2cpu/config.json b/src/sonic-pit/pit-sysdiag/cases/bmc2cpu/config.json new file mode 100644 index 00000000000..d020a6fffa4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/bmc2cpu/config.json @@ -0,0 +1,6 @@ +{ + "name": "bmc2cpu", + "description": "Check ping info", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/bmcupgrade_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/bmcupgrade_tc/config.json new file mode 100644 index 00000000000..b44c7b353a5 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/bmcupgrade_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "bmcupgrade-test", + "description": "Bmc upgrade and checkout test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/common/tc-tlv-eeprom/config.json b/src/sonic-pit/pit-sysdiag/cases/common/tc-tlv-eeprom/config.json new file mode 100644 index 00000000000..ee2db5e5f19 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/common/tc-tlv-eeprom/config.json @@ -0,0 +1,20 @@ +{ + "name": "tlv-eeprom-test", + "description": "Test system TLV EEPROM", + "type": "auto", + "tags": ["production", "delivery"], + "expectation": { + "override": "enable", + "Serial Number": { + "type": "string", + "prefix": "R1241FCL", + "subfix": "none", + "keywords": [], + "length": 19 + }, + "Part Number": { + "type": "regex", + "pattern": "R1241-F[A-Z]{4}-[0-9]{2}" + } + } +} diff --git a/src/sonic-pit/pit-sysdiag/cases/cpld_firmware_program_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/cpld_firmware_program_tc/config.json new file mode 100755 index 00000000000..aaaaf6fd2d8 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/cpld_firmware_program_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "cpld-firmware-program-test", + "description": "cpld firmware program test", + "type": "manual", + "tags": ["manufacture", "development"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/cpu2external/config.json b/src/sonic-pit/pit-sysdiag/cases/cpu2external/config.json new file mode 100644 index 00000000000..b01b5c1f4bc --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/cpu2external/config.json @@ -0,0 +1,6 @@ +{ + "name": "cpu2external", + "description": "Check ping info", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/cpu_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/cpu_tc/config.json new file mode 100644 index 00000000000..b1784294432 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/cpu_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "cpu-test", + "description": "Check CPU information", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/dac_pin_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/dac_pin_tc/config.json new file mode 100644 index 00000000000..2a48abc4def --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/dac_pin_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "dac_pin_tc", + "description": "Check dac pin info", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/emmc_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/emmc_tc/config.json new file mode 100644 index 00000000000..6a5ac1db21d --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/emmc_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "emmc-test", + "description": "Check EMMC storage", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/fan_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/fan_tc/config.json new file mode 100644 index 00000000000..1828876d176 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/fan_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "fan-test", + "description": "Check fan status", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/firmware_version_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/firmware_version_tc/config.json new file mode 100644 index 00000000000..14e2bc05eea --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/firmware_version_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "firmware-version-test", + "description": "firmware version info", + "type": "auto", + "tags": ["manufacture", "delivery", "emc", "power", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/fpga_firmware_program_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/fpga_firmware_program_tc/config.json new file mode 100755 index 00000000000..e83d04a7eea --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/fpga_firmware_program_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "fpga-firmware-program-test", + "description": "fpga firmware program test", + "type": "manual", + "tags": ["manufacture", "development"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/gpio_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/gpio_tc/config.json new file mode 100644 index 00000000000..63782a8437d --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/gpio_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "gpio-test", + "description": "Check gpios function and test jtag", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/i2c_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/i2c_tc/config.json new file mode 100644 index 00000000000..b75f3e2909b --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/i2c_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "i2c-test", + "description": "Check i2c devices under I2C bus", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/internal_usb_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/internal_usb_tc/config.json new file mode 100644 index 00000000000..482bbdacaf3 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/internal_usb_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "internal_usb-test", + "description": "Check usb bus between CPU and BMC", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/lpc_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/lpc_tc/config.json new file mode 100644 index 00000000000..afe9cbeff2a --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/lpc_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "lpc-test", + "description": "Check lpc devices under LPC bus", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/mac_address_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/mac_address_tc/config.json new file mode 100644 index 00000000000..febb4838e29 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/mac_address_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "mac-address-test", + "description": "Check mac address", + "type": "auto", + "tags": ["manufacture", "delivery", "emc", "power", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/memory_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/memory_tc/config.json new file mode 100644 index 00000000000..f079c826a1c --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/memory_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "memory-test", + "description": "Check memory and pattern test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/memstress_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/memstress_tc/config.json new file mode 100644 index 00000000000..b848840f405 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/memstress_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "memstress-test", + "description": " Memory Stress Test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc", "stress"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/mgmt_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/mgmt_tc/config.json new file mode 100644 index 00000000000..a455222e88f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/mgmt_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "mgmtport-test", + "description": "Check mgmt port function", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/netstress_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/netstress_tc/config.json new file mode 100644 index 00000000000..aca1b5e540a --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/netstress_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "netstress-test", + "description": "Check Net Stress Test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power", "stress"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/oob_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/oob_tc/config.json new file mode 100644 index 00000000000..a16c01512d3 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/oob_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "oob-test", + "description": "l2 mgmt switch test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/optical_pin_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/optical_pin_tc/config.json new file mode 100644 index 00000000000..938ef64c61c --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/optical_pin_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "optical_pin_tc", + "description": "Check optical pin info", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/pcie_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/pcie_tc/config.json new file mode 100644 index 00000000000..5481232c8ba --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/pcie_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "pcie-test", + "description": "Check PCIe devices", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/pciestress_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/pciestress_tc/config.json new file mode 100644 index 00000000000..e594ec09d25 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/pciestress_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "pciestress-test", + "description": "pcie stress test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc", "stress"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/psu_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/psu_tc/config.json new file mode 100644 index 00000000000..184826ea02c --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/psu_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "psu-test", + "description": "check psu status", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_read_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_read_tc/case_config.json new file mode 100755 index 00000000000..b60fe4231bf --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_read_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "replaceable_eeprom_read-test", + "description": "Check fru eeprom's key&value", + "type": "auto", + "tags": ["manufacture", "delivery", "development", "pa", "power"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_write_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_write_tc/case_config.json new file mode 100755 index 00000000000..a8a8b306501 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/replaceable_eeprom_write_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "replaceable_eeprom_write-test", + "description": "Check eeprom write function", + "type": "auto", + "tags": ["manufacture", "development", "pa", "power"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/rtc_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/rtc_tc/config.json new file mode 100644 index 00000000000..baa81c1841f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/rtc_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "rtc-test", + "description": "Check RTC function", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/sensor_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/sensor_tc/config.json new file mode 100644 index 00000000000..9da2d1d588d --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/sensor_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "sensor-test", + "description": "Check sensors health", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "power", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/sfp_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/sfp_tc/config.json new file mode 100644 index 00000000000..d7079c0242b --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/sfp_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "sfp-test", + "description": "Check SFP modules and access test", + "type": "auto", + "tags": ["manufacture", "delivery", "emc", "power", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_read_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_read_tc/case_config.json new file mode 100755 index 00000000000..93f228049b1 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_read_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "slot_eeprom_read-test", + "description": "Check fru eeprom's key&value", + "type": "auto", + "tags": ["manufacture", "delivery", "development", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_write_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_write_tc/case_config.json new file mode 100755 index 00000000000..bdf1fda7db4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/slot_eeprom_write_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "slot_eeprom_write-test", + "description": "Check eeprom write function", + "type": "auto", + "tags": ["manufacture", "development", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/snake_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/snake_tc/config.json new file mode 100644 index 00000000000..d2b3f8927b7 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/snake_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "snake-test", + "description": "Port snake Test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/software_system_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/software_system_tc/config.json new file mode 100644 index 00000000000..3ef3348fe60 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/software_system_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "software-system-test", + "description": "Check software system function", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/ssd_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/ssd_tc/config.json new file mode 100644 index 00000000000..a7f83ca9694 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/ssd_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "ssd-test", + "description": "Check SSD capacity", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/ssdstress_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/ssdstress_tc/config.json new file mode 100644 index 00000000000..ef317a18b13 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/ssdstress_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "ssdstress-test", + "description": "Check SSD Stress Test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power", "stress"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/svid_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/svid_tc/config.json new file mode 100644 index 00000000000..ea777cf0f53 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/svid_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "svid-test", + "description": "Switch Chip Voltage Test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/switch_firmware_program_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/switch_firmware_program_tc/config.json new file mode 100755 index 00000000000..eedd0f3203f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/switch_firmware_program_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "switch-firmware-program-test", + "description": "switch pcie firmware program test", + "type": "manual", + "tags": ["manufacture", "development"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_read_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_read_tc/case_config.json new file mode 100755 index 00000000000..aced7300396 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_read_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "tlv_eeprom_read_-test", + "description": "Check eeprom and mac test", + "type": "auto", + "tags": ["manufacture", "delivery", "development", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_write_tc/case_config.json b/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_write_tc/case_config.json new file mode 100755 index 00000000000..8020c5de88a --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/tlv_eeprom_write_tc/case_config.json @@ -0,0 +1,6 @@ +{ + "name": "tlv_eeprom_write_test", + "description": "write tlv-eeprom and check", + "type": "auto", + "tags": ["manufacture", "development", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/traffic_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/traffic_tc/config.json new file mode 100644 index 00000000000..280e774dde5 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/traffic_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "traffic-test", + "description": "Centec traffic test", + "type": "manual", + "tags": ["manufacture", "delivery"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/usb_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/usb_tc/config.json new file mode 100644 index 00000000000..a635af0244f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/usb_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "usb-test", + "description": "Check USB r/w", + "type": "auto", + "tags": ["manufacture", "delivery", "emc", "pa"] +} diff --git a/src/sonic-pit/pit-sysdiag/cases/usbstress_tc/config.json b/src/sonic-pit/pit-sysdiag/cases/usbstress_tc/config.json new file mode 100644 index 00000000000..131a3a71ec4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/cases/usbstress_tc/config.json @@ -0,0 +1,6 @@ +{ + "name": "usbstress-test", + "description": "Usb Stress test", + "type": "auto", + "tags": ["manufacture", "delivery", "pa", "emc", "power", "stress"] +} diff --git a/src/sonic-pit/pit-sysdiag/config/common/slot_test.bin b/src/sonic-pit/pit-sysdiag/config/common/slot_test.bin new file mode 100755 index 00000000000..6a40c2c2c26 Binary files /dev/null and b/src/sonic-pit/pit-sysdiag/config/common/slot_test.bin differ diff --git a/src/sonic-pit/pit-sysdiag/config/common/syseeprom_orig.json b/src/sonic-pit/pit-sysdiag/config/common/syseeprom_orig.json new file mode 100755 index 00000000000..e69de29bb2d diff --git a/src/sonic-pit/pit-sysdiag/config/common/syseeprom_test.json b/src/sonic-pit/pit-sysdiag/config/common/syseeprom_test.json new file mode 100755 index 00000000000..87b9d2602a4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/common/syseeprom_test.json @@ -0,0 +1,19 @@ +{ + + "Base MAC Address": "88:2A:5E:C9:5B:57", + "ONIE Version": "2.5.01", + "Platform Name": "x86_64-h3c_s9825-64d-w1-r0", + "Serial Number": "219801A34H6216V00008", + "Product Name": "S9825-64D-W1-ZZY", + "Part Number": "9801A34H", + "Manufacturer": "H3C", + "Manufacture Date": "06/22/2021 01:01:01", + "Manufacture Country": "CHN", + "Device Version": "8", + "Vendor Name": "H3C", + "Diag Version": "0.1.0.16", + "Service Tag": "www.h3c.com", + "Label Revision": "R01", + "MAC Addresses": "5", + "Vendor Extension": "" +} diff --git a/src/sonic-pit/pit-sysdiag/config/common/test.bin b/src/sonic-pit/pit-sysdiag/config/common/test.bin new file mode 100755 index 00000000000..6a40c2c2c26 Binary files /dev/null and b/src/sonic-pit/pit-sysdiag/config/common/test.bin differ diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/CTRL_CPLD.vme b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/CTRL_CPLD.vme new file mode 100644 index 00000000000..9c2a0599dea Binary files /dev/null and b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/CTRL_CPLD.vme differ diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/case_config.json new file mode 100644 index 00000000000..1ed171fdb53 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/case_config.json @@ -0,0 +1,66 @@ +{ + "asic": "centec", + "arch": "arm64", + "cpu_info": { + "BogoMIPS":"40", + "CPU(s)": "2" + }, + "memory_free_size": 100, + "emmc_bom":[ + { + "manufacturer": "Toshiba", + "pn": "064G02" + } + ], + "i2c":{ + "CTRL_CPLD" : { + "bus" : 2, + "address" : "0x0d", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT_CPLD" : { + "bus" : 3, + "address" : "0x30", + "register" : "0x0F", + "flag" : "rw" + } + }, + "gpio":{ + "gpio504":{ + "pin":"GPIO8", + "net":"CTC7132_CPLD_JTAG_EN" + }, + "gpio505":{ + "pin":"GPIO9", + "net":"CTC7132_CPLD_JTAG_TCK_R" + }, + "gpio506":{ + "pin":"GPIO10", + "net":"CTC7132_CPLD_JTAG_TMS_R" + }, + "gpio507":{ + "pin":"GPIO11", + "net":"CTC7132_CPLD_JTAG_TDI_R" + }, + "gpio508":{ + "pin":"GPIO12", + "net":"CTC7132_CPLD_JTAG_TDO" + }, + "gpio509":{ + "pin":"GPIO13", + "net":"CTC7132_I2CEXP0_REST_N" + }, + "gpio510":{ + "pin":"GPIO14", + "net":"CTC7132_I2CEXP1_REST_N" + } + }, + "jtag":["CTRL_CPLD"], + "server_ip": "192.168.1.100", + "fan_setting":{ + "tolerance": 25, + "stable_time": 10, + "pwm_range":[5, 100] + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/platform_config.json new file mode 100644 index 00000000000..111dc5ec891 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-lc-r0/platform_config.json @@ -0,0 +1,17 @@ +{ + "test_cases":[ + "cpu_tc", + "memory_tc", + "emmc_tc", + "rtc_tc", + "fan_tc", + "gpio_tc", + "i2c_tc", + "mgmt_tc", + "oob_tc", + "sensor_tc", + "sfp_tc", + "usb_tc", + "traffic_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/case_config.json new file mode 100644 index 00000000000..8d6e5add9a5 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/case_config.json @@ -0,0 +1,18 @@ +{ + "firmware_version":["bmc","cpld","onie","uboot","sdk"], + "bmc_version":"AliBMC-odm-obmc-rj", + "cpld_version":{ + "CTRL_MODULE_CPLD": "21071930", + "PORT_BOARD_CPLD": "21060517" + }, + "onie_version":"2018.05", + "uboot_version":"2.14", + "sdk_version":"5.5.9", + "mac_address": { + "offset":{ + "cpu_offset": 0, + "bmc_offset": 2 + }, + "check_list":["cpu", "bmc"] + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/platform_config.json new file mode 100644 index 00000000000..64dc096c7f9 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48e4t-rj-r0/platform_config.json @@ -0,0 +1,6 @@ +{ + "test_cases": [ + "mac_address_tc", + "firmware_version_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/CTRL_CPLD.vme b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/CTRL_CPLD.vme new file mode 100644 index 00000000000..763484ae43a Binary files /dev/null and b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/CTRL_CPLD.vme differ diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/case_config.json new file mode 100644 index 00000000000..4237e10db5e --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/case_config.json @@ -0,0 +1,72 @@ +{ + "asic": "centec", + "arch": "arm64", + "cpu_info": { + "BogoMIPS":"40", + "CPU(s)": "2" + }, + "memory_free_size": 100, + "emmc_bom":[ + { + "manufacturer": "Toshiba", + "pn": "064G02" + } + ], + "i2c":{ + "CTRL_CPLD" : { + "bus" : 2, + "address" : "0x0d", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT1_CPLD" : { + "bus" : 3, + "address" : "0x30", + "register" : "0x0F", + "flag" : "rw" + }, + "PORT2_CPLD" : { + "bus" : 4, + "address" : "0x31", + "register" : "0x0F", + "flag" : "rw" + } + }, + "gpio":{ + "gpio504":{ + "pin":"GPIO8", + "net":"CTC7132_CPLD_JTAG_EN" + }, + "gpio505":{ + "pin":"GPIO9", + "net":"CTC7132_CPLD_JTAG_TCK_R" + }, + "gpio506":{ + "pin":"GPIO10", + "net":"CTC7132_CPLD_JTAG_TMS_R" + }, + "gpio507":{ + "pin":"GPIO11", + "net":"CTC7132_CPLD_JTAG_TDI_R" + }, + "gpio508":{ + "pin":"GPIO12", + "net":"CTC7132_CPLD_JTAG_TDO" + }, + "gpio509":{ + "pin":"GPIO13", + "net":"CTC7132_I2CEXP0_REST_N" + }, + "gpio510":{ + "pin":"GPIO14", + "net":"CTC7132_I2CEXP1_REST_N" + } + }, + "jtag":["CTRL_CPLD"], + "server_ip": "192.168.1.100", + "fan_setting":{ + "tolerance": 25, + "stable_time": 10, + "pwm_range":[5, 100] + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/platform_config.json new file mode 100644 index 00000000000..111dc5ec891 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/arm64-alibaba_as61-48x4t-lc-r0/platform_config.json @@ -0,0 +1,17 @@ +{ + "test_cases":[ + "cpu_tc", + "memory_tc", + "emmc_tc", + "rtc_tc", + "fan_tc", + "gpio_tc", + "i2c_tc", + "mgmt_tc", + "oob_tc", + "sensor_tc", + "sfp_tc", + "usb_tc", + "traffic_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/case_config.json new file mode 100644 index 00000000000..ace1b01e0ab --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/case_config.json @@ -0,0 +1,29 @@ +{ + "fan_info":{ + "position" : "bmc", + "count" :6, + "direction":"in", + "ratio_target": [10,80,20], + "speed_tolerance": 1000, + "speed_max":20000, + "speed_min":0 + }, + "psu_info":{ + "position" : "bmc", + "count" :2, + + "in_power_min":0, + "in_power_max":0, + "in_vol_min":0, + "in_vol_max":0, + "in_curr_min":0, + "in_curr_max":0, + + "out_power_min":0, + "out_power_max":0, + "out_vol_min":0, + "out_vol_max":0, + "out_curr_min":0, + "out_curr_max":0 + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/platform_config.json new file mode 100644 index 00000000000..9e0fca7f5b3 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as14-40d-cl-r0/platform_config.json @@ -0,0 +1,6 @@ +{ + "test_cases": [ + "fan_tc", + "psu_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/case_config.json new file mode 100644 index 00000000000..ace1b01e0ab --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/case_config.json @@ -0,0 +1,29 @@ +{ + "fan_info":{ + "position" : "bmc", + "count" :6, + "direction":"in", + "ratio_target": [10,80,20], + "speed_tolerance": 1000, + "speed_max":20000, + "speed_min":0 + }, + "psu_info":{ + "position" : "bmc", + "count" :2, + + "in_power_min":0, + "in_power_max":0, + "in_vol_min":0, + "in_vol_max":0, + "in_curr_min":0, + "in_curr_max":0, + + "out_power_min":0, + "out_power_max":0, + "out_vol_min":0, + "out_vol_max":0, + "out_curr_min":0, + "out_curr_max":0 + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/platform_config.json new file mode 100644 index 00000000000..e5428deaff2 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-cl-r0/platform_config.json @@ -0,0 +1,6 @@ +{ + "test_cases": [ + "psu_tc", + "fan_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/case_config.json new file mode 100644 index 00000000000..0fe4acf475f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/case_config.json @@ -0,0 +1,30 @@ +{ + "firmware_version":["bmc","cpld","onie","bios","fpga","sdk","switch_pcie"], + "bmc_version":"AliBMC-odm-obmc-rj", + "cpld_version":{ + "40CCQ_CPLD_2": "21022313", + "40CCQ_CPLD_1": "21022314", + "44CCQ_CPLD_2": "21022313", + "CPU_BOARD_CPLD": "21102035", + "CPU_MODULE_CPLD": "20120713", + "44CCQ_CPLD_1": "21022314", + "MAC_BOARD_CPLD_2": "21022314", + "MAC_BOARD_CPLD_1": "21022315", + "CPU_BOARD_CPLD-FAN": "21012815" + }, + "onie_version":"2018.05", + "bios_version":"5.14(3CNHU021A0)", + "fpga_version":"7a500508-20201203", + "sdk_version":"6.5.21", + "switch_pcie_version":{ + "PCIE_FW": "D000_05", + "PCIE_FW_LOADER": "2.9" + }, + "mac_address": { + "offset":{ + "cpu_offset": 0, + "bmc_offset": 1 + }, + "check_list":["cpu", "bmc"] + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/platform_config.json new file mode 100644 index 00000000000..64dc096c7f9 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-alibaba_as24-128d-rj-r0/platform_config.json @@ -0,0 +1,6 @@ +{ + "test_cases": [ + "mac_address_tc", + "firmware_version_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/case_config.json new file mode 100755 index 00000000000..c6aed5d70a2 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/case_config.json @@ -0,0 +1,9 @@ +{ + "asic": "centec", + "arch": "arm64", + "cpu_info": { + "BogoMIPS":"4400", + "CPU(s)": "8" + }, + "memory_free_size": 100 +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/platform_config.json new file mode 100755 index 00000000000..8987e8f8434 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s6850-48y8c-w1-r0/platform_config.json @@ -0,0 +1,6 @@ +{ + "test_cases":[ + "eeprom_tc", + "tlv_eeprom_set_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/case_config.json new file mode 100755 index 00000000000..c6aed5d70a2 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/case_config.json @@ -0,0 +1,9 @@ +{ + "asic": "centec", + "arch": "arm64", + "cpu_info": { + "BogoMIPS":"4400", + "CPU(s)": "8" + }, + "memory_free_size": 100 +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/platform_config.json new file mode 100755 index 00000000000..34921b3baf6 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-h3c_s9825-64d-w1-r0/platform_config.json @@ -0,0 +1,10 @@ +{ + "test_cases":[ + "tlv_eeprom_read_tc", + "tlv_eeprom_write_tc", + "replaceable_eeprom_read_tc", + "replaceable_eeprom_write_tc", + "slot_eeprom_read_tc", + "slot_eeprom_write_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/case_config.json new file mode 100644 index 00000000000..9419148c4b6 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/case_config.json @@ -0,0 +1,161 @@ +{ + "asic": "centec", + "arch": "arm64", + "cpu_info": { + "model_cmd": "cat /proc/cpuinfo", + "cores_cmd": "cat /proc/cpuinfo | grep \"cpu cores\" | wc -l", + "MHz_cmd": "cat /proc/cpuinfo | grep \"cpu MHz\" | awk '{print $4}'", + "pattern": "model name\\s*:(\\s*.*)", + "model_name": "Intel(R) Xeon(R) CPU D-1627 @ 2.90GHz", + "CPU(s)": "8", + "cpu_MHz": "2.90GHz" + }, + + "memstress_info":{ + "mem_test_count": "1", + "mem_run_size_percentage": "90%", + "memstress_test_time": 1 + }, + + + "rtc_info":{ + "delay_time": 5, + "max_time_diff": 1 + }, + "net_info":{ + "link_speed": "1000Mb/s", + "cpu_remote_ip": "10.105.72.170", + "ip_pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "loss_pattern":"\\s+0%\\s+packet\\s+loss", + "bmc_usb0_ip": "10.105.72.43", + "bmc_username": "root", + "bmc_password": "0penBmc" + }, + "optical_info":{ + "optical_type":"QSFP", + "QSFP":{ + "optical_port":["200G","400G"], + "total_portnum":32, + "200G":{ + "port_num":[0,24], + "Identifier":["QSFP-DD Double Density 8X Pluggable Transceiver","QSFP28 or later"], + "Vendor_Name":["PA02QMA01-SD-R","LEONI"], + "Vendor_PN_len":[5,30], + "Vendor_SN_len":[5,30] + }, + "400G":{ + "port_num":[24,32], + "Identifier":["QSFP-DD Double Density 8X Pluggable Transceiver"], + "Vendor_Name":["LUXSHARE-ICT","Amphenol"], + "Vendor_Part_Number_len":[5,30], + "Vendor_Serial_Number_len":[5,30] + } + }, + "SFP":{ + "200G":{}, + "400G":{} + } + }, + "dac_info":{ + "dac_type":"QSFP", + "QSFP":{ + "dac_port":["200G","400G"], + "total_portnum":32, + "200G":{ + "port_num":[0,24], + "Identifier":["QSFP28 or later"], + "Vendor_Name":["LUXSHARE-TECH","Amphenol"], + "Vendor_PN_len":[5,30], + "Vendor_SN_len":[5,30] + }, + "400G":{ + "port_num":[24,32], + "Identifier":["QSFP-DD Double Density 8X Pluggable Transceiver"], + "Vendor_Name":["Molex"], + "Vendor_Part_Number_len":[5,30], + "Vendor_Serial_Number_len":[5,30] + } + }, + "SFP":{ + "200G":{}, + "400G":{} + } + + }, + "netstress_info":{ + "environment" : ["external", "bmc"], + "external_ip": "10.105.72.170", + "external_password": " ", + "bmc_password": "0penBmc", + "external_name": "wangmingyu", + "duration_value": "600", + "bmc_name": "root", + "bmc_ip": "10.105.72.43", + "net_stress_count": 1, + "net_stress_timeout": "610", + "net_standard_speed" : 50 + }, + "bmcupgrade_info": { + "ip":"10.105.72.43", + "flash_path":"/home/wmy/flash-s3ip", + "version_test":"0.04.00", + "bmc_pattern":"([0-9]+.[0-9]+.[0-9]+)" + }, + + "ssdstress_info": + { + "dev_path": "/stress.txt", + "rw_mode": "randread", + "io_size": "4k", + "num_jobs": "1", + "iop_pattern" : "iops=([0-9]+)", + "dev_pattern" : "/dev/sda3", + "ssd_run_size_percentage": "95%", + "iops_value_standard": "3000", + "ssd_test_count": 1, + "total_ssd_value": "32G" + }, + "usbstress_info":{ + "stress_test_time": 1 + }, + "pcie":{ + "X552":{ + "width":"x1", + "speed":"2.5GT/s", + "num":4 + } + }, + "fpga":{ + "FPGA1":{ + "num":1 + } + }, + "pciestress_info":{ + "stress_test_time": 1 + }, + "svid_info":{ + "high_voltage": 0.9, + "low_voltage": 0.7, + "normal_voltage": 0.8 + }, + "snake_info":{ + "sdk_path" : "/home/admin/5517_999_HSDK_0.07.00_20211210_S3IP", + "log_path" : "/var/log/sdk_rsh.log", + "tx_pkt_title" : "XLMIB_TPKT", + "rx_pkt_title" : "XLMIB_RPKT", + "package_type" :[ + { + "size" : 512, + "count" : 3200 + }, + { + "size" : 512, + "count" : 6400 + }, + { + "size" : 512, + "count" : 9600 + } + ] + } +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/platform_config.json new file mode 100644 index 00000000000..b94420a5be5 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-hq_zsr5517-r0/platform_config.json @@ -0,0 +1,18 @@ +{ + "test_cases":[ + "rtc_tc", + "netstress_tc", + "pciestress_tc", + "ssdstress_tc", + "memstress_tc", + "usbstress_tc", + "biosme_tc", + "svid_tc", + "cpu2external", + "bmc2cpu", + "dac_pin_tc", + "optical_pin_tc", + "snake_tc", + "bmcupgrade_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/case_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/case_config.json new file mode 100755 index 00000000000..2f418cd5165 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/case_config.json @@ -0,0 +1,119 @@ +{ + "software_system":{ + "sysdiag_version":"1.1", + "timezone":"Asia/Shanghai", + "sonic_version":"SONiC.dev_202012_td4_100g.0-dirty-20220118.203718" + }, + "cpld_firmware_type":["LED_CPLD1","LED_CPLD2","FAN_CPLD","SYS_CPLD"], + "fpga_firmware_type":["FPGA"], + "bios_firmware_type":["BIOS"], + "switch_firmware_type":["SWITCH_PCIE"], + "cpld_firmware_baseline":{ + "SYS_CPLD": { + "absolute_path":true, + "file_path": "/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme" + }, + "LED_CPLD1": { + "absolute_path":false, + "file_path": "" + }, + "LED_CPLD2": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + }, + "FAN_CPLD": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + } + }, + "fpga_firmware_baseline":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"" + } + }, + "bios_firmware_baseline":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "switch_firmware_baseline":{ + "SWITCH_PCIE": { + "absolute_path":false, + "file_path": "", + "version": "" + } + }, + "cpld_firmware_test":{ + "SYS_CPLD": { + "absolute_path":true, + "file_path": "/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"/etc/muxi/fw/8853-48cd8dq-w/components_fw/TD4_syscpld_v0b.25h.vme" + }, + "LED_CPLD1": { + "absolute_path":false, + "file_path": "", + "version": "" + }, + "LED_CPLD2": { + "absolute_path":false, + "file_path": "", + "version": "" + }, + "FAN_CPLD": { + "absolute_path":false, + "file_path": "", + "version": "" + } + }, + "fpga_firmware_test":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "bios_firmware_test":{ + "FPGA": { + "absolute_path":false, + "file_path": "", + "version": "", + "slave_upgrade":false, + "same_with_master":false, + "slave_file_path":"", + "slave_version":"" + } + }, + "switch_firmware_test":{ + "SWITCH_PCIE": { + "absolute_path":false, + "file_path": "", + "version": "" + } + } + +} diff --git a/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/platform_config.json b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/platform_config.json new file mode 100755 index 00000000000..0ef09667144 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/config/platform/x86_64-muxi_8853_48cd8dq_w-r0/platform_config.json @@ -0,0 +1,5 @@ +{ + "test_cases": [ + "fpga_firmware_program_tc" + ] +} diff --git a/src/sonic-pit/pit-sysdiag/src/__init__.py b/src/sonic-pit/pit-sysdiag/src/__init__.py new file mode 100644 index 00000000000..593b05e8ed8 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/__init__.py @@ -0,0 +1 @@ +__all__ = ["sysdiag"] diff --git a/src/sonic-pit/pit-sysdiag/src/banner.py b/src/sonic-pit/pit-sysdiag/src/banner.py new file mode 100644 index 00000000000..89cf67e176e --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/banner.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + + +banner_string = """ + ____ ___ _____ ____ ____ _ + | _ \_ _|_ _| / ___| _ _ ___| _ \(_) __ _ __ _ + | |_) | | | | _____ \___ \| | | / __| | | | |/ _` |/ _` | + | __/| | | | |_____| ___) | |_| \__ \ |_| | | (_| | (_| | + |_| |___| |_| |____/ \__, |___/____/|_|\__,_|\__, | + |___/ |___/ +""" diff --git a/src/sonic-pit/pit-sysdiag/src/bios_firmware_program_tc.py b/src/sonic-pit/pit-sysdiag/src/bios_firmware_program_tc.py new file mode 100755 index 00000000000..4a315e85712 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/bios_firmware_program_tc.py @@ -0,0 +1,435 @@ +# coding:utf-8 +import json +import os +import sys +import traceback + +from errcode import * +from function import load_platform_util_module + +# from click._compat import raw_input +from test_case import TestCaseCommon + +MASTER_ORIG_FW_CFG = "bios_master_firmware_baseline.json" +SLAVE_ORIG_FW_CFG = "bios_slave_firmware_baseline.json" + + +class BIOSFIRMWAREPROGRAMTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + get_version_func = "self.firmware_util.get_fw_version" + firmware_upgrade_func = "self.firmware_util.firmware_upgrade" + firmware_refresh_func = "self.firmware_util.firmware_refresh" + slave_upgrade = False + fw_extra = "master" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + # this param specified the case config directory + MODULE_NAME = "bios_firmware_program_tc" + TestCaseCommon.__init__( + self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file + ) + self.firmware_util = None + + try: + firmware_module = load_platform_util_module( + self.__PLATFORM_SPECIFIC_MODULE_NAME + ) + platform_util_class = getattr( + firmware_module, self.__PLATFORM_SPECIFIC_CLASS_NAME + ) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + self.logger.log_err(traceback.format_exc()) + sys.exit(1) + + try: + self.firmware_type = self.platform_cfg_json["bios_firmware_type"] + self.test_firmware_list = self.platform_cfg_json["bios_firmware_test"] + self.baseline_firmware_list = self.platform_cfg_json["bios_firmware_baseline"] + self.platform_path = os.path.dirname(self.platform_cfg_file) + except Exception as e: + self.logger.log_err(str(e), True) + self.logger.log_err(traceback.format_exc()) + sys.exit(1) + + def get_upgrade_path(self, firmware_list, item): + """ + @param firmware_list: firmware list + @param item: firmware type + @return: get path success:bool,skip upgrade:bool,firmware path:string + """ + support_slave, skip_upgrade = self.check_item_support_slave(item) + if "absolute_path" not in firmware_list[item]: + firmware_list[item]["absolute_path"] = True + # True if absolute_path does not set + if skip_upgrade is True: + return False, True, "" + elif ( + skip_upgrade is False + and support_slave is True + and "same_with_master" in firmware_list[item] + and firmware_list[item]["same_with_master"] is False + ): + # Check whether slave file exist + if ( + "slave_file_path" not in firmware_list[item] + or firmware_list[item]["slave_file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["slave_file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + # Get secondary flash firmware file path, @same_with_master defualt False, + # use firmware_list[item]["file_path"] if secondary flash firmware file path + # is the same with primary flash, else use firmware_list[item]["slave_file_path"] + else: + if ( + "file_path" not in firmware_list[item] + or firmware_list[item]["file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + + def upgrade_firmware_refresh(self, firmware_list, test_type="test"): + """ + + @param firmware_list: firmware list + @param test_type: sting to print to console + @return: upgrade and refresh firmware success:bool + """ + ret = True + try: + for item in self.firmware_type: + result, skip_upgrade, fw_path = self.get_upgrade_path( + firmware_list, item + ) + if skip_upgrade: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip upgrade" % item, + True, + ) + continue + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + result = eval(self.firmware_upgrade_func)(item, fw_path, self.fw_extra) + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + else: + result = eval(self.firmware_refresh_func)( + item, fw_path, self.fw_extra + ) + if not result: + self.fail_reason.append("%s firmware refresh fail" % item) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + def save_baseline_and_upgrade(self): + """ + + @return: save baseline version and upgrade success:bool + """ + try: + + version_running_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "w", + ) as f: + json.dump( + version_running_read_back, f, indent=4, separators=(",", ": ") + ) + return self.upgrade_firmware_refresh( + self.test_firmware_list, test_type="test" + ) + except Exception as e: + self.fail_reason.append(str(e)) + return False + + def check_all_items_support_slave(self): + item = "" + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if support_slave is True: + self.logger.log_info( + "there are slave_upgrade attr in item %s" % item, True + ) + return True + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + raise Exception("check %s support slave failed" % item) + return False + + def check_item_support_slave(self, item): + """ + + @param item: firmware type + @return: is support slave upgrade:bool,skip upgrade + """ + if self.slave_upgrade: + if ( + "slave_upgrade" not in self.test_firmware_list[item] + or "slave_upgrade" not in self.baseline_firmware_list[item] + ): + if ( + "slave_upgrade" not in self.test_firmware_list[item] + and "slave_upgrade" not in self.baseline_firmware_list[item] + ): + # if "slave_upgrade" not set, False by default + return False, True + else: + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # if "slave_upgrade" != baseline fw, throw exception, JSON file invalid + elif ( + self.test_firmware_list[item]["slave_upgrade"] is False + and self.baseline_firmware_list[item]["slave_upgrade"] is False + ): + return False, True + # "slave_upgrade" is False, does not support upgrade secondary flash + elif ( + self.test_firmware_list[item]["slave_upgrade"] + != self.baseline_firmware_list[item]["slave_upgrade"] + ): + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade" != baseline fw, throw exception, JSON file invalid + elif self.test_firmware_list[item]["slave_upgrade"] is True: + return True, False + else: + return False, False + + def check_firmware_version(self, firmware_cfg, test_type="test"): + """ + + @param firmware_cfg: firmware config file content + @param test_type: string to print,distinguish the two modes + @return:check firmware version success:bool + """ + version_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + ret = True + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if skip is True: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip check version" + % item, + True, + ) + continue + # Skip if slave_upgrade not exist + if skip is False and support_slave is True: + if ( + test_type is "test" + and "same_with_master" not in firmware_cfg[item] + or firmware_cfg[item]["same_with_master"] is False + ): + version_config_file = firmware_cfg[item]["slave_version"] + # Check slave flash fw version + else: + version_config_file = firmware_cfg[item]["version"] + else: + version_config_file = firmware_cfg[item]["version"] + if version_config_file == version_read_back[item]["version"]: + self.logger.log_info( + "%s %s firmware check success" % (item, test_type), True + ) + else: + self.logger.log_err( + "%s %s firmware check fail,config file version:[%s],device version:[%s]" + % ( + item, + test_type, + version_config_file, + version_read_back[item]["version"], + ), + True, + ) + self.fail_reason.append( + "%s %s firmware check fail" % (item, test_type) + ) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return ret + + def check_and_recover_firmware(self): + """ + + @return: check and recover firmware to baseline success:bool + """ + try: + version_test_check_result = self.check_firmware_version( + self.test_firmware_list, test_type="test" + ) + if version_test_check_result is not True: + return False + else: + return self.upgrade_firmware_refresh( + self.baseline_firmware_list, test_type="baseline" + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return False + + def check_baseline_firmware(self): + """ + + @return: check baseline firmware version success:bool + """ + ret = True + try: + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "r", + ) as f: + version_running_read_back_list = json.load(f) + version_running_check_result = self.check_firmware_version( + version_running_read_back_list, test_type="baseline" + ) + if version_running_check_result is not True: + ret = False + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + test_case_list = [ + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + ] + test_help_list = [ + "1.save master running firmware version and program test firmware", + "2.check master test firmware version and program baseline firmware", + "3.check master baseline firmware version", + "4.save slave running firmware version and program test firmware", + "5.check slave test firmware version and program baseline firmware", + "6.check slave baseline firmware version", + ] + + def run_test(self, *argv): + ret = E.OK + #also_print_console = True + + for item in self.test_help_list: + self.logger.log_info(item, True) + + choice = input("Please enter your choice (0 to quit):") + try: + if int(choice) not in range(0, (len(self.test_case_list) + 1)): + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + else: + if int(choice) is 0: + # self.fail_reason.append("user cancelled input") + self.logger.log_info("user cancelled input", also_print_console) + ret = E.OK + else: + index = int(choice) + # continue test item while check is success + test_item = "self.%s" % self.test_case_list[index - 1] + self.slave_upgrade = True if index > 3 else False + self.fw_extra = "master" if not self.slave_upgrade else "slave" + self.logger.log_info( + "[%s]:" % self.test_help_list[index - 1], also_print_console + ) + if ( + self.slave_upgrade is False + or self.check_all_items_support_slave() is True + ): + # skip test item while there are no "slave_upgrade" in all "firmware_type" item + result = eval(test_item)() + if result is not True: + self.logger.log_info( + "item %s =======> test fail" + % self.test_help_list[index - 1], + also_print_console, + ) + return E.EUPG25001 + index - 1 + elif index == 1 or index == 2 or index == 4 or index == 5: + self.logger.log_info( + "item %s =======> test success" + % self.test_help_list[index - 1], + also_print_console, + ) + # system reboot may be required in test item 1,2,4 or 5 + # break out of the loop to end the test item + else: + self.logger.log_info( + "there are no slave_upgrade attr, item %s =======> test skip" + % self.test_help_list[index - 1], + True + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/biosme_tc.py b/src/sonic-pit/pit-sysdiag/src/biosme_tc.py new file mode 100644 index 00000000000..69e01a80092 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/biosme_tc.py @@ -0,0 +1,59 @@ +# -*- coding:utf-8 +from pit_util_common import run_command +from test_case import TestCaseCommon +from errcode import E + + +class BIOSMETC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "biosme_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.cmdlist = [ + ["--help"], + ["lock"], + ["status", "recovery mode", "BIOS recovery open fail.", E.EBIOSME32001], + ["unlock"], + ["status", "normal", "BIOS recovery close fail.", E.EBIOSME32002] + ] + + def bios_me_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK BIOS ME CONTROL TEST]:", + also_print_console) + for item in self.cmdlist: + cmd = "pmonutil me {}".format(item[0]) + status, log = run_command(cmd) + if status != 0 or len(log) < 0: + self.fail_reason.append( + "pmonutil {} cmd error.".format(item[0])) + self.logger.log_err("FAIL!", also_print_console) + ret = E.EIO + return ret + else: + if item[0] == "--help": + ret = E.OK + self.logger.log_info(log, also_print_console) + elif item[0] == "status": + if item[1] not in log: + self.fail_reason.append(item[2]) + ret = item[3] + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + ret = E.OK + self.logger.log_info( + "current ME status: {}".format(log), also_print_console) + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + ret = self.bios_me_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/bmc2cpu.py b/src/sonic-pit/pit-sysdiag/src/bmc2cpu.py new file mode 100644 index 00000000000..af483443033 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/bmc2cpu.py @@ -0,0 +1,129 @@ +# -*- coding:utf-8 +import pit_util_common +from pit_util_common import run_command +from test_case import TestCaseCommon +from errcode import E +import re + + +# BMC test class +class BMC2CPU(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "bmc2cpu" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.net_info_dict = None + try: + if self.platform_cfg_json and "net_info" in self.platform_cfg_json.keys(): + self.net_info_dict = self.platform_cfg_json["net_info"] + except Exception as e: + self.logger.log_err(str(e), True) + self.bmc_usb0_ip = self.net_info_dict["bmc_usb0_ip"] + self.bmc_username = self.net_info_dict["bmc_username"] + self.bmc_password = self.net_info_dict["bmc_password"] + # self.SshBmc = pit_util_common.remote(self.logger, self.fail_reason, + # self.bmc_username, self.bmc_usb0_ip, self.bmc_password) + + def check_cpu_ip_test(self, also_print_console=False): + self.logger.log_info("[CHECK IP ADDRESS]:", also_print_console) + + ip_cmd = "ifconfig eth0 | grep inet | awk '{print $2}' | head -1" + ip_status, ip_log = run_command(ip_cmd) + ip_pattern = self.net_info_dict["ip_pattern"] + ip_result = re.findall(ip_pattern, ip_log)[0] + if ip_status != 0 or len(ip_log) <= 0: + self.fail_reason.append("get cpu ip address fail.") + ret = E.EIO + elif ip_result != ip_log: + self.fail_reason.append("cpu ip address error.") + ret = E.EIO + else: + ret = E.OK + self.cpu_eth0_ip = ip_result + self.logger.log_info( + "cpu ip address: {} ".format(ip_result), + also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + def check_bmc_ip_test(self, also_print_console=False): + self.logger.log_info("[CHECK LINK STATUS]:", also_print_console) + run_command( + "ssh-keygen -f \"/root/.ssh/known_hosts\" -R {}".format( + self.net_info_dict["bmc_usb0_ip"])) + bmc_ip_cmd = "ifconfig eth0 | grep inet | awk '{print $2}' | awk -F ':' '{print $2}'| head -1" + # self.SshBmc.connect(also_print_console) + ret, bmc_ip_log = pit_util_common.ssh_command(self,bmc_ip_cmd,self.bmc_username, self.bmc_usb0_ip, self.bmc_password) + if ret == E.OK: + ip_pattern = self.net_info_dict["ip_pattern"] + ip_result = re.findall(ip_pattern, bmc_ip_log.strip())[0] + if len(bmc_ip_log) <= 0: + self.fail_reason.append("get bmc ip address fail.") + ret = E.EIO + elif ip_result != bmc_ip_log.strip(): + self.fail_reason.append("cpu ip address error.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info( + "bmc ip address: {} ".format(ip_result), + also_print_console) + if ret != E.OK: + self.fail_reason.append("get link status fail.") + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + # self.SshBmc.disconnect() + return ret + + def bmc_ping_cpu_test(self, also_print_console=False): + self.logger.log_info("[CHECK PING STATUS]:", also_print_console) + + cpu_eth0_ip = self.cpu_eth0_ip + self.logger.log_info( + "cpu ip add: {} ".format(cpu_eth0_ip), + also_print_console) + # self.SshBmc.connect() + ping_cmd = "ping {} -c 10 -I eth0".format(cpu_eth0_ip) + ret, ping_log = pit_util_common.ssh_command(self,ping_cmd,self.bmc_username, self.bmc_usb0_ip, self.bmc_password,time=15) + if ret == E.OK: + if len(ping_log) <= 0: + self.fail_reason.append("bmc ping cpu test fail.") + ret = E.EIO + else: + ret = E.OK + loss_pattern = self.net_info_dict["loss_pattern"] + if not re.search(loss_pattern, ping_log): + ret = E.EMGMT11002 + self.fail_reason.append("ping test loss packet.") + else: + self.logger.log_info("ping test ok ", also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + # self.SshBmc.disconnect() + return ret + + def run_test(self, *argv): + ret = self.check_cpu_ip_test(True) + if ret != E.OK: + return ret + ret = self.check_bmc_ip_test(True) + if ret != E.OK: + return ret + ret = self.bmc_ping_cpu_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/bmcupgrade_tc.py b/src/sonic-pit/pit-sysdiag/src/bmcupgrade_tc.py new file mode 100644 index 00000000000..7a93ebcc7e8 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/bmcupgrade_tc.py @@ -0,0 +1,175 @@ +# -*- coding:utf-8 +import time +import re +import sys +import os + +from posixpath import abspath +from errcode import E +from typing import Pattern +from pit_util_common import run_command, load_platform_util_module +from test_case import TestCaseCommon + + +class BMCUPGRADETC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "bmcupgrade_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, + logger, platform_cfg_file, case_cfg_file) + fw_upgrade_module = load_platform_util_module( + self.__PLATFORM_SPECIFIC_MODULE_NAME) + try: + platform_util_class = getattr( + fw_upgrade_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.bmc = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + self.bmcupgrade_info_dict = None + try: + if self.platform_cfg_json and "bmcupgrade_info" in self.platform_cfg_json.keys(): + self.bmcupgrade_info_dict = self.platform_cfg_json["bmcupgrade_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def bmc_upgrade_test(self, also_print_console=False): + self.logger.log_info("[UPGRADE BMC TEST]:", also_print_console) + if not os.path.exists(self.bmcupgrade_info_dict['flash_path']): + self.fail_reason.append("get bmc upgrade file fail") + ret = E.EFWBMCUPGRADE29010 + self.logger.log_err("FAIL!", also_print_console) + return ret + bmc_version_running = self.bmc.get_bmc_version() + bmc_flash_running = self.bmc.get_bmc_flash() + if bmc_version_running == "N/A": + self.fail_reason.append("get bmc version fail") + ret = E.EFWBMCUPGRADE29007 + self.logger.log_err("FAIL!", also_print_console) + return ret + elif bmc_flash_running == "N/A": + self.fail_reason.append("get bmc flash fail") + ret = E.EFWBMCUPGRADE29008 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.logger.log_info("current version: {} {}".format( + bmc_flash_running, bmc_version_running), also_print_console) + if "master" in bmc_flash_running: + upgrade = "slave" + else: + upgrade = "master" + ret = self.bmc_update(upgrade) + if ret == E.OK: + time.sleep(90) + bmc_version_test_readback = self.bmc.get_bmc_version() + bmc_flash_test_readback = self.bmc.get_bmc_flash() + self.logger.log_info( + "Updated version: {} {}".format( + bmc_flash_test_readback, + bmc_version_test_readback), + also_print_console) + if bmc_flash_running != bmc_flash_test_readback: + self.logger.log_info( + "bmc flash should be not match", also_print_console) + self.logger.log_info( + "bmc_flash_running:{} after_change ---> bmc_flash_test_readback:{}".format( + bmc_flash_running, bmc_flash_test_readback), also_print_console) + pattern = self.bmcupgrade_info_dict["bmc_pattern"] + bmc_version_test_readback = re.findall( + pattern, bmc_version_test_readback)[0] + self.logger.log_info("bmc_version_test_readback: {} ".format( + bmc_version_test_readback), also_print_console) + self.logger.log_info( + "bmcupgrade_info_dict[version_test]: {} ".format( + self.bmcupgrade_info_dict['version_test']), + also_print_console) + if bmc_version_test_readback != self.bmcupgrade_info_dict['version_test']: + self.fail_reason.append("bmc version not match.") + ret = E.EFWBMCUPGRADE29005 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.logger.log_info( + "bmc version match", also_print_console) + else: + self.fail_reason.append("bmc flash match") + ret = E.EFWBMCUPGRADE29006 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.logger.log_err("FAIL!", also_print_console) + return ret + self.logger.log_info("PASS.", also_print_console) + return ret + + def bmc_update(self, upgrade, also_print_console=True): + self.logger.log_info("upgrade standby flash [{}]".format( + upgrade), also_print_console) + if self.bmc.firmware_upgrade( + "bmc", + "{}".format( + self.bmcupgrade_info_dict['flash_path']), + "{}".format(upgrade)): + if self.bmc.set_bmc_boot_flash("{}".format(upgrade)): + time.sleep(10) + self.logger.log_info( + "set boot flash to [{}] success, please wait for reboot! \n".format(upgrade), + also_print_console) + if not self.ping_bmc(True): + self.fail_reason.append("ping bmc fail,time out") + ret = E.EFWBMCUPGRADE29009 + return ret + else: + ret = E.OK + self.logger.log_info("reboot successfully! \n",also_print_console) + else: + self.fail_reason.append( + "set boot flash to [{}] fail".format(upgrade)) + ret = E.EFWBMCUPGRADE29003 + return ret + else: + self.fail_reason.append("bmc upgrade [{}] fail".format(upgrade)) + ret = E.EFWBMCUPGRADE29001 + return ret + return ret + + def ping_bmc(self, also_print_console=False): + ping_cmd = "ping {} -c 3".format(self.bmcupgrade_info_dict['ip']) + turn = 12 * 5 + test_time = 0 + while turn > 0: + self.logger.log_info( + "start to ping {}, this is {} count".format( + self.bmcupgrade_info_dict['ip'], + test_time), + also_print_console) + status, content = run_command(ping_cmd) + self.logger.log_info(content, also_print_console) + time.sleep(5) + if " 0%" in content: + self.logger.log_info( + "******ping pass: {} connect ******".format( + self.bmcupgrade_info_dict['ip']), + also_print_console) + self.logger.log_info( + "set boot flash success", also_print_console) + return True + else: + self.logger.log_info("{} not connect, waiting! this is {} turn".format( + self.bmcupgrade_info_dict['ip'], test_time), also_print_console) + turn -= 1 + test_time += 1 + continue + self.fail_reason.append("time out , {} not connect".format( + self.bmcupgrade_info_dict['ip'])) + return False + + def run_test(self, *argv): + for i in range(2): + ret = self.bmc_upgrade_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/cpld_firmware_program_tc.py b/src/sonic-pit/pit-sysdiag/src/cpld_firmware_program_tc.py new file mode 100755 index 00000000000..79462e90999 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/cpld_firmware_program_tc.py @@ -0,0 +1,431 @@ +# coding:utf-8 +import json +import os +import sys + +from errcode import * +from function import load_platform_util_module + +# from click._compat import raw_input +from test_case import TestCaseCommon + +MASTER_ORIG_FW_CFG = "cpld_master_firmware_baseline.json" +SLAVE_ORIG_FW_CFG = "cpld_slave_firmware_baseline.json" + + +class CPLDFIRMWAREPROGRAMTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + get_version_func = "self.firmware_util.get_fw_version" + firmware_upgrade_func = "self.firmware_util.firmware_upgrade" + firmware_refresh_func = "self.firmware_util.firmware_refresh" + slave_upgrade = False + fw_extra = "master" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + # this param specified the case config directory + MODULE_NAME = "cpld_firmware_program_tc" + TestCaseCommon.__init__( + self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file + ) + self.firmware_util = None + + try: + firmware_module = load_platform_util_module( + self.__PLATFORM_SPECIFIC_MODULE_NAME + ) + platform_util_class = getattr( + firmware_module, self.__PLATFORM_SPECIFIC_CLASS_NAME + ) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + try: + self.firmware_type = self.platform_cfg_json["cpld_firmware_type"] + self.test_firmware_list = self.platform_cfg_json["cpld_firmware_test"] + self.baseline_firmware_list = self.platform_cfg_json["cpld_firmware_baseline"] + self.platform_path = os.path.dirname(self.platform_cfg_file) + except Exception as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + def get_upgrade_path(self, firmware_list, item): + """ + + @param firmware_list: firmware list + @param item: firmware type + @return: get path success:bool,skip upgrade:bool,firmware path:string + """ + support_slave, skip_upgrade = self.check_item_support_slave(item) + if "absolute_path" not in firmware_list[item]: + firmware_list[item]["absolute_path"] = True + # absolute_path字段不存在时默认值为True + if skip_upgrade is True: + return False, True, "" + elif ( + skip_upgrade is False + and support_slave is True + and "same_with_master" in firmware_list[item] + and firmware_list[item]["same_with_master"] is False + ): + # 判断备区升级文件是否存在 + if ( + "slave_file_path" not in firmware_list[item] + or firmware_list[item]["slave_file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["slave_file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + # 获取备区升级固件所在路径,“same_with_master”字段省略默认为False,备区固件路径和主区一样则直接使用firmware_list[item]["file_path"] + # 不一样则使用firmware_list[item]["slave_file_path"] + else: + if ( + "file_path" not in firmware_list[item] + or firmware_list[item]["file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + + def upgrade_firmware_refresh(self, firmware_list, test_type="test"): + """ + + @param firmware_list: firmware list + @param test_type: sting to print to console + @return: upgrade and refresh firmware success:bool + """ + ret = True + try: + for item in self.firmware_type: + result, skip_upgrade, fw_path = self.get_upgrade_path( + firmware_list, item + ) + if skip_upgrade: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip upgrade" % item, + True, + ) + continue + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + result = eval(self.firmware_upgrade_func)(item, fw_path, self.fw_extra) + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + else: + result = eval(self.firmware_refresh_func)( + item, fw_path, self.fw_extra + ) + if not result: + self.fail_reason.append("%s firmware refresh fail" % item) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + def save_baseline_and_upgrade(self): + """ + + @return: save baseline version and upgrade success:bool + """ + try: + + version_running_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "w", + ) as f: + json.dump( + version_running_read_back, f, indent=4, separators=(",", ": ") + ) + return self.upgrade_firmware_refresh( + self.test_firmware_list, test_type="test" + ) + except Exception as e: + self.fail_reason.append(str(e)) + return False + + def check_all_items_support_slave(self): + item = "" + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if support_slave is True: + self.logger.log_info( + "there are slave_upgrade attr in item %s" % item, True + ) + return True + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + raise Exception("check %s support slave failed" % item) + return False + + def check_item_support_slave(self, item): + """ + + @param item: firmware type + @return: is support slave upgrade:bool,skip upgrade + """ + if self.slave_upgrade: + if ( + "slave_upgrade" not in self.test_firmware_list[item] + or "slave_upgrade" not in self.baseline_firmware_list[item] + ): + if ( + "slave_upgrade" not in self.test_firmware_list[item] + and "slave_upgrade" not in self.baseline_firmware_list[item] + ): + # 不存在“slave_upgrade”字段,默认为false + return False, True + else: + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif ( + self.test_firmware_list[item]["slave_upgrade"] is False + and self.baseline_firmware_list[item]["slave_upgrade"] is False + ): + return False, True + # “slave_upgrade”字段为False,不支持备区升级 + elif ( + self.test_firmware_list[item]["slave_upgrade"] + != self.baseline_firmware_list[item]["slave_upgrade"] + ): + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif self.test_firmware_list[item]["slave_upgrade"] is True: + return True, False + else: + return False, False + + def check_firmware_version(self, firmware_cfg, test_type="test"): + """ + + @param firmware_cfg: firmware config file content + @param test_type: string to print,distinguish the two modes + @return:check firmware version success:bool + """ + version_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + ret = True + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if skip is True: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip check version" + % item, + True, + ) + continue + # 不支持备区升级直接跳过检测 + if skip is False and support_slave is True: + if ( + test_type is "test" + and "same_with_master" not in firmware_cfg[item] + or firmware_cfg[item]["same_with_master"] is False + ): + version_config_file = firmware_cfg[item]["slave_version"] + # 比对备区test版本固件版本号,“same_with_master”字段省略默认为False,备区版本号和主区一样则直接使用firmware_cfg[item]["version"] + # 不一样则存放在firmware_cfg[item]["slave_version"] + else: + version_config_file = firmware_cfg[item]["version"] + else: + version_config_file = firmware_cfg[item]["version"] + if version_config_file == version_read_back[item]["version"]: + self.logger.log_info( + "%s %s firmware check success" % (item, test_type), True + ) + else: + self.logger.log_err( + "%s %s firmware check fail,config file version:[%s],device version:[%s]" + % ( + item, + test_type, + version_config_file, + version_read_back[item]["version"], + ), + True, + ) + self.fail_reason.append( + "%s %s firmware check fail" % (item, test_type) + ) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return ret + + def check_and_recover_firmware(self): + """ + + @return: check and recover firmware to baseline success:bool + """ + try: + version_test_check_result = self.check_firmware_version( + self.test_firmware_list, test_type="test" + ) + if version_test_check_result is not True: + return False + else: + return self.upgrade_firmware_refresh( + self.baseline_firmware_list, test_type="baseline" + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return False + + def check_baseline_firmware(self): + """ + + @return: check baseline firmware version success:bool + """ + ret = True + try: + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "r", + ) as f: + version_running_read_back_list = json.load(f) + version_running_check_result = self.check_firmware_version( + version_running_read_back_list, test_type="baseline" + ) + if version_running_check_result is not True: + ret = False + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + test_case_list = [ + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + ] + test_help_list = [ + "1.save master running firmware version and program test firmware", + "2.check master test firmware version and program baseline firmware", + "3.check master baseline firmware version", + "4.save slave running firmware version and program test firmware", + "5.check slave test firmware version and program baseline firmware", + "6.check slave baseline firmware version", + ] + + def run_test(self, *argv): + ret = E.OK + also_print_console = True + for item in self.test_help_list: + self.logger.log_info(item, True) + + choice = input("Please enter your choice (0 to quit):") + try: + if int(choice) not in range(0, (len(self.test_case_list) + 1)): + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + else: + if int(choice) is 0: + # self.fail_reason.append("user cancelled input") + self.logger.log_info("user cancelled input", also_print_console) + ret = E.OK + else: + index = int(choice) + # continue test item while check is success + test_item = "self.%s" % self.test_case_list[index - 1] + self.slave_upgrade = True if index > 3 else False + self.fw_extra = "master" if not self.slave_upgrade else "slave" + self.logger.log_info( + "[%s]:" % self.test_help_list[index - 1], also_print_console + ) + if ( + self.slave_upgrade is False + or self.check_all_items_support_slave() is True + ): + # skip test item while there are no "slave_upgrade" in all "firmware_type" item + result = eval(test_item)() + if result is not True: + self.logger.log_info( + "item %s =======> test fail" + % self.test_help_list[index - 1], + also_print_console, + ) + return E.EUPG25001 + index - 1 + elif index == 1 or index == 2 or index == 4 or index == 5: + self.logger.log_info( + "item %s =======> test success" + % self.test_help_list[index - 1], + also_print_console, + ) + # system reboot may be required in test item 1,2,4 or 5 + # break out of the loop to end the test item + else: + self.logger.log_info( + "there are no slave_upgrade attr, item %s =======> test skip" + % self.test_help_list[index - 1], + True, + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/cpu2external.py b/src/sonic-pit/pit-sysdiag/src/cpu2external.py new file mode 100644 index 00000000000..f375aea2368 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/cpu2external.py @@ -0,0 +1,124 @@ +# -*- coding:utf-8 +from pit_util_common import run_command +from test_case import TestCaseCommon +from errcode import E +import re + + +# CPU test class +class CPU2EXTERNAL(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "cpu2external" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.net_info_dict = None + try: + if self.platform_cfg_json and "net_info" in self.platform_cfg_json.keys(): + self.net_info_dict = self.platform_cfg_json["net_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def net_ip_test(self, also_print_console=False): + self.logger.log_info("[CHECK IP ADDRESS]:", also_print_console) + + ip_cmd = "ifconfig eth0 | grep inet | awk '{print $2}' | head -1" + ip_status, ip_log = run_command(ip_cmd) + ip_pattern = self.net_info_dict["ip_pattern"] + if ip_status != 0 or len(ip_log) <= 0: + self.fail_reason.append("get ip address fail.") + ret = E.EIO + elif not re.match(ip_pattern, ip_log): + self.fail_reason.append("cpu ip address error.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info( + "ip address: {} ".format(ip_log), + also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def net_speed_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK LINK STATUS AND SPEED]:", + also_print_console) + + link_sta_cmd = "ifconfig eth0" + sta_status, sta_log = run_command(link_sta_cmd) + if sta_status != 0 or len(sta_log) <= 0: + self.fail_reason.append("get link status fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info( + "link status:\n {} ".format(sta_log), + also_print_console) + if ret == E.OK: + link_speed_cmd = "ethtool eth0 | grep Speed | awk '{print $2}'" + speed_status, speed_log = run_command(link_speed_cmd) + if speed_status != 0 or len(speed_log) <= 0: + self.fail_reason.append("get link speed fail.") + ret = E.EIO + else: + link_speed = self.net_info_dict["link_speed"] + if link_speed != speed_log: + ret = E.EMGMT11002 + self.fail_reason.append("link speed not match.") + else: + ret = E.OK + self.logger.log_info( + "link speed: {} ".format(speed_log), + also_print_console) + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def net_ping_test(self, also_print_console=False): + self.logger.log_info("[CHECK PING STATUS]:", also_print_console) + + cpu_remote_ip = self.net_info_dict["cpu_remote_ip"] + self.logger.log_info( + "remote ip add: {} ".format(cpu_remote_ip), + also_print_console) + ping_cmd = "ping {} -c 10 -I eth0".format(cpu_remote_ip) + ping_status, ping_log = run_command(ping_cmd) + if ping_status != 0 or len(ping_log) <= 0: + self.fail_reason.append("ping test fail.") + ret = E.EIO + else: + loss_pattern = self.net_info_dict["loss_pattern"] + if not re.search(loss_pattern, ping_log): + ret = E.EMGMT11002 + self.fail_reason.append("ping test loss packet.") + else: + ret = E.OK + self.logger.log_info("ping test ok ", also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + ret = self.net_ip_test(True) + if ret != E.OK: + return ret + ret = self.net_speed_test(True) + if ret != E.OK: + return ret + ret = self.net_ping_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/cpu_tc.py b/src/sonic-pit/pit-sysdiag/src/cpu_tc.py new file mode 100644 index 00000000000..6b659a33275 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/cpu_tc.py @@ -0,0 +1,88 @@ +from function import run_command +from test_case import TestCaseCommon +from errcode import E + + +# CPU test class +class CPUTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "cpu_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.cpu_info_dict = None + try: + if self.platform_cfg_json and 'cpu_info' in self.platform_cfg_json.keys(): + self.cpu_info_dict = self.platform_cfg_json['cpu_info'] + except Exception as e: + self.logger.log_err(str(e), True) + + def test_cpu_info(self, also_print_console=False): + ret = E.OK + self.logger.log_info("check_cpu_info start", also_print_console) + + cmd = "lscpu | head -n25" + status, log = run_command(cmd) + if status != 0 or len(log) <= 0: + reason = "Failed, get cpu info failed, command {}, status {}, log {}".format( \ + cmd, status, log) + self.log_reason(reason) + ret = E.ECPU3005 + else: + lines = log.splitlines() + expected_cpu_model = self.cpu_info_dict.get('Model name') + expected_bogomips = self.cpu_info_dict.get('BogoMIPS') + expected_cpu_num = self.cpu_info_dict.get('CPU(s)') + expected_cpu_mhz = self.cpu_info_dict.get('CPU MHz') + for line in lines: + cols = line.strip() + if len(cols) < 2: + continue + + if expected_cpu_model and colos[0] == "Model name": + if cols[1].strip() != expected_cpu_model: + reason = "Failed, CPU model name {}(expected {})".format( \ + cols[0], expected_cpu_model) + self.log_reason(reason) + ret = E.ECPU3001 + + if expected_bogomips and cols[0] == 'BogoMIPS': + read_bogomips = float(cols[1].strip()) + conf_bogomips = float(expected_bogomips) + if read_bogomips <= (conf_bogomips * 0.99) or \ + read_bogomips >= conf_bogomips * 1.01: + reason = "Failed, BogoMIPS {}(expected {})".format( \ + cols[0], expected_bogomips) + self.log_reason(reason) + ret = E.ECPU3001 + + if expected_cpu_num and cols[0] == 'CPU(s)': + if cols[1].strip() != self.cpu_info_dict.get('CPU(s)'): + reason = "Failed, CPU number {}(expected {})".format( \ + cols[0], expected_cpu_num) + self.fail_reason.append(reason) + ret = E.ECPU3001 + + if expected_cpu_mhz and cols[0] == 'CPU MHz': + read_cpu_mhz = float(cols[1].strip()) + conf_cpu_mhz = float(expected_cpu_mhz) + if read_cpu_mhz <= (conf_cpu_mhz * 0.99) or \ + read_cpu_mhz >= (conf_cpu_mhz * 1.01): + reason = "Failed, CPU MHz {}(expected {})".format( \ + cols[0], expected_cpu_mhz) + self.log_reason(reason) + ret = E.ECPU3001 + + if ret != E.OK: + self.logger.log_err("test_cpu_info done, FAILED", also_print_console) + else: + self.logger.log_info("test_cpu_info done, PASS", also_print_console) + + return ret + + def run_test(self, *argv): + try: + ret = self.test_cpu_info(True) + return ret + except Exception as e: + self.logger.log_err("{} check cpu info got exception: {}".format(str(e))) + self.logger.log_err(traceback.format_exc()) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/dac_pin_tc.py b/src/sonic-pit/pit-sysdiag/src/dac_pin_tc.py new file mode 100644 index 00000000000..f84e55aa0a1 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/dac_pin_tc.py @@ -0,0 +1,276 @@ +import os +import re +import subprocess +from test_case import TestCaseCommon +from errcode import * +from pit_util_common import run_command + + +class DACPINTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "dac_pin_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.dac_info_dict = None + try: + if self.platform_cfg_json and "dac_info" in self.platform_cfg_json.keys(): + self.dac_info_dict = self.platform_cfg_json["dac_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def check_presence(self, also_print_console): + presence_reset = [] + self.logger.log_info( + "[CHECK DAC PRESENCE DEVICE]:", + also_print_console) + presence_cmd = "transceiverutil show presence | grep -a 'Ethernet' | awk '{print $2}'" + status_presence, log_presence = run_command(presence_cmd) + if status_presence != 0 or len(log_presence) <= 0: + self.fail_reason.append("get dac fail.") + ret = E.EIO + else: + ret = E.OK + presence_list = log_presence.splitlines() + for index, presence in enumerate(presence_list): + if presence == 'Not': + presence_reset.append(index) + return ret, presence_reset + + def dac_presence_test(self, also_print_console): + all_presence_continue = False + ret, presence_reset = self.check_presence(also_print_console) + if ret != E.OK: + return all_presence_continue, ret + if len(presence_reset) != 0: + self.logger.log_info("[RESET DAC PRESENCE]:", also_print_console) + for presence_reset_index in presence_reset: + reset_cmd = "transceiverutil reset Ethernet{}".format( + presence_reset_index) + status_reset, log_reset = run_command(reset_cmd) + if status_reset != 0 or len(log_reset) <= 0: + self.fail_reason.append("reset dac fail.") + ret = E.EIO + return all_presence_continue, ret + else: + self.logger.log_info(log_reset, also_print_console) + + ret, presence_reset = self.check_presence(also_print_console) + if len(presence_reset) != 0: + ret = E.ESFP18001 + self.fail_reason.append( + "please check number of:{} dac status ".format(presence_reset)) + return all_presence_continue, ret + else: + ret = E.OK + all_presence_continue = True + self.logger.log_info("dac status: Present", also_print_console) + + else: + ret = E.OK + all_presence_continue = True + self.logger.log_info("dac status: Present", also_print_console) + return all_presence_continue, ret + + def dac_eeprom_test(self, also_print_console=False): + self.logger.log_info("[CHECK DAC EEPROM DEVICE]:", also_print_console) + dac_type = self.dac_info_dict["dac_type"] + qsfp_eeprom_dict = self.dac_info_dict[dac_type] + dac_port = qsfp_eeprom_dict["dac_port"][0] + port_num = qsfp_eeprom_dict[dac_port]["port_num"] + + for number in range(port_num[0], port_num[1]): + eeprom_cmd = "transceiverutil show eeprom | grep -a -A 20 'Ethernet{}:'".format( + number) + " | grep -a -E 'Identifier:|Vendor Name:|Vendor PN:|Vendor SN:' | grep -a -v 'Extended'| awk -F ':' '{print $2}'" + status_eeprom, log_eeprom = run_command(eeprom_cmd) + log_eeprom_list = log_eeprom.splitlines() + if status_eeprom != 0 or len(log_eeprom_list) != 4: + self.fail_reason.append("port{} get dac eeprom fail.".format(number)) + ret = E.EIO + return ret + else: + ret = E.OK + if log_eeprom_list[0].strip( + ) not in qsfp_eeprom_dict[dac_port]["Identifier"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Identifier".format(number)) + ret = E.ESFP18008 + return ret + if log_eeprom_list[1].strip( + ) not in qsfp_eeprom_dict[dac_port]["Vendor_Name"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: Vendor Name".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[2].strip()) <= qsfp_eeprom_dict[dac_port]["Vendor_PN_len"][0] or len( + log_eeprom_list[2].strip()) >= qsfp_eeprom_dict[dac_port]["Vendor_PN_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: Vendor PN".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[3].strip()) <= qsfp_eeprom_dict[dac_port]["Vendor_SN_len"][0] or len( + log_eeprom_list[3].strip()) >= qsfp_eeprom_dict[dac_port]["Vendor_SN_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor SN".format(number)) + ret = E.ESFP18008 + return ret + + dac_port = qsfp_eeprom_dict["dac_port"][1] + port_num = qsfp_eeprom_dict[dac_port]["port_num"] + for number in range(port_num[0], port_num[1]): + eeprom_cmd = "transceiverutil show eeprom | grep -a -A 10 'Ethernet{}:'".format( + number) + " | grep -a -E 'Identifier:|Vendor Name:|Vendor Part Number|Vendor Serial Number:'|awk -F ':' '{print $2}'" + status_eeprom, log_eeprom = run_command(eeprom_cmd) + log_eeprom_list = log_eeprom.splitlines() + if status_eeprom != 0 or len(log_eeprom_list) != 4: + self.fail_reason.append("get dac eeprom fail.") + ret = E.EIO + else: + ret = E.OK + if log_eeprom_list[0].strip( + ) not in qsfp_eeprom_dict[dac_port]["Identifier"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Identifier".format(number)) + ret = E.ESFP18008 + return ret + if log_eeprom_list[1].strip( + ) not in qsfp_eeprom_dict[dac_port]["Vendor_Name"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Name".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[2].strip()) <= qsfp_eeprom_dict[dac_port]["Vendor_Part_Number_len"][0] or len( + log_eeprom_list[2].strip()) >= qsfp_eeprom_dict[dac_port]["Vendor_Part_Number_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Part Number".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[3].strip()) <= qsfp_eeprom_dict[dac_port]["Vendor_Serial_Number_len"][0] or len( + log_eeprom_list[3].strip()) >= qsfp_eeprom_dict[dac_port]["Vendor_Serial_Number_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Serial Number".format(number)) + ret = E.ESFP18008 + return ret + self.logger.log_info("dac eeprom check: Pass", also_print_console) + return ret + + def dac_tx_switch_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK DAC TX SWITCH DEVICE]:", + also_print_console) + intr_cmd = "transceiverutil show intr" + status_intr, log_intr = run_command(intr_cmd) + if status_intr != 0 or len(log_intr) <= 0: + self.fail_reason.append("get dac fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info("read dac intr OK", also_print_console) + if ret == E.OK: + modsel_cmd = "transceiverutil show modsel" + status_modsel, log_modsel = run_command(modsel_cmd) + if status_modsel != 0 or len(log_modsel) <= 0: + self.fail_reason.append("get dac fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info("read dac modsel OK", also_print_console) + if ret == E.OK: + lpmode_switch_control = ['off', 'on'] + lpmode_switch_status = ['On', 'Off'] + dac_type = self.dac_info_dict["dac_type"] + qsfp_eeprom_dict = self.dac_info_dict[dac_type] + total_portnum = qsfp_eeprom_dict["total_portnum"] + for control in lpmode_switch_control: + for number in range(0, int(total_portnum)): + lpmode_switch_cmd = "transceiverutil lpmode {} Ethernet{}".format( + control, number) + status_lpmode, log_lpmode = run_command(lpmode_switch_cmd) + if status_lpmode != 0 or len(log_lpmode) <= 0: + self.fail_reason.append("lpmode switch fail.") + ret = E.EIO + else: + lpmode_switch_readback_cmd = "transceiverutil show lpmode | grep -a 'Ethernet{} '".format( + number) + "|awk '{print $2}'" + status_lpmode_switch, log_lpmode_switch = run_command( + lpmode_switch_readback_cmd) + if status_lpmode_switch != 0 or len( + log_lpmode_switch) <= 0: + self.fail_reason.append("lpmode switch fail.") + ret = E.EIO + else: + if log_lpmode_switch != lpmode_switch_status[lpmode_switch_control.index( + control)]: + ret = E.ESFP18015 + self.fail_reason.append( + "Ethernet{} lpmode switch fail.".format(number)) + return ret + else: + ret = E.OK + self.logger.log_info("lpmode switch OK", also_print_console) + return ret + + def dac_reset_switch_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK DAC RESET SWITCH DEVICE]:", + also_print_console) + dac_type = self.dac_info_dict["dac_type"] + qsfp_eeprom_dict = self.dac_info_dict[dac_type] + total_portnum = qsfp_eeprom_dict["total_portnum"] + for number in range(0, int(total_portnum)): + reset_switch_cmd = "transceiverutil reset Ethernet{} ".format( + number) + status_reset_switch, log_rest_switch = run_command( + reset_switch_cmd) + if status_reset_switch != 0 or len(log_rest_switch) <= 0: + self.fail_reason.append("dac reset fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info( + "Ethernet{} reset OK".format(number), + also_print_console) + if ret == E.OK: + reset_cmd = "transceiverutil show reset" + status_reset, log_rest = run_command(reset_cmd) + if status_reset != 0 or len(log_rest) <= 0: + self.fail_reason.append("reset dac fail.") + ret = E.EIO + else: + + if "Not" in log_rest: + ret = E.ESFP18016 + self.fail_reason.append("dac reset switch fail.") + else: + ret = E.OK + self.logger.log_info("reset switch OK", also_print_console) + return ret + + def run_test(self, *argv): + all_presence_continue, ret = self.dac_presence_test(True) + if ret != E.OK: + return ret + elif all_presence_continue: + dac_type = self.dac_info_dict["dac_type"] + if dac_type == "QSFP": + ret = self.dac_eeprom_test(True) + if ret != E.OK: + return ret + ret = self.dac_tx_switch_test(True) + if ret != E.OK: + return ret + ret = self.dac_reset_switch_test(True) + if ret != E.OK: + return ret + ret = self.dac_eeprom_test(True) + if ret != E.OK: + return ret + elif dac_type == "SFP": + pass + else: + pass + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/emmc_tc.py b/src/sonic-pit/pit-sysdiag/src/emmc_tc.py new file mode 100644 index 00000000000..2c1c8a402e6 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/emmc_tc.py @@ -0,0 +1,138 @@ +import os +import subprocess +from test_case import TestCaseCommon +from errcode import E + +def run_command(cmd): + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + if err: + out += err + return proc.returncode, out.rstrip('\n') + +class EMMCTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "emmc_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.test_size = 1 # unit: MBytes + self.emmc_bom_list = None # default + ''' + "emmc_test_size":"100M", + "emmc_bom":[ + { + "manufacturer":"Toshiba", + "pn":"064G02" + }, + ], + ''' + if self.platform_cfg_json and 'emmc_test_size' in self.platform_cfg_json.keys(): + size = self.platform_cfg_json['emmc_test_size'] + if size.endswith("m") or size.endswith("M"): + self.test_size = int(size.strip("mM")) + else: + self.test_size = int(size) + if self.platform_cfg_json and 'emmc_bom' in self.platform_cfg_json.keys(): + self.emmc_bom_list = self.platform_cfg_json['emmc_bom'] + + + def emmc_info_check(self, also_print_console=False): + """ + get emmc CID data + """ + ret = E.OK + self.logger.log_info( "[EMMC INFO CHECK]:", also_print_console) + + status, out = run_command("mmc cid read /sys/block/mmcblk0/device") + if status: + err = "Read eMMC info failed!" + self.fail_reason.append(err) + ret = E.ESSD2001 + else: + with open("/sys/block/mmcblk0/size", 'r') as f: + value = f.read() + size = round(float(value) * 512 / 1024 / 1024 / 1024, 1) + + self.logger.log_info(out, also_print_console) + self.logger.log_info("size: {}GB".format(size), also_print_console) + + if self.emmc_bom_list: + matched = False + for emmc_bom in self.emmc_bom_list: + if out.find(emmc_bom["manufacturer"]) != -1 and out.find(emmc_bom["pn"]) != -1: + matched = True + break + if not matched: + ret = E.ESSD2001 + self.fail_reason.append("emmc info not match") + + if ret != E.OK: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def emmc_read_test(self, also_print_console=False): + self.logger.log_info( "[EMMC READ TEST]:", also_print_console) + + bs_count = self.test_size * 64 + cmd = "dd if=/dev/mmcblk0 of=/dev/null bs=16k count=%d iflag=direct,nonblock" % bs_count + self.logger.log_info(cmd, also_print_console) + status, out = run_command(cmd) + if status: + err = "emmc read test failed!" + self.fail_reason.append(err) + ret = E.ESSD2002 + else: + self.logger.log_info(out, also_print_console) + ret = E.OK + + if ret != E.OK: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def emmc_write_test(self, also_print_console=False): + self.logger.log_info( "[EMMC WRITE TEST]:", also_print_console) + + bs_count = self.test_size * 64 + cmd = "dd if=/dev/urandom of=/tmp/txtfile bs=16k count=%d oflag=direct,nonblock" % bs_count + self.logger.log_info(cmd, also_print_console) + status, out = run_command(cmd) + if status: + err = "emmc write test failed!" + self.fail_reason.append(err) + ret = E.ESSD2003 + else: + self.logger.log_info(out, also_print_console) + ret = E.OK + os.remove("/tmp/txtfile") + + if ret != E.OK: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def run_test(self, *argv): + ret = self.emmc_info_check(True) + if ret != E.OK: + return ret + + loop = int(argv[0]) + for i in range(1, loop + 1): + ret = self.emmc_read_test(True) + if ret != E.OK: + self.logger.log_err("emmc r/w loop {} failed!".format(i), True) + break + ret = self.emmc_write_test(True) + if ret != E.OK: + self.logger.log_err("emmc r/w loop {} failed!".format(i), True) + break + return ret + \ No newline at end of file diff --git a/src/sonic-pit/pit-sysdiag/src/errcode.py b/src/sonic-pit/pit-sysdiag/src/errcode.py new file mode 100644 index 00000000000..01752441ad8 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/errcode.py @@ -0,0 +1,251 @@ +# error code +from enum import IntEnum, unique + + +NO_ERR = 0 +IO_ERR = 1 +TEST_FAIL_ERR = 2 +NORUN_ERR = 3 +NULL_ERR = 4 +ABSENT_ERR = 5 +OUT_THRD_ERR = 6 +FCS_ERR = 7 +DROP_ERR = 8 +UNMATCH_ERR = 9 +OTHER_ERR = 99 + + +# error message +ERR_MSG = { + NO_ERR: "no error", + IO_ERR: "i/o error", + TEST_FAIL_ERR: "test failed", + NORUN_ERR: "not running", + NULL_ERR: "empty content", + ABSENT_ERR: "device not present", + OUT_THRD_ERR: "value is out of threshold", + FCS_ERR: "fcs error", + DROP_ERR: "drop packets", + UNMATCH_ERR: "content is not matched", + OTHER_ERR: "other error" +} + + +@unique +class E(IntEnum): + OK = 0 # Pass + EFAIL = 1 # Fail + EIO = 2 # I/O error + EEXIST = 3 # files not exists + ESUPPORT = 4 # unsupported ops + + EUSB1000 = 1000 # usb not found + EUSB1001 = 1001 # usb mount fail + EUSB1002 = 1002 # usb read error + EUSB1003 = 1003 # usb write error + EUSB1004 = 1004 # usb speed not match + EUSB1005 = 1005 # usb net card ping test drop packet + EUSB1006 = 1006 # usb stress fail + + ESSD2001 = 2001 # ssd vendor info not match + ESSD2002 = 2002 # ssd read fail + ESSD2003 = 2003 # ssd write fail + ESSD2004 = 2004 # ssd smart error + + ECPU3001 = 3001 # cpu vendor info not matched + ECPU3002 = 3002 # cpu stress test failed + ECPU3003 = 3003 # cpu cores info not matched + ECPU3004 = 3004 # cpu MHz info not match + + EMEM4001 = 4001 # memory vendor info not matched + EMEM4002 = 4002 # memtester fail + EMEM4003 = 4003 # memory ecc happened + EMEM4004 = 4004 # free memory is too small + EMEM4005 = 4005 # memmory stress test eeror + + EPSU5000 = 5000 # PSU error + EPSU5001 = 5001 # PSU vendor info not matched + EPSU5002 = 5002 # PSU input voltage out of threshold + EPSU5003 = 5003 # PSU input current out of threshold + EPSU5004 = 5004 # PSU ouput voltage out of threshold + EPSU5005 = 5005 # PSU output current out of threshold + EPSU5006 = 5006 # PSU absent + EPSU5007 = 5007 # PSU status invalid + EPSU5008 = 5008 # PSU input power out of threshold + EPSU5009 = 5009 # PSU output power out of threshold + + EFAN6000 = 6000 # fan error + EFAN6001 = 6001 # fan vendor info not matched + EFAN6002 = 6002 # fan speed out of threshold + EFAN6003 = 6003 # fan stop or not running + EFAN6004 = 6004 # fan absent + EFAN6005 = 6005 # fan control failed + + ESSR7001 = 7001 # sensor count not matched + ESSR7002 = 7002 # sensor access failed + ESSR7003 = 7003 # sensor value out of threshold + + EIIC8001 = 8001 # I2C devices absent + EIIC8002 = 8002 # I2C read/write access failed + EIIC8003 = 8003 # I2C read value not match writed + + EPCI9001 = 9001 # PCIe devices absent + EPCI9002 = 9002 # FPGA read/write fail + EPCI9003 = 9003 # FPGA read value not match writed + EPCI9004 = 9004 # PCIe speed not matched + EPCI9005 = 9005 # PCIe width not matched + EPCI9006 = 9006 # PCIe ue/ce happened + EPCI9007 = 9007 # FPGA number can't cat + EPCI9008 = 9008 # FPGA number can't cat + + ELPC10001 = 10001 # LPC devices absent + ELPC10002 = 10002 # LPC access BMC fail + ELPC10003 = 10003 # LPC read value not match writed + + EMGMT11001 = 11001 # mgmtport status invalid + EMGMT11002 = 11002 # cpu ping server drop packet + EMGMT11003 = 11003 # cpu ping bmc drop packet + EMGMT11004 = 11004 # bmc ping server drop packet + EMGMT11005 = 11005 # mgmtport firmware not matched + EMGMT11006 = 11006 # cpu MAC invalid + EMGMT11007 = 11007 # bmc MAC invalid + EMGMT11008 = 11008 # MAC check error + + ERTC12001 = 12001 # rtc access fail + ERTC12002 = 12002 # rtc timing uncertainty + + EGPIO13001 = 13001 # gpio pin access fail + EGPIO13002 = 13002 # jtag upgrade fail + + ELED14001 = 14001 # SYS LED fault + ELED14002 = 14002 # Power LED fault + ELED14003 = 14003 # FAN LED fault + ELED14004 = 14004 # Location LED fault + ELED14005 = 14005 # BMC LED fault + ELED14006 = 14006 # Port LEDs fault + ELED14007 = 14007 # fan-module LED fault + ELED14008 = 14008 # PSU-module LED fault + + EUART15001 = 15001 # console baud_rate not matched + EUART15002 = 15002 # cpu console output fault + EUART15003 = 15003 # bmc console output fault + EUART15004 = 15004 # bmc sol fault + + EFRU16001 = 16001 # fru program fail + EFRU16002 = 16002 # fru access fail + EFRU16003 = 16003 # fru content not matched + + EFW17001 = 17001 # firmware version get failed + EFW17002 = 17002 # firmware version not matched + EFW17003 = 17003 # firmware upgrade failed + EFW17004 = 17004 # master/slave switch failed + EFW17005 = 17005 # master flash boot failed + EFW17006 = 17006 # slave flash boot failed + EFW17007 = 17007 # get config firmware version failed + + ESFP18001 = 18001 # sfp absent + ESFP18002 = 18002 # sfp tx fault + ESFP18003 = 18003 # sfp rx lose + ESFP18004 = 18004 # sfp tx disable + ESFP18005 = 18005 # sfp under low power mode + ESFP18006 = 18006 # sfp under reset + ESFP18007 = 18007 # sfp present changed + ESFP18008 = 18008 # sfp eeprom access failed + ESFP18009 = 18009 # sfp temperature over threshold + ESFP18010 = 18010 # sfp present signal not detected + ESFP18011 = 18011 # sfp tx_disable signal not detected + ESFP18012 = 18012 # sfp tx_fault signal not detected + ESFP18013 = 18013 # sfp rx_lose signal not detected + ESFP18014 = 18014 # qsfp interrupt signal not detected + ESFP18015 = 18015 # qsfp lpmode signal not detected + ESFP18016 = 18016 # qsfp reset signal not detected + + EPORT19001 = 19001 # port link down + EPORT19002 = 19002 # sdk init failed + EPORT19003 = 19003 # sdk exit failed + EPORT19004 = 19004 # vlan config failed + EPORT19005 = 19005 # tx send packet failed + EPORT19006 = 19006 # port speed not matched >90% + EPORT19007 = 19007 # fcs error + EPORT19008 = 19008 # drop packet + + EPHY20001 = 20001 # external phy register access failed + + EBMC21001 = 21001 # BMC cpu test failed + EBMC21002 = 21002 # BMC memory test failed + EBMC21003 = 21003 # BMC emmc test failed + EBMC21004 = 21004 # BMC i2c test failed + EBMC21005 = 21005 # BMC rtc test failed + EBMC21006 = 21006 # BMC gpio test failed + EBMC21007 = 21007 # BMC spi test failed + EBMC21008 = 21008 # BMC mdio test failed + + ESPI22001 = 22001 # spi bus devices access failed + + ESW23001 = 23001 # switch chip tcam fault + + EKR24001 = 24001 # kr not recognize + EKR24002 = 24002 # kr link down + EKR24003 = 24003 # kr drop packet + + EDISK25001 = 25001 # disk capacity not enough + EDISK25002 = 25002 # disk size not enough + + ETIMEZONE26001 = 26001 # time zone not match + + EFWVERSION27001 = 27001 # bios version not match + EFWVERSION27002 = 27002 # pit version not match + # sonic node running version is lower than sonic node baseline version. + EFWVERSION27003 = 27003 + # pit node running version is lower than sonic node baseline version. + EFWVERSION27004 = 27004 + + ENET28001 = 28001 # link speed not match + ENET28002 = 28002 # ping test loss packet + + EFWBMCUPGRADE29001 = 29001 # bmc upgrade [slave] fail + EFWBMCUPGRADE29002 = 29002 # bmc upgrade [master] fail + EFWBMCUPGRADE29003 = 29003 # bmc set boot flash to [slave] fail + EFWBMCUPGRADE29004 = 29004 # bmc set boot flash to [master] fail + EFWBMCUPGRADE29005 = 29005 # bmc version not match + EFWBMCUPGRADE29006 = 29006 # bmc flash match + EFWBMCUPGRADE29007 = 29007 # get bmc version fail + EFWBMCUPGRADE29008 = 29008 # get bmc flash fail + EFWBMCUPGRADE29009 = 29009 # ping bmc fail,time out + EFWBMCUPGRADE29010 = 29010 # get bmc upgrade file fail + + ESSDSTRESS30001 = 30001 # ssd total size not match. + ESSDSTRESS30002 = 30002 # fio ssd stress test fail. + ESSDSTRESS30003 = 30003 # ssd read counts error. + + ENETSTRESS31001 = 31001 # cpu ip not legal. + ENETSTRESS31002 = 31002 # ip ping test loss packet. + ENETSTRESS31003 = 31003 # iperf tool not exist. + ENETSTRESS31004 = 31004 # iperf test loss packet. + + EBIOSME32001 = 32001 # BIOS recovery open fail. + EBIOSME32002 = 32002 # BIOS recovery close fail. + + ESVID33001 = 33001 # high voltage not match. + ESVID33002 = 33002 # low voltage not match. + ESVID33003 = 33003 # standerd voltage not match. + + ESNAKE34001 = 34001 # snake sdk enter demon mode fail. + ESNAKE34002 = 34002 # snake port status error. + ESNAKE34003 = 34003 # snake port pkt conut fail. + + ESSH35001 = 35001 # [ remote ] connect fail ,PASSWORD OR HOSTNAME is wrong + # [ remote ] connect timeout ,PASSWORD OR HOSTNAME is wrong + ESSH35002 = 35002 + ESSH35003 = 35003 # [ remote ] connect fail ,EOF ERROR + + EUPG36001 = 36001 # save master running version fail or upgrade test firmware fail + EUPG36002 = 36002 # check master test firmware version fail or upgrade original firmware fail + EUPG36003 = 36003 # check master original firmware version fail + EUPG36004 = 36004 # save slave running version fail or upgrade test firmware fail + EUPG36005 = 36005 # check slave test firmware version fail or upgrade original firmware fail + EUPG36006 = 36006 # check slave original firmware version fail + + ESYS37001 = 37001 # check sysdiag version fail + ESYS37002 = 37002 # check system timezone fail + diff --git a/src/sonic-pit/pit-sysdiag/src/fan_tc.py b/src/sonic-pit/pit-sysdiag/src/fan_tc.py new file mode 100644 index 00000000000..89551e69426 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/fan_tc.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +__version__ = 'v0.1.0' +__author__ = "Pytool Li" +import os +from re import T +import sys +import json +import time +import requests +import traceback +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import * +from function import load_platform_util_module + + +from function import restful_command + +DEFAULT_FAN_CONTROL_INTERVAL = 5 # interval for fan control command + +class FANTC(TestCaseCommon): + __FANUTIL_MODULE_NAME = "fanutil" + __FANUTIL_CLASS_NAME = "FanUtil" + __SENSORUTIL_MODULE_NAME = "sensorutil" + __SENSORUTIL_CLASS_NAME = "SensorUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "fan_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + self.fan_util = None + self.fan_info_dict = None # Expected result + self.fan_all = None # fan info read from H/W + + # Load platform test case list and per case test configuration + try: + if self.platform_cfg_json and 'fan_info' in self.platform_cfg_json: + self.fan_info_dict = self.platform_cfg_json['fan_info'] + except Exception as e: + self.logger.log_err(str(e), True) + + self.position = self.fan_info_dict['position'] + + # Load platform utils + try: + sensor_util_module = load_platform_util_module(self.__SENSORUTIL_MODULE_NAME) + sensor_util_class = getattr(sensor_util_module, self.__SENSORUTIL_CLASS_NAME) + self.sensor_util = sensor_util_class() + except AttributeError as e: + self.logger.log_err(str(e)) + sys.exit(1) + + try: + fan_util_module = load_platform_util_module(self.__FANUTIL_MODULE_NAME) + fan_util_class = getattr(fan_util_module, self.__FANUTIL_CLASS_NAME) + self.fan_util = fan_util_class() + except AttributeError as e: + self.logger.log_err(str(e)) + sys.exit(2) + + # Fill information + self.system_air_flow = self.sensor_util.get_sys_airflow() + self.fan_all = self.fan_util.get_all_raw() + if not self.fan_all: + self.logger.log_err("Failed to get all fan status") + sys.exit(1) + + self.fan_count = self.get_fan_count() + + def get_fan_count(self): + if self.fan_all: + return self.fan_all["Number"] + else: + return self.fan_util.get_num_fans() + + def get_motor_count(self, fanid): + fan_name = "FAN%d" % fanid + if fan_name not in self.fan_all or "Rotors" not in fan_all[fan_name]: + return -1 + else: + return fan_all[fan_name]["Rotors"] + + # Speed test, per fan per rotor + def speed_check(self, fan_all, ratio): + ret = E.OK + fan_count = fan_all["Number"] + speed_tolerance = self.fan_info_dict["speed_tolerance"] + + msg = "speed_check: count {}, tolerance {}, ratio {}%".format(\ + fan_count, speed_tolerance, ratio) + self.logger.log_dbg(msg) + + for fanid in range(1, fan_count+1): + fan_name = "FAN%d" % fanid + fan_info = fan_all[fan_name] + num_motors = fan_info["Rotors"] + for motorid in range(1, num_motors+1): + motor_name = "Rotor%d" % motorid + try: + speed = fan_info[motor_name]["Speed"] + speed_max = fan_info[motor_name]["SpeedMax"] + speed_min = fan_info[motor_name]["SpeedMin"] + except Exception as e: + reason = "Failed to get {} {} info, missing " \ + "Speed/SpeedMin/SpeedMax".format(fan_name, motor_name) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + ret = E.EFAN6005 + continue + + # Check speed range + if speed > speed_max or speed < speed_min: + reason = "{} {} speed {} out of range.".format(fanid+1, motorid+1) + self.log_reason(reason) + ret = E.EFAN6005 + continue + + # Check controlled fan speed + speed_target = speed_min + ratio*(speed_max-speed_min)/100 + speed_delta = speed_target - speed + if (speed_delta > (-speed_tolerance)) and (speed_delta < speed_tolerance): + msg = "{} {} fan control test done, PASS" + self.logger_dbg(msg) + else: + reason = "Failed, {} {} ratio {}%, speed {}, target speed {}".format(\ + fan_name, motor_name, ratio, speed, speed_target) + self.log_reason(reason) + ret = E.EFAN6005 + + return ret + + def test_fan_info(self): + ''' + S3IP 4.9.1 Fan Info Test + a) Test logic + a.1) Get fan count as $fan_count + a.2) Iterate over all fans, with commands + * Get fan FRU if applicable + * Check KEY in FRU if applicable + * Check fan status + b) Result + b.1) All commands done sucessfully + b.2) For step a.2): KEY must contains the followings keys + ["PartNumber", "SerialNumber", "AirFlow", "Presence"] + b.3) "PartNumber", "SerialNumber", should be non-null strings with differnt chars + b.4) "Presence" should be "True" + b.5) "AirFlow"" should be consistent with system airflow + b.6) $fan_count should be consistent with $system_fan_count + ''' + + self.logger.log_info("test_fan_info start") + ret = E.OK + self.fan_count = self.get_fan_count() + if self.fan_count != self.fan_info_dict['count']: + reason = "Failed, system fan count %d(expected %d)" \ + % (self.fan_count, self.fan_info_dict['count']) + self.log_reason(reason) + return E.EFAN6001 + + self.logger.log_dbg("System fan count %d, OK" % self.fan_count) + + for fanid in range(1, self.fan_count+1): + fan_name = "FAN%d" % fanid + + self.logger.log_dbg("Testing {} info".format(fan_name)) + fan_info = self.fan_all[fan_name] + if not fan_info: + reason = "Failed, {} not in all_fan_info".format(fan_name) + self.log_reason(reason) + continue + + # Check presence + if "Presence" not in fan_info: + reason = "Failed: {} info, Presence is missing".format(fan_name) + self.log_reason(reason) + continue + + if fan_info['Presence'] != 'yes': + reason = "Failed: {} is absent".format(fan_name) + self.log_reason(reason) + ret = E.EFAN6004 + continue + + # Check FRU keys + fan_info_keys = ["PartNumber", "SerialNumber", "AirFlow"] + for info_key in fan_info_keys: + if info_key not in fan_info: + reason = "Failed, {} fan info key {} missing".format(fan_name, info_key) + self.fail_reason.append(reason) + self.log_log_dbg(reason) + ret = E.EFAN6001 + + string_keys = ["PartNumber", "SerialNumber"] + for str_key in string_keys: + if len(fan_info[str_key]) < 2: + reason = "Failed, {} {} is {}, length too short".format(\ + fan_name, str_key, fan_info[str_key]) + self.fail_reason.append(reason) + self.log_log_dbg(reason) + ret = E.EFAN6001 + + air_flow = fan_info["AirFlow"] + sys_air_flow = self.system_air_flow + if air_flow != sys_air_flow: + reason = "Failed, {} airflow {} not consistent with " \ + "system airflow {}".format(fan_name, air_flow, sys_air_flow) + self.fail_reason.append(reason) + self.logger.log_dbg(reason) + ret = E.EFAN6001 + continue + + self.logger.log_dbg("{} info test done, PASS".format(fan_name)) + + if ret == E.OK: + self.logger.log_info("test_fan_info PASS.", True) + else: + self.logger.log_info("test_fan_info FAILED.", True) + + return ret + + def test_fan_motor_status(self, fan_name): + ret = E.OK + fan_info = self.fan_ll[fan_name] + motor_count = fan_info["Rotors"] + + for motorid in range(1, motor_count+1): + motor_name = "Rotor%d" % motorid + motor_info = fan_info[motor_name] + + # Check running + running = motor_info["Running"] + if running != "yes": + reason = "{} {} running status {}(expected 'yes')".format( \ + fan_name, motor_name, running) + self.fail_reason.append(reason) + ret = E.EFAN6003 + + # Check Hardware alarm + hwalarm = motor_info["HwAlarm"] + if hwalarm != "no": + reason = "{} {} hardware alarm status {}(expected 'no')".format( \ + fan_name, motor_name, hwalarm) + self.fail_reason.append(reason) + ret = E.EFAN6003 + + try: + speed = int(motor_info["Speed"]) + speed_min = int(motor_info["SpeedMin"]) + speed_max = int(motor_info["SpeedMax"]) + if speed not in range(speed_min, speed_max): + reason = "Failed, {} {} speed {} not in range {}~{}".format( \ + fan_name, motor_name, speed, speed_min, speed_max) + self.fail_reason.append(reason) + ret = E.EFAN6002 + except Exception as e: + reason = "Failed, {} {} Speed/SpeedMin/SpeedMax invalid".format(\ + fan_name, motor_name) + self.fail_reason.append(reason) + ret = E.EFAN6001 + + return ret + + def test_fan_status(self): + ''' + S3IP 4.9.2 Fan Status Test + a) Test logic + a.1) Get fan count as $fan_count + a.2) Iterate over all fans, with commands + * Get motor count + * Get motor speed, running_state, alarm_status + * Get speed range of each motor + * Check runing_state, alarm_status + b) Result + b.1) $fan_count match system expected $system_fan_count + b.2) All commands done sucessfully + b.3) $running_state should be 'True' for each motor + b.4) $alarm_status should be 'False' for each motor + b.4) $speed should be in range($speed_min, $speed_max) for each motor + ''' + ret = E.OK + self.logger.log_info("test_fan_status start", True) + self.fan_count = self.get_fan_count() + if self.fan_count != self.fan_info_dict['count']: + reason = "Failed, system fan count %d(expected %d)" \ + % (self.fan_count, self.fan_info_dict['count']) + self.fail_reason.append(reason) + return E.EFAN6001 + + for fanid in range(1, self.fan_count+1): + fan_name = "FAN%d" % fanid + + self.logger.log_dbg("Testing {} status".format(fan_name)) + fan_info = self.fan_all[fan_name] + motor_count = fan_info["Rotors"] + if motor_count != self.fan_info_dict["motor_count"]: + reason = "Failed, {} motor count {}(expected {}".format( + fan_name, motor_count, self.fan_info_dict["motor_count"]) + self.fail_reason.append(reason) + ret = E.EFAN6001 + continue + + fan_ret = self.test_fan_motor_status(fan_name) + if fan_ret != E.OK: + ret = fan_ret + self.logger.log_err("{} speed test failed.".format(fan_name)) + + self.logger.log_dbg("{} fan status test done, PASS".format(fan_name)) + + if ret == E.OK: + self.logger.log_dbg("test_fan_status PASS.") + else: + self.logger.log_dbg("test_fan_status FAILED.") + + return ret + + + def test_fan_control(self): + ''' + S3IP 4.9.3 Fan Control Test + a) Test logic + a.1) Stop fan control service + a.2) Set fan speed to $fan_speed_test + a.3) Wait $stable_interval seconds, let fan speed to be stable + a.4) Read fan speed back as $fan_speed_readback, check against $fan_speed_test + a.5) Iterate over stop a.2 ~ a.4, for 3 differnt value + a.6) Restart fan control service + b) Result + b.1) All commands done sucessfully + b.2) $fan_speed_test should be consistent with $fan_speed_readback for each iteration + ''' + ret = E.OK + if "fan_control_interval" in self.fan_info_dict: + control_interval = self.fan_info_dict["fan_control_interval"] + else: + control_interval = DEFAULT_FAN_CONTROL_INTERVAL + + self.logger.log_info("test_fan_control start") + self.stop_services() + + for ratio in self.fan_info_dict["ratio_target"]: + if not self.fan_util.set_fan_speed_ratio(ratio): + reason = "Failed, set all fan speed ratio to {} failed.".format(ratio) + self.log_reason(reason) + ret = E.EFAN6005 + continue + + time.sleep(control_interval) + try: + # Instant update + fan_all = self.fan_util.get_all_raw() + except Exception as e: + reason = "Failed, get all fan info got exception: {}".format(str(e)) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + ret = E.EFAN6005 + + chk_ret = self.speed_check(fan_all, ratio) + if chk_ret != E.OK: + ret = chk_ret + + if ret == E.OK: + self.logger.log_info("test_fan_control PASS.", True) + else: + self.logger.log_info("test_fan_control FAILED.", True) + + self.restore_services() + return ret + + def run_test(self, *argv): + final_result = E.OK + + try: + ret = self.test_fan_info() + if ret != E.OK : + final_result = E.EFAIL + except Exception as e: + reason = "Failed, {} test_fan_info exception: {}".format(self.get_tc_name(), str(e)) + self.fail_reason.append(reason) + self.logger.log_err(traceback.format_exc()) + self.logger.log_err(reason) + + try: + ret = self.test_fan_status() + if ret != E.OK : + final_result = E.EFAIL + except Exception as e: + reason = "Failed, {} test_fan_status exception: {}".format(self.get_tc_name(), str(e)) + self.fail_reason.append(reason) + self.logger.log_err(traceback.format_exc()) + self.logger.log_err(reason) + + try: + ret = self.test_fan_control() + if ret != E.OK : + final_result = E.EFAIL + except Exception as e: + reason = "Failed, {} test_fan_control exception: {}".format(self.get_tc_name(), str(e)) + self.fail_reason.append(reason) + self.logger.log_err(traceback.format_exc()) + self.logger.log_err(reason) + + return final_result + + def stop_services(self): + self.fan_util.stop_fan_control() + + def restore_services(self): + self.fan_util.start_fan_control() diff --git a/src/sonic-pit/pit-sysdiag/src/firmware_version_tc.py b/src/sonic-pit/pit-sysdiag/src/firmware_version_tc.py new file mode 100644 index 00000000000..a4fbc341264 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/firmware_version_tc.py @@ -0,0 +1,114 @@ +import os +import sys +import json +import re +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import * +from function import load_platform_util_module + +class FIRMWAREVERSIONTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "firmware_version_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.firmware_util = None + self.firmware_version_cfg = None + + try: + firmware_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + platform_util_class = getattr(firmware_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + try: + if self.platform_cfg_json and "firmware_version" in self.platform_cfg_json.keys(): + self.firmware_version_cfg = self.platform_cfg_json["firmware_version"] + except Exception as e: + self.logger.log_err(str(e), True) + + def get_case_config_file_value(self, key, also_print_console=False): + config_value = None + try: + config_value = self.platform_cfg_json[key] + except KeyError: + err = "no {} in case_config.json file".format(key) + self.fail_reason.append(err) + except Exception as e: + self.fail_reason.append(str(e)) + + return config_value + + def deal_firmware_version_check_test(self, firmware_item, firmware_util_func, util_return_json, also_print_console=False): + """ + deal firmware version check test + + @param: + firmware_item: firmware test item (bmc or bios or cpld ...) + firmware_util_func: firmware util get firmware version func + util_return_json: firmware util func return vlaue is json format (True or False) + @return: + #(E.OK) for success, other for failure + """ + ret = E.OK + self.logger.log_info("[{} CHECK]:".format(firmware_item.upper().replace("_", " ")), also_print_console) + config_version = self.get_case_config_file_value(firmware_item, also_print_console) + if config_version == None: + ret = E.EFW17007 + else: + firmware_version = eval(firmware_util_func)() + if firmware_version == None or str(firmware_version) in "N/A" or (util_return_json == True and type(firmware_version) != dict): + self.fail_reason.append("get firmware version failed") + ret = E.EFW17001 + elif util_return_json == False: + self.logger.log_info("read version is: %s" % (firmware_version), also_print_console) + else: + header = ["NAME","VERSION"] + status_table = [] + for key,value in firmware_version.items(): + status_table.append([key,value]) + if len(status_table) > 0: + self.logger.log_info("read version is:", also_print_console) + self.logger.log_info(tabulate(status_table, header, tablefmt="simple"), also_print_console) + config_version = json.dumps(config_version) + firmware_version = json.dumps(firmware_version) + + if config_version not in firmware_version: + self.fail_reason.append("check firmware version is wrong!") + ret = E.EFW17002 + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + """ + util_return_dict_map + firmware test item util return vlaue is josn(str or dict) + "onie": False + """ + util_return_dict_map = { + "onie": False, + "bmc": False, + "cpld": True, + "bios": False, + "fpga": False, + "sdk": False, + "switch_pcie": True, + "uboot": False + } + ret = E.OK + for version in self.firmware_version_cfg: + util_return_json = util_return_dict_map.get(version, None) + if util_return_json != None: + test_item = "%s_version" % (version) + util_func_name = "self.firmware_util.get_%s_version" % (version) + ret = self.deal_firmware_version_check_test(test_item, util_func_name, util_return_json, True) + if ret != E.OK: + break + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/fpga_firmware_program_tc.py b/src/sonic-pit/pit-sysdiag/src/fpga_firmware_program_tc.py new file mode 100755 index 00000000000..90f32b86ca2 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/fpga_firmware_program_tc.py @@ -0,0 +1,431 @@ +# coding:utf-8 +import json +import os +import sys + +from errcode import * +from function import load_platform_util_module + +# from click._compat import raw_input +from test_case import TestCaseCommon + +MASTER_ORIG_FW_CFG = "fpga_master_firmware_baseline.json" +SLAVE_ORIG_FW_CFG = "fpga_slave_firmware_baseline.json" + + +class FPGAFIRMWAREPROGRAMTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + get_version_func = "self.firmware_util.get_fw_version" + firmware_upgrade_func = "self.firmware_util.firmware_upgrade" + firmware_refresh_func = "self.firmware_util.firmware_refresh" + slave_upgrade = False + fw_extra = "master" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + # this param specified the case config directory + MODULE_NAME = "fpga_firmware_program_tc" + TestCaseCommon.__init__( + self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file + ) + self.firmware_util = None + + try: + firmware_module = load_platform_util_module( + self.__PLATFORM_SPECIFIC_MODULE_NAME + ) + platform_util_class = getattr( + firmware_module, self.__PLATFORM_SPECIFIC_CLASS_NAME + ) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + try: + self.firmware_type = self.platform_cfg_json["fpga_firmware_type"] + self.test_firmware_list = self.platform_cfg_json["fpga_firmware_test"] + self.baseline_firmware_list = self.platform_cfg_json["fpga_firmware_baseline"] + self.platform_path = os.path.dirname(self.platform_cfg_file) + except Exception as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + def get_upgrade_path(self, firmware_list, item): + """ + + @param firmware_list: firmware list + @param item: firmware type + @return: get path success:bool,skip upgrade:bool,firmware path:string + """ + support_slave, skip_upgrade = self.check_item_support_slave(item) + if "absolute_path" not in firmware_list[item]: + firmware_list[item]["absolute_path"] = True + # absolute_path字段不存在时默认值为True + if skip_upgrade is True: + return False, True, "" + elif ( + skip_upgrade is False + and support_slave is True + and "same_with_master" in firmware_list[item] + and firmware_list[item]["same_with_master"] is False + ): + # 判断备区升级文件是否存在 + if ( + "slave_file_path" not in firmware_list[item] + or firmware_list[item]["slave_file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["slave_file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + # 获取备区升级固件所在路径,“same_with_master”字段省略默认为False,备区固件路径和主区一样则直接使用firmware_list[item]["file_path"] + # 不一样则使用firmware_list[item]["slave_file_path"] + else: + if ( + "file_path" not in firmware_list[item] + or firmware_list[item]["file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + + def upgrade_firmware_refresh(self, firmware_list, test_type="test"): + """ + + @param firmware_list: firmware list + @param test_type: sting to print to console + @return: upgrade and refresh firmware success:bool + """ + ret = True + try: + for item in self.firmware_type: + result, skip_upgrade, fw_path = self.get_upgrade_path( + firmware_list, item + ) + if skip_upgrade: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip upgrade" % item, + True, + ) + continue + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + result = eval(self.firmware_upgrade_func)(item, fw_path, self.fw_extra) + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + else: + result = eval(self.firmware_refresh_func)( + item, fw_path, self.fw_extra + ) + if not result: + self.fail_reason.append("%s firmware refresh fail" % item) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + def save_baseline_and_upgrade(self): + """ + + @return: save baseline version and upgrade success:bool + """ + try: + + version_running_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "w", + ) as f: + json.dump( + version_running_read_back, f, indent=4, separators=(",", ": ") + ) + return self.upgrade_firmware_refresh( + self.test_firmware_list, test_type="test" + ) + except Exception as e: + self.fail_reason.append(str(e)) + return False + + def check_all_items_support_slave(self): + item = "" + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if support_slave is True: + self.logger.log_info( + "there are slave_upgrade attr in item %s" % item, True + ) + return True + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + raise Exception("check %s support slave failed" % item) + return False + + def check_item_support_slave(self, item): + """ + + @param item: firmware type + @return: is support slave upgrade:bool,skip upgrade + """ + if self.slave_upgrade: + if ( + "slave_upgrade" not in self.test_firmware_list[item] + or "slave_upgrade" not in self.baseline_firmware_list[item] + ): + if ( + "slave_upgrade" not in self.test_firmware_list[item] + and "slave_upgrade" not in self.baseline_firmware_list[item] + ): + # 不存在“slave_upgrade”字段,默认为false + return False, True + else: + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif ( + self.test_firmware_list[item]["slave_upgrade"] is False + and self.baseline_firmware_list[item]["slave_upgrade"] is False + ): + return False, True + # “slave_upgrade”字段为False,不支持备区升级 + elif ( + self.test_firmware_list[item]["slave_upgrade"] + != self.baseline_firmware_list[item]["slave_upgrade"] + ): + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif self.test_firmware_list[item]["slave_upgrade"] is True: + return True, False + else: + return False, False + + def check_firmware_version(self, firmware_cfg, test_type="test"): + """ + + @param firmware_cfg: firmware config file content + @param test_type: string to print,distinguish the two modes + @return:check firmware version success:bool + """ + version_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + ret = True + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if skip is True: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip check version" + % item, + True, + ) + continue + # 不支持备区升级直接跳过检测 + if skip is False and support_slave is True: + if ( + test_type is "test" + and "same_with_master" not in firmware_cfg[item] + or firmware_cfg[item]["same_with_master"] is False + ): + version_config_file = firmware_cfg[item]["slave_version"] + # 比对备区test版本固件版本号,“same_with_master”字段省略默认为False,备区版本号和主区一样则直接使用firmware_cfg[item]["version"] + # 不一样则存放在firmware_cfg[item]["slave_version"] + else: + version_config_file = firmware_cfg[item]["version"] + else: + version_config_file = firmware_cfg[item]["version"] + if version_config_file == version_read_back[item]["version"]: + self.logger.log_info( + "%s %s firmware check success" % (item, test_type), True + ) + else: + self.logger.log_err( + "%s %s firmware check fail,config file version:[%s],device version:[%s]" + % ( + item, + test_type, + version_config_file, + version_read_back[item]["version"], + ), + True, + ) + self.fail_reason.append( + "%s %s firmware check fail" % (item, test_type) + ) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return ret + + def check_and_recover_firmware(self): + """ + + @return: check and recover firmware to baseline success:bool + """ + try: + version_test_check_result = self.check_firmware_version( + self.test_firmware_list, test_type="test" + ) + if version_test_check_result is not True: + return False + else: + return self.upgrade_firmware_refresh( + self.baseline_firmware_list, test_type="baseline" + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return False + + def check_baseline_firmware(self): + """ + + @return: check baseline firmware version success:bool + """ + ret = True + try: + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "r", + ) as f: + version_running_read_back_list = json.load(f) + version_running_check_result = self.check_firmware_version( + version_running_read_back_list, test_type="baseline" + ) + if version_running_check_result is not True: + ret = False + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + test_case_list = [ + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware", + ] + test_help_list = [ + "1.save master running firmware version and program test firmware", + "2.check master test firmware version and program baseline firmware", + "3.check master baseline firmware version", + "4.save slave running firmware version and program test firmware", + "5.check slave test firmware version and program baseline firmware", + "6.check slave baseline firmware version", + ] + + def run_test(self, *argv): + ret = E.OK + also_print_console = True + for item in self.test_help_list: + self.logger.log_info(item, True) + + choice = input("Please enter your choice (0 to quit):") + try: + if int(choice) not in range(0, (len(self.test_case_list) + 1)): + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + else: + if int(choice) is 0: + # self.fail_reason.append("user cancelled input") + self.logger.log_info("user cancelled input", also_print_console) + ret = E.OK + else: + index = int(choice) + # continue test item while check is success + test_item = "self.%s" % self.test_case_list[index - 1] + self.slave_upgrade = True if index > 3 else False + self.fw_extra = "master" if not self.slave_upgrade else "slave" + self.logger.log_info( + "[%s]:" % self.test_help_list[index - 1], also_print_console + ) + if ( + self.slave_upgrade is False + or self.check_all_items_support_slave() is True + ): + # skip test item while there are no "slave_upgrade" in all "firmware_type" item + result = eval(test_item)() + if result is not True: + self.logger.log_info( + "item %s =======> test fail" + % self.test_help_list[index - 1], + also_print_console, + ) + return E.EUPG25001 + index - 1 + elif index == 1 or index == 2 or index == 4 or index == 5: + self.logger.log_info( + "item %s =======> test success" + % self.test_help_list[index - 1], + also_print_console, + ) + # system reboot may be required in test item 1,2,4 or 5 + # break out of the loop to end the test item + else: + self.logger.log_info( + "there are no slave_upgrade attr, item %s =======> test skip" + % self.test_help_list[index - 1], + True, + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/fruidutil.py b/src/sonic-pit/pit-sysdiag/src/fruidutil.py new file mode 100755 index 00000000000..0d9a75617b2 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/fruidutil.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import re +import argparse +from function import exec_cmd + +# psu1_fru_info = """ +# FRU Device Description : PSU1_FRU (ID 34) +# Product Manufacturer : Great Wall +# Product Name : GW-CRPS1600D2W +# Product Part Number : +# Product Version : 00.01.01 +# Product Serial : 2L020080022 +# """ + +usage = """ +fruidutil get [NODE+NUM] + set [NODE+NUM] -f + +OPTIONS: + option fru information + node fru node name + -f Path of xxx.bin file + -h Print use guide +""" + + +def fruidutil(args): + """ + FRU Commands: list read write edit + fru list - list all fru + fru list - list specific fru data + fru read - store fru data to file + fru write - write fru data from file + fru edit field
- edit FRU string + + You should move fru file to /usr/local/bin first + """ + cmd = '' + if args.option in ['get', 'set']: + cmd += 'fru list' if args.option == 'get' else 'fru edit' + if args.node: + find_num = re.findall('[a-z0]+(\d+)', args.node) + if find_num: + num = find_num[0] + if num <= 0: + print('error: num is incorrect') + else: + cmd += ' ' + re.sub('\d', '', args.node) + num + if args.f: + cmd += ' ' + args.f + cmd = cmd.replace('edit', 'write') + try: + fru_info = exec_cmd(cmd) + print(fru_info) + except Exception as err: + print('{} run failed as: {}'.format(cmd, err)) + else: + print('error: num is missing') + else: + print(usage) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Base fru command", add_help=False, usage=usage) + parser.add_argument('option', type=str, help='get/set') + parser.add_argument('node', type=str, help=' ex:psu1|fan1... num start from 1') + parser.add_argument('-f', type=str, help='[file] example: /root/fru.bin') + parser.add_argument('-h', action='help', help='Help Information') + args = parser.parse_args() + fruidutil(args) \ No newline at end of file diff --git a/src/sonic-pit/pit-sysdiag/src/function.py b/src/sonic-pit/pit-sysdiag/src/function.py new file mode 100644 index 00000000000..8515e8b84df --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/function.py @@ -0,0 +1,108 @@ +import subprocess +from subprocess import check_output +import imp +import os +import json +import requests + + +PLATFORM_ROOT_PATH = "/usr/share/sonic/device" +PLATFORM_ROOT_PATH_DOCKER = "/usr/share/sonic/platform" + +SONIC_CFGGEN_PATH = "/usr/local/bin/sonic-cfggen" +HWSKU_KEY = "DEVICE_METADATA['localhost']['hwsku']" +PLATFORM_KEY = "DEVICE_METADATA['localhost']['platform']" + + +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0].decode() + proc.wait() + platform = stdout.rstrip("\n") + # platform = platform.lower().rstrip("-r0") + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0].decode() + proc.wait() + hwsku = stdout.rstrip("\n") + except OSError as e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +def load_platform_util_module(module_name): + platform_util_module = None + + (platform, hwsku) = get_platform_and_hwsku() + platform_path = '' + if len(platform) != 0: + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + else: + platform_path = PLATFORM_ROOT_PATH_DOCKER + + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file = "/".join([platform_path, "plugins", module_name + ".py"]) + platform_util_module = imp.load_source(module_name, module_file) + except IOError as e: + return None + + return platform_util_module + + +def convert_unicode(input): + if isinstance(input, dict): + return {convert_unicode(key): convert_unicode(val) for key, val in input.iteritems()} + elif isinstance(input, unicode): + return input.encode("utf-8") + else: + return input + + +def exec_cmd(cmd_str): + output = None + try: + output = check_output(cmd_str, shell=True,) + except Exception as err: + print("{} exec cmd failed.{}".format(cmd_str, err)) + return output + + +def run_command(cmd): + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if err and proc.returncode != 0: + return proc.returncode, err + return 0, out.decode().rstrip('\n') + + +def restful_command(cmd): + url = "http://240.1.1.1:8080/api/hw/rawcmd" + data = json.dumps({ + "Command" : cmd + }) + + for i in range(3): + try: + r = requests.post(url, data) + r.url + response_json = r.json() + + if "status" in response_json and response_json["status"] == "OK": + ret = response_json["data"]["Outputs"] + return True, ret + else: + return False, "" + except Exception as e: + print(e) + continue + return False, "" diff --git a/src/sonic-pit/pit-sysdiag/src/gpio_tc.py b/src/sonic-pit/pit-sysdiag/src/gpio_tc.py new file mode 100644 index 00000000000..5d65664f79d --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/gpio_tc.py @@ -0,0 +1,138 @@ +import sys +import os +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import * +from function import load_platform_util_module + + +# gpio test class +class GPIOTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "gpio_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.gpio_map = None # default + self.jtag_map = None # default + """case_config.json example + "gpio":{ + "gpio486":{ + "pin":"GPIO50", + "net":"CPU_GPIO_JTAG_EN" + }, + "gpio479":{ + "pin":"GPIO43", + "net":"CPU_GPIO_JTAG_TCK" + }, + "gpio450":{ + "pin":"GPIO14", + "net":"CPU_GPIO_JTAG_TMS" + }, + "gpio446":{ + "pin":"GPIO10", + "net":"CPU_GPIO_JTAG_TDO" + }, + "gpio445":{ + "pin":"GPIO9", + "net":"CPU_GPIO_JTAG_TDI" + } + }, + "jtag":["BASEBOARD_CPLD"] + """ + try: + self.platform_conf_path = os.path.dirname(os.path.abspath(self.platform_cfg_file)) + if self.platform_cfg_json and 'gpio' in self.platform_cfg_json.keys(): + self.gpio_map = self.platform_cfg_json['gpio'] + if self.platform_cfg_json and 'jtag' in self.platform_cfg_json.keys(): + self.jtag_map = self.platform_cfg_json['jtag'] + except Exception as e: + self.logger.log_err(str(e), True) + + + fwupgrade_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + try: + platform_util_class = getattr(fwupgrade_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + + def _read_gpio_pin_value(self, gpio_value_file): + value = -1 + try: + with open(gpio_value_file, "r") as f: + value = f.read() + except IOError as e: + self.logger.log_err(str(e)) + + return value + + + def gpio_pin_check(self, also_print_console=True): + if not self.gpio_map: + err = "gpio conf is null!" + self.fail_reason.append(err) + self.logger.log_err(err, also_print_console) + return E.EEXIST + + self.logger.log_info("[GPIO PIN CHECK]:", also_print_console) + header = ["GPIO", "Pin", "Net", "Status"] + status_tbl = [] + ret = E.OK + for gpio, desc in self.gpio_map.items(): + pin_name = desc['pin'] + net_name = desc['net'] + gpio_value_file = os.path.join("/sys/class/gpio", gpio, "value") + value = self._read_gpio_pin_value(gpio_value_file) + if value < 0: + ret = E.EGPIO13001 + status = "Not_OK" + err = "{} read failed!".format(gpio) + self.fail_reason.append(err) + else: + status = "OK" + status_tbl.append([gpio, pin_name, net_name, status]) + + if len(status_tbl) > 0: + self.logger.log_info(tabulate(status_tbl, header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def jtag_upgrade_by_gpio(self, also_print_console=False): + self.logger.log_info("[GPIO JTAG FUNCTION CHECK]:", also_print_console) + ret = E.OK + for jtag in self.jtag_map: + jtag_upgrade_file = os.path.join(self.platform_conf_path, jtag + '.vme') + if jtag_upgrade_file and os.path.exists(jtag_upgrade_file): + status = self.firmware_util.firmware_upgrade("cpld", jtag_upgrade_file, jtag) + if not status: + ret = E.EGPIO13002 + self.fail_reason.append("ScanJtag failed") + else: + ret = E.EEXIST + self.fail_reason.append("missing vme file") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + ret = self.gpio_pin_check(True) + if ret != E.OK: + return ret + + ret = self.jtag_upgrade_by_gpio(True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/i2c_tc.py b/src/sonic-pit/pit-sysdiag/src/i2c_tc.py new file mode 100644 index 00000000000..a34f9916e7e --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/i2c_tc.py @@ -0,0 +1,117 @@ +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import E +from function import run_command + +GLOBAL_VALUE = 0xA5 +GLOBAL_VALUE_CLEAR = 0x5A + +class I2CTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "i2c_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.i2c_devices = None # default + """ case_config.json example + "i2c":{ + "PORT_CPLD" : { + "bus" : 2, + "address" : "0x30", + "register" : "0x0F", + "flag" : "rw" + }, + "FCB_CPLD" : { + "bus" : 2, + "address" : "0x31", + "register" : "0x0F", + "flag" : "rw" + } + }, + """ + try: + if self.platform_cfg_json and 'i2c' in self.platform_cfg_json.keys(): + self.i2c_devices = self.platform_cfg_json['i2c'] + except Exception as e: + self.logger.log_err(str(e), True) + + + def i2c_device_rw_test(self, also_print_console=False): + ''' + I2C device read/write test + ''' + if self.i2c_devices is None: + self.fail_reason.append("{}: i2c devices config null".format(self.module_name)) + return E.EEXIST + + ret = E.OK + header = ['Device', 'Bus', 'Address', 'Status'] + status_tbl = [] + + self.logger.log_info( "[I2C DEVICES R/W TEST]:", also_print_console) + devices = self.i2c_devices.keys() + for device in devices: + dev_detail = self.i2c_devices.get(device) + bus = dev_detail["bus"] + addr = dev_detail["address"] + reg = dev_detail["register"] + flag = dev_detail["flag"] + status = "OK" + + if flag == 'rw': + # write i2c register + cmd = "i2cset -f -y {0} {1} {2} {3}".format(bus, addr, reg, GLOBAL_VALUE) + code,out = run_command(cmd) + # read i2c register + cmd = "i2cget -f -y {0} {1} {2}".format(bus, addr, reg) + code, out = run_command(cmd) + # valigate value + if code or int(out, 16) != GLOBAL_VALUE: + ret = E.EIIC8003 + err = "[{}] {} r/w test failed!".format(self.module_name, device) + self.fail_reason.append(err) + status = "Not_OK" + + # write i2c register, clear last writed value + cmd = "i2cset -f -y {0} {1} {2} {3}".format(bus, addr, reg, GLOBAL_VALUE_CLEAR) + code,out = run_command(cmd) + # read i2c register + cmd = "i2cget -f -y {0} {1} {2}".format(bus, addr, reg) + code, out = run_command(cmd) + # valigate value + if code or int(out, 16) != GLOBAL_VALUE_CLEAR: + ret = E.EIIC8003 + err = "[{}] {} r/w test failed!".format(self.module_name, device) + self.fail_reason.append(err) + status = "Not_OK" + elif flag == 'ro': + cmd = "i2cget -f -y {0} {1} {2}".format(bus, addr, reg) + code, out = run_command(cmd) + if code: + ret = E.EIIC8002 + err = "[{}] {} r/w test failed!".format(self.module_name, device) + self.fail_reason.append(err) + status = "Not_OK" + + status_tbl.append([device, bus, addr, status]) + if len(status_tbl) > 0: + self.logger.log_info(tabulate(status_tbl, header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + """ + test case main process + """ + loop = argv[0] + for i in range(1, loop + 1): + ret = self.i2c_device_rw_test(True) + if ret != E.OK: + self.logger.log_err("i2c r/w loop {} failed!".format(i), True) + break + + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/internal_usb_tc.py b/src/sonic-pit/pit-sysdiag/src/internal_usb_tc.py new file mode 100644 index 00000000000..c1d4983d625 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/internal_usb_tc.py @@ -0,0 +1,60 @@ +import time + +from test_case import TestCaseCommon +from errcode import E +from function import run_command + + +class INTERNALUSBTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "internal_usb_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + def get_internal_usb_dev(self, also_print_console=False): + self.logger.log_info("[CHECK INTERNAL USB DEVICE]:", also_print_console) + cmd = "ifconfig -a | grep usb" + status, device = run_command(cmd) + if not status and device: + self.logger.log_info("get usb network device.", also_print_console) + ret = E.OK + else: + ret = E.EEXIST + self.fail_reason.append("usb network device not found!") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def cpu_ping_bmc_test(self, also_print_console=True): + self.logger.log_info("[CPU BMC LINK TEST]:", also_print_console) + cmd = "ifconfig usb0 up " + status, output = run_command(cmd) + cmd = "ifconfig usb0 192.168.1.100 " + status, output = run_command(cmd) + time.sleep(10) + count = 5 + ping_cmd = "ping 192.168.1.101 -c %d -I usb0 | grep received" % count + status, output = run_command(ping_cmd) + self.logger.log_info(output, also_print_console) + + if output.find(" 0% packet loss") > 0: + ret = E.OK + else: + self.fail_reason.append("cpu ping server lost packages") + ret = E.EUSB1005 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + def run_test(self, *argv): + ret = self.get_internal_usb_dev(True) + if ret != E.OK: + return ret + ret = self.cpu_ping_bmc_test(True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/lpc_tc.py b/src/sonic-pit/pit-sysdiag/src/lpc_tc.py new file mode 100644 index 00000000000..fbb0ff4c319 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/lpc_tc.py @@ -0,0 +1,94 @@ +import os +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import E +from function import run_command + +GLOBAL_VALUE1 = 0x5A +GLOBAL_VALUE2 = 0xA5 + +# LPC bus and devices test class +class LPCTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "lpc_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.lpc_devices = None # default + """ case_config.json example + "lpc":{ + "CTRL_CPLD":{ + "path":"/sys/bus/platform/devices/ctrl_cpld", + "register":"sw_rw_test", + "flag":"rw" + } + }, + """ + try: + if self.platform_cfg_json and 'lpc' in self.platform_cfg_json.keys(): + self.lpc_devices = self.platform_cfg_json['lpc'] + except Exception as e: + self.logger.log_err(str(e)) + + + def lpc_device_rw_test(self, also_print_console=False): + self.logger.log_info( "[LPC DEVICE RW TEST]:", also_print_console) + ret = E.OK + + if self.lpc_devices: + for device in self.lpc_devices.keys(): + try: + device_conf = self.lpc_devices.get(device) + reg_file = os.path.join(device_conf['path'], device_conf['register']) + + if device_conf['flag'] == "rw": + with open(reg_file, 'w') as f: + f.write(str(GLOBAL_VALUE1)) + with open(reg_file, 'r') as f: + value = f.read() + self.logger.log_info("{} write {}, read {}".format(device, GLOBAL_VALUE1, value), also_print_console) + + if int(value) != GLOBAL_VALUE1: + ret = E.ELPC10003 + self.fail_reason.append("{} rw fail".format(device)) + + with open(reg_file, 'w') as f: + f.write(str(GLOBAL_VALUE2)) + with open(reg_file, 'r') as f: + value = f.read() + self.logger.log_info("{} write {}, read {}".format(device, GLOBAL_VALUE2, value), also_print_console) + + if int(value) != GLOBAL_VALUE2: + ret = E.ELPC10003 + self.fail_reason.append("{} rw fail".format(device)) + + elif device_conf['flag'] == "ro": + with open(reg_file, 'r') as f: + value = f.read() + self.logger.log_info("{} read {}={}".format(device, device_conf['register'], value), also_print_console) + else: + pass + + except Exception as e: + self.logger.log_err(str(e)) + self.fail_reason.append(str(e)) + ret = E.ELPC10001 + + else: + ret = E.EEXIST + self.fail_reason.append("lpc config NULL") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + loop = argv[0] + for i in range(1, loop + 1): + ret = self.lpc_device_rw_test(True) + if ret != E.OK: + self.logger.log_err("lpc r/w loop {} failed!".format(i), True) + break + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/mac_address_tc.py b/src/sonic-pit/pit-sysdiag/src/mac_address_tc.py new file mode 100644 index 00000000000..1b93748c353 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/mac_address_tc.py @@ -0,0 +1,79 @@ +import os +import sys +import json +import re +from test_case import TestCaseCommon +from function import load_platform_util_module +from errcode import E + +class MACADDRESSTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "baseutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "BaseUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "mac_address_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.base_util = None + self.mac_address_dict = None + + try: + base_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + platform_util_class = getattr(base_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.base_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + try: + if self.platform_cfg_json and "mac_address" in self.platform_cfg_json.keys(): + self.mac_address_dict = self.platform_cfg_json["mac_address"] + except Exception as e: + self.logger.log_err(str(e), True) + + def check_mac_test(self, check_item, base_util_func, offset, base_mac, also_print_console=False): + """ + check mac test + + @param: + check_item: test item (cpu or bmc) + base_util_func: base util get mac func + offset: check mac offset (eg base_mac +1 or +2) + base_mac: the mac for device + @return: + #(E.OK) for success, other for failure + """ + ret = E.OK + self.logger.log_info("[CHECK %s MAC]:" % check_item.upper(), also_print_console) + item_mac = eval(base_util_func)() + if item_mac == "N/A": + ret = E.EMGMT11006 + self.fail_reason.append("get mac error, %s_mac:%s" % (check_item.lower(), item_mac.lower())) + else: + self.logger.log_info("%s_mac = %s, base_mac = %s" % (check_item.lower(), item_mac.lower(), base_mac.lower()), also_print_console) + #check mac + offset_mac = re.sub("(\w\w)(?=\w)", "\g<1>:", "{:012x}".format(int((int("0x"+base_mac.replace(":", ""), 16) + offset) & 0xffffffffffff))) + if offset_mac.lower() != item_mac.lower(): + ret = E.EMGMT11008 + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + ret = E.OK + base_mac = self.base_util.get_base_mac() + if base_mac == "N/A": + ret = E.EMGMT11006 + self.fail_reason.append("get mac error, base_mac:%s" % (base_mac.lower())) + return ret + + for check_item in self.mac_address_dict.get("check_list"): + item_offset = "%s_offset" % (check_item) + offset = self.mac_address_dict.get("offset")[item_offset] + util_func_name = "self.base_util.get_%s_mac" % (check_item) + + ret = self.check_mac_test(check_item, util_func_name, offset, base_mac, True) + if ret != E.OK: + break + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/memory_tc.py b/src/sonic-pit/pit-sysdiag/src/memory_tc.py new file mode 100644 index 00000000000..c423ab241dd --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/memory_tc.py @@ -0,0 +1,161 @@ +from function import run_command +from test_case import TestCaseCommon +from errcode import E + +X86_ARCH_LIST = ["x86", "x86_64", "amd", "amd64"] +ARM_ARCH_LIST = ["arm", "arm64"] + +# memory test class +class MEMORYTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "memory_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.arch = "x86" # default arch + self.memory_bom_list = None # default conf + self.free_mem_size = 100 # free memory size in kB. if free mem is less than free_mem_size, fail. + try: + if self.platform_cfg_json and 'memory_bom' in self.platform_cfg_json.keys(): + self.memory_bom_list = self.platform_cfg_json['memory_bom'] + if self.platform_cfg_json and 'arch' in self.platform_cfg_json.keys(): + self.arch = self.platform_cfg_json['arch'] + if self.platform_cfg_json and 'memory_free_size' in self.platform_cfg_json.keys(): + self.free_mem_size = int(self.platform_cfg_json['memory_free_size']) + except Exception as e: + self.logger.log_err(str(e), True) + + + def _memory_info_check_by_dmidecode(self, also_print_console=True): + ret = E.OK + pn_list = [] + vendor_list = [] + + status, out = run_command("dmidecode -t 17") + if status != 0 or len(out) <= 0: + self.fail_reason.append("dmidecode exec failed.") + ret = E.EMEM4001 + else: + for item in out.splitlines(): + self.logger.log_info(item, also_print_console) + if ":" in item: + key = item.split(":")[0].strip() + value = item.split(":")[1].strip() + #self.logger.log_info("%-30s: %s" % (key, value), True) + if key == 'Part Number' and value != 'NO DIMM': + pn_list.append(value) + if key == 'Manufacturer' and value != 'NO DIMM': + vendor_list.append(value) + + # memory bom check + if self.memory_bom_list: + memory_matched = False + for memory_bom in self.memory_bom_list: + if memory_bom["manufacturer"] in vendor_list and memory_bom["pn"] in pn_list: + memory_matched = True + break + if not memory_matched: + ret = E.EMEM4001 + self.fail_reason.append("memory not matched") + + return ret + + + def _arm_memory_ecc_check(self, also_print_console=True): + return E.OK + + + def _x86_memory_ecc_check(self, also_print_console=True): + status, out = run_command("edac-util -v") + self.logger.log_info(out, also_print_console) + if status: + self.fail_reason.append("memort ecc occured") + return E.EMEM4003 + else: + return E.OK + + + def memory_info_check(self, also_print_console=True): + self.logger.log_info("[CHECK MEMORY INFO]:", also_print_console) + + if self.arch in X86_ARCH_LIST: + ret = self._memory_info_check_by_dmidecode() + elif self.arch in ARM_ARCH_LIST: + ret = E.OK + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def test_memory_capacity(self, also_print_console=True): + self.logger.log_info("[CHECK MEMORY CAPACITY]:", also_print_console) + ret = E.OK + status, out = run_command("free -t") + self.logger.log_info(out, also_print_console) + if status: + self.fail_reason.append("free exec failed") + ret = E.EIO + else: + for line in out.splitlines(): + if line.find("Total") >= 0: + free_mem = line.split()[3] + if int(free_mem) < self.free_mem_size: + self.fail_reason.append("free memory less than {}kB".format(self.free_mem_size)) + ret = E.EMEM4004 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def test_memory_by_memtester(self, also_print_console=True): + self.logger.log_info("[MEMORY MEMTESTER]:", True) + + status, out = run_command("memtester 1M 1") + self.logger.log_info(out, also_print_console) + if status: + self.fail_reason.append("memtester failed") + ret = E.EMEM4002 + else: + ret = E.OK + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def memory_ecc_check(self, also_print_console=True): + self.logger.log_info("[CHECK MEMORY ECC]:", True) + + if self.arch in X86_ARCH_LIST: + ret = self._x86_memory_ecc_check() + elif self.arch in ARM_ARCH_LIST: + ret = self._arm_memory_ecc_check() + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + ret = self.memory_info_check() + if ret != E.OK: + return ret + ret = self.test_memory_capacity() + if ret != E.OK: + return ret + ret = self.test_memory_by_memtester() + if ret != E.OK: + return ret + ret = self.memory_ecc_check() + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/memstress_tc.py b/src/sonic-pit/pit-sysdiag/src/memstress_tc.py new file mode 100644 index 00000000000..13fc062a1a8 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/memstress_tc.py @@ -0,0 +1,83 @@ +# -*- coding:utf-8 -*- +import pit_util_common + +from errcode import E +from test_case import TestCaseCommon +from pit_util_common import run_command + +# memstress test class + + +class MEMSTRESS(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "memstress_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + try: + self.memstress_info_dict = None + if self.platform_cfg_json and "memstress_info" in self.platform_cfg_json.keys(): + self.memstress_info_dict = self.platform_cfg_json["memstress_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def test_memory_by_memtester(self, also_print_console=False): + ret = E.EFAIL + self.logger.log_info("[MEMORY MEMTESTER]:", also_print_console) + available_value_cmd = "free -m | grep Mem | awk '{print $7}'" + available_status, available_value = run_command(available_value_cmd) + if available_status != 0 or len(available_value) <= 0: + self.fail_reason.append("get available mem size fail.") + ret = E.EIO + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + available_mem_value = int(available_value) + mem_run_size_percentage = float( + self.memstress_info_dict["mem_run_size_percentage"].replace( + "%", "")) / 100 + mem_run_size_value = int( + available_mem_value * + mem_run_size_percentage) + mem_test_count = self.memstress_info_dict["mem_test_count"] + self.logger.log_info("available memmory size: {}M".format( + available_mem_value), also_print_console) + self.logger.log_info( + "memmory run size: {}M".format(mem_run_size_value), + also_print_console) + memstress_test_cmd = "memtester {}M {}".format( + mem_run_size_value, mem_test_count) + waiting = pit_util_common.waiting( + self.logger, "memstress testing...") + status, out = run_command(memstress_test_cmd) + waiting.stop("memstress test stop!") + if status != 0 or len(out) <= 0: + self.fail_reason.append("memtester cmd error.") + ret = E.EMEM4002 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.logger.log_info(out, also_print_console) + for value in out.split("\n"): + if ":" in value and value.split(":")[-1] != "": + if "ok" not in value.split(":")[-1]: + self.fail_reason.append( + "memmory stress test error.") + ret = E.EMEM4005 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + ret = E.OK + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + for i in range(self.memstress_info_dict["memstress_test_time"]): + ret = self.test_memory_by_memtester(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/mgmt_tc.py b/src/sonic-pit/pit-sysdiag/src/mgmt_tc.py new file mode 100644 index 00000000000..c8ba1450668 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/mgmt_tc.py @@ -0,0 +1,103 @@ +import sys +from function import * +from test_case import TestCaseCommon +from errcode import * + +PLATFORM_SPECIFIC_MODULE_NAME = "mgmtport" +PLATFORM_SPECIFIC_CLASS_NAME = "MgmtPortUtil" + +class MGMTTC(TestCaseCommon): + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "mgmt_tc" + self.mgmtport_util = None + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.server_ip = None # external ip + mgmtport_module = load_platform_util_module(PLATFORM_SPECIFIC_MODULE_NAME) + try: + mgmtport_class = getattr(mgmtport_module, PLATFORM_SPECIFIC_CLASS_NAME) + self.mgmtport_util = mgmtport_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + try: + if self.platform_cfg_json and 'server_ip' in self.platform_cfg_json.keys(): + self.server_ip = self.platform_cfg_json['server_ip'] + except Exception as e: + self.logger.log_err(str(e), True) + + + def check_mgmt_status(self, port_status, also_print_console=True): + ret = E.OK + + self.logger.log_info("[MGMT PORT STATUS CHECK]:", also_print_console) + + # check OS MGMT link status + if not port_status: + ret = E.EMGMT11001 + self.fail_reason.append("get eth0 status failed") + else: + for key, value in port_status.items(): + self.logger.log_info("{}: {}".format(key, value), also_print_console) + if key == "Speed": + if value != "1000": + ret = E.EMGMT11001 + self.fail_reason.append("speed is {}".format(value)) + + if key == "Duplex": + if value != "full": + ret = E.EMGMT11001 + self.fail_reason.append("duplex is {}".format(value)) + + if key == "AN": + if value != True: + ret = E.EMGMT11001 + self.fail_reason.append("auto-neg is {}".format(value)) + + if key == "Link": + if value != True: + ret = E.EMGMT11001 + self.fail_reason.append("link detected {}".format(value)) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def mgmt_ping_test(self, also_print_console=True): + ret = E.OK + self.logger.log_info("[MGMT PORT PING CHECK]:", also_print_console) + + if self.server_ip == None: + self.fail_reason.append("no server ip specified") + return E.EEXIST + else: + # OS ping server + count = '5' + ping_cmd = "ping %s -c %s -I eth0 | grep received | awk '{print $4}'" % (self.server_ip, count) + status, result = run_command(ping_cmd) + self.logger.log_info("send {} {} packets, revice {} packets".format(self.server_ip, count, result), + also_print_console) + # Compare the packages transmitted count and received count, failed if mismatch + if status != 0 or result != count: + self.fail_reason.append("ping {} lost packets".format(self.server_ip)) + ret = E.EMGMT11002 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def run_test(self, *argv): + port_status = self.mgmtport_util.get_all() + # If any test-case failed, do not continue + ret = self.check_mgmt_status(port_status) + if ret != NO_ERR: + return ret + + ret = self.mgmt_ping_test() + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/netstress_tc.py b/src/sonic-pit/pit-sysdiag/src/netstress_tc.py new file mode 100644 index 00000000000..0ef03eaa674 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/netstress_tc.py @@ -0,0 +1,137 @@ +# -*- coding:utf-8 -*- +import re +import pit_util_common + +from errcode import E +from test_case import TestCaseCommon +from pit_util_common import run_command, isIpV4AddrLegal + + +class NETSTRESSTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "netstress_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.netstress_info_dict = None + try: + if self.platform_cfg_json and "netstress_info" in self.platform_cfg_json.keys(): + self.netstress_info_dict = self.platform_cfg_json["netstress_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def ping_test(self, ip): + ping_cmd = "ping {} -c 10".format(ip) + waiting = pit_util_common.waiting(self.logger, "ping testing...") + ping_status, ping_log = run_command(ping_cmd) + waiting.stop("ping test stop!") + if " 0% packet loss" not in ping_log: + return False + else: + return True + + def show_cpu_ip(self): + ip_cmd = "ifconfig eth0 | grep inet | awk '{print $2}' | head -1" + ip_statu, ip_log = run_command(ip_cmd) + if isIpV4AddrLegal(ip_log): + return True, ip_log + else: + return False, ip_log + + def get_pid_and_remove(self): + pid_stau, pid_out = run_command( + "netstat -tunlp | grep iperf | awk '{print $7}'") + if pid_stau != 0 or len(pid_out) <= 0: + return False + else: + iperf_pid = pid_out.split("/")[0] + run_command("kill -9 {}".format(iperf_pid)) + return True + + def netstress_test(self, also_print_console=False): + env_list = self.netstress_info_dict["environment"] + for env in env_list: + self.logger.log_info( + "[CPU TO {} NET STRESS TEST]:".format( + str(env).upper()), also_print_console) + stu, cpu_ip = self.show_cpu_ip() + if not stu: + ret = E.ENETSTRESS31001 + self.fail_reason.append("cpu ip not legal.") + self.logger.log_err("{}".format(cpu_ip), also_print_console) + self.logger.log_err("FAIL!", also_print_console) + return ret + env_ip = self.netstress_info_dict["{}_ip".format(env)] + env_name = self.netstress_info_dict["{}_name".format(env)] + env_password = self.netstress_info_dict["{}_password".format(env)] + self.logger.log_info( + "cpu ip: {}".format(cpu_ip), + also_print_console) + self.logger.log_info( + "{} ip: {}".format( + env, env_ip), also_print_console) + if self.ping_test(env_ip): + self.logger.log_info( + "ping {} ip success!".format(env), + also_print_console) + else: + ret = E.ENETSTRESS31002 + self.fail_reason.append( + "cpu to {} ip ping test loss packet.".format(env)) + self.logger.log_err("FAIL!", also_print_console) + return ret + run_command("iperf -s -D -i 1 -p 3389") + if "bmc" in env: + run_command( + "ssh-keygen -f \"/root/.ssh/known_hosts\" -R {}".format( + self.netstress_info_dict["bmc_ip"])) + remote = pit_util_common.remote( + self.logger, self.fail_reason, env_name, env_ip, env_password) + ret = remote.connect(also_print_console) + if ret != E.OK: + return ret + waiting = pit_util_common.waiting( + self.logger, "netstress testing...") + output = remote.command( + "iperf -c {} -p 3389 -i 1 -t {}".format( + cpu_ip, self.netstress_info_dict["duration_value"]), int( + self.netstress_info_dict["net_stress_timeout"])) + waiting.stop("netstress test stop!") + band = re.findall("\\s([0-9]+.[0-9]+)\\sMbits/sec", output) + if band == []: + ret = E.ENETSTRESS31003 + self.fail_reason.append("iperf tool not in {}".format(env)) + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + min_speed = min(map(float, band)) + self.logger.log_info( + "iperf min speed: {}Mbits/sec".format(min_speed), + also_print_console) + if min_speed < self.netstress_info_dict["net_standard_speed"]: + ret = E.ENETSTRESS31004 + self.fail_reason.append( + "cpu to {} ip iperf test loss packet.".format(env)) + self.logger.log_err("FAIL!", also_print_console) + return ret + remote.disconnect() + if not self.get_pid_and_remove(): + self.fail_reason.append("get iperf pid error") + ret = E.EIO + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + ret = E.OK + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + for i in range(self.netstress_info_dict["net_stress_count"]): + ret = self.netstress_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/oob_tc.py b/src/sonic-pit/pit-sysdiag/src/oob_tc.py new file mode 100644 index 00000000000..7452c0ed44a --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/oob_tc.py @@ -0,0 +1,113 @@ +import sys +from test_case import TestCaseCommon +from errcode import E +from function import run_command, load_platform_util_module + + +class OOBTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "bmcutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "BmcUtil" + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "oob_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.server_ip = None # external ip + try: + if self.platform_cfg_json and 'server_ip' in self.platform_cfg_json.keys(): + self.server_ip = self.platform_cfg_json['server_ip'] + except Exception as e: + self.logger.log_err(str(e)) + + bmc_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + try: + bmc_util_class = getattr(bmc_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.bmc_util = bmc_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + + def cpu_ping_bmc_test(self, also_print_console=True): + self.logger.log_info("[CPU PING BMC TEST]:", also_print_console) + + count = 5 + cmd = "ping 240.1.1.1 -c %d -I eth0.4088 | grep received" % count + status, output = run_command(cmd) + self.logger.log_info(output, also_print_console) + + if output.find(" 0% packet loss") > 0: + ret = E.OK + else: + self.fail_reason.append("cpu ping bmc lost packages") + ret = E.EMGMT11003 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def cpu_ping_server_test(self, also_print_console=True): + self.logger.log_info("[CPU PING SERVER TEST]:", also_print_console) + + count = 5 + ping_cmd = "ping %s -c %d -I eth0 | grep received" % (self.server_ip, count) + status, output = run_command(ping_cmd) + self.logger.log_info(output, also_print_console) + + if output.find(" 0% packet loss") > 0: + ret = E.OK + else: + self.fail_reason.append("cpu ping server lost packages") + ret = E.EMGMT11002 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def bmc_ping_server_test(self, also_print_console=True): + self.logger.log_info("[BMC PING SERVER TEST]:", also_print_console) + + count = 5 + ping_cmd = "ping %s -c %d -I eth0 | grep received" % (self.server_ip, count) + try: + status, output = self.bmc_util.exec_raw_cmd(ping_cmd) + self.logger.log_info(output[0], also_print_console) + + if status and output[0].find(" 0% packet loss") > 0: + ret = E.OK + else: + self.fail_reason.append("bmc ping server lost packages") + ret = E.EMGMT11004 + except Exception as e: + self.fail_reason.append("bmc ping server fail: {}".format(str(e))) + ret = E.EMGMT11004 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + ret = self.cpu_ping_bmc_test() + if ret != E.OK: + return ret + + if not self.server_ip: + self.fail_reason.append("server IP is null") + ret = E.EEXIST + else: + ret = self.cpu_ping_server_test() + if ret != E.OK: + return ret + ret = self.bmc_ping_server_test() + + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/optical_pin_tc.py b/src/sonic-pit/pit-sysdiag/src/optical_pin_tc.py new file mode 100644 index 00000000000..6e3b6f1126f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/optical_pin_tc.py @@ -0,0 +1,282 @@ +import os +import re +import subprocess +from test_case import TestCaseCommon +from errcode import * +from pit_util_common import run_command + + +class OPTICALPINTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "optical_pin_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.optical_info_dict = None + try: + if self.platform_cfg_json and "optical_info" in self.platform_cfg_json.keys(): + self.optical_info_dict = self.platform_cfg_json["optical_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def check_presence(self, also_print_console): + presence_reset = [] + self.logger.log_info( + "[CHECK OPTICAL PRESENCE DEVICE]:", + also_print_console) + presence_cmd = "transceiverutil show presence | grep -a 'Ethernet' | awk '{print $2}'" + status_presence, log_presence = run_command(presence_cmd) + if status_presence != 0 or len(log_presence) <= 0: + self.fail_reason.append("get optical fail.") + ret = E.EIO + else: + ret = E.OK + presence_list = log_presence.splitlines() + for index, presence in enumerate(presence_list): + if presence == 'Not': + presence_reset.append(index) + return ret, presence_reset + + def optical_presence_test(self, also_print_console): + all_presence_continue = False + ret, presence_reset = self.check_presence(also_print_console) + if ret != E.OK: + return all_presence_continue, ret + if len(presence_reset) != 0: + self.logger.log_info( + "[RESET OPTICAL PRESENCE]:", + also_print_console) + for presence_reset_index in presence_reset: + reset_cmd = "transceiverutil reset Ethernet{}".format( + presence_reset_index) + status_reset, log_reset = run_command(reset_cmd) + if status_reset != 0 or len(log_reset) <= 0: + self.fail_reason.append("reset optical fail.") + ret = E.EIO + return all_presence_continue, ret + else: + self.logger.log_info(log_reset, also_print_console) + + ret, presence_reset = self.check_presence(also_print_console) + if len(presence_reset) != 0: + ret = E.ESFP18001 + self.fail_reason.append( + "please check number of:{} optical status ".format(presence_reset)) + return all_presence_continue, ret + else: + ret = E.OK + all_presence_continue = True + self.logger.log_info( + "optical status: Present", + also_print_console) + + else: + ret = E.OK + all_presence_continue = True + self.logger.log_info("optical status: Present", also_print_console) + return all_presence_continue, ret + + def optical_eeprom_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK OPTICAL EEPROM DEVICE]:", + also_print_console) + + optical_type = self.optical_info_dict["optical_type"] + qsfp_eeprom_dict = self.optical_info_dict[optical_type] + + optical_port = qsfp_eeprom_dict["optical_port"][0] + port_num = qsfp_eeprom_dict[optical_port]["port_num"] + for number in range(port_num[0], port_num[1]): + eeprom_cmd = "transceiverutil show eeprom | grep -a -A 20 'Ethernet{}:'".format( + number) + " | grep -a -E 'Identifier:|Vendor Name:|Vendor PN:|Vendor SN:' | grep -a -v 'Extended'| awk -F ': ' '{print $2}'" + status_eeprom, log_eeprom = run_command(eeprom_cmd) + log_eeprom_list = log_eeprom.splitlines() + if status_eeprom != 0 or len(log_eeprom_list) != 4: + self.fail_reason.append("port{} get optical eeprom fail.".format(number)) + ret = E.EIO + return ret + else: + ret = E.OK + if log_eeprom_list[0].strip( + ) not in qsfp_eeprom_dict[optical_port]["Identifier"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Identifier".format(number)) + ret = E.ESFP18008 + return ret + if log_eeprom_list[1].strip( + ) not in qsfp_eeprom_dict[optical_port]["Vendor_Name"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: Vendor Name".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[2].strip()) <= qsfp_eeprom_dict[optical_port]["Vendor_PN_len"][0] or len( + log_eeprom_list[2].strip()) >= qsfp_eeprom_dict[optical_port]["Vendor_PN_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: Vendor PN".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[3].strip()) <= qsfp_eeprom_dict[optical_port]["Vendor_SN_len"][0] or len( + log_eeprom_list[3].strip()) >= qsfp_eeprom_dict[optical_port]["Vendor_SN_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor SN".format(number)) + ret = E.ESFP18008 + return ret + + optical_port = qsfp_eeprom_dict["optical_port"][1] + port_num = qsfp_eeprom_dict[optical_port]["port_num"] + for number in range(port_num[0], port_num[1]): + eeprom_cmd = "transceiverutil show eeprom | grep -a -A 10 'Ethernet{}:'".format( + number) + " | grep -a -E 'Identifier:|Vendor Name:|Vendor Part Number|Vendor Serial Number:'|awk -F ': ' '{print $2}'" + status_eeprom, log_eeprom = run_command(eeprom_cmd) + log_eeprom_list = log_eeprom.splitlines() + if status_eeprom != 0 or len(log_eeprom_list) != 4: + self.fail_reason.append("get optical eeprom fail.") + ret = E.EIO + else: + ret = E.OK + if log_eeprom_list[0].strip( + ) not in qsfp_eeprom_dict[optical_port]["Identifier"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Identifier".format(number)) + ret = E.ESFP18008 + return ret + if log_eeprom_list[1].strip( + ) not in qsfp_eeprom_dict[optical_port]["Vendor_Name"]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Name".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[2].strip()) <= qsfp_eeprom_dict[optical_port]["Vendor_Part_Number_len"][0] or len( + log_eeprom_list[2].strip()) >= qsfp_eeprom_dict[optical_port]["Vendor_Part_Number_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Part Number".format(number)) + ret = E.ESFP18008 + return ret + if len(log_eeprom_list[3].strip()) <= qsfp_eeprom_dict[optical_port]["Vendor_Serial_Number_len"][0] or len( + log_eeprom_list[3].strip()) >= qsfp_eeprom_dict[optical_port]["Vendor_Serial_Number_len"][1]: + self.fail_reason.append( + "Ethernet{} eeprom fail: error Vendor Serial Number".format(number)) + ret = E.ESFP18008 + return ret + self.logger.log_info("optical eeprom check: Pass", also_print_console) + return ret + + def optical_tx_switch_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK OPTICAL TX SWITCH DEVICE]:", + also_print_console) + intr_cmd = "transceiverutil show intr" + status_intr, log_intr = run_command(intr_cmd) + if status_intr != 0 or len(log_intr) <= 0: + self.fail_reason.append("get optical fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info("read optical intr OK", also_print_console) + if ret == E.OK: + modsel_cmd = "transceiverutil show modsel" + status_modsel, log_modsel = run_command(modsel_cmd) + if status_modsel != 0 or len(log_modsel) <= 0: + self.fail_reason.append("get optical fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info("read optical modsel OK", also_print_console) + if ret == E.OK: + lpmode_switch_control = ['off', 'on'] + lpmode_switch_status = ['On', 'Off'] + optical_type = self.optical_info_dict["optical_type"] + qsfp_eeprom_dict = self.optical_info_dict[optical_type] + total_portnum = qsfp_eeprom_dict["total_portnum"] + for control in lpmode_switch_control: + for number in range(0, int(total_portnum)): + lpmode_switch_cmd = "transceiverutil lpmode {} Ethernet{}".format( + control, number) + status_lpmode, log_lpmode = run_command(lpmode_switch_cmd) + if status_lpmode != 0 or len(log_lpmode) <= 0: + self.fail_reason.append("lpmode switch fail.") + ret = E.EIO + else: + lpmode_switch_readback_cmd = "transceiverutil show lpmode | grep -a 'Ethernet{} '".format( + number) + "|awk '{print $2}'" + status_lpmode_switch, log_lpmode_switch = run_command( + lpmode_switch_readback_cmd) + if status_lpmode_switch != 0 or len( + log_lpmode_switch) <= 0: + self.fail_reason.append("lpmode switch fail.") + ret = E.EIO + else: + if log_lpmode_switch != lpmode_switch_status[lpmode_switch_control.index( + control)]: + ret = E.ESFP18015 + self.fail_reason.append( + "Ethernet{} lpmode switch fail.".format(number)) + return ret + else: + ret = E.OK + self.logger.log_info("lpmode switch OK", also_print_console) + return ret + + def optical_reset_switch_test(self, also_print_console=False): + self.logger.log_info( + "[CHECK OPTICAL RESET SWITCH DEVICE]:", + also_print_console) + optical_type = self.optical_info_dict["optical_type"] + qsfp_eeprom_dict = self.optical_info_dict[optical_type] + total_portnum = qsfp_eeprom_dict["total_portnum"] + for number in range(0, int(total_portnum)): + reset_switch_cmd = "transceiverutil reset Ethernet{}".format( + number) + status_reset_switch, log_rest_switch = run_command( + reset_switch_cmd) + if status_reset_switch != 0 or len(log_rest_switch) <= 0: + self.fail_reason.append("optical reset fail.") + ret = E.EIO + else: + ret = E.OK + self.logger.log_info( + "Ethernet{} reset OK".format(number), + also_print_console) + if ret == E.OK: + reset_cmd = "transceiverutil show reset" + status_reset, log_rest = run_command(reset_cmd) + if status_reset != 0 or len(log_rest) <= 0: + self.fail_reason.append("reset optical fail.") + ret = E.EIO + else: + if "Not" in log_rest: + ret = E.ESFP18016 + self.fail_reason.append("optical reset switch fail.") + else: + ret = E.OK + self.logger.log_info("reset switch OK", also_print_console) + return ret + + def run_test(self, *argv): + all_presence_continue, ret = self.optical_presence_test(True) + if ret != E.OK: + return ret + elif all_presence_continue: + optical_type = self.optical_info_dict["optical_type"] + if optical_type == "QSFP": + ret = self.optical_eeprom_test(True) + if ret != E.OK: + return ret + ret = self.optical_tx_switch_test(True) + if ret != E.OK: + return ret + ret = self.optical_reset_switch_test(True) + if ret != E.OK: + return ret + ret = self.optical_eeprom_test(True) + if ret != E.OK: + return ret + elif optical_type == "SFP": + pass + else: + pass + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/pcie_tc.py b/src/sonic-pit/pit-sysdiag/src/pcie_tc.py new file mode 100644 index 00000000000..a489ebe1b00 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/pcie_tc.py @@ -0,0 +1,182 @@ +import os +import re +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import * +from function import run_command + +GLOBAL_VALUE1 = '0x5A' +GLOBAL_VALUE2 = '0xA5' + +# pcie bus and devices test class +class PCIETC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "pcie_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.pcie_devices = None # default + self.fpga_devices = None # default + """ case_config.json example + "pcie":{ + "I210": { + "width":"x1", + "speed": "2.5GT/s", + "num":1 + }, + "b870":{ + "width":"x4", + "speed":"8GT/s", + "num":1 + }, + "X552":{ + "width":"x1", + "speed":"2.5GT/s", + "num":2 + } + }, + "fpga":{ + "FPGA":{ + "path":"/sys/bus/platform/devices/switchboard_fpga/FPGA", + "register":"scratch" + } + }, + """ + try: + if self.platform_cfg_json and 'pcie' in self.platform_cfg_json.keys(): + self.pcie_devices = self.platform_cfg_json['pcie'] + if self.platform_cfg_json and 'fpga' in self.platform_cfg_json.keys(): + self.fpga_devices = self.platform_cfg_json['fpga'] + except Exception as e: + self.logger.log_err(str(e), True) + + + def _get_bus_dev_func(self, device): + bus_dev_func = [] + ret, out = run_command("lspci | grep {}".format(device)) + for line in out.splitlines(): + if re.search("[0-9a-f]{2}\:[0-9a-f]{2}\.[0-9a-f]{1}", line): + bus_dev_func.append(re.findall("[0-9a-f]{2}\:[0-9a-f]{2}\.[0-9a-f]{1}", line)[0]) + else: + bus_dev_func.append(None) + + return bus_dev_func + + + def _get_device_conf(self, busid): + ret, output = run_command("lspci -s {} -vvv | grep -i LnkSta | grep Speed".format(busid)) + if ret or output == '': + return '', '' + else: + speed = output.strip(" \t\n").split(',')[0].split('Speed')[1].strip() + width = output.strip(" \t\n").split(',')[1].split('Width')[1].strip() + return speed, width + + + def check_pcie_device(self, also_print_console=False): + self.logger.log_info( "[PCIE DEVICE CHECK]:", also_print_console) + header = ['Device', 'Bus', 'Width', 'Speed'] + ret = E.OK + status_tbl = [] + + if self.pcie_devices: + for device in self.pcie_devices.keys(): + conf_width = self.pcie_devices[device]["width"] + conf_speed = self.pcie_devices[device]["speed"] + dev_num = int(self.pcie_devices[device]["num"]) + + busid_list = self._get_bus_dev_func(device) + if not busid_list: + self.fail_reason.append("{} not found".format(device)) + ret = E.EPCI9001 + status_tbl.append([device, 'None', 'None', 'None']) + else: + if len(busid_list) != dev_num: + self.logger.log_err("{} number expect {}, real {}".format(device, dev_num, len(busid_list))) + self.fail_reason.append("{} number mismatch".format(device)) + ret = E.EPCI9001 + + for busid in busid_list: + speed, width = self._get_device_conf(busid) + if conf_width != width: + self.fail_reason.append("{} width not matched".format(device)) + ret = E.EPCI9005 + if conf_speed != speed: + self.fail_reason.append("{} speed not matched".format(device)) + ret = E.EPCI9004 + status_tbl.append([device, busid, width, speed]) + + else: + ret = E.EEXIST + self.fail_reason.append("pcie config NULL") + + if len(status_tbl) > 0: + self.logger.log_info(tabulate(status_tbl, header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def fpga_device_rw_test(self, also_print_console=False): + self.logger.log_info( "[FPGA RW TEST]:", also_print_console) + ret = E.OK + + if self.fpga_devices: + for device in self.fpga_devices.keys(): + try: + device_conf = self.fpga_devices.get(device) + driver_path = device_conf['path'] + reg_file = os.path.join(driver_path, device_conf['register']) + + with open(reg_file, 'w') as f: + f.write(GLOBAL_VALUE1) + with open(reg_file, 'r') as f: + value = f.read().strip() + self.logger.log_info("{} write {}, read {}".format(device, GLOBAL_VALUE1, value), also_print_console) + + if int(value, 16) != int(GLOBAL_VALUE1, 16): + ret = E.EPCI9003 + self.fail_reason.append("{} rw fail".format(device)) + + with open(reg_file, 'w') as f: + f.write(GLOBAL_VALUE2) + with open(reg_file, 'r') as f: + value = f.read().strip() + self.logger.log_info("{} write {}, read {}".format(device, GLOBAL_VALUE2, value), also_print_console) + + if int(value, 16) != int(GLOBAL_VALUE2, 16): + ret = E.EPCI9003 + self.fail_reason.append("{} rw fail".format(device)) + + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = E.EPCI9002 + + else: + ret = E.EEXIST + self.fail_reason.append("fpga config NULL") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + ret = self.check_pcie_device(True) + if ret != E.OK: + return ret + + loop = argv[0] + for i in range(1, loop + 1): + ret = self.fpga_device_rw_test(True) + if ret != E.OK: + self.logger.log_err("pcie r/w loop {} failed!".format(i), True) + return ret + + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/pciestress_tc.py b/src/sonic-pit/pit-sysdiag/src/pciestress_tc.py new file mode 100644 index 00000000000..f9f8bc3e5f4 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/pciestress_tc.py @@ -0,0 +1,155 @@ +import re +import random +from errcode import * +from tabulate import tabulate +from test_case import TestCaseCommon +from pit_util_common import run_command, load_platform_util_module + + +class PCIESTRESSTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "pciestress_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.pcie_devices = None + self.fpga_devices = None + self.pciestress_info = None + try: + if self.platform_cfg_json and "pcie" in self.platform_cfg_json.keys(): + self.pcie_devices = self.platform_cfg_json["pcie"] + if self.platform_cfg_json and "fpga" in self.platform_cfg_json.keys(): + self.fpga_devices = self.platform_cfg_json["fpga"] + if self.platform_cfg_json and "pciestress_info" in self.platform_cfg_json.keys(): + self.pciestress_info = self.platform_cfg_json["pciestress_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def _get_bus_dev_func(self, device): + bus_dev_func = [] + ret, out = run_command("lspci | grep {}".format(device)) + for line in out.splitlines(): + if re.search("[0-9a-f]{2}\\:[0-9a-f]{2}\\.[0-9a-f]{1}", line): + bus_dev_func.append( + re.findall( + "[0-9a-f]{2}\\:[0-9a-f]{2}\\.[0-9a-f]{1}", + line)[0]) + else: + bus_dev_func.append(None) + return bus_dev_func + + def _get_device_conf(self, busid): + ret, output = run_command( + "lspci -s {} -vvv | grep -i LnkSta | grep Speed".format(busid)) + if ret or output == "": + return "", "" + else: + speed = output.strip(" \t\n").split( + ",")[0].split("Speed")[1].strip() + width = output.strip(" \t\n").split( + ",")[1].split("Width")[1].strip() + return speed, width + + def check_pcie_device(self, also_print_console=False): + self.logger.log_info("[PCIE DEVICE CHECK]:", also_print_console) + header = ["Device", "Bus", "Width", "Speed"] + ret = E.EFAIL + status_tbl = [] + if self.pcie_devices: + for device in self.pcie_devices: + conf_width = self.pcie_devices[device]["width"] + conf_speed = self.pcie_devices[device]["speed"] + dev_num = int(self.pcie_devices[device]["num"]) + busid_list = self._get_bus_dev_func(device) + if not busid_list: + self.fail_reason.append("{} not found".format(device)) + ret = E.EPCI9001 + self.logger.log_err("FAIL!", also_print_console) + return ret + if len(busid_list) != dev_num: + self.logger.log_err("{} number expect {}, real {}".format( + device, dev_num, len(busid_list)), also_print_console) + self.fail_reason.append( + "{} number mismatch".format(device)) + ret = E.EPCI9001 + self.logger.log_err("FAIL!", also_print_console) + return ret + for busid in busid_list: + speed, width = self._get_device_conf(busid) + if conf_width != width: + self.fail_reason.append( + "{} width not matched".format(device)) + ret = E.EPCI9005 + self.logger.log_err("FAIL!", also_print_console) + return ret + if conf_speed != speed: + self.fail_reason.append( + "{} speed not matched".format(device)) + ret = E.EPCI9004 + self.logger.log_err("FAIL!", also_print_console) + return ret + status_tbl.append([device, busid, width, speed]) + ret = E.OK + else: + ret = E.EEXIST + self.fail_reason.append("pcie config NULL") + if len(status_tbl) > 0: + self.logger.log_info( + tabulate( + status_tbl, + header, + tablefmt="simple"), + also_print_console) + self.logger.log_info("PASS.", also_print_console) + return ret + + def fpga_device_rw_test(self, also_print_console=False): + self.logger.log_info("[FPGA STRESS TEST]:", also_print_console) + ret = E.EFAIL + + if self.fpga_devices: + for device in self.fpga_devices.keys(): + device_num = self.fpga_devices[device]["num"] + if device_num == "": + ret = E.EPCI9007 + self.fail_reason.append( + "invalid device : {}".format(device)) + break + random_num = "0x{:x}".format(random.randint(0x0000, 0xffff)) + pcie = load_platform_util_module("com_utils") + pcie.write_fpga_reg(device_num, random_num) + read_num = eval(pcie.read_fpga_reg( + device_num).strip().split()[-1]) + ret = E.OK + if random_num != hex(read_num): + ret = E.EPCI9003 + self.fail_reason.append("{} stress fail".format(device)) + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.fail_reason.append("fpga config NULL") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + test_times = self.pciestress_info["stress_test_time"] + for index in range(1, test_times + 1): + ret = self.check_pcie_device(True) + if ret != E.OK: + self.logger.log_err( + "check pcie devices {} time failed!".format(index), True) + return ret + ret = self.fpga_device_rw_test(True) + if ret != E.OK: + self.logger.log_err( + "check pcie FPGA r/w {} time failed!".format(index), True) + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/pit_util_common.py b/src/sonic-pit/pit-sysdiag/src/pit_util_common.py new file mode 100644 index 00000000000..57ed8fc7113 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/pit_util_common.py @@ -0,0 +1,383 @@ +# -*- coding:utf-8 -*- +from __future__ import print_function +from test_case import TCBase, TestCaseCommon +from subprocess import PIPE, Popen +from errcode import E +import subprocess +import threading +import time +import sys +import signal +import syslogger +import pexpect +import imp +import os +import re + +# from threading import Lock, Thread +PLATFORM_ROOT_PATH = "/usr/share/sonic/device" +PLATFORM_ROOT_PATH_DOCKER = "/usr/share/sonic/platform" + +SONIC_CFGGEN_PATH = "/usr/local/bin/sonic-cfggen" +HWSKU_KEY = "DEVICE_METADATA['localhost']['hwsku']" +PLATFORM_KEY = "DEVICE_METADATA['localhost']['platform']" + + +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip("\n") + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip("\n") + except OSError as e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +def load_platform_util_module(module_name): + platform_util_module = None + + (platform, hwsku) = get_platform_and_hwsku() + platform_path = '' + if len(platform) != 0: + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + else: + platform_path = PLATFORM_ROOT_PATH_DOCKER + + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file = "/".join([platform_path, "plugins", module_name + ".py"]) + platform_util_module = imp.load_source(module_name, module_file) + except IOError as e: + return None + + return platform_util_module + + # try: + # platform_util_class = getattr(platform_util_module, class_name) + # platform_util = platform_util_class() + # except AttributeError as e: + # return None + + # return platform_util + + +def run_command(cmd): + proc = subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = proc.communicate() + if err and proc.returncode != 0: + return proc.returncode, err + return 0, out.rstrip('\n') + + +def execute_cmd(cmdlist): + for cmd in cmdlist: + proc = Popen( + cmd, + stdin=PIPE, + stderr=sys.stderr, + close_fds=True, + stdout=sys.stdout, + universal_newlines=True, + shell=True, + bufsize=1) + proc.communicate() + signal.signal(signal.SIGINT, quit) + return proc.returncode + + +def execute_cmd_return_match(cmd, pattern, linemode=True): + statu, log = run_command(cmd) + if statu != 0 or len(log) <= 0: + print("cmd execute error") + else: + if linemode: + for line in log.splitlines(): + result_list = re.findall(pattern, line) + if result_list != []: + return result_list + else: + result_list = re.findall(pattern, log) + return result_list + + +def search_pattern_in_content_return_match(output, pattern, linemode=True): + if linemode: + for line in output.splitlines(): + result_list = re.findall(pattern, line) + if result_list != []: + return result_list + else: + result_list = re.findall(pattern, output) + return result_list + + +def execute_cmd_return_boolen(cmd, paras, linemode=True): + flag = 0 + fail_list = [] + if isinstance(paras, dict): + for key, value in paras.items(): + if linemode: + match_list = execute_cmd_return_match(cmd, key, True) + result = ''.join(match_list) + if value != result: + fail_list.append(value) + flag += 1 + else: + match_list = execute_cmd_return_match(cmd, key, False) + result = ''.join(match_list) + if value != result: + fail_list.append(value) + flag += 1 + + if flag: + return False, fail_list + else: + return True, fail_list + + elif isinstance(paras, list): + for pattern in paras: + if linemode: + match_list = execute_cmd_return_match(cmd, pattern, True) + if match_list is None: + fail_list.append(pattern) + flag += 1 + else: + match_list = execute_cmd_return_match(cmd, pattern, False) + print(match_list) + if match_list == []: + fail_list.append(pattern) + flag += 1 + + if flag: + return False, fail_list + else: + return True, fail_list + else: + pass + + +class waiting(): + def __init__(self, logger, mesg): + self.logger = logger + self.mesg = mesg + self.lock = threading.Lock() + self.lock.acquire() + self.thread = threading.Thread(target=self.flush_display) + self.thread.start() + + def flush_display(self): + while True: + for item in ["\\", "|", "/", "—"]: + print("\r{} {}".format(self.mesg, item), end='') + sys.stdout.flush() + time.sleep(0.25) + if not self.lock.locked(): + break + time.sleep(0.25) + if not self.lock.locked(): + break + + def stop(self, mesg=None): + if mesg is None: + mesg = self.mesg + self.lock.release() + print("\r{} ".format(mesg), end="") + sys.stdout.flush() + print('\r') + self.logger.log_info(mesg) + + +class sort_list(): + def __init__(self): + pass + + def tryint(self, s): + try: + return int(s) + except ValueError: + return s + + def str2int(self, v_str): + return [self.tryint(sub_str) + for sub_str in re.split('([0-9]+)', v_str)] + + def sort_humanly(self, v_list): + return sorted(v_list, key=self.str2int, reverse=True) + + +class remote(): + def __init__(self, logger, fail_reason, username, ip, password): + self.logger = logger + self.username = username + self.ip = ip + self.password = password + self.fail_reason = fail_reason + + def connect(self, also_print_console=False): + try: + command = 'ssh {}@{}'.format(self.username, self.ip) + self.obj = pexpect.spawn(command, timeout=10) + expect_list = [ + 'yes/no', + 'password:', + '~', + 'Permission denied', + pexpect.TIMEOUT, + pexpect.EOF, + ] + while True: + index = self.obj.expect(expect_list, timeout=10) + if index == 0: + self.obj.sendline("yes") + continue + if index == 1: + self.obj.sendline(self.password) + continue + if index == 2: + ret = E.OK + return ret + if index == 3: + self.obj.close() + ret = E.ESSH35001 + self.fail_reason.append( + "[ remote ] connect fail ,PASSWORD OR HOSTNAME is wrong") + self.logger.log_err("FAIL!", also_print_console) + return ret + if index == 4: + self.obj.close() + ret = E.ESSH35002 + self.fail_reason.append( + "[ remote ] connect timeout ,PASSWORD OR HOSTNAME is wrong") + self.logger.log_err("FAIL!", also_print_console) + return ret + if index == 5: + self.obj.close() + ret = E.ESSH35003 + self.fail_reason.append( + "[ remote ] connect timeout ,PASSWORD OR HOSTNAME is wrong") + self.logger.log_err("FAIL!", also_print_console) + return ret + except BaseException as error: + print(error) + print('[ remote ] Error !!! ') + + def command(self, cmd, time=5, expect_list=['#', '~']): + self.obj.sendline(cmd) + self.obj.expect('\r\n', timeout=time) + self.obj.expect(expect_list, timeout=time) + try: + output = self.obj.before.decode() + index = output.rfind('\r\n') + if index == -1: + output = '' + else: + output = output[0:index] + if output[0] == '|': + index = output.find('\r\n') + if index == -1: + output = '' + else: + output = output[index + 2:] + return output + except BaseException as error: + print(error) + print('[ remote ] Error !!! ') + + def disconnect(self): + self.obj.close() + + +def ssh_command( + self, + cmd, + username, + ip, + password, + time=10, + also_print_console=False): + ssh = pexpect.spawn('ssh {}@{} "{}"'.format(username, ip, cmd)) + try: + expect_list = [ + 'yes/no', + 'password:', + '# ', + pexpect.TIMEOUT, + pexpect.EOF + ] + while True: + index = ssh.expect(expect_list, timeout=time) + if index == 0: + ssh.sendline("yes") + continue + if index == 1: + ssh.sendline(password) + expect_list = ['password:', 'Permission denied', pexpect.EOF] + index = ssh.expect(expect_list, timeout=time) + if index == 0 or index == 1: + ssh.close() + ret = E.ESSH35001 + output = '[ remote ] connect fail, PASSWORD OR HOSTNAME is wrong' + self.fail_reason.append(output) + self.logger.log_err("FAIL!", also_print_console) + return ret, output + if index == 2: + ret = E.OK + output = ssh.before.decode() + return ret, output + if index == 3: + ret = E.ESSH35002 + output = '[ remote ] connect timeout, PASSWORD OR HOSTNAME is wrong' + self.fail_reason.append(output) + self.logger.log_err("FAIL!", also_print_console) + return ret, output + if index == 4: + ret = E.ESSH35003 + output = '[ remote ] connect EOF err, PASSWORD OR HOSTNAME is wrong' + self.fail_reason.append(output) + self.logger.log_err("FAIL!", also_print_console) + return ret, output + except BaseException as err: + output = str(err) + ret = E.EFAIL + return ret, output + + +def isIpV4AddrLegal(ipStr): + if '.' not in ipStr: + return False + ip_split_list = ipStr.strip().split('.') + if len(ip_split_list) != 4: + return False + for i in range(4): + try: + ip_split_list[i] = int(ip_split_list[i]) + except BaseException: + print("IP invalid for not number: " + ipStr) + return False + if ip_split_list[i] <= 255 and ip_split_list[i] >= 0: + pass + else: + print("IP invalid: " + ipStr) + return False + if int(ip_split_list[0] == 0): + print("ip format wrong") + return False + return True diff --git a/src/sonic-pit/pit-sysdiag/src/psu_tc.py b/src/sonic-pit/pit-sysdiag/src/psu_tc.py new file mode 100644 index 00000000000..b0fecd254a7 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/psu_tc.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +__version__ = 'v0.1.0' +__author__ = "Pytool Lishoulei" +import os +import sys +import json +import traceback +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import E +from function import load_platform_util_module +from function import restful_command + +class PSUTC(TestCaseCommon): + __PSU_UTIL_MODULE_NAME = "psuutil" + __PSU_UTIL_CLASS_NAME = "PsuUtil" + __SENSORUTIL_MODULE_NAME = "sensorutil" + __SENSORUTIL_CLASS_NAME = "SensorUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "psu_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.psu_util = None + self.sensor_util = None + self.psu_info_dict = None + self.psu_all = None + + try: + if self.platform_cfg_json and 'psu_info' in self.platform_cfg_json: + self.psu_info_dict = self.platform_cfg_json['psu_info'] + except Exception as e: + self.logger.log_err(str(e), True) + + self.position = self.psu_info_dict['position'] + + # Load platform utils + try: + sensor_util_module = load_platform_util_module(self.__SENSORUTIL_MODULE_NAME) + sensor_util_class = getattr(sensor_util_module, self.__SENSORUTIL_CLASS_NAME) + self.sensor_util = sensor_util_class() + except AttributeError as e: + self.logger.log_err(str(e)) + sys.exit(1) + + try: + psu_module = load_platform_util_module(self.__PSU_UTIL_MODULE_NAME) + psu_util_class = getattr(psu_module, self.__PSU_UTIL_CLASS_NAME) + self.psu_util = psu_util_class() + except AttributeError as e: + msg = "Failed, create PsuUtil instance got exception: {}".format(str(e)) + self.logger.log_err(msg) + self.logger.log_err(traceback.format_exc()) + sys.exit(2) + + # Fill info + self.system_air_flow = self.sensor_util.get_sys_airflow() + self.psu_all = self.psu_util.get_all_raw() + if not self.psu_all: + reason = "Failed, get psu all info failed." + self.logger.log_err(reason) + sys.exit(3) + + self.psu_count = self.psu_util.get_num_psus() + + def write_sysfs(self,file,v): + if self.position == "bmc": + rc,_ = restful_command("echo {} > {}".format(v,file)) + return rc + else: + with open(file,'w') as f: + return f.write(v) + + def read_sysfs(self,file): + if self.position == "bmc": + rc,v = restful_command("cat {}".format(file)) + if rc: + return v + else: + with open(file,'r') as f: + return f.read().strip() + return "" + + def get_psu_count(self): + return self.psu_count + + def get_psu_attribute(self,psuid,name): + try: + return int(self.read_sysfs('/sys/s3ip/psu/psu{n}/{}'.format(psuid,name))) + except Exception as e: + self.fail_reason.append("fail to get psu{}.{}".format(psuid,name)) + self.logger.log_err("fail to get psu{}.{}".format(psuid,name), True) + return 0 + + def test_psu_info(self): + ''' + S3IP 4.9.4 PSU info test + a) Test logic + 1) Get PSU count as $(psu_count) + 2) Iterate over all PSUs, with commands + * Get PSU FRU if applicable + * Check KEY in FRU if applicable + * Check PSU status + b) Result + b.1) All commands done sucessfully + b.2) For step a.2): KEY must contains the followings keys + ["PartNumber", "SerialNumber", "AirFlow", "Presence"] + b.3) "PartNumber", "SerialNumber", should be non-null strings with differnt chars + b.4) "Presence" should be "True" + b.5) "AirFlow"" should be consistent with system airflow + b.6) $psu_count should be consistent with $system_psu_count + ''' + + self.logger.log_info("test_psu_info start") + ret = E.OK + if self.psu_count != self.psu_info_dict['count']: + reason = "Failed, system psu count %d(expected %d)" \ + % (self.psu_count, self.psu_info_dict['count']) + self.log_reason(reason) + return E.EPSU5001 + + for psuid in range(1, self.psu_count+1): + psu_name = "PSU%d" % psuid + + self.logger.log_dbg("Testing {} info".format(psu_name)) + psu_info = self.psu_all[psu_name] + if not psu_info: + reason = "Failed, {} not in all_psu_ninfo".format(psu_name) + self.log_reason(reason) + ret = E.EPSU5001 + continue + + # Check presence + if "Presence" not in psu_info: + reason = "Failed: {} info, Presence is missing".format(psu_name) + self.log_reason(reason) + ret = E.EPSU5001 + continue + + if psu_info['Presence'] != 'yes': + reason = "Failed: {} is absent".format(psu_name) + self.log_reason(reason) + ret = E.EPSU5001 + continue + + # Check FRU keys + psu_info_keys = ["PartNumber", "SerialNumber", "AirFlow"] + for info_key in psu_info_keys: + if info_key not in psu_info: + reason = "Failed, {} PSU info key {} missing".format(psu_name, info_key) + self.fail_reason.append(reason) + self.log_log_dbg(reason) + ret = E.EPSU5001 + + string_keys = ["PartNumber", "SerialNumber"] + for str_key in string_keys: + if len(psu_info[str_key]) < 2: + reason = "Failed, {} {} is {}, length too short".format(\ + psu_name, str_key, psu_info[str_key]) + self.fail_reason.append(reason) + self.log_log_dbg(reason) + ret = E.EPSU5001 + + air_flow = psu_info["AirFlow"] + sys_air_flow = self.system_air_flow + if air_flow != sys_air_flow: + reason = "Failed, {} airflow {} not consistent with " \ + "system airflow {}".format(psu_name, air_flow, sys_air_flow) + self.fail_reason.append(reason) + self.logger.log_dbg(reason) + ret = E.EPSU5001 + continue + + self.logger.log_dbg("{} info test done, PASS".format(psu_name)) + + if ret == E.OK: + self.logger.log_info("test_psu_info PASS.", True) + else: + self.logger.log_info("test_psu_info FAILED.", True) + + return ret + + def test_psu_status(self): + ''' + S3IP 4.9.5 Test PSU status + a) Test logic + a.1) Get PSU count as $psu_count + a.2) Iterate over all PSUs with commands + *) Read PSU input status, include in_status, in_power, in_voltage, in_current + *) Read PSU input sensor range + *) Read PSU output status, include out_status, out_power, out_voltage, out_current + *) Read PSU output sensor range + b) Result + 1) $(psu_count) should consistent with $(system_psu_count) + 2) in_status should be True, in_power, in_voltage, in_current should be + in range of in_power_range, in_voltage_range, in_current_range + 3) out_status should True, out_power, out_voltage, out_current should be in + range of out_power_range, out_voltage_range, out_current_range + ''' + self.logger.log_info("test_psu_status start") + ret = E.OK + + if self.psu_count != self.psu_info_dict['count']: + self.logger.log_err("psu_count FAIL !", True) + reason = "Failed, system PSU count %d(expected %d)" \ + % (self.psu_count, self.psu_info_dict['count']) + self.fail_reason.append(reason) + return E.EPSU5001 + + for psuid in range(1, self.psu_count+1): + psu_name = "PSU%d" % psuid + + psu_info = self.psu_all[psu_name] + self.logger.log_dbg("Testing {} status".format(psu_name)) + + try: + chk_ret = self.check_inputs(psu_name) + if chk_ret != E.OK: + ret = chk_ret + except Exception as e: + reason = "Failed, check {} input got exception: {}".format( \ + psu_name, str(e)) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + ret = E.EPSU5001 + + try: + chk_ret = self.check_outputs(psu_name) + if chk_ret != E.OK: + ret = chk_ret + except Exception as e: + reason = "Failed, check {} output got exception: {}".format( \ + psu_name, str(e)) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + ret = E.EPSU5001 + + self.logger.log_dbg("{} info test done, PASS".format(psu_name)) + + return ret + + def check_inputs(self, psu_name): + #j = json.dumps(psu_info, sort_keys=True, indent=4, separators=(',', ': ')) + #print j + ret = E.OK + psu_info = self.psu_all[psu_name] + psu_inputs = psu_info["Inputs"] + input_keys = ["Status", "Voltage", "Current", "Power"] + input_vars = [None, None, None, None] + input_key_error_code = [E.EPSU5007, E.EPSU5002, E.EPSU5003, E.EPSU5008] + + self.logger.log_dbg("Checking {} inputs status".format(psu_name)) + in_key_len = len(input_keys) + for keyid in range(in_key_len): + in_key = input_keys[keyid] + if in_key not in psu_inputs: + reason = "Failed, {} status missing {}".format(psu_name, in_key) + self.log_reason(reason) + ret = E.EPSU5007 + + input_vars[keyid] = psu_inputs[input_keys[keyid]] + if in_key == "Status": + if input_vars[keyid] != True: + reason = "Failed, {} input status is NOT True" + self.log_reason(reason) + break # Don't check others if input stauts is not True + else: + try: + low_thd = input_vars[keyid]["LowAlarm"] + high_thd = input_vars[keyid]["HighAlarm"] + value = input_vars[keyid]["Value"] + except Exception as e: + reason = "Failed, {} input {} value {} malformed".format( \ + psu_name, in_key, input_vars[keyid]) + self.log_reason(reason) + ret = E.EPSU5007 + + if value < low_thd or value > high_thd: + reason = "{} input {} out of range, value {}(expected {}~{})".format(\ + psu_name, in_key, value, low_thd, high_thd) + self.log_reason(reason) + ret = input_key_error_code[keyid] + + if ret == E.OK: + self.logger.log_dbg("{} inputs status check done, PASS".format(psu_name)) + else: + self.logger.log_dbg("{} inputs status check done, FAILED".format(psu_name)) + + return ret + + def check_outputs(self, psu_name): + ret = E.OK + psu_info = self.psu_all[psu_name] + psu_outputs = psu_info["Outputs"] + output_keys = ["Status", "Voltage", "Current", "Power"] + output_vars = [None, None, None, None] + output_key_error_code = [E.EPSU5007, E.EPSU5004, E.EPSU5005, E.EPSU5009] + + self.logger.log_dbg("Checking {} outputs status".format(psu_name)) + out_key_len = len(output_keys) + for keyid in range(out_key_len): + out_key = output_keys[keyid] + if out_key not in psu_outputs: + reason = "Failed, {} status missing {}".format(psu_name, out_key) + self.log_reason(reason) + ret = E.EPSU5007 + + output_vars[keyid] = psu_outputs[output_keys[keyid]] + if out_key == "Status": + if output_vars[keyid] != True: + reason = "Failed, {} output status is NOT True" + self.log_reason(reason) + break # Don't check others if output stauts is not True + else: + try: + low_thd = output_vars[keyid]["LowAlarm"] + high_thd = output_vars[keyid]["HighAlarm"] + value = output_vars[keyid]["Value"] + except Exception as e: + reason = "Failed, {} output {} value {} malformed".format( \ + psu_name, in_key, output_vars[keyid]) + self.log_reason(reason) + ret = E.EPSU5007 + + if value < low_thd or value > high_thd: + reason = "{} output {} out of range, value {}(expected {}~{})".format(\ + psu_name, in_key, value, low_thd, high_thd) + self.log_reason(reason) + ret = output_key_error_code[keyid] + + if ret == E.OK: + self.logger.log_dbg("{} outputs status check done, PASS".format(psu_name)) + else: + self.logger.log_dbg("{} outputs status check done, FAILED".format(psu_name)) + return E.OK + + def run_test(self, *argv): + final_result = E.OK + try: + ret = self.test_psu_info() + if ret != E.OK : + final_result = ret + except Exception as e: + reason = "Failed, {} test_psu_info exception: {}".format(self.get_tc_name(), str(e)) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + final_result = E.EFAIL + + try: + ret = self.test_psu_status() + if ret != E.OK : + return ret + except Exception as e: + reason = "Failed, {} test_psu_status exception: {}".format(self.get_tc_name(), str(e)) + self.log_reason(reason) + self.logger.log_err(traceback.format_exc()) + final_result = E.EFAIL + + return final_result diff --git a/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_read_tc.py b/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_read_tc.py new file mode 100755 index 00000000000..be8ceacf3ef --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_read_tc.py @@ -0,0 +1,108 @@ +import re +from function import run_command, exec_cmd +from test_case_base import TestCaseCommon +from errcode import E +key_list = ['Product Name', 'Part Number', 'Serial Number', 'Manufacturer Name', + # Product Extra 1 + ] + +# REPLACEABLE_EEPROM_READ test class +class REPLACEABLE_EEPROM_READ_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "replaceable_eeprom_read_tc" + self.rec = '' + self.code = 0 + self.info_dic = {} + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + + def check_eeprom_info(self, replaceable_part, console_print=False): + """ + Read&Check FRU-EEPROM Information + """ + self.logger.log_info("[ CHECK FRU-EEPROM INFO ]: ==>{}<==".format(replaceable_part), console_print) + + cmd = 'python fruidutil.py get {}'.format(replaceable_part) + self.code, self.rec = run_command(cmd) + + if self.code != 0: + self.fail_reason.append("get eeprom info fail.") + return False + elif 'failed' not in self.rec: + self.logger.log_info("Details:", console_print) + self.logger.log_info(self.rec, console_print) + else: + pass + + for key in key_list: + if key not in self.rec: + self.logger.log_info('Key word {} is missing'.format(key), console_print) + else: + val = re.findall('{}+ +?: ([\S ]+)'.format(key), self.rec, re.IGNORECASE) + if not val: + self.logger.log_info('{} info is missing'.format(key), console_print) + else: + self.info_dic[key] = val[0] + + if len(self.info_dic) != len(key_list): + self.logger.log_info('FAILED', console_print) + return False + else: + self.logger.log_info('PASS', console_print) + return True + + def check_value(self, console_print=False): + """ + Check Mac Information + """ + if self.code != 0: + return False + self.logger.log_info('[ CHECK FRU EEPROM VALUE ]', console_print) + + + if not self.info_dic: + self.fail_reason.append('Missing Fru Keyword') + self.logger.log_info("Missing Fru Keyword", console_print) + return False + for key in self.info_dic: + val = self.info_dic[key] + if len(set(val)) < 2: + self.fail_reason.append("{} Value is not conform: {}".format(key, val)) + self.logger.log_info("{} Value is not conform: {}".format(key, val), console_print) + return False + self.logger.log_info("PASS", console_print) + return True + + + def run_test(self): + pass_cnt = 0 + fail_cnt = 0 + test_list = [] + fan_nums = exec_cmd("cat /sys/switch/fan/num") + psu_nums = exec_cmd("cat /sys/switch/psu/num") + ret1 = exec_cmd("cat /sys/switch/fan/fan*/status").split("\n") + ret2 = exec_cmd("cat /sys/switch/psu/psu*/status").split("\n") + + for i in range(int(fan_nums)): + if ret1[i] == '1': + test_list.append('fan{}'.format(i + 1)) + + for i in range(int(psu_nums)): + if ret2[i] == '1': + test_list.append('psu{}'.format(i + 1)) + + for k in test_list: + self.info_dic = {} + res1 = self.check_eeprom_info('{}'.format(k), True) + res2 = self.check_value(True) + pass_cnt += 1 if res1 else 0 + fail_cnt += 1 if not res1 else 0 + pass_cnt += 1 if res2 else 0 + fail_cnt += 1 if not res2 else 0 + + + + + result = E.OK if pass_cnt == 2 * len(test_list) else E.EFAIL + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_write_tc.py b/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_write_tc.py new file mode 100755 index 00000000000..4fda2e3dd26 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/replaceable_eeprom_write_tc.py @@ -0,0 +1,101 @@ +from function import exec_cmd +from test_case_base import TestCaseCommon +from errcode import E +key_list = ['Product Name', 'Part Number', 'Serial Number', 'Manufacturer', + # "AaAa", "BbBb", "CcCc" + ] +FRU_TEST = "../config/common/test.bin" +FRU_ORIG = "../config/common/orig.bin" + + +# REPLACEABLE_EEPROM_WRITE test class +class REPLACEABLE_EEPROM_WRITE_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "replaceable_eeprom_write_tc" + self.rec = '' + self.code = 0 + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + def write_replaceable_frueeprom(self, replaceable_part, console_print=False): + """ + STORE&SET EEPROM Information by xxx.bin + """ + + self.logger.log_info("[ CHECK SET FRU-EEPROM ]: ==>{}<==".format(replaceable_part), console_print) + cmd = 'python fruidutil.py get {}'.format(replaceable_part) + res_orig = exec_cmd(cmd) + if not res_orig or 'failed' in res_orig: + self.fail_reason.append("get orig-fru info fail.") + else: + self.logger.log_info(res_orig, console_print) + + + eeprom_orig = exec_cmd('cat FRU.bin') + exec_cmd('mv FRU.bin {}'.format(FRU_ORIG)) + eeprom_test = exec_cmd('cat {}'.format(FRU_TEST)) + set_test_cmd = 'python fruidutil.py set {} -f {}'.format(replaceable_part, FRU_TEST) + exec_cmd(set_test_cmd) + res_test_readback = exec_cmd(cmd) + if not res_test_readback: + self.fail_reason.append("get test-readback-fru info fail.") + else: + self.logger.log_info("------||. test fru-syseeprom after set .||------", console_print) + self.logger.log_info(res_test_readback, console_print) + + eeprom_test_readback = exec_cmd('cat FRU.bin') + + if eeprom_test != eeprom_test_readback: + result1 = False + self.logger.log_info("===== Check test fru-eeprom failed =====", console_print) + else: + result1 = True + self.logger.log_info("===== Set test fru-eeprom Success =====", console_print) + + set_orig_cmd = 'python fruidutil.py set {} -f {}'.format(replaceable_part, FRU_ORIG) + exec_cmd(set_orig_cmd) + res_orig_readback = exec_cmd(cmd) + if not res_orig_readback: + self.fail_reason.append("get orig-readback-fru info fail.") + else: + self.logger.log_info("------||. orig fru-syseeprom after set .||------", console_print) + self.logger.log_info(res_orig_readback, console_print) + + eeprom_orig_readback = exec_cmd('cat FRU.bin') + if eeprom_orig_readback != eeprom_orig: + result2 = False + self.logger.log_info("===== Check orig-readback fru-eeprom failed =====", console_print) + else: + result2 = True + self.logger.log_info("===== Set orig fru-eeprom Success =====", console_print) + + exec_cmd('rm FRU.bin') + + return result1, result2 + + def run_test(self): + pass_cnt = 0 + fail_cnt = 0 + test_list = [] + fan_nums = exec_cmd("cat /sys/switch/fan/num") + psu_nums = exec_cmd("cat /sys/switch/psu/num") + ret1 = exec_cmd("cat /sys/switch/fan/fan*/status").split("\n") + ret2 = exec_cmd("cat /sys/switch/psu/psu*/status").split("\n") + + for i in range(int(fan_nums)): + if ret1[i] == '1': + test_list.append('fan{}'.format(i + 1)) + + for i in range(int(psu_nums)): + if ret2[i] == '1': + test_list.append('psu{}'.format(i + 1)) + + for k in test_list: + res1, res2 = self.write_replaceable_frueeprom('{}'.format(k), True) + pass_cnt += 1 if res1 else 0 + fail_cnt += 1 if not res1 else 0 + pass_cnt += 1 if res2 else 0 + fail_cnt += 1 if not res2 else 0 + + result = E.OK if pass_cnt == 2 * len(test_list) else E.EFAIL + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/rtc_tc.py b/src/sonic-pit/pit-sysdiag/src/rtc_tc.py new file mode 100644 index 00000000000..2075508899c --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/rtc_tc.py @@ -0,0 +1,125 @@ +# -*- coding:utf-8 +import time +from test_case import TestCaseCommon +from function import run_command +from errcode import E + +class RTCTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "rtc_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, + platform_cfg_file, case_cfg_file) + self.rtc_info_dict = None + try: + if self.platform_cfg_json and \ + "rtc_info" in self.platform_cfg_json.keys(): + self.rtc_info_dict = self.platform_cfg_json["rtc_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def read_rtc(self, also_print_console=False): + ret = E.OK + self.logger.log_info("[RTC READ CHECK]:", also_print_console) + + cmd = "hwclock -r" + code, out = run_command(cmd) + if code: + ret = E.ERTC12001 + self.fail_reason.append(out) + self.logger.log_info(out, also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def rtc_precision_test(self, wait_time=5, also_print_console=False): + ret = E.OK + rtc_since_epoch_file = "/sys/class/rtc/rtc0/since_epoch" + + self.logger.log_info("[RTC PRECISION CHECK]:", also_print_console) + try: + with open(rtc_since_epoch_file, "r") as f: + start_sec = int(f.read()) + timeArraystart = time.localtime(start_sec) + otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArraystart) + log_msg = "rtc time: {}".format(otherStyleTime) + self.logger.log_info(log_msg, also_print_console) + log_msg = "system time: {}".format( + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + self.logger.log_info(log_msg, also_print_console) + + self.logger.log_info("time sleep: " + str(wait_time), also_print_console) + time.sleep(wait_time) + + with open(rtc_since_epoch_file, "r") as f: + end_sec = int(f.read()) + timeArrayend = time.localtime(end_sec) + otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArrayend) + log_msg = "rtc time: {}".format(otherStyleTime) + self.logger.log_info(log_msg, also_print_console) + log_msg = "system time: {}".format( + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + self.logger.log_info(log_msg, also_print_console) + + timeCompare = end_sec - start_sec + self.logger.log_info("time difference: " + str(timeCompare), + also_print_console) + if timeCompare < (wait_time - 1) or timeCompare > (wait_time + 1): + self.fail_reason.append("{} beyond {}".format(timeCompare, wait_time)) + ret = E.ERTC12002 + except IOError as e: + self.fail_reason.append(str(e)) + ret = E.ERTC12001 + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def rtc_functional_test(self, also_print_console=False): + self.logger.log_info("[RTC READ CHECK]:", also_print_console) + time_start = time.strftime("%Y-%m-%d %H:%M:%S") + self.logger.log_info( + "current time: %s" % + time_start, also_print_console) + current_secs_before = int(time.time()) + delay_interval = self.rtc_info_dict["delay_time"] + self.logger.log_info( + "please waiting {} sec".format(delay_interval), + also_print_console) + time.sleep(delay_interval) + time_end = time.strftime("%Y-%m-%d %H:%M:%S") + self.logger.log_info("current time: %s" % time_end, also_print_console) + current_secs_after = int(time.time()) + delta_interval = current_secs_after - current_secs_before + if abs( + delta_interval - + delay_interval) > self.rtc_info_dict["max_time_diff"]: + self.fail_reason.append("time out of sync") + ret = E.ERTC12002 + else: + ret = E.OK + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + # RTC functional test + ret = self.rtc_functional_test(True) + if ret != E.OK: + return ret + + # RTC read test + ret = self.read_rtc(True) + if ret != E.OK: + return ret + + # RTC precision test + ret = self.rtc_precision_test(5, True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/sensor_tc.py b/src/sonic-pit/pit-sysdiag/src/sensor_tc.py new file mode 100644 index 00000000000..33dbe4115d0 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/sensor_tc.py @@ -0,0 +1,100 @@ +import sys +from tabulate import tabulate +from test_case import TestCaseCommon +from function import load_platform_util_module +from errcode import E + + +class SENSORTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "sensorutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "SensorUtil" + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "sensor_tc" + self.sensor_util = None + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + sensor_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + try: + platform_util_class = getattr(sensor_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.sensor_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + def load_sensor_info(self): + sensor_dict = {} + if self.sensor_util: + sensor_dict = self.sensor_util.get_all() + return sensor_dict + + def sensor_verify(self, sensor_dict, also_print_console=False): + if not sensor_dict: + self.fail_reason.append("get sensors failed!") + return E.EEXIST + + ret = E.OK + header = ["Sensor", 'InputName', 'Status', 'Value', 'LowThd', 'HighThd'] + status_table = [] + + self.logger.log_info( "[SENSORS VALUE CHECK]:", also_print_console) + + try: + for sensor_name, sensor_obj in sensor_dict.items(): + if cmp(sensor_name, 'Number') == 0: + continue + + si_names = [k for k in sensor_obj.keys() if (cmp('Present', k) != 0 and sensor_obj['Present'])] + si_names.sort() + for si_name in si_names: + si = sensor_obj[si_name] + sval = si.get('Value') + slow = si.get('LowThd') + shigh = si.get("HighThd") + sunit = si.get('Unit') + sdesc = si.get('Description') + fault = False + + try: + sval = float(sval) + except: + sval = 0.0 + fault = True + + try: + slow = float(slow) + except: + slow = 0.0 + fault = True + + try: + shigh = float(shigh) + except: + shigh = 0.0 + fault = True + + status = 'NOT_OK' + if fault == False and sval >= slow and sval <= shigh: + status = 'OK' + else: + ret = E.ESSR7003 + self.fail_reason.append("{} out of threshold".format(si_name)) + + status_table.append([sensor_name, si_name, status, (str(sval)+sunit), (str(slow)+sunit), (str(shigh)+sunit)]) + except Exception as e: + self.fail_reason.append(str(e)) + ret = E.ESSR7002 + + if len(status_table) > 0: + status_table.sort() + self.logger.log_info(tabulate(status_table, header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + def run_test(self, *argv): + sensor_dict = self.load_sensor_info() + return self.sensor_verify(sensor_dict, True) diff --git a/src/sonic-pit/pit-sysdiag/src/sfp_tc.py b/src/sonic-pit/pit-sysdiag/src/sfp_tc.py new file mode 100644 index 00000000000..9d758a65aba --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/sfp_tc.py @@ -0,0 +1,151 @@ +import sys +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import E +from function import load_platform_util_module + +# sfp test class +class SFPTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "sfputil" + __PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "sfp_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + self.sfp_util = None + self.sfp_port_list = [] + self.qsfp_port_list = [] + + sfp_module = load_platform_util_module(self.__PLATFORM_SPECIFIC_MODULE_NAME) + try: + platform_util_class = getattr(sfp_module, self.__PLATFORM_SPECIFIC_CLASS_NAME) + self.sfp_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + self.sfp_port_list = self.sfp_util.sfp_port_list + self.qsfp_port_list = self.sfp_util.qsfp_ports + + + def get_sfp_status(self, also_print_console=False): + ret = E.OK + sfp_header = ['PORT', 'PRESENT', 'TX_DIS', 'RX_LOS', 'TX_FAULT'] + status_tbl = [] + ports = self.sfp_port_list + + self.logger.log_info("[SFP PIN CHECK]:", also_print_console) + for port in ports: + present = self.sfp_util.get_presence(port) + tx_dis = self.sfp_util.get_tx_disable(port) + rx_los = self.sfp_util.get_rx_los(port) + tx_fault = self.sfp_util.get_tx_fault(port) + + if present: + if tx_dis: + ret = E.ESFP18004 + self.fail_reason.append("sfp {} tx_dis happened".format(port)) + + if rx_los: + ret = E.ESFP18003 + self.fail_reason.append("sfp {} rx_los happened".format(port)) + + if tx_fault: + ret = E.ESFP18002 + self.fail_reason.append("sfp {} tx_fault happened".format(port)) + else: + ret = E.ESFP18001 + self.fail_reason.append("sfp {} absent".format(port)) + + status_tbl.append(["Port"+str(port), present, tx_dis, rx_los, tx_fault]) + + if len(status_tbl) > 0: + self.logger.log_info(tabulate(status_tbl, sfp_header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def get_qsfp_status(self, also_print_console=False): + ret = E.OK + qsfp_header = ['PORT', 'PRESENT', 'LPMODE', 'RESET', 'INTL'] + status_tbl = [] + ports = self.qsfp_port_list + self.logger.log_info("[QSFP PIN CHECK]:", also_print_console) + for port in ports: + present = self.sfp_util.get_presence(port) + lpmode = self.sfp_util.get_low_power_mode(port) + reset = self.sfp_util.get_reset(port) + intl = self.sfp_util.get_interrupt(port) + + if not present: + ret = E.ESFP18001 + self.fail_reason.append("qsfp {} absent".format(port)) + else: + if reset: + ret = E.ESFP18006 + self.fail_reason.append("qsfp {} under reset".format(port)) + if lpmode: + ret = E.ESFP18005 + self.fail_reason.append("qsfp {} under lpmode".format(port)) + + status_tbl.append(["Port"+str(port), present, lpmode, reset, intl]) + + if len(status_tbl) > 0: + self.logger.log_info(tabulate(status_tbl, qsfp_header, tablefmt="simple"), also_print_console) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def read_sff_eeprom(self, also_print_console=False): + ret = E.OK + ports = self.sfp_port_list + self.qsfp_port_list + + self.logger.log_info("[SFF EEPROM READ CHECK]:", also_print_console) + for port in ports: + try: + if not self.sfp_util.get_presence(port): + ret = E.ESFP18001 + self.fail_reason.append("port{} absent".format(port)) + self.logger.log_err("Port{}: Fail".format(port), also_print_console) + continue + + sff_eeprom = self.sfp_util.get_eeprom_raw(port) + if sff_eeprom is None: + ret = E.ESFP18008 + err = "read port{} sff eeprom failed".format(port) + self.fail_reason.append(err) + self.logger.log_err("Port{}: Fail".format(port), also_print_console) + else: + self.logger.log_info("Port{}: Pass".format(port), also_print_console) + except Exception as e: + ret = E.ESFP18008 + self.fail_reason.append(str(e)) + self.logger.log_err("Port{}: Fail".format(port), also_print_console) + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def run_test(self, *argv): + ret = self.get_sfp_status(True) + if ret != E.OK: + return ret + + ret = self.get_qsfp_status(True) + if ret != E.OK: + return ret + + ret = self.read_sff_eeprom(True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/slot_eeprom_read_tc.py b/src/sonic-pit/pit-sysdiag/src/slot_eeprom_read_tc.py new file mode 100755 index 00000000000..eedb0d4b6b9 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/slot_eeprom_read_tc.py @@ -0,0 +1,100 @@ +import re +from function import run_command, exec_cmd +from test_case_base import TestCaseCommon +from errcode import E +key_list = ['Board Product Name', 'Board Part Number', 'Board Serial Number', 'Board Manufacturer', 'Board Part number', 'Mfg. Date/Time' + ] + +# SLOT_EEPROM_READ test class +class SLOT_EEPROM_READ_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "slot_eeprom_read_tc" + self.rec = '' + self.code = 0 + self.info_dic = {} + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + + def check_eeprom_info(self, replaceable_part, console_print=False): + """ + Read&Check FRU-EEPROM Information + """ + self.logger.log_info("[ CHECK FRU-EEPROM INFO ]: ==>{}<==".format(replaceable_part), console_print) + + cmd = 'python fruidutil.py get {}'.format(replaceable_part) + self.code, self.rec = run_command(cmd) + + if self.code != 0: + self.fail_reason.append("get eeprom info fail.") + return False + elif 'failed' not in self.rec: + self.logger.log_info("Details:", console_print) + self.logger.log_info(self.rec, console_print) + + + for key in key_list: + if key not in self.rec: + self.logger.log_info('Key word {} is missing'.format(key), console_print) + else: + val = re.findall('{}+ +?: ([\S ]+)'.format(key), self.rec, re.IGNORECASE) + if not val: + self.logger.log_info('{} info is missing'.format(key), console_print) + else: + self.info_dic[key] = val[0] + + if len(self.info_dic) != len(key_list): + self.logger.log_info('FAILED', console_print) + return False + else: + self.logger.log_info('PASS', console_print) + return True + + def check_value(self, console_print=False): + """ + Check Mac Information + """ + if self.code != 0: + return False + self.logger.log_info('[ CHECK FRU EEPROM VALUE ]', console_print) + + + if not self.info_dic: + self.fail_reason.append('Missing Fru Keyword') + self.logger.log_info("Missing Fru Keyword", console_print) + return False + for key in self.info_dic: + val = self.info_dic[key] + if len(set(val)) < 2: + self.fail_reason.append("{} Value is not conform: {}".format(key, val)) + self.logger.log_info("{} Value is not conform: {}".format(key, val), console_print) + return False + self.logger.log_info("PASS", console_print) + return True + + + def run_test(self): + pass_cnt = 0 + fail_cnt = 0 + test_list = [] + slot_nums = exec_cmd("cat /sys/switch/slot/num") + ret1 = exec_cmd("cat /sys/switch/slot/slot*/status").split("\n") + + for i in range(int(slot_nums)): + if ret1[i] == '1': + test_list.append('slot{}'.format(i + 1)) + + for k in test_list: + self.info_dic = {} + res1 = self.check_eeprom_info('{}'.format(k), True) + res2 = self.check_value(True) + pass_cnt += 1 if res1 else 0 + fail_cnt += 1 if not res1 else 0 + pass_cnt += 1 if res2 else 0 + fail_cnt += 1 if not res2 else 0 + + + + + result = E.OK if pass_cnt == 2 * len(test_list) else E.EFAIL + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/slot_eeprom_write_tc.py b/src/sonic-pit/pit-sysdiag/src/slot_eeprom_write_tc.py new file mode 100755 index 00000000000..5f4b3173ec3 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/slot_eeprom_write_tc.py @@ -0,0 +1,94 @@ +from function import run_command, exec_cmd +from test_case_base import TestCaseCommon +from errcode import E +key_list = ['Product Name', 'Part Number', 'Serial Number', 'Manufacturer', + ] +FRU_TEST = "../config/common/slot_test.bin" +FRU_ORIG = "../config/common/slot_orig.bin" + + +# SLOT_EEPROM_WRITE_TC test class +class SLOT_EEPROM_WRITE_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "slot_eeprom_write_tc" + self.rec = '' + self.code = 0 + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + def write_slot_fru_eeprom(self, replaceable_part, console_print=False): + """ + STORE&WRITE EEPROM Information by xxx.bin + """ + + self.logger.log_info("[ CHECK SET FRU-EEPROM ]: ==>{}<==".format(replaceable_part), console_print) + cmd = 'python fruidutil.py get {}'.format(replaceable_part) + res_orig = exec_cmd(cmd) + if not res_orig or 'failed' in res_orig: + self.fail_reason.append("get orig-fru info fail.") + else: + self.logger.log_info(res_orig, console_print) + + + eeprom_orig = exec_cmd('cat FRU.bin') + exec_cmd('mv FRU.bin {}'.format(FRU_ORIG)) + eeprom_test = exec_cmd('cat {}'.format(FRU_TEST)) + set_test_cmd = 'python fruidutil.py set {} -f {}'.format(replaceable_part, FRU_TEST) + exec_cmd(set_test_cmd) + res_test_readback = exec_cmd(cmd) + if not res_test_readback: + self.fail_reason.append("get test-readback-fru info fail.") + else: + self.logger.log_info("------||. test fru-syseeprom after set .||------", console_print) + self.logger.log_info(res_test_readback, console_print) + + eeprom_test_readback = exec_cmd('cat FRU.bin') + + if eeprom_test != eeprom_test_readback: + result1 = False + self.logger.log_info("===== Check test fru-eeprom failed =====", console_print) + else: + result1 = True + self.logger.log_info("===== Set test fru-eeprom Success =====", console_print) + + set_orig_cmd = 'python fruidutil.py set {} -f {}'.format(replaceable_part, FRU_ORIG) + exec_cmd(set_orig_cmd) + res_orig_readback = exec_cmd(cmd) + if not res_orig_readback: + self.fail_reason.append("get orig-readback-fru info fail.") + else: + self.logger.log_info("------||. orig fru-syseeprom after set .||------", console_print) + self.logger.log_info(res_orig_readback, console_print) + + eeprom_orig_readback = exec_cmd('cat FRU.bin') + if eeprom_orig_readback != eeprom_orig: + result2 = False + self.logger.log_info("===== Check orig-readback fru-eeprom failed =====", console_print) + else: + result2 = True + self.logger.log_info("===== Set orig fru-eeprom Success =====", console_print) + + exec_cmd('rm FRU.bin') + + return result1, result2 + + def run_test(self): + pass_cnt = 0 + fail_cnt = 0 + test_list = [] + slot_nums = exec_cmd("cat /sys/switch/slot/num") + ret1 = exec_cmd("cat /sys/switch/slot/slot*/status").split("\n") + + for i in range(int(slot_nums)): + if ret1[i] == '1': + test_list.append('fan{}'.format(i + 1)) + + for k in test_list: + res1, res2 = self.write_slot_fru_eeprom('{}'.format(k), True) + pass_cnt += 1 if res1 else 0 + fail_cnt += 1 if not res1 else 0 + pass_cnt += 1 if res2 else 0 + fail_cnt += 1 if not res2 else 0 + + result = E.OK if pass_cnt == 2 * len(test_list) else E.EFAIL + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/snake_tc.py b/src/sonic-pit/pit-sysdiag/src/snake_tc.py new file mode 100644 index 00000000000..06cdab49910 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/snake_tc.py @@ -0,0 +1,166 @@ +# -*- coding:utf-8 +import time +import os +from test_case import TestCaseCommon +from pit_util_common import run_command +from errcode import E + + +class SNAKETC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "snake_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, + logger, platform_cfg_file, case_cfg_file) + self.snake_info_dict = None + self.sdk_path = None + self.log_path = None + self.tx_title = None + self.rx_title = None + try: + if self.platform_cfg_json and "snake_info" in self.platform_cfg_json.keys(): + self.snake_info_dict = self.platform_cfg_json["snake_info"] + self.sdk_path = self.snake_info_dict["sdk_path"] + self.log_path = self.snake_info_dict["log_path"] + self.tx_title = self.snake_info_dict["tx_pkt_title"] + self.rx_title = self.snake_info_dict["rx_pkt_title"] + self.package_type = self.snake_info_dict["package_type"] + except Exception as e: + self.logger.log_err(str(e), True) + self.sdk_status = False + self.port_list = [] + self.port_group = [] + + def sdk_command(self, command=None, delay=0.2): + status = 0 + if command != None: + cmd = 'pmonutil sdksh "{}"'.format(command) + status, out = run_command(cmd) + time.sleep(delay) + ret, data = run_command('cat {}'.format(self.log_path)) + os.system('echo > {}'.format(self.log_path)) + return status, data.strip().strip(b'\x00') + + def init_sdk(self, also_print_console=False): + ret = E.EFAIL + if self.sdk_status: + ret = E.OK + return ret + os.chdir(self.sdk_path) + check_user_cmd = 'pgrep bcm.user' + status, out = run_command(check_user_cmd) + if status: + self.fail_reason.append("init sdk remote mode fail") + ret = E.EIO + else: + process_id = out + if process_id != '': + status, out = run_command('kill -9 {}'.format(process_id)) + if status: + self.fail_reason.append("init sdk remote mode fail") + ret = E.EIO + else: + if out != '': + self.logger.log_info(out, also_print_console) + else: + ret = E.OK + else: + ret = E.OK + if ret == E.OK: + clear_log_cmd = 'echo > {}'.format(self.log_path) + os.system(clear_log_cmd) + sdk_remote_cmd = './sdk_run.sh -d -f {} >/dev/null 2>&1'.format( + self.log_path) + os.system(sdk_remote_cmd) + status, out = self.sdk_command() + if not 'Enter daemon mode' in (out.strip().split('\n'))[-1]: + self.logger.log_info(out, also_print_console) + ret = E.ESNAKE34001 + self.fail_reason.append("snake sdk enter demon mode fail.") + else: + self.sdk_status = True + else: + return ret + self.init_port_list() + self.init_port_group() + return ret + + def init_port_list(self): + self.port_list = [] + status, out = self.sdk_command('ps') + data = out.strip().split('\n') + list_index = data.index('Request cmd received: [ps]') + port_data = data[(list_index)+3:] + for port_str in port_data: + self.port_list.append({'port': port_str.replace( + '(', '').split()[0], 'status': port_str.replace('(', '').split()[2]}) + + def init_port_group(self): + self.port_group = [] + port_g = {} + port_t = ''.join([i for i in self.port_list[0]['port'] if not i.isdigit()]) + port_g.update({port_t:0}) + for item in self.port_list: + port_t = ''.join([i for i in item['port'] if not i.isdigit()]) + if port_t in port_g: + port_g[port_t] += 1 + else: + port_g.update({port_t:1}) + for key in port_g: + self.port_group.append('{}0-{}{}'.format(key,key,(port_g[key]-1))) + + def snake_test(self,lb_type='MAC', also_print_console=False): + ret = self.init_sdk(also_print_console) + if self.port_list == [] or self.port_group == []: + ret = E.ESNAKE34002 + self.fail_reason.append("snake port info error.") + if ret == E.OK: + for package in self.package_type: + for group in self.port_group: + self.sdk_command('port {} en=on lb={}'.format(group,lb_type) ,delay=1) + self.init_port_list() + for port in self.port_list: + if port['status'] == 'up': + self.sdk_command( + 'tx {} pbm={} length={} untagged=yes'.format(package['count'], port['port'], package['size'])) + self.sdk_command('port {} en=off'.format(port['port'])) + status, out = self.sdk_command('show c') + for item in out.strip().split('\n')[3:]: + if self.tx_title in item: + tx_pkt = item.replace('+', '').split()[-1] + if self.rx_title in item: + rx_pkt = item.replace('+', '').split()[-1] + if tx_pkt == rx_pkt: + self.logger.log_info('port {} status {} tx_pkt {} rx_pkt {} pass'.format( + port['port'], port['status'], tx_pkt, rx_pkt), also_print_console) + else: + self.logger.log_err('port {} status {} tx_pkt {} rx_pkt {} fail'.format( + port['port'], port['status'], tx_pkt, rx_pkt), also_print_console) + ret = E.ESNAKE34003 + self.fail_reason.append("snake port {} pkt count fail.".format(port['port'])) + else: + self.logger.log_err('port {} status {} fail'.format( + port['port'], port['status']), also_print_console) + ret = E.ESNAKE34002 + self.fail_reason.append("snake port {} status error.".format(port['port'])) + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def snake_mac_test(self, also_print_console=False): + self.logger.log_info("[SNAKE MAC TEST]:", also_print_console) + return self.snake_test('MAC',also_print_console) + + def snake_phy_test(self, also_print_console=False): + self.logger.log_info("[SNAKE PHY TEST]:", also_print_console) + return self.snake_test('PHY',also_print_console) + + def run_test(self, *argv): + ret = self.snake_mac_test(True) + if ret != E.OK: + return ret + ret = self.snake_phy_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/software_system_tc.py b/src/sonic-pit/pit-sysdiag/src/software_system_tc.py new file mode 100644 index 00000000000..b684da2eeba --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/software_system_tc.py @@ -0,0 +1,128 @@ +# coding:utf-8 +import sys +import re +from function import run_command +from errcode import * +from test_case import TestCaseCommon +from sysdiag import VERSION + +SYSDIAG_VERSION_DESC = "sysdiag version" +SONIC_VERSION_DESC = "SONiC Software Version:" + + +def find_spec(spec, subject): + match = re.search(spec + r"\s(.*)", subject) + if match: + result = match.group(1) + return True, result + else: + result = "" + return False, result + + +class SOFTWARESYSTEMTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "software_system_tc" + TestCaseCommon.__init__( + self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file + ) + try: + if ( + self.platform_cfg_json + and "software_system" in self.platform_cfg_json.keys() + ): + self.software_system_cfg = self.platform_cfg_json["software_system"] + + except Exception as e: + self.logger.log_err(str(e), True) + + def check_sysdiag_version(self, also_print_console=True): + ret = E.OK + if "sysdiag_version" not in self.software_system_cfg: + return E.EFAIL + sysdiag_version = self.software_system_cfg["sysdiag_version"] + self.logger.log_info("[SYSDIAG VERSION CHECK]:", also_print_console) + + # cmd = "python3 ./sysdiag.py -v" if sys.version > '3' else "python2 ./sysdiag.py -v" + # code, out = run_command(cmd) + # if code: + # ret = E.EFAIL + # self.fail_reason.append(out) + # return ret + + # code,VERSION = find_spec(SYSDIAG_VERSION_DESC,out) + # if code and VERSION == sysdiag_version: + if sysdiag_version == str(VERSION): + self.logger.log_info("sysdiag version check success.", also_print_console) + else: + self.logger.log_err( + "sysdiag version check failed. local version is %s , check version is %s" + % (VERSION, sysdiag_version), + also_print_console, + ) + self.fail_reason.append("sysdiag version check failed") + ret = E.ESYS26001 + return ret + + def check_system_timezone(self, also_print_console=True): + ret = E.OK + if "timezone" not in self.software_system_cfg: + return E.EFAIL + timezone = self.software_system_cfg["timezone"] + self.logger.log_info("[SYSTEM TIMEZONE CHECK]:", also_print_console) + cmd = "cat /etc/timezone" + code, out = run_command(cmd) + if code: + ret = E.EFAIL + self.fail_reason.append(out) + return ret + if out == timezone: + self.logger.log_info("system timezone check success.", also_print_console) + else: + self.logger.log_err( + "system timezone check failed. local timezone is %s , check timezone is %s" + % (out, timezone), + also_print_console, + ) + self.fail_reason.append("system timezone check failed") + ret = E.ESYS26002 + return ret + + def check_sonic_version(self, also_print_console=True): + ret = E.OK + if "sonic_version" not in self.software_system_cfg: + return E.EFAIL + sonic_version = self.software_system_cfg["sonic_version"] + self.logger.log_info("[SONIC VERSION CHECK]:", also_print_console) + + cmd = "show version" + code, out = run_command(cmd) + if code: + ret = E.EFAIL + self.fail_reason.append(out) + return ret + + code, result = find_spec(SONIC_VERSION_DESC, out) + if code and result == sonic_version: + self.logger.log_info("sonic version check success.", also_print_console) + else: + self.logger.log_err( + "sonic version check failed. local version is %s , check version is %s" + % (result, sonic_version), + also_print_console, + ) + self.fail_reason.append("sonic version check failed") + ret = E.ESYS26001 + return ret + + def run_test(self, *argv): + ret = self.check_sysdiag_version() + if ret != E.OK: + return ret + ret = self.check_system_timezone() + if ret != E.OK: + return ret + ret = self.check_sonic_version() + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/ssd_tc.py b/src/sonic-pit/pit-sysdiag/src/ssd_tc.py new file mode 100644 index 00000000000..e44a25a8717 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/ssd_tc.py @@ -0,0 +1,202 @@ +import os +import re +import subprocess +from test_case import TestCaseCommon +from errcode import * + + +def run_command(cmd): + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + if err: + out += err + return proc.returncode, out.rstrip('\n') + + +class SSDTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "ssd_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + self.test_size = 1 # unit: MBytes, default + self.ssd_bom_list = None # default + ''' + "ssd_test_size":"100M", + "ssd_bom":[ + { + "model": "INTEL SSDSCKKB240G8", + "size": "240 GB" + }, + { + "model": "MTFDDAV240TDS", + "size": "240 GB" + } + ], + ''' + try: + if self.platform_cfg_json and 'ssd_test_size' in self.platform_cfg_json.keys(): + size = self.platform_cfg_json['ssd_test_size'] + if size.endswith("m") or size.endswith("M"): + self.test_size = int(size.strip("mM")) + else: + self.test_size = int(size) + if self.platform_cfg_json and 'ssd_bom' in self.platform_cfg_json.keys(): + self.ssd_bom_list = self.platform_cfg_json['ssd_bom'] + except Exception as e: + self.logger.log_err(str(e)) + + + def search_dir_by_name(self, name, dir): + result = [] + try: + files = os.listdir(dir) + for file in files: + if name in file: + result.append(os.path.join(dir, file)) + except Exception as e: + pass + return result + + def get_ssd_location(self): + ret = NO_ERR + dir = "/sys/block/" + spect = "sd" + ssdpath = [] + result = self.search_dir_by_name(spect, dir) + if len(result) <= 0: + ret = ABSENT_ERR + else: + for item in result: + with open(os.path.join(item, "removable"), 'r') as fd: + value = fd.read() + if value.strip() == "0": # found ssd + ssd_disk = "/dev/" + os.path.basename(item) + ssdpath.append(ssd_disk) + if not ssdpath: # not found ssd + self.logger.log_err("no ssd found") + ret = ABSENT_ERR + + if ret: + self.fail_reason.append("ssd not found!") + return ret, ssdpath + + + def ssd_info_check(self, ssdpath, also_print_console=False): + """ + Read SSD Information + """ + RET = E.OK + ssd = {} + self.logger.log_info("[SSD INFO CHECK]:", also_print_console) + for path in ssdpath: + status, out = run_command("smartctl -i {}".format(path)) + self.logger.log_info(out, also_print_console) + if status: + err = "Read ssd {} info failed!".format(path) + self.fail_reason.append(err) + RET = E.ESSD2001 + else: + if self.ssd_bom_list: + matched = False + for ssd_bom in self.ssd_bom_list: + if out.find(ssd_bom["model"]) != -1 and out.find(ssd_bom["size"]) != -1: + matched = True + break + if not matched: + RET = E.ESSD2001 + self.fail_reason.append("ssd info not match") + + if RET != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return RET + + + def ssd_health_check(self, ssdpath, also_print_console=False): + """ + SSD SMART overall-health self-assessment test + """ + ret = E.OK + self.logger.log_info("[SSD HEALTH CHECK]:", also_print_console) + for path in ssdpath: + status, out = run_command("smartctl -H {} | grep result".format(path)) + self.logger.log_info(out, also_print_console) + + if out.find("PASSED") == -1: + err = "ssd {} health check failed!".format(path) + ret = E.ESSD2004 + self.fail_reason.append(err) + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + + def ssd_read_test(self, also_print_console=False): + self.logger.log_info("[SSD READ TEST]:", also_print_console) + + bs_count = self.test_size * 64 + cmd = "dd if=/dev/sda of=/dev/null bs=16k count=%d iflag=direct,nonblock" % bs_count + self.logger.log_info(cmd, also_print_console) + status, out = run_command(cmd) + if status: + err = "[{}] read test failed!".format(self.module_name) + self.fail_reason.append(err) + else: + self.logger.log_info(out, also_print_console) + + if status: + self.logger.log_err("FAIL!", also_print_console) + ret = E.ESSD2002 + else: + self.logger.log_info("PASS.", also_print_console) + ret = E.OK + + return ret + + + def ssd_write_test(self, also_print_console=False): + self.logger.log_info("[SSD WRITE TEST]:", also_print_console) + + bs_count = self.test_size * 64 + cmd = "dd if=/dev/urandom of=/tmp/txtfile_ssd bs=16k count=%d oflag=direct,nonblock" % bs_count + self.logger.log_info(cmd, also_print_console) + status, out = run_command(cmd) + if status: + err = "[{}] write test failed!".format(self.module_name) + self.fail_reason.append(err) + else: + self.logger.log_info(out, also_print_console) + os.remove("/tmp/txtfile_ssd") + + if status: + self.logger.log_err("FAIL!", also_print_console) + ret = E.ESSD2003 + else: + self.logger.log_info("PASS.", also_print_console) + ret = E.OK + return ret + + + def run_test(self, *argv): + status, ssdpath = self.get_ssd_location() + if status: + return E.ESSD2001 + + ret = self.ssd_info_check(ssdpath, True) + if ret != E.OK: + return ret + + ret = self.ssd_health_check(ssdpath, True) + if ret != E.OK: + return ret + + ret = self.ssd_read_test(True) + if ret != E.OK: + return ret + + ret = self.ssd_write_test(True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/ssdstress_tc.py b/src/sonic-pit/pit-sysdiag/src/ssdstress_tc.py new file mode 100644 index 00000000000..6d87ac9141b --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/ssdstress_tc.py @@ -0,0 +1,113 @@ +# -*- coding:utf-8 -*- +import re +import pit_util_common +from pit_util_common import run_command +from test_case import TestCaseCommon +from errcode import E + + +class SSDSTRESSTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "ssdstress_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.ssdstress_info_dict = None + try: + if self.platform_cfg_json and "ssdstress_info" in self.platform_cfg_json.keys(): + self.ssdstress_info_dict = self.platform_cfg_json["ssdstress_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def ssd_status_check(self, also_print_console=False): + dev_pattern = self.ssdstress_info_dict['dev_pattern'] + ssd_totalsize_cmd = "df -h | grep %s | awk '{print $2}'" % dev_pattern + total_size_status, total_size_log = run_command(ssd_totalsize_cmd) + self.logger.log_info( + "ssd total size: {}".format(total_size_log), + also_print_console) + if int( + self.ssdstress_info_dict["total_ssd_value"].split("G")[0]) != int( + total_size_log.split("G")[0]): + return False + else: + return True + + def ssd_stress_test(self, also_print_console=False): + self.logger.log_info("[SSD STRESS TEST]:", also_print_console) + dev_pattern = self.ssdstress_info_dict['dev_pattern'] + ssd_availsize_cmd = "df -h | grep %s | awk '{print $4}'" % dev_pattern + + if not self.ssd_status_check(True): + self.fail_reason.append("ssd total size not match") + ret = E.ESSDSTRESS30001 + self.logger.log_err("FAIL!", also_print_console) + return ret + + avail_size_status, avail_size_log = run_command(ssd_availsize_cmd) + if avail_size_status != 0 or len(avail_size_log) <= 0: + self.fail_reason.append("get disk info fail.") + ret = E.EIO + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + ssd_run_size_percentage = float( + self.ssdstress_info_dict["ssd_run_size_percentage"].replace( + "%", "")) / 100 + available_ssd_value = float(avail_size_log.split("G")[0]) + ssd_run_size_value = int( + available_ssd_value * + ssd_run_size_percentage) + + self.logger.log_info("ssd available size: {}G".format( + available_ssd_value), also_print_console) + self.logger.log_info( + "ssd run size: {}G".format(ssd_run_size_value), + also_print_console) + ssd_stress_cmd = "fio -filename={} -direct=1 -iodepth 1 -thread -rw={} -ioengine=libaio -bs={} -size={}G -numjobs={} -group_reporting -name=ssd_stress" \ + .format(self.ssdstress_info_dict["dev_path"], self.ssdstress_info_dict["rw_mode"], self.ssdstress_info_dict["io_size"], + ssd_run_size_value, self.ssdstress_info_dict["num_jobs"]) + waiting = pit_util_common.waiting( + self.logger, "ssdstress testing...") + status, stress_log = run_command(ssd_stress_cmd) + run_command("rm -rf /stress.txt") + waiting.stop("ssdstress test stop!") + + if status != 0 or len(stress_log) <= 0: + self.fail_reason.append("fio ssd stress test fail.") + ret = E.ESSDSTRESS30002 + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + iop_pattern = self.ssdstress_info_dict["iop_pattern"] + read_iops = re.findall(iop_pattern, stress_log)[0] + self.logger.log_info( + "read iops: {}".format(read_iops), + also_print_console) + if int(read_iops) < int( + self.ssdstress_info_dict["iops_value_standard"]): + ret = E.ESSDSTRESS30003 + self.fail_reason.append("ssd read counts error") + self.logger.log_err("FAIL!", also_print_console) + return ret + + if not self.ssd_status_check(True): + self.fail_reason.append("ssd total size not match") + ret = E.ESSDSTRESS30003 + self.logger.log_err("FAIL!", also_print_console) + return ret + + ret = E.OK + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + for i in range(self.ssdstress_info_dict["ssd_test_count"]): + ret = self.ssd_stress_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/svid_tc.py b/src/sonic-pit/pit-sysdiag/src/svid_tc.py new file mode 100644 index 00000000000..d200ddf96ca --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/svid_tc.py @@ -0,0 +1,80 @@ +# -*- coding:utf-8 +from pit_util_common import run_command +from test_case import TestCaseCommon +from errcode import E + + +class SVIDTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "svid_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.svid_info_dict = None + try: + if self.platform_cfg_json and "svid_info" in self.platform_cfg_json.keys(): + self.svid_info_dict = self.platform_cfg_json["svid_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def svid_test(self, also_print_console=False): + ret = E.EFAIL + self.logger.log_info("[SVID HIGH VOLTAGE TEST]:", also_print_console) + self.logger.log_info("setting voltage...\n", also_print_console) + get_cmd = "pmonutil volget" + cmd_list = [ + "pmonutil volset high", + "pmonutil volset low", + "pmonutil volset normal" + ] + for cmd in cmd_list: + self.logger.log_info( + "set svid {} voltage".format( + cmd.split()[2]), + also_print_console) + status, out = run_command(cmd) + if status != 0 or len(out) < 0: + self.fail_reason.append("set svid fail.") + ret = E.EIO + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + self.logger.log_info("set svid success!", also_print_console) + get_status, get_log = run_command(get_cmd) + if get_status != 0 or len(get_log) <= 0: + self.fail_reason.append("get svid fail.") + ret = E.EIO + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + switch_vol_test_readback = round( + int(get_log) / float(1000), 1) + if switch_vol_test_readback != self.svid_info_dict["{}_voltage".format( + format(cmd.split()[2]))]: + if cmd.split()[2] == "high": + ret = E.ESVID33001 + elif cmd.split()[2] == "low": + ret = E.ESVID33002 + elif cmd.split()[2] == "normal": + ret = E.ESVID33003 + self.fail_reason.append( + "{} voltage not match.".format( + cmd.split()[2])) + self.logger.log_err("FAIL!", also_print_console) + return ret + else: + ret = E.OK + self.logger.log_info("current svid voltage: {}V\n".format( + switch_vol_test_readback), also_print_console) + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + ret = self.svid_test(True) + if ret != E.OK: + return ret + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/switch_firmware_program_tc.py b/src/sonic-pit/pit-sysdiag/src/switch_firmware_program_tc.py new file mode 100755 index 00000000000..ae5d6804722 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/switch_firmware_program_tc.py @@ -0,0 +1,417 @@ +# coding:utf-8 +import json +import os +import sys + +from errcode import * +from function import load_platform_util_module + +# from click._compat import raw_input +from test_case import TestCaseCommon + +MASTER_ORIG_FW_CFG = "switch_master_firmware_baseline.json" +SLAVE_ORIG_FW_CFG = "switch_slave_firmware_baseline.json" + + +class SWITCHFIRMWAREPROGRAMTC(TestCaseCommon): + __PLATFORM_SPECIFIC_MODULE_NAME = "fwmgrutil" + __PLATFORM_SPECIFIC_CLASS_NAME = "FwMgrUtil" + get_version_func = "self.firmware_util.get_fw_version" + firmware_upgrade_func = "self.firmware_util.firmware_upgrade" + firmware_refresh_func = "self.firmware_util.firmware_refresh" + slave_upgrade = False + fw_extra = "master" + + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + # this param specified the case config directory + MODULE_NAME = "switch_firmware_program_tc" + TestCaseCommon.__init__( + self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file + ) + self.firmware_util = None + + try: + firmware_module = load_platform_util_module( + self.__PLATFORM_SPECIFIC_MODULE_NAME + ) + platform_util_class = getattr( + firmware_module, self.__PLATFORM_SPECIFIC_CLASS_NAME + ) + self.firmware_util = platform_util_class() + except AttributeError as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + try: + self.firmware_type = self.platform_cfg_json["switch_firmware_type"] + self.test_firmware_list = self.platform_cfg_json["switch_firmware_test"] + self.baseline_firmware_list = self.platform_cfg_json["switch_firmware_baseline"] + self.platform_path = os.path.dirname(self.platform_cfg_file) + except Exception as e: + self.logger.log_err(str(e), True) + sys.exit(1) + + def get_upgrade_path(self, firmware_list, item): + """ + + @param firmware_list: firmware list + @param item: firmware type + @return: get path success:bool,skip upgrade:bool,firmware path:string + """ + support_slave, skip_upgrade = self.check_item_support_slave(item) + if "absolute_path" not in firmware_list[item]: + firmware_list[item]["absolute_path"] = True + # absolute_path字段不存在时默认值为True + if skip_upgrade is True: + return False, True, "" + elif ( + skip_upgrade is False + and support_slave is True + and "same_with_master" in firmware_list[item] + and firmware_list[item]["same_with_master"] is False + ): + # 判断备区升级文件是否存在 + if ( + "slave_file_path" not in firmware_list[item] + or firmware_list[item]["slave_file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["slave_file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + # 获取备区升级固件所在路径,“same_with_master”字段省略默认为False,备区固件路径和主区一样则直接使用firmware_list[item]["file_path"] + # 不一样则使用firmware_list[item]["slave_file_path"] + else: + if ( + "file_path" not in firmware_list[item] + or firmware_list[item]["file_path"] == "" + ): + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + else: + fw_path = ( + (self.platform_path + "/") + if firmware_list[item]["absolute_path"] is not True + else "" + ) + fw_path += firmware_list[item]["file_path"] + if os.path.exists(fw_path) is False: + self.fail_reason.append( + "there are no upgrade firmware file in item %s" % item + ) + return False, False, "" + return True, False, fw_path + + def upgrade_firmware_refresh(self, firmware_list, test_type="test"): + """ + + @param firmware_list: firmware list + @param test_type: sting to print to console + @return: upgrade and refresh firmware success:bool + """ + ret = True + try: + for item in self.firmware_type: + result, skip_upgrade, fw_path = self.get_upgrade_path( + firmware_list, item + ) + if skip_upgrade: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip upgrade" % item, + True, + ) + continue + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + result = eval(self.firmware_upgrade_func)(item, fw_path, self.fw_extra) + if not result: + self.fail_reason.append( + "upgrade %s %s %s firmware fail" + % (item, self.fw_extra, test_type) + ) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + def save_baseline_and_upgrade(self): + """ + + @return: save baseline version and upgrade success:bool + """ + try: + + version_running_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "w", + ) as f: + json.dump( + version_running_read_back, f, indent=4, separators=(",", ": ") + ) + return self.upgrade_firmware_refresh( + self.test_firmware_list, test_type="test" + ) + except Exception as e: + self.fail_reason.append(str(e)) + return False + + def check_all_items_support_slave(self): + item = "" + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if support_slave is True: + self.logger.log_info( + "there are slave_upgrade attr in item %s" % item, True + ) + return True + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + raise Exception("check %s support slave failed" % item) + return False + + def check_item_support_slave(self, item): + """ + + @param item: firmware type + @return: is support slave upgrade:bool,skip upgrade + """ + if self.slave_upgrade: + if ( + "slave_upgrade" not in self.test_firmware_list[item] + or "slave_upgrade" not in self.baseline_firmware_list[item] + ): + if ( + "slave_upgrade" not in self.test_firmware_list[item] + and "slave_upgrade" not in self.baseline_firmware_list[item] + ): + # 不存在“slave_upgrade”字段,默认为false + return False, True + else: + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif ( + self.test_firmware_list[item]["slave_upgrade"] is False + and self.baseline_firmware_list[item]["slave_upgrade"] is False + ): + return False, True + # “slave_upgrade”字段为False,不支持备区升级 + elif ( + self.test_firmware_list[item]["slave_upgrade"] + != self.baseline_firmware_list[item]["slave_upgrade"] + ): + raise Exception( + 'firmware_test[%s]["slave_upgrade"] must be equal' + ' firmware_baseline[%s]["slave_upgrade"].' % (item, item) + ) + # test fw "slave_upgrade"字段和baseline fw不一致,抛出异常,JSON文件存在问题 + elif self.test_firmware_list[item]["slave_upgrade"] is True: + return True, False + else: + return False, False + + def check_firmware_version(self, firmware_cfg, test_type="test"): + """ + + @param firmware_cfg: firmware config file content + @param test_type: string to print,distinguish the two modes + @return:check firmware version success:bool + """ + version_read_back = eval(self.get_version_func)( + self.firmware_type, self.fw_extra + ) + ret = True + try: + for item in self.firmware_type: + support_slave, skip = self.check_item_support_slave(item) + if skip is True: + self.logger.log_info( + "there are no slave_upgrade attr in %s skip check version" + % item, + True, + ) + continue + # 不支持备区升级直接跳过检测 + if skip is False and support_slave is True: + if ( + test_type is "test" + and "same_with_master" not in firmware_cfg[item] + or firmware_cfg[item]["same_with_master"] is False + ): + version_config_file = firmware_cfg[item]["slave_version"] + # 比对备区test版本固件版本号,“same_with_master”字段省略默认为False,备区版本号和主区一样则直接使用firmware_cfg[item]["version"] + # 不一样则存放在firmware_cfg[item]["slave_version"] + else: + version_config_file = firmware_cfg[item]["version"] + else: + version_config_file = firmware_cfg[item]["version"] + if version_config_file == version_read_back[item]["version"]: + self.logger.log_info( + "%s %s firmware check success" % (item, test_type), True + ) + else: + self.logger.log_err( + "%s %s firmware check fail,config file version:[%s],device version:[%s]" + % ( + item, + test_type, + version_config_file, + version_read_back[item]["version"], + ), + True, + ) + self.fail_reason.append( + "%s %s firmware check fail" % (item, test_type) + ) + ret = False + break + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return ret + + def check_and_recover_firmware(self): + """ + + @return: check and recover firmware to baseline success:bool + """ + try: + version_test_check_result = self.check_firmware_version( + self.test_firmware_list, test_type="test" + ) + if version_test_check_result is not True: + return False + else: + return self.upgrade_firmware_refresh( + self.baseline_firmware_list, test_type="baseline" + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + return False + + def check_baseline_firmware(self): + """ + + @return: check baseline firmware version success:bool + """ + ret = True + try: + with open( + self.platform_path + + "/" + + (MASTER_ORIG_FW_CFG if not self.slave_upgrade else SLAVE_ORIG_FW_CFG), + "r", + ) as f: + version_running_read_back_list = json.load(f) + version_running_check_result = self.check_firmware_version( + version_running_read_back_list, test_type="baseline" + ) + if version_running_check_result is not True: + ret = False + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append(str(e)) + ret = False + return ret + + test_case_list = [ + "save_baseline_and_upgrade", + "check_and_recover_firmware", + "check_baseline_firmware" + ] + test_help_list = [ + "1.save master running firmware version and program test firmware", + "2.check master test firmware version and program baseline firmware", + "3.check master baseline firmware version" + ] + + def run_test(self, *argv): + ret = E.OK + also_print_console = True + for item in self.test_help_list: + self.logger.log_info(item, True) + + choice = input("Please enter your choice (0 to quit):") + try: + if int(choice) not in range(0, (len(self.test_case_list) + 1)): + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + else: + if int(choice) is 0: + # self.fail_reason.append("user cancelled input") + self.logger.log_info("user cancelled input", also_print_console) + ret = E.OK + else: + index = int(choice) + # continue test item while check is success + test_item = "self.%s" % self.test_case_list[index - 1] + self.slave_upgrade = True if index > 3 else False + self.fw_extra = "master" if not self.slave_upgrade else "slave" + self.logger.log_info( + "[%s]:" % self.test_help_list[index - 1], also_print_console + ) + if ( + self.slave_upgrade is False + or self.check_all_items_support_slave() is True + ): + # skip test item while there are no "slave_upgrade" in all "firmware_type" item + result = eval(test_item)() + if result is not True: + self.logger.log_info( + "item %s =======> test fail" + % self.test_help_list[index - 1], + also_print_console, + ) + return E.EUPG25001 + index - 1 + elif index == 1 or index == 2 or index == 4 or index == 5: + self.logger.log_info( + "item %s =======> test success" + % self.test_help_list[index - 1], + also_print_console, + ) + # system reboot may be required in test item 1,2,4 or 5 + # break out of the loop to end the test item + else: + self.logger.log_info( + "there are no slave_upgrade attr, item %s =======> test skip" + % self.test_help_list[index - 1], + True, + ) + except Exception as e: + self.logger.log_err(str(e), True) + self.fail_reason.append("input invalid param:%s" % choice) + ret = E.EFAIL + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/sysdiag.py b/src/sonic-pit/pit-sysdiag/src/sysdiag.py new file mode 100755 index 00000000000..b44cc46a506 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/sysdiag.py @@ -0,0 +1,719 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from function import get_platform_and_hwsku, run_command +import imp +import inspect +import os +import signal +import sys +import time +import json +import getopt +import argparse +from optparse import OptionParser +import threading +from tabulate import tabulate +sys.path.append(os.path.dirname(__file__)) +import syslogger +from errcode import * +from banner import banner_string +from test_case import TestCaseCommon +import traceback + +from pit_util_common import * +import pit_util_common + + +THIS_MODULE = "SYSDIAG" +VERSION = 1.1 +TITLE_PRINT = True +# Diag tags +DIAG_TAG_DELIVERY = "delivery" +DIAG_TAG_MANUFACTURE = "manufacture" +DIAG_TAG_USER = "user" +DIAG_TAG_EMC = "emc" +DIAG_TAG_POWER = "power" +DIAG_TAG_PA = "pa" +DIAG_TAG_STRESS = "stress" +DIAG_TAG_DEFAULT = DIAG_TAG_DELIVERY +DIAG_TAGS = [ + DIAG_TAG_DELIVERY, + DIAG_TAG_MANUFACTURE, + DIAG_TAG_EMC, + DIAG_TAG_POWER, + DIAG_TAG_PA, + DIAG_TAG_STRESS] + +# TestCases list +DELIVERY_TEST_CASE_LIST = [] +MANUFACTURE_TEST_CASE_LIST = [] +USER_TEST_CASE_LIST = [] + +CMD_DIAG_TEST = "diag" +CMD_SINGLE_TEST = "single" +CMD_LIST_TEST = "list" +CMD_FAILED_CASE = "failed" +CMD_DEFAULT = CMD_DIAG_TEST + +PERFORM_TEST_ARGS = ["start", "stop"] + +SYSDIAG_PLATFORM_CFG_FILE = "case_config.json" +SYSDIAG_PLATFORM_CASE_CFG_FILE = "platform_config.json" +SYSDIAG_PLATFORM_PRE_SCRIPT = "pre_handle.sh" + +# case types +CASE_TYPE_AUTO = "auto" +CASE_TYPE_MANUAL = "manual" +CASE_TYPE_UTILITY = "utility" +CASE_TYPE_DEFAULT = CASE_TYPE_AUTO +CASE_TYPES = [CASE_TYPE_AUTO, CASE_TYPE_MANUAL, CASE_TYPE_UTILITY] + +LOG_VERBOSE = False +LOG_QUIET = False +LOG_LANG = "en" + +# global param, signal "Ctrl C" +is_sigint_up = False + +# find classes define from .py +def get_classes(arg): + def _valid_cls(attr): + if inspect.isclass(attr) and \ + attr.__bases__[0].__name__ == TestCaseCommon.__name__: + return True + else: + return False + classes = [] + clsmembers = inspect.getmembers(arg, _valid_cls) + for (name, _) in clsmembers: + classes.append(name) + return classes + + +# Diag types +CMD_DEFAULT = CMD_DIAG_TEST +DIAG_TYPE_DELIVERY = "delivery" +DIAG_TYPE_MANUFACTURE = "manufacture" +DIAG_TAG_EMC = "emc" +DIAG_TAG_POWER = "power" +DIAG_TAG_PA = "pa" +DIAG_TYPE_USER = "user" + +# Psydo type, list all cases +DIAG_TYPE_DEFAULT = DIAG_TYPE_DELIVERY +DIAG_TYPES = [DIAG_TYPE_DELIVERY, DIAG_TYPE_MANUFACTURE, DIAG_TAG_EMC, DIAG_TAG_POWER, DIAG_TAG_PA] + + +SYSDIAG_PLATFORM_CFG_FILE = "platform_config.json" +SYSDIAG_PLATFORM_CASE_CFG_FILE = "case_config.json" +SYSDIAG_PLATFORM_PRE_SCRIPT = "pre_handle.sh" + +# case types +CASE_TYPE_AUTO = "auto" +CASE_TYPE_MANUAL = "manual" +CASE_TYPE_UTILITY = "utility" +CASE_TYPE_DEFAULT = CASE_TYPE_AUTO +CASE_TYPES = [CASE_TYPE_AUTO, CASE_TYPE_MANUAL, CASE_TYPE_UTILITY] +SPLITER = "=================================================================" +BANNER = """ + ____ ___ _____ ____ ____ _ + | _ \_ _|_ _| / ___| _ _ ___| _ \(_) __ _ __ _ + | |_) | | | | _____ \___ \| | | / __| | | | |/ _` |/ _` | + | __/| | | | |_____| ___) | |_| \__ \ |_| | | (_| | (_| | + |_| |___| |_| |____/ \__, |___/____/|_|\__,_|\__, | + |___/ |___/ +""" + +# Global vars +############################################################# +g_lang = "EN" + +USAGE = ''' +sysdiag + + System diagnostic test utility + +Usage: + sysdiag [FUNCTION] [OPTIONS...] + +FUNCTION: + -v, --version Show sysdiag version + -d, --diag [-g ] [-t ] Run diagnostic test, default tag is delivery + -s, --single Run single test, test case file should be given + -l, --list [-g ] Show all possible cases, default tag is delivery + +OPTIONS: + -h, --help Print sysdiag user guide + -c, --chinese Test outputs in Chinese + -q, --quiet don't print status messages to stdout") + -t, --type Specify type of test cases, default type is auto + -g, --tag Specify tag of test cases, default tag is delivery + -i, --interval Set test interval when loop test, unit minute + -p, --pre handle pre handle +''' +# global param, signal "Ctrl C" +is_sigint_up = False + +# class SysDiag +class SysDiag(object): + def __init__(self, logger): + self.log_json_file = "/var/log/sysdiag_result.json" + self.filename = "/var/log/sysdiag_result.json" + self.log = logger + # command default config + self.tag = DIAG_TAG_DEFAULT + self.type = CASE_TYPE_DEFAULT + self.cmd = CMD_DEFAULT + self.signal_test_case = None + # test case list and result + self.test_case_list = [] + self.test_case_files = [] + self.test_result = None + self.pass_case_cnt = 0 + self.pass_case_dict = {} + self.fail_case_cnt = 0 + self.fail_case_dict = {} + self.cases_result = {} + self.diag_run_flag = False + self.run_case_list = [] + self.run_case_flag = True + self.run_fail_list = [] + # console print on-off + self.console_print_flag = True + # run interval, unit second + self.interval = 0 + # pre handle + self.pre_handle_scrip = '' + self.pre_handle = False + # case stress test times, eg. cpld r/w test 10000 times. + self.case_test_count = 1 + self.init_options() + + def show_banner(self): + banner = banner_string + self.log.log_info(banner, True) + + def init_options(self): + self.arg_parser = argparse.ArgumentParser(add_help=False) + self.opt_parser = OptionParser(add_help_option=False) + self.opt_parser.add_option("-v", "--version", action="store_true", dest="version", help="Show sysdiag version") + self.opt_parser.add_option("-d", "--diag", action="store_false", dest="diag",help="Run diagnostic test, default tag is delivery") + self.opt_parser.add_option("-s", "--single", action="store_true", dest="single", help="Run single test, test case file should be given") + self.opt_parser.add_option("-l", "--list", action="store_true", dest="list", help="Show all possible cases, default tag is delivery") + self.opt_parser.add_option("-h", "--help", action="store_true", dest="help", help="Print sysdiag user guide") + self.opt_parser.add_option("-c", "--chinese", action="store_false", dest="chinese", help="Test outputs in Chinese") + self.opt_parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="don't print status messages to stdout") + self.opt_parser.add_option("-t", "--type", action="store_true", dest="type", help=" Specify type of test cases, default type is auto") + self.opt_parser.add_option("-g", "--tag", action="store_true", dest="tag", help=" Specify tag of test cases, default tag is delivery") + self.opt_parser.add_option("-i", "--interval", action="store_true", dest="interval", help=" Set test interval when loop test, unit second") + self.opt_parser.add_option("-p", "--pre", action="store_true", dest="pre", help="pre handle") + + def parse_opts_and_args(self): + global LOG_LANG + + try: + opts, args = getopt.getopt(sys.argv[1:], "vhdcqs:lpt:g:i:f:N", + ["version", "help", "diag", "chinese", + "quiet", "single=", "list", "pre", + "type=", "tag=", "interval=", "failed", + "count="]) + except getopt.GetoptError: + print("\nYour inputs is invalid!") + sys.exit(IO_ERR) + + self.log.log_info( + "Parse input options and arguments ...", + self.console_print_flag) + for opt, opt_val in opts: + if opt in ("-h", "--help"): + sysdiag_usage(NO_ERR) + if opt in ('-v', '--version'): + self.log.log_info( + "sysdiag version: {0}".format(VERSION), + self.console_print_flag) + sys.exit(0) + if opt in ('-d', "--diag"): + self.cmd = CMD_DIAG_TEST + elif opt in ('-f', '--failed'): + self.run_case_flag = False + elif opt in ('-c', "--chinese"): + LOG_LANG = "cn" + # add tag config, to support emc/ac-power/pa test + elif opt in ("-g", "--tag"): + self.tag = opt_val + if opt_val not in DIAG_TAGS: + self.log.log_err( + "Invalid input, case tag must be {}".format(DIAG_TAGS), + self.console_print_flag) + sys.exit(IO_ERR) + elif opt in ("-q", "--quiet"): + self.console_print_flag = False + self.set_logger(self.console_print_flag) + elif opt in ("-s", "--single"): + self.cmd = CMD_SINGLE_TEST + self.signal_test_case = opt_val + elif opt in ("-l", "--list"): + self.cmd = CMD_LIST_TEST + elif opt in ("-t", "--type"): + if opt_val in CASE_TYPES: + self.type = opt_val + else: + self.log.log_err( + "Invalid type, must be {}".format(CASE_TYPES), + self.console_print_flag) + sys.exit(IO_ERR) + elif opt in ("-i", "--interval"): + self.interval = int(opt_val) + elif opt in ("-p", "--pre"): + self.pre_handle = True + elif opt in ("-N", "--count"): + self.case_test_count = int(opt_val) + else: + sysdiag_usage(IO_ERR) + + return 0 + + def generate_test_case_list(self): + (platform, hwsku) = get_platform_and_hwsku() + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + platform_case_config = os.path.join( + current_dir, + "../config/platform", + platform, + SYSDIAG_PLATFORM_CFG_FILE) + platform_config = os.path.join( + current_dir, + "../config/platform", + platform, + SYSDIAG_PLATFORM_CASE_CFG_FILE) + self.pre_handle_scrip = os.path.join( + current_dir, + "../config/platform", + platform, + SYSDIAG_PLATFORM_PRE_SCRIPT) + with open(platform_config, 'r') as f: + diag_cfg_content = json.load(f) + + except IOError as e: + self.log.log_err(str(e), self.console_print_flag) + self.log.log_err(traceback.format_exc(), self.console_print_flag) + raise SystemExit(1) + + self.log.log_info( + "Generate test case list ...", + self.console_print_flag) + index = 1 + + if self.signal_test_case: + diag_case_list = [self.signal_test_case] + else: + diag_case_list = diag_cfg_content['test_cases'] + + for case_file_name in diag_case_list: + try: + case_path = os.path.join( + os.path.dirname(__file__), + case_file_name + '.py') + case_module = imp.load_source(case_file_name, case_path) + except Exception as e: + self.log.log_err(str(e), self.console_print_flag) + self.log.log_err( + traceback.format_exc(), + self.console_print_flag) + raise SystemExit(IO_ERR) + + try: + case_class_name = get_classes(case_module)[0] + case_class = getattr(case_module, case_class_name) + case = case_class(index, self.log, platform_case_config) + except Exception as e: + self.log.log_err(str(e), self.console_print_flag) + self.log.log_err( + traceback.format_exc(), + self.console_print_flag) + raise SystemExit(IO_ERR) + + self.test_case_list.append(case) + self.test_case_files.append(case_file_name) + index = index + 1 + return + + def run_one_case(self, tc): + case_name = tc.get_tc_name() + + ret = E.EFAIL + try: + self.log.log_info( + "==================== {} ====================".format(case_name), + self.console_print_flag) + tc.get_start_time() + ret = tc.run_test() + tc.get_stop_time() + + self.diag_run_flag = True + msg, err = tc.generate_result(ret) + self.log.log_info(msg, self.console_print_flag) + + case_result = {} + tc_file = self.test_case_files[self.test_case_list.index(tc)] + tc_file_path = os.path.join("/var/log", tc_file + '.json') + + os.system("rm -f {}".format(tc_file_path)) + result_dict = tc.generate_result_dict(ret) + tc.record_result_dict_to_file(result_dict, tc_file_path) + case_result[tc.get_tc_name()] = result_dict + self.cases_result.update(case_result) + + if ret != E.OK: + self.fail_case_cnt += 1 + if self.fail_case_dict.get(case_name): + self.fail_case_dict[case_name] = self.fail_case_dict[case_name] + 1 + else: + self.fail_case_dict[case_name] = 1 + self.log.log_err(err, self.console_print_flag) + else: + self.pass_case_cnt += 1 + if self.pass_case_dict.get(case_name): + self.pass_case_dict[case_name] = self.pass_case_dict[case_name] + 1 + else: + self.pass_case_dict[case_name] = 1 + # record run test_case name + if case_name not in self.run_case_list: + self.run_case_list.append(case_name) + + except Exception as e: + self.log.log_err(str(e), self.console_print_flag) + self.fail_case_cnt += 1 + if self.fail_case_dict.get(case_name): + self.fail_case_dict[case_name] = self.fail_case_dict[case_name] + 1 + else: + self.fail_case_dict[case_name] = 1 + # record run test_case name + if case_name not in self.run_case_list: + self.run_case_list.append(case_name) + return + + def run(self): + self.parse_opts_and_args() + if self.run_case_flag: + self.generate_test_case_list() + else: + self.fail_case_test() + + if not self.test_case_list: + self.log.log_warn( + "No test cases generated.", + self.console_print_flag) + return + + if self.cmd == CMD_LIST_TEST: + self.print_case_list(self.tag) + return + + self.log.log_info("Start switch diag ...", self.console_print_flag) + # pre handle + if self.pre_handle: + if os.path.exists(self.pre_handle_scrip): + ret, out = run_command(self.pre_handle_scrip) + self.log.log_info(out, self.console_print_flag) + else: + self.log.log_warn("miss pre handle script", self.console_print_flag) + + self.log.log_info("Start switch diag ...", self.console_print_flag) + + # pre handle + if self.pre_handle: + if os.path.exists(self.pre_handle_scrip): + ret, out = run_command(self.pre_handle_scrip) + self.log.log_info(out, self.console_print_flag) + else: + self.log.log_info("=======> Miss pre handle script <=======", + self.console_print_flag) + + self.log.log_info("Start switch diag ...", self.console_print_flag) + # pre handle + if self.pre_handle: + if os.path.exists(self.pre_handle_scrip): + ret, out = run_command(self.pre_handle_scrip) + self.log.log_info(out, self.console_print_flag) + else: + self.log.log_warn( + "miss pre handle script", + self.console_print_flag) + + # main loop + while True: + for tc in self.test_case_list: + case_tags = tc.get_tc_tags() + case_name = tc.get_tc_name() + + # if Ctrl C catched, set interval to 0 + if is_sigint_up: + self.interval = 0 + break + + # single case test + if self.cmd == CMD_SINGLE_TEST: + self.run_one_case(tc) + break + # diag test + elif self.cmd in [CMD_DIAG_TEST]: + if self.tag in case_tags: + # auto test + if self.type == tc.get_tc_type(): + self.run_one_case(tc) + else: + # manual/utility test + pass + else: + pass + + if self.interval: + time.sleep(self.interval * 60) + else: + break + + return + + def generate_final_result(self): + if not self.diag_run_flag: + return NORUN_ERR + + self.log.log_info( + "=============================================", + self.console_print_flag) + self.log.log_info( + "Generate final test result...", + self.console_print_flag) + + if self.fail_case_cnt > 0: + self.test_result = 'fail' + ret = TEST_FAIL_ERR + else: + self.test_result = 'pass' + ret = NO_ERR + + self.log.log_info( + "System diagnostic summary: tag {}, result {}, total {}, failed {}".format( + self.tag, + self.test_result, + self.pass_case_cnt + + self.fail_case_cnt, + self.fail_case_cnt), + self.console_print_flag) + + self.log.log_info("Details: ", self.console_print_flag) + + header = ["CaseName", 'PassCnt', 'FailCnt', 'Result'] + detail_table = [] + for case_name in self.run_case_list: + pass_cnt = self.pass_case_dict.get(case_name, 0) + fail_cnt = self.fail_case_dict.get(case_name, 0) + result = "FAIL" if fail_cnt else "PASS" + detail_table.append( + [case_name, str(pass_cnt), str(fail_cnt), result]) + if len(detail_table) > 0: + self.log.log_info( + tabulate( + detail_table, + header, + tablefmt="grid"), + self.console_print_flag) + + # record result to jsonfile + self.print_final_result_to_json() + return ret + + def print_final_result_to_json(self): + result = {} + info = {} + info['final_result'] = self.test_result + info['total_count'] = self.pass_case_cnt + self.fail_case_cnt + info['failed_count'] = self.fail_case_cnt + info['detailed_result'] = {} + info['detailed_result']['pass_case'] = self.pass_case_dict.keys() + info['detailed_result']['failed_case'] = self.fail_case_dict.keys() + result["result"] = info + result["details"] = self.cases_result + search_cmd = "ls {} | grep sysdiag_result | wc -l".format( + self.log_json_file) + search_status, search_log = run_command(search_cmd) + if search_status != 0 or search_log < 0: + self.log.log_info("find filename error!", self.console_print_flag) + else: + if int(search_log) == 0: + log_name = self.log_json_file + "sysdiag_result.json" + else: + log_name = self.log_json_file + \ + "sysdiag_result{}.json".format(int(search_log)) + with open(log_name, "w") as f: + json.dump(result, f, indent=4, separators=(',', ': ')) + json_list = [] + for files in os.listdir(self.log_json_file): + if "sysdiag_result" in files: + json_list.append(files) + sorting = pit_util_common.sort_list() + log_list = sorting.sort_humanly(json_list) + if int(search_log) >= 1: + for index in range(len(log_list) - 1, 0, -1): + oldfile = os.path.join( + self.log_json_file, + "sysdiag_result{}.json".format(index)) + newfile = os.path.join( + self.log_json_file, + "sysdiag_result{}.json".format( + index + 1)) + os.rename(oldfile, newfile) + run_command( + "mv {} {}".format( + "{}sysdiag_result.json".format( + self.log_json_file), "{}sysdiag_result1.json".format( + self.log_json_file))) + oldfile = os.path.join( + self.log_json_file, + "sysdiag_result{}.json".format( + len(log_list))) + newfile = os.path.join(self.log_json_file, "sysdiag_result.json") + os.rename(oldfile, newfile) + + def print_case_list(self, case_tag): + self.log.log_info( + "==================== TestCase List ====================", + self.console_print_flag) + header = ["Index", "CaseFile", "CaseName", "Type", "Description"] + tc_list = [] + for tc in self.test_case_list: + tc_tags = tc.get_tc_tags() + if case_tag in tc_tags or case_tag == '': + id = tc.index() + tc_name = tc.get_tc_name() + tc_type = tc.get_tc_type() + tc_desc = tc.get_tc_description() + tc_file = self.test_case_files[self.test_case_list.index(tc)] + tc_list.append([id, tc_file, tc_name, tc_type, tc_desc]) + if len(tc_list) > 0: + self.log.log_info( + tabulate( + tc_list, + header, + tablefmt="simple"), + self.console_print_flag) + + def fail_case_test(self): + with open(self.filename) as f: + data = json.load(f) + self.fail_run_case_list = data["result"]["detailed_result"]["failed_case"] + for case in self.fail_run_case_list: + self.run_fail_list.append(case.replace("-test", "_tc")) + index = 1 + (platform, hwsku) = get_platform_and_hwsku() + current_dir = os.path.dirname(os.path.abspath(__file__)) + platform_case_config = os.path.join( + current_dir, + "../config/platform", + platform, + SYSDIAG_PLATFORM_CFG_FILE) + for case_fail_name in self.run_fail_list: + try: + case_path = os.path.join( + os.path.dirname(__file__), + case_fail_name + '.py') + case_module = imp.load_source(case_fail_name, case_path) + except Exception as e: + self.log.log_err(str(e), self.console_print_flag) + self.log.log_err( + traceback.format_exc(), + self.console_print_flag) + raise SystemExit(IO_ERR) + try: + case_class_name = get_classes(case_module)[0] + case_class = getattr(case_module, case_class_name) + case = case_class(index, self.log, platform_case_config) + except Exception as e: + self.log.log_err(str(e), self.console_print_flag) + self.log.log_err( + traceback.format_exc(), + self.console_print_flag) + raise SystemExit(IO_ERR) + self.test_case_list.append(case) + self.test_case_files.append(case_fail_name) + index = index + 1 + return + + def set_logger(self, flag): + self.log.set_logger_also_print_console(flag) + + +############# common function ############## + +def sysdiag_usage(exit_code): + print("") + print("sysdiag") + print("") + print(" System diagnostic test utility") + print("") + print("Usage:") + print(" sysdiag [FUNCTION] [OPTIONS...]") + print("") + print("FUNCTION:") + print(" -v, --version Show sysdiag version") + print(" -d, --diag [-g ] [-t ] Run diagnostic test, default tag is delivery") + print(" -s, --single Run single test, test case file should be given") + print(" -l, --list [-g ] Show all possible cases, default tag is delivery") + print(" -f, --failed Test last failed case again") + print("") + print("OPTIONS:") + print(" -h, --help Print sysdiag user guide") + print(" -c, --chinese Test outputs in Chinese") + print(" -q, --quiet Do not show outputs") + print(" -t, --type Specify type of test cases, default type is auto") + print(" -g, --tag Specify tag of test cases, default tag is delivery") + print(" -i, --interval Set test interval when loop test, unit minute") + print(" -p, --pre handle pre handle") + print(" -N, --count stress test times for one case") + sys.exit(exit_code) + + +def signal_handler(signum, frame): + global is_sigint_up + is_sigint_up = True + print('\033[1;42mCatched interrupt signal! Waiting for test exit ...\033[0m') + + +def sysdiag_main(): + logger = None + diag = None + try: + logger = syslogger.sys_log_init_logger(THIS_MODULE) + except Exception as e: + err_msg = "Failed to init syslogger" + logger.log_err(err_msg) + raise IOError(err_msg) + + # Create SysDiag + try: + diag = SysDiag(logger) + except Exception as e: + err_msg = "Create SysDiag instance failed: %s" % str(e) + logger.log_err(err_msg) + return IO_ERR + + signal.signal(signal.SIGINT, signal_handler) + time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + if TITLE_PRINT: + spliter = "=================================================================" + diag.log.log_info(spliter, True) + diag.show_banner() + diag.log.log_info(spliter, True) + print("\n {}\n".format(time_str)) + diag.log.log_info("Start to Diagnose Switch Hardware ...", True) + diag.run() + ret = diag.generate_final_result() + return ret + + +if __name__ == "__main__": + ret = sysdiag_main() + exit(ret) diff --git a/src/sonic-pit/pit-sysdiag/src/sysdiag_log.py b/src/sonic-pit/pit-sysdiag/src/sysdiag_log.py new file mode 100644 index 00000000000..71c364271cf --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/sysdiag_log.py @@ -0,0 +1,120 @@ +#!/usr/bin/python + + +import syslog +import traceback +import logging.handlers + + +DEF_LOG_DIR = "/var/log" +DEF_LOG_BYTES = 20 * 1024 * 1024 +DEF_LOG_BACKUP = 5 + + +# RAW syslog facilities +def sys_log_info(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_WARNING, ctn) + print(ctn) + syslog.closelog() + +def sys_log_warn(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_WARNING, ctn) + print(ctn) + syslog.closelog() + + +def sys_log_err(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_ERR, ctn) + print(ctn) + syslog.closelog() + + +def sys_log_crit(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_CRIT, ctn) + print(ctn) + syslog.closelog() + + +# SysLogger +class SysLogger(object): + def __init__(self, module_name, size_bytes=DEF_LOG_BYTES, + backup_count=DEF_LOG_BACKUP, log_file=None): + self.module_name = module_name + self.logger = None + self.also_print_console = True + if log_file: + log_fname = log_file + else: + log_fname = "/".join([DEF_LOG_DIR, module_name.lower() + ".log"]) + + self.logger = logging.getLogger(module_name) + if self.logger is None: + err_msg = "Failed to get logger for module %s" % module_name + sys_log_err(self.module_name, err_msg) + raise IOError(err_msg) + + self.logger.setLevel(logging.INFO) + fh = logging.handlers.RotatingFileHandler(log_fname, + maxBytes=size_bytes, + backupCount=backup_count) + if not fh: + err_msg = "Failed to create RotatingFileHandler for log file" + sys_log_err(self.module_name, err_msg) + raise IOError(err_msg) + + fmt_str = '%(asctime)s - %(name)s - %(levelname)s: %(message)s' + formatter = logging.Formatter(fmt_str) + if not formatter: + err_msg = "Failed to create formatter for %s" % fmt_str + sys_log_err(self.module_name, err_msg) + raise IOError(err_msg) + + fh.setFormatter(formatter) + self.logger.addHandler(fh) + + def log_dbg(self, ctn, also_print_console=False): + self.logger.debug(ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_info(self, ctn, also_print_console=False): + self.logger.info(ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_warn(self, ctn, also_print_console=False): + self.logger.warning(ctn) + # sys_log_warn(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_err(self, ctn, also_print_console=False): + self.logger.error(ctn) + # sys_log_err(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_crit(self, ctn, also_print_console=False): + self.logger.critical(ctn) + #sys_log_crit(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def set_logger(self, also_print_console): + self.also_print_console = also_print_console + +# Initialize logger +def sys_log_init_logger(module_name): + sys_logger = None + + try: + sys_logger = SysLogger(module_name) + except IOError as e: + sys_log_err(module_name, "{}".format(traceback.format_exc())) + sys_log_err(module_name, "Create logger exception: %s" % str(e)) + + return sys_logger diff --git a/src/sonic-pit/pit-sysdiag/src/syseeprom-write.py b/src/sonic-pit/pit-sysdiag/src/syseeprom-write.py new file mode 100755 index 00000000000..336d1dd4457 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/syseeprom-write.py @@ -0,0 +1,156 @@ +#!/usr/bin/python +import json +import sys +import struct +import glob +from ctypes import * +import os +# from sonic_py_common import device_info +# import sonic_platform +# +CACHE_ROOT = '/var/cache/sonic/decode-syseeprom' +CACHE_FILE = 'syseeprom_cache' + +TLV_CODE_PRODUCT_NAME = 0x21 +TLV_CODE_PART_NUMBER = 0x22 +TLV_CODE_SERIAL_NUMBER = 0x23 +TLV_CODE_MAC_BASE = 0x24 +TLV_CODE_MANUF_DATE = 0x25 +TLV_CODE_DEVICE_VERSION = 0x26 +TLV_CODE_LABEL_REVISION = 0x27 +TLV_CODE_PLATFORM_NAME = 0x28 +TLV_CODE_ONIE_VERSION = 0x29 +TLV_CODE_MAC_SIZE = 0x2A +TLV_CODE_MANUF_NAME = 0x2B +TLV_CODE_MANUF_COUNTRY = 0x2C +TLV_CODE_VENDOR_NAME = 0x2D +TLV_CODE_DIAG_VERSION = 0x2E +TLV_CODE_SERVICE_TAG = 0x2F +TLV_CODE_VENDOR_EXT = 0xFD +TLV_CODE_CRC_32 = 0xFE + + +class TLVINFO_HEADER(Structure): + _fields_ = [("signature", c_char * 8), + ("version", c_ubyte), + ("totallen", c_ushort)] + + def dump(self): + return struct.pack('8s', self.signature) + \ + struct.pack('B', self.version) + \ + struct.pack('>H', self.totallen) + + +class TLVINFO_DATA: + data = [] + + def add_tlv_str(self, type, value): + self.data.append(struct.pack('B', type) + struct.pack('B', len(value)) + value.encode()) + + def add_tlv_mac(self, type, value): + self.data.append(struct.pack('B', type) + struct.pack('B', len(value))) + for v in value: + self.data.append(struct.pack('B', int(v, 16))) + + def add_tlv_val(self, type, value): + self.data.append(struct.pack('B', type) + struct.pack('B', 1)) + self.data.append(struct.pack('B', int(value, 16))) + + def dump(self): + r = '' + for m in self.data: + r += bytes(m) + return r + struct.pack('B', TLV_CODE_CRC_32) + struct.pack('B', 4) + + +def crc32(crc, p, len): + crc = 0xffffffff & ~crc + for i in range(len): + crc = crc ^ p[i] + for j in range(8): + crc = (crc >> 1) ^ (0xedb88320 & -(crc & 1)) + return 0xffffffff & ~crc + + +def crc(header, data): + r = '' + for m in header: + r += bytes(m) + for m in data: + r += bytes(m) + c = crc32(0, bytearray(r), len(r)) + s = struct.pack('>I', c) + return s + + +def usage(): + usage_str = ''' + write tlv eeprom to eeprom + + Usage: + syseeprom-write.py + + e.g.: + syseeprom-write.py ../config/common/syseeprom_test.json + ''' + print(usage_str) + +def main(): + + if not sys.argv: + usage() + exit() + + path = sys.argv[1] + print(path) + + current_dir = os.path.dirname(os.path.abspath(__file__)) + syseeprom_config = os.path.join(current_dir, path) + print(syseeprom_config) + with open(syseeprom_config, 'r') as f: + syseeprom_test = json.load(f) + + tlvinfo_header = TLVINFO_HEADER('TlvInfo', 1, 0) + tlvinfo_data = TLVINFO_DATA() + #onie_machine = os.popen("cat /host/machine.conf | grep 'onie_machine=' | sed 's/onie_machine=//'").read().strip() + #tlvinfo_data.add_tlv_str(TLV_CODE_ONIE_MACHINE, onie_machine) + + eth0_mac_str = syseeprom_test["Base MAC Address"] + eth0_mac = eth0_mac_str.split(':') + tlvinfo_data.add_tlv_mac(TLV_CODE_MAC_BASE, eth0_mac) + tlvinfo_data.add_tlv_val(TLV_CODE_DEVICE_VERSION, syseeprom_test["Device Version"]) + + tlvinfo_data.add_tlv_str(TLV_CODE_ONIE_VERSION, syseeprom_test["ONIE Version"]) + tlvinfo_data.add_tlv_str(TLV_CODE_PLATFORM_NAME, syseeprom_test["Platform Name"]) + tlvinfo_data.add_tlv_str(TLV_CODE_SERIAL_NUMBER, syseeprom_test["Serial Number"]) + tlvinfo_data.add_tlv_str(TLV_CODE_PRODUCT_NAME, syseeprom_test["Product Name"]) + tlvinfo_data.add_tlv_str(TLV_CODE_PART_NUMBER, syseeprom_test["Part Number"]) + tlvinfo_data.add_tlv_str(TLV_CODE_MANUF_NAME, syseeprom_test["Manufacturer"]) + tlvinfo_data.add_tlv_str(TLV_CODE_MANUF_DATE, syseeprom_test["Manufacture Date"]) + tlvinfo_data.add_tlv_str(TLV_CODE_MANUF_COUNTRY, syseeprom_test["Manufacture Country"]) + tlvinfo_data.add_tlv_str(TLV_CODE_VENDOR_NAME, syseeprom_test["Vendor Name"]) + tlvinfo_data.add_tlv_str(TLV_CODE_DIAG_VERSION, syseeprom_test["Diag Version"]) + tlvinfo_data.add_tlv_str(TLV_CODE_SERVICE_TAG, syseeprom_test["Service Tag"]) + tlvinfo_data.add_tlv_str(TLV_CODE_LABEL_REVISION, syseeprom_test["Label Revision"]) + tlvinfo_data.add_tlv_mac(TLV_CODE_MAC_SIZE, ["00", "05"]) + tlvinfo_data.add_tlv_str(TLV_CODE_VENDOR_EXT, syseeprom_test["Vendor Extension"]) + + tlvinfo_header.totallen = len(tlvinfo_data.dump()) + 4 + + try: + f = open('sys_eeprom.bin', 'w+') + f.write(tlvinfo_header.dump()) + f.write(tlvinfo_data.dump()) + f.write(crc(tlvinfo_header.dump(), tlvinfo_data.dump())) + f.close() + os.system("sudo cat sys_eeprom.bin > /sys/switch/debug/eeprom") + os.system("decode-syseeprom --init") + # init_syseeprom() + + # os.system("sudo rm -f /var/cache/sonic/decode-syseeprom/syseeprom_cache") + except BaseException: + print('Unable to write file ./sys_eeprom.bin') + + +if __name__ == "__main__": + main() diff --git a/src/sonic-pit/pit-sysdiag/src/syslogger.py b/src/sonic-pit/pit-sysdiag/src/syslogger.py new file mode 100644 index 00000000000..574a372acdb --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/syslogger.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +import syslog +import logging +from logging.handlers import RotatingFileHandler + +DIAG_LOGGER_NAME = "sysdiag" +DIAG_LOG_DIR = "/var/log" +DIAG_LOG_SIZE = 20*1024*1024 +DIAG_LOG_BACKUP = 5 + +# RAW syslog facilities +def sys_log_warn(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_WARNING, ctn) + syslog.closelog() + + +def sys_log_err(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_ERR, ctn) + syslog.closelog() + + +def sys_log_crit(module_name, ctn): + syslog.openlog(module_name) + syslog.syslog(syslog.LOG_CRIT, ctn) + syslog.closelog() + +# sysdiag logger class +class SysLogger(): + def __init__(self, + module_name, + log_file=None, + size_bytes=DIAG_LOG_SIZE, + backup_count=DIAG_LOG_BACKUP + ): + self.module_name = module_name + self.logger = None + self.also_print_console = True + if log_file: + log_fname = log_file + else: + log_fname = "/".join([DIAG_LOG_DIR, module_name.lower() + ".log"]) + + self.logger = logging.getLogger(module_name) + if self.logger is None: + err_msg = "Failed to get logger for module %s" % module_name + sys_log_err(self.module_name, err_msg) + raise IOError(err_msg) + + self.logger.setLevel(logging.DEBUG) + + fmt_str = '%(asctime)s - %(name)s - %(levelname)s: %(message)s' + formatter = logging.Formatter(fmt_str) + if not formatter: + err_msg = "Failed to create formatter for %s" % fmt_str + sys_log_err(self.module_name, err_msg) + raise IOError(err_msg) + + handler = RotatingFileHandler(log_fname, maxBytes=size_bytes, backupCount=backup_count) + handler.setFormatter(formatter) + + #console = logging.StreamHandler() + #console.setFormatter(formatter) + + self.logger.addHandler(handler) + #self.logger.addHandler(console) + + def get_logger(self): + return self.logger + + def log_dbg(self, ctn, also_print_console=False): + self.logger.debug(ctn) + if also_print_console and self.also_print_console: + print(ctn) + + + def log_info(self, ctn, also_print_console=False): + self.logger.info(ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_warn(self, ctn, also_print_console=False): + self.logger.warning(ctn) + #sys_log_warn(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_err(self, ctn, also_print_console=False): + self.logger.error(ctn) + #sys_log_err(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def log_crit(self, ctn, also_print_console=False): + self.logger.critical(ctn) + #sys_log_crit(self.module_name, ctn) + if also_print_console and self.also_print_console: + print(ctn) + + def set_logger_also_print_console(self, also_print_console): + self.also_print_console = also_print_console + +# Initialize logger +def sys_log_init_logger(module_name): + sys_logger = None + + try: + sys_logger = SysLogger(module_name=module_name) + except IOError as e: + sys_log_err(module_name, "Create logger exception: %s" % str(e)) + + return sys_logger diff --git a/src/sonic-pit/pit-sysdiag/src/tc_daemon.py b/src/sonic-pit/pit-sysdiag/src/tc_daemon.py new file mode 100644 index 00000000000..06f98c9e941 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/tc_daemon.py @@ -0,0 +1,168 @@ +import json +import os +import sys +import signal +import atexit +import time + +TEST_PASS = 0 +TEST_FAIL_ERR = 2 +NORUN_ERR = 3 + + +# test daemon class define +class TCDaemon(): + def __init__(self, module_name, logger, stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): + self.module_name = module_name + self.logger = logger + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = "/tmp/" + module_name + ".pid" + self.resultfile = "/tmp/" + module_name + ".result" + self.start_time_file = "/tmp/" + module_name + "_start_time" + self.log_result = "/var/log/" + module_name + ".result" + self.auto_end = False # auto test end flag, if true, test-daemon will stop when count reatch to test_count + self.test_count = -1 # test-daemon execute count + self.skip_err = True # when test error happened, skip and continue execution + + def set_skip_err(self, flag): + self.skip_err = flag + + def set_auto_end(self, count): + """ + set test daemon auto stop when count==self.test_count + """ + self.auto_end = True + self.test_count = count + + def daemonize(self): + if os.path.exists(self.pidfile): + self.logger.log_warn('{} already running'.format(self.module_name)) + raise RuntimeError('Already running') + + try: + if os.fork() > 0: + raise SystemExit(0) + except OSError: + self.logger.log_err('{} daemon fork failed'.format(self.module_name)) + raise RuntimeError('fork failed!') + + os.chdir('/') + os.setsid() + os.umask(0o22) + + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError: + self.logger.log_err('fork #2 failed: %d (%s)\n' % (self.module_name)) + raise RuntimeError('fork failed!') + + sys.stdout.flush() + sys.stderr.flush() + + with open(self.stdin, 'rb', 0) as f: + os.dup2(f.fileno(), sys.stdin.fileno()) + with open(self.stdout, 'ab', 0) as f: + os.dup2(f.fileno(), sys.stdout.fileno()) + with open(self.stderr, 'ab', 0) as f: + os.dup2(f.fileno(), sys.stderr.fileno()) + + # Write the PID file + with open(self.pidfile, 'w') as f: + f.write(str(os.getpid())) + + # Arrange to have the PID file removed on exit/signal + # atexit.register(lambda: os.remove(self.pidfile)) + + signal.signal(signal.SIGTERM, self.__signal_handler) + + # Signal handler for termination (required) + @staticmethod + def __signal_handler(signo, frame): + raise SystemExit(1) + + def daemon_start(self, *argv): + try: + self.daemonize() + except RuntimeError: + raise SystemExit(1) + self.before_daemon_run() + self.daemon_run(*argv) + + def daemon_stop_previous(self): + """ previous operations before daemon_stop """ + pass + + def record_result_dict_to_file(self, result_dict, json_file): + with open(json_file, 'w') as f: + json.dump(result_dict, f, indent=4, separators=(',', ': ')) + + def daemon_stop(self): + ret = TEST_PASS + try: + self.daemon_stop_previous() + start_time = time.time() + end_time = time.time() + diff_time = round((end_time - start_time) / 60, 2) + if os.path.exists(self.pidfile): + with open(self.pidfile) as f: + os.kill(int(f.read()), signal.SIGTERM) + os.remove(self.pidfile) + if os.path.exists(self.start_time_file): + with open(self.start_time_file, "r") as f: + start_time = float(f.read()) + end_time = time.time() + diff_time = round((end_time - start_time) / 60, 2) + self.logger.log_info("{} stress test {} mins.".format(self.module_name, diff_time), True) + os.remove(self.start_time_file) + + if os.path.exists(self.resultfile): + with open(self.resultfile, 'r') as f: + result = f.read() + if result.find('fail') >= 0: + self.logger.log_err('{} stress test fail!'.format(self.module_name), True) + ret = TEST_FAIL_ERR + else: + self.logger.log_info('{} stress test pass!'.format(self.module_name), True) + os.remove(self.resultfile) + else: + self.logger.log_info('{} stress test pass!'.format(self.module_name), True) + else: + self.logger.log_warn('{} not running'.format(self.module_name), True) + return NORUN_ERR + except OSError as e: + if 'No such process' in str(e) and os.path.exists(self.pidfile): + os.remove(self.pidfile) + if os.path.exists(self.resultfile): + with open(self.resultfile, 'r') as f: + result = f.read() + if result.find('fail') >= 0: + self.logger.log_err('{} stress test fail!'.format(self.module_name), True) + ret = TEST_FAIL_ERR + else: + self.logger.log_info('{} stress test pass!'.format(self.module_name), True) + os.remove(self.resultfile) + result_dict = {} + result_dict['testNo'] = 1 + result_dict['testItem'] = self.module_name + result_dict['startTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) + result_dict['endTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)) + result_dict['duration'] = str(round(end_time - start_time, 2)) + 's' + result_dict['result'] = "Pass" if ret == TEST_PASS else "Fail" + result_dict['errorCode'] = '' + result_dict['failReason'] = '' + self.record_result_dict_to_file(result_dict, self.log_result) + return ret + + # run test + def daemon_run(self, *argv): + """ detail test process """ + pass + + def before_daemon_run(self): + start_time = time.time() + with open(self.start_time_file, "w") as f: + f.write(str(start_time)) diff --git a/src/sonic-pit/pit-sysdiag/src/test_case.py b/src/sonic-pit/pit-sysdiag/src/test_case.py new file mode 100644 index 00000000000..f41cb6610e5 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/test_case.py @@ -0,0 +1,176 @@ +import os +import json +import re +import time +from collections import OrderedDict +from errcode import E + +CASE_CONFIG_JSON_FILE = "config.json" + + +TC_TAG_A = "auto" +TC_TAG_M = "manual" +TC_TAG_P = "production" +TC_TAG_D = "delivery" +TC_TAG_R = "rack" +TC_TAG_U = "utilities" +TC_TAG_DEF = "uninitialized" + +# Used to validate test case tags +TC_TAG_VALID_LIST = [TC_TAG_A, TC_TAG_M, TC_TAG_P, TC_TAG_D, TC_TAG_R, TC_TAG_U] + +class TCBase(object): + def __init__(self, index, case_cfg_file): + # self.logger = logger().getlogger() + self.case_cfg_file = case_cfg_file + self.config = [] + self.case_index = index + #self.case_name = case_name + self.values = [] + #self.success = True + self.fail_reason = [] + self.__parse_config_json() + + def __parse_config_json(self): + try: + with open(self.case_cfg_file, 'r') as f: + self.config = json.load(f, object_pairs_hook=OrderedDict) + except IOError as e: + self.logger.log_err(str(e)) + raise SystemExit(1) + + def set_index(self, index): + self.case_index = index + + def index(self): + return self.case_index + + def get_tc_type(self): + """ test case type: auto, manual, utilties""" + return self.config["type"] + + def get_tc_tags(self): + """ test case tags: production, delivery, rack """ + return self.config["tags"] + + def validate_tags(self): + return (self.config["tags"] in TC_TAG_VALID_LIST) + + def get_tc_name(self): + """ test case name """ + return self.config["name"] + + def get_tc_description(self): + """ test case description """ + return self.config["description"] + + def get_tc_expectation(self): + """ test case expectation """ + return self.config["expectation"] + + def verify_by_regex(self, content, pattern): + return re.match(content, pattern) + + def verify_by_range(self, content, pattern): + if content in pattern: + return True + else: + return False + + def verify_by_string(self, content, pattern): + if content.find(pattern) >= 0: + return True + else: + return False + + def verify(self, content, pattern, type): + if type == "regex": + return self.verify_by_regex(content, pattern) + elif type == "range": + return self.verify_by_range(content, pattern) + elif type == "string": + return self.verify_by_string(content, pattern) + else: + return False + + def run_test(self, *argv): + """ + test case process function + @argv: your input params + @return: code, 0 for success. else for fail + """ + pass + + def generate_result(self, code): + result = "Pass" if code == E.OK else "Fail" + msg = "Test case {}: [{}] =======> {}".format(self.case_index, self.get_tc_name(), result) + err = "ErrorCode: {}\nFail reasons: {}".format(code.name, self.fail_reason) + + return msg, err + + def generate_result_dict(self, code): + result = {} + result['testNo'] = self.case_index + result['testItem'] = self.get_tc_name() + result['startTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)) + result['endTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.stop_time)) + result['duration'] = str(round(self.stop_time - self.start_time, 2)) + 's' + result['result'] = "Pass" if code == E.OK else "Fail" + result['errorCode'] = code.name + result['failReason'] = self.fail_reason + return result + + def record_result_dict_to_file(self, result_dict, json_file): + with open(json_file, 'w') as f: + json.dump(result_dict, f, indent=4, separators=(',', ': ')) + + def get_start_time(self): + self.fail_reason = [] + self.start_time = time.time() + + def get_stop_time(self): + self.stop_time = time.time() + + +class TestCaseCommon(TCBase): + def __init__(self, index, module_name, logger, platform_cfg_file, case_cfg_file=None): + """ + @index: test case id; + @module_name: test case name; + @logger: logger + @platform_cfg_file: platform config json file, "config/platform/***/case_config.json" + @case_cfg_file: case config file, "config/cases/${module_name}/config.json" + """ + if not case_cfg_file: + current_dir = os.path.dirname(__file__) + case_cfg_file = os.path.join(current_dir, "../cases", module_name, CASE_CONFIG_JSON_FILE) + else: + case_cfg_file = case_cfg_file + self.module_name = module_name + self.platform_cfg_file = platform_cfg_file + self.platform_cfg_json = None # platform config content, json format + self.logger = logger + + TCBase.__init__(self, index, case_cfg_file) + self.__read_platform_config() + + def __read_platform_config(self): + """ + read platform config content + """ + try: + with open(self.platform_cfg_file, 'r') as f: + content = json.load(f, object_pairs_hook=OrderedDict) + self.platform_cfg_json = content + except IOError : + self.platform_cfg_json = None + + def get_module_name(self): + return self.module_name + + def log_reason(self, reason, debug=False): + self.fail_reason.append(reason) + if debug: + self.logger.log_dbg(reason) + else: + self.logger.log_err(reason) diff --git a/src/sonic-pit/pit-sysdiag/src/test_case_base.py b/src/sonic-pit/pit-sysdiag/src/test_case_base.py new file mode 100644 index 00000000000..51513bed831 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/test_case_base.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +import os +import json +import re +import time +from errcode import * +from collections import OrderedDict + +TC_TAG_A = "auto" +TC_TAG_M = "manual" +TC_TAG_P = "production" +TC_TAG_D = "delivery" +TC_TAG_R = "rack" +TC_TAG_U = "utilities" +TC_TAG_DEF = "uninitialized" + +# Used to validate test case tags +TC_TAG_VALID_LIST = [TC_TAG_A, TC_TAG_M, TC_TAG_P, TC_TAG_D, TC_TAG_R, TC_TAG_U] + + +# class TestCaseBase +class TestCaseBase(object): + def __init__(self, index, case_cfg_json): + # intermediate result should be placed here in the derived classes + self.tags = [] + self.config = [] + self.index = index + self.case_cfg_json = case_cfg_json + self.__parse_config_json() + pass + + def __parse_config_json(self): + try: + with open(self.case_cfg_json, 'r') as f: + self.config = json.load(f, object_pairs_hook=OrderedDict) + self.tags = self.config["tags"] + except IOError as e: + self.logger.log_err(str(e)) + raise SystemExit(1) + + def set_index(self, index): + self.index = index + + def index(self): + return self.index + + def get_tc_type(self): + """ test case type: auto, manual, utilties""" + return self.config["type"] + + def get_tc_tags(self): + """ test case tags: production, delivery, rack """ + return self.config["tags"] + + def validate_tags(self): + return self.config["tags"] in TC_TAG_VALID_LIST + + def get_tc_name(self): + """ test case name """ + return self.config["name"] + + def get_tc_description(self): + """ test case description """ + return self.config["description"] + + def get_tc_expectation(self): + """ test case expectation """ + return self.config["expectation"] + + def run(self): + # Should run derived class logic + pass + + def generate_result(self, code): + result = "Pass" if code == E.OK else "Fail" + msg = "Test case {}: [{}] =======> {}".format(self.index, self.get_tc_name(), result) + err = "ErrorCode: {}\nFail reasons: {}".format(code.name, self.fail_reason) + + return msg, err + + def generate_result_dict(self, code): + result = {} + result['testNo'] = self.index + result['testItem'] = self.get_tc_name() + result['startTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)) + result['endTime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.stop_time)) + result['duration'] = str(round(self.stop_time - self.start_time, 2)) + 's' + result['result'] = "Pass" if code == E.OK else "Fail" + result['errorCode'] = code.name + result['failReason'] = self.fail_reason + return result + + def get_start_time(self): + self.fail_reason = [] + self.start_time = time.time() + + def get_stop_time(self): + self.stop_time = time.time() + +class TestCaseCommon(TestCaseBase): + def __init__(self, index, module_name, logger, platform_case_cfg, case_cfg_json): + """ + :param index: test case id + :param module_name: test case name + :param logger: logger + :param platform_case_cfg: json file from "config/platform/--*Platform*--/case_config.json" + """ + if not case_cfg_json: + current_dir = os.path.dirname(__file__) + case_cfg_file = os.path.join(current_dir, "../cases", module_name, "case_config.json") # 需要创建测试case对应config.json + else: + case_cfg_file = case_cfg_json + self.module_name = module_name + self.platform_case_cfg = platform_case_cfg + self.platform_cfg_json = None # platform config content, json format + self.logger = logger + TestCaseBase.__init__(self, index, case_cfg_file) + self.__read_platform_config() + + def __read_platform_config(self): + """ + read platform config content + """ + try: + with open(self.platform_case_cfg, 'r') as f: + content = json.load(f, object_pairs_hook=OrderedDict) + self.platform_cfg_json = content + except IOError: + self.platform_cfg_json = None + + def get_module_name(self): + return self.module_name diff --git a/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_read_tc.py b/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_read_tc.py new file mode 100755 index 00000000000..8f15662f37c --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_read_tc.py @@ -0,0 +1,97 @@ +import re +from function import run_command +from test_case_base import TestCaseCommon +from errcode import E +key_list = ['Product Name', 'Part Number', 'Serial Number', 'Base MAC Address', + 'MAC Addresses', 'Platform Name', 'Vendor Name', 'Manufacturer', + ] + +# tlv_eeprom_read_tc test class +class TLV_EEPROM_READ_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "tlv_eeprom_read_tc" + self.rec = '' + self.code = 0 + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + + def check_eeprom_info(self, console_print=False): + """ + Read EEPROM Information + """ + self.logger.log_info("[CHECK EEPROM INFO]:", console_print) + + cmd = 'show platform syseeprom' + self.code, self.rec = run_command(cmd) + + flag = 0 + miss_list = [] + if self.code != 0: + self.fail_reason.append("get syseeprom info fail.") + result1 = False + else: + self.logger.log_info("Details:", console_print) + self.logger.log_info(self.rec, console_print) + for key in key_list: + if key in str(self.rec): + pass + else: + miss_list.append(key) + flag = 1 + if flag == 1: + self.logger.log_info('Key:{} is Missing'.format(miss_list), console_print) + return False + else: + result1 = True + return result1 + + def check_mac(self, console_print=False): + """ + Check Mac Information + """ + if self.code != 0: + return False + self.logger.log_info('[CHECK EEPROM MAC:]', console_print) + + find_mac = re.findall( + 'Base MAC Address.+([\dabcdef]{2}:[\dabcdef]{2}:[\dabcdef]{2}:[\dabcdef]{2}:[\dabcdef]{2}:[\dabcdef]{2})', + self.rec, re.IGNORECASE) + if not find_mac: + result2 = False + self.fail_reason.append("find mac failed.") + self.logger.log_info('FAIL', console_print) + return result2 + else: + mac_add = find_mac[0] + self.logger.log_info("Mac address: {}".format(mac_add), console_print) + check_mac1 = mac_add.count('0') != 12 and mac_add.upper().count('F') != 12 + if not check_mac1: + self.fail_reason.append("invalid mac") + self.logger.log_info('FAIL', console_print) + check_mac2 = bin(int(mac_add[:2], 16))[-1] != '1' + if not check_mac2: + self.fail_reason.append("multicast mac") + self.logger.log_info('FAIL', console_print) + result2 = True if check_mac1 and check_mac2 else False + self.logger.log_info('PASS', console_print) + + return result2 + + def run_test(self): + pass_cnt = 0 + fail_cnt = 0 + res1 = self.check_eeprom_info(True) + res2 = self.check_mac(True) + if res1: + pass_cnt += 1 + else: + fail_cnt += 1 + + if res2: + pass_cnt += 1 + else: + fail_cnt += 1 + + result = E.OK if res1 and res2 else E.EFAIL + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_write_tc.py b/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_write_tc.py new file mode 100755 index 00000000000..4397d24c56f --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/tlv_eeprom_write_tc.py @@ -0,0 +1,116 @@ +import json +import os +import re +from function import run_command, convert_unicode +from test_case_base import TestCaseCommon +from errcode import E +import time + +key_list = ['Product Name', 'Part Number', 'Serial Number', 'Base MAC Address', + 'MAC Addresses', 'Platform Name', 'Vendor Name', 'Manufacturer'] + +SYSEEPROM_TEST = "../config/common/syseeprom_test.json" +SYSEEPROM_ORIG = "../config/common/syseeprom_orig.json" + + +# TLV_EEPROM_WRITE test class +class TLV_EEPROM_WRITE_TC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "tlv_eeprom_write_tc" + self.rec = '' + self.code = 0 + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + + def parse_syseeprom(self, console_print=False): + cmd = 'show platform syseeprom' + res = run_command(cmd) + + if res[0] != 0: + self.fail_reason.append("get syseeprom info fail.") + return False + else: + eeprom_list = res[1].split("\n") + eeprom_result = {} + for lines in eeprom_list: + if "0x" in lines and "CRC" not in lines: + key = lines.split(" ")[0] + val_find = re.findall("0x.......(.*)", lines) + if val_find: + eeprom_result[key] = val_find[0] + else: + self.logger.log_info("===== Parse tlv-eeprom Failed! =====", console_print) + return False + + return eeprom_result + + + def write_eeprom_info(self, console_print=False): + """ + STORE&SET EEPROM Information + """ + cmd = 'show platform syseeprom' + pass_cnt = 0 + fail_cnt = 0 + current_dir = os.path.dirname(os.path.abspath(__file__)) + self.logger.log_info("[RECORD EEPROM INFO]:", console_print) + + # STORE Orig tlv-eeprom + syseeprom_orig = self.parse_syseeprom() + with open(os.path.join(current_dir, SYSEEPROM_ORIG), 'w') as f: + json.dump(syseeprom_orig, f, indent=4, separators=(',', ': ')) + + + run_command("python syseeprom-write.py {}".format(SYSEEPROM_TEST)) + self.logger.log_info("Time sleep 3 seconds.....", console_print) + time.sleep(3) + + code, rec = run_command(cmd) + self.logger.log_info("------||. test tlv-syseeprom after set .||------", console_print) + self.logger.log_info(rec, console_print) + syseeprom_test_readback = self.parse_syseeprom() + with open(os.path.join(current_dir, SYSEEPROM_TEST), 'r') as f: + syseeprom_test = json.load(f) + + syseeprom_test = convert_unicode(syseeprom_test) + if syseeprom_test_readback != syseeprom_test: + result1 = False + self.logger.log_info("===== Check test tlv-eeprom failed =====", console_print) + else: + result1 = True + self.logger.log_info("===== Set test tlv-eeprom Success =====", console_print) + + run_command("python syseeprom-write.py {}".format(SYSEEPROM_ORIG)) + self.logger.log_info("Time sleep 3 seconds.....", console_print) + time.sleep(3) + + code, rec = run_command(cmd) + self.logger.log_info("------||. orig tlv-syseeprom after set .||------", console_print) + self.logger.log_info(rec, console_print) + syseeprom_orig_readback = self.parse_syseeprom() + if syseeprom_orig_readback != syseeprom_orig: + result2 = False + self.logger.log_info("===== Check orig tlv-eeprom failed =====", console_print) + else: + result2 = True + self.logger.log_info("===== Set orig tlv-eeprom Success =====", console_print) + + if result1: + pass_cnt += 1 + else: + fail_cnt += 1 + + if result2: + pass_cnt += 1 + else: + fail_cnt += 1 + + result = E.OK if result1 and result2 else E.EFAIL + + return pass_cnt, fail_cnt, result + + + def run_test(self, *argv): + pass_cnt, fail_cnt, result = self.write_eeprom_info(True) + return [pass_cnt, fail_cnt, result] + diff --git a/src/sonic-pit/pit-sysdiag/src/traffic_tc.py b/src/sonic-pit/pit-sysdiag/src/traffic_tc.py new file mode 100644 index 00000000000..2f0ef4f8fc9 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/traffic_tc.py @@ -0,0 +1,363 @@ +import sys +import os +import re +import time +import threading +import subprocess +from tabulate import tabulate +from test_case import TestCaseCommon +from errcode import * +from function import * + +CTC_CMD_FORMAT = "ctc_shell -e \"{}\"" + +def run_command(cmd): + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + if re.search("connect error", err): + return 1, err + return proc.returncode, out.rstrip('\n') + + +class TRAFFICTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "traffic_tc" + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + if self.platform_cfg_json and self.platform_cfg_json['asic']: + self.asic = self.platform_cfg_json['asic'] + else: + self.asic = None + if self.asic == "centec": + cmd = "docker cp syncd:/usr/bin/ctc_shell /usr/bin/" + run_command(cmd) + + platform, hwsku = get_platform_and_hwsku() + if len(platform) != 0: + self.platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + else: + self.platform_path = PLATFORM_ROOT_PATH_DOCKER + self.hwsku_path = "/".join([self.platform_path, hwsku]) + self.sdk_path = "/".join([self.platform_path, "sdk"]) + self.port_lanes = [] + self.port_names = [] + + + def load_port_config_ini(self, also_print_console=False): + self.logger.log_info("Get port config ...", also_print_console) + port_config_file = "/".join([self.hwsku_path, "port_config.ini"]) + + try: + f = open(port_config_file, "r") + except: + return IO_ERR + + for line in f: + line.strip() + if re.search("^#", line): + continue + self.port_names.append(line.split()[0]) + self.port_lanes.append(int(line.split()[1])) + + return NO_ERR + + + def port_link_status_check(self, also_print_console=False): + RET = NO_ERR + + self.logger.log_info("[PORT LINK STATUS CHECK]:", also_print_console) + cmd = CTC_CMD_FORMAT.format("show port mac-link") + ret, out = run_command(cmd) + if ret: + self.fail_reason.append("exec cmd fail") + RET |= IO_ERR + else: + lines = out.splitlines() + for line in lines: + line = line.strip() + if re.search(r"0x[0-9A-F]+", line): + port_lane = int(re.findall("0x[0-9A-F]+",line)[0].strip(), 16) + index = self.port_lanes.index(port_lane) + port_link = line.split()[2] + if port_link == 'down': + self.fail_reason.append("{} down".format(self.port_names[index])) + RET |= NORUN_ERR + if RET: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return RET + + + def clear_default_config(self, also_print_console=False): + self.logger.log_info("Clear default config ...", also_print_console) + cmd = "systemctl stop lldp snmp swss syncd" + run_command(cmd) + cmd = "echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6" + run_command(cmd) + cmd = "echo 1 > /proc/sys/net/ipv6/conf/default/disable_ipv6" + run_command(cmd) + cmd = "config vlan del 1000" + run_command(cmd) + return NO_ERR + + + def restore_default_config(self, also_print_console=False): + self.logger.log_info("Restore default config ...", also_print_console) + + ret = NO_ERR + cmd = "systemctl start swss" + run_command(cmd) + return ret + + + def clear_port_packets(self, also_print_console=False): + self.logger.log_info("Clear port counters ...", also_print_console) + ret = NO_ERR + cmd = CTC_CMD_FORMAT.format("clear packet stats") + ret, output = run_command(cmd) + cmd = CTC_CMD_FORMAT.format("clear stats mac-all all") + ret, output = run_command(cmd) + return ret + + + def is_sdk_running(self): + cmd = "ps -ef | grep ctcsdk | grep -v grep" + ret, out = run_command(cmd) + if ret == 0 and out != '': + return True + else: + return False + + + def start_sdk(self, also_print_console=False): + self.logger.log_info("Start sdk ...", also_print_console) + cmd = "chmox +x {}/ctcsdk".format(self.sdk_path) + run_command(cmd) + cmd = "chmox +x {}/ctc_shell".format(self.sdk_path) + run_command(cmd) + cmd = "cp {}/start_up.cfg-snake-traffic {}/start_up.cfg".format(self.sdk_path, self.sdk_path) + run_command(cmd) + cmd = "cd {} && ./ctcsdk &".format(self.sdk_path) + thread = threading.Thread(target=run_command, args=(cmd,)) + thread.start() + + + def stop_sdk(self, also_print_console=False): + self.logger.log_info("Stop sdk ...", also_print_console) + cmd = "ps -ef | grep ctcsdk | grep -v grep | awk '{print $2}' | xargs kill -9 " + run_command(cmd) + + + def start_send_packets(self, count, also_print_console=False): + self.logger.log_info("Start traffic ...", also_print_console) + ret = NO_ERR + cmd = CTC_CMD_FORMAT.format("packet tx mode 0 dest-gport {} bypass pkt-file {}/pkt_file.1 count {}".format(self.port_lanes[0], self.sdk_path, count)) + ret, output = run_command(cmd) + #time.sleep(1) + cmd = CTC_CMD_FORMAT.format("packet tx mode 0 dest-gport {} bypass pkt-file {}/pkt_file.2 count {}".format(self.port_lanes[1], self.sdk_path, count)) + ret, output = run_command(cmd) + #time.sleep(1) + + cmd = CTC_CMD_FORMAT.format("packet tx mode 0 dest-gport {} bypass pkt-file {}/pkt_file.1 count {}".format(self.port_lanes[-1], self.sdk_path, count)) + ret, output = run_command(cmd) + #time.sleep(1) + cmd = CTC_CMD_FORMAT.format("packet tx mode 0 dest-gport {} bypass pkt-file {}/pkt_file.2 count {}".format(self.port_lanes[-2], self.sdk_path, count)) + ret, output = run_command(cmd) + #time.sleep(1) + return ret + + + def stop_send_packets(self, also_print_console=False): + ret = NO_ERR + self.logger.log_info("Stop traffic ...", also_print_console) + + try: + cmd = CTC_CMD_FORMAT.format("vlan remove vlan 10") + ret, out = run_command(cmd) + cmd = CTC_CMD_FORMAT.format("vlan remove vlan 100") + ret, out = run_command(cmd) + if ret: + return status + except Exception as e: + ret |= IO_ERR + self.logger.log_err(str(e)) + + return ret + + + def __get_port_packets(self, port, direc="tx"): + good = 0 + fcs = 0 + + if direc == 'tx': + cmd = CTC_CMD_FORMAT.format("show stats mac-tx port {}".format(port)) + elif direc =='rx': + cmd = CTC_CMD_FORMAT.format("show stats mac-rx port {}".format(port)) + ret, out = run_command(cmd) + lines = out.splitlines() + for line in lines: + line = line.strip() + if line.startswith("good unicast"): + good += int(line.split()[2]) + + if line.startswith("fcs error"): + fcs += int(line.split()[2]) + + if line.startswith("bad"): + fcs += int(line.split()[2]) + + return (good, fcs) + + + def port_counter_check(self, also_print_console=False): + ret = NO_ERR + header = ["Port", "Tx_count", "Tx_err", "Rx_count", "Rx_err"] + counter_tbl = [] + + self.logger.log_info("[PORT TX/RX_COUNTER CHECK]:", also_print_console) + + try: + for port in self.port_lanes: + name = self.port_names[self.port_lanes.index(port)] + tx_good, tx_fcs = self.__get_port_packets(port, "tx") + rx_good, rx_fcs = self.__get_port_packets(port, "rx") + + if tx_fcs != 0 or rx_fcs != 0: + ret |= FCS_ERR + self.fail_reason.append("{} fcs error".format(name)) + + if tx_good == 0 or rx_good == 0: + ret |= NORUN_ERR + self.fail_reason.append("{} no traffic".format(name)) + + counter_tbl.append([name, tx_good, tx_fcs, rx_good, rx_fcs]) + except Exception as e: + ret |= IO_ERR + self.fail_reason.append(str(e)) + + if len(counter_tbl) > 0: + self.logger.log_info(tabulate(counter_tbl, header, tablefmt="simple"), also_print_console) + + if ret: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def __port_rate_valid(self, speed, rate): + ret = NO_ERR + limit = 0.9 + if speed.find("G") > 0: + speed_value = int(speed.strip('G')) + rate_value = int(rate[0]) + rate_unit = rate[1] + + if rate_unit == "Mbps": + standard_rate = speed_value * 1000 * limit + ret = NO_ERR if rate_value >= standard_rate else UNMATCH_ERR + elif rate_unit == "Gbps": + standard_rate = speed_value * limit + ret = NO_ERR if rate_value >= standard_rate else UNMATCH_ERR + else: + ret = UNMATCH_ERR + else: + ret = UNMATCH_ERR + + return ret + + + def port_speed_check(self, also_print_console=False): + ret = NO_ERR + header = ["Port", "Speed", "Tx_rate", "Rx_rate", "Result"] + rate_tbl = [] + + self.logger.log_info("[PORT SPEED CHECK]:", also_print_console) + + try: + cmd = CTC_CMD_FORMAT.format("show port mac-link") + status, out = run_command(cmd) + time.sleep(2) + cmd = CTC_CMD_FORMAT.format("show port mac-link") + status, out = run_command(cmd) + if status: + self.fail_reason.append("exec cmd fail") + ret |= IO_ERR + else: + lines = out.splitlines() + for line in lines: + line = line.strip() + if line.startswith("0x"): + port = int(line.split()[0], 16) + name = self.port_names[self.port_lanes.index(port)] + speed = line.split()[4] + rx_rate = (line.split()[8], line.split()[9]) + tx_rate = (line.split()[10], line.split()[11]) + status = self.__port_rate_valid(speed, rx_rate) + status |= self.__port_rate_valid(speed, tx_rate) + if status: + self.fail_reason.append("{} low speed".format(name)) + result = "Fail" + else: + result = "Pass" + ret |= status + rate_tbl.append([name, speed, tx_rate[0]+tx_rate[1], rx_rate[0]+rx_rate[1], result]) + except Exception as e: + ret |= IO_ERR + self.fail_reason.append(str(e)) + + if len(rate_tbl) > 0: + self.logger.log_info(tabulate(rate_tbl, header, tablefmt="simple"), also_print_console) + if ret: + self.logger.log_info("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + + return ret + + + def run_test(self, *argv): + ret = NO_ERR + + self.stop_sdk() + ret = self.clear_default_config(True) + time.sleep(2) + ret |= self.load_port_config_ini(True) + if ret: + self.logger.log_err("get port config fail!", True) + self.restore_default_config(True) + return ret + + ret = self.start_sdk(True) + time.sleep(30) + if not self.is_sdk_running(): + self.logger.log_err("sdk start failed!", True) + self.restore_default_config(True) + return NORUN_ERR + + ret = self.port_link_status_check(True) + if ret: + self.stop_sdk(True) + self.restore_default_config(True) + return ret + + self.clear_port_packets(True) + time.sleep(1) + self.start_send_packets(1000, True) + + # traffic test time length + time.sleep(60) + + ret = self.port_speed_check(True) + + self.stop_send_packets(True) + + time.sleep(2) + ret |= self.port_counter_check(True) + + self.stop_sdk(True) + self.restore_default_config(True) + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/usb_tc.py b/src/sonic-pit/pit-sysdiag/src/usb_tc.py new file mode 100644 index 00000000000..c23973949fc --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/usb_tc.py @@ -0,0 +1,131 @@ +import os +import subprocess +from test_case import TestCaseCommon +from errcode import * + + +def run_command(cmd): + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + if err: + out += err + return proc.returncode, out.rstrip('\n') + + +class USBTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "usb_tc" # this param specified the case config dirictory + TestCaseCommon.__init__(self, index, MODULE_NAME, logger, platform_cfg_file, case_cfg_file) + + def search_dir_by_name(self, name, dir): + result = [] + try: + files = os.listdir(dir) + for file in files: + if name in file: + result.append(os.path.join(dir, file)) + except Exception as e: + self.logger.log_err(str(e), True) + return result + + def get_usb_location(self): + dir = "/sys/block/" + spect = "sd" + usbpath = [] + result = self.search_dir_by_name(spect, dir) + if len(result) <= 0: + return E.EUSB1000, usbpath + + for item in result: + with open(os.path.join(item, "removable"), 'r') as fd: + value = fd.read() + if value.strip() == "1": # found Udisk + usbpath.append(item) + if not usbpath: # not found Udisk + self.logger.log_err("no usb found") + return E.EUSB1000, usbpath + return E.OK, usbpath + + def get_usb_info(self, path): + size_file = os.path.join(path, "size") + try: + with open(size_file, "r") as f: + value = f.read() + return E.OK, {"dev": os.path.basename(path), "size": round(float(value) * 512 / 1024 / 1024 / 1024, 1)} + except IOError as e: + return E.EIO, str(e) + + def usb_rw_test(self, usb_dev, also_print_console=False): + totalret = E.OK + cmd = "fdisk -l |grep '%s'|grep 'Disk' -v|awk '{print $1}'" % usb_dev + #cmd = "fdisk -l |grep '%s'|grep 'Disk' -v|sort -k4 |tail -n1|awk '{print $1}'" % usb_dev + ret, usb = run_command(cmd) + usb_list = usb.splitlines() + if not usb_list: + usb_list .append("/dev/" + usb_dev) + + for usb_disk in usb_list: + totalret = E.OK + self.logger.log_info("[USB READ/WRITE TEST]:", also_print_console) + + ret, log = run_command("dd if=/dev/urandom of=/tmp/usbtest.txt bs=1M count=10") + ret, log = run_command("mkdir -p /tmp/usb_test && mount %s /tmp/usb_test/" % usb_disk) + if ret: + self.logger.log_err("mount usb failed", also_print_console) + self.fail_reason.append("mount usb failed") + totalret = E.EUSB1001 + continue + + ret, log = run_command("cp -rf /tmp/usbtest.txt /tmp/usb_test/usbtest.txt") + if ret: + self.logger.log_err("copy file failed!", also_print_console) + self.fail_reason.append("copy file failed!") + totalret = E.EUSB1003 + else: + self.logger.log_info("copy file success.", also_print_console) + + ret, log = run_command("diff /tmp/usbtest.txt /tmp/usb_test/usbtest.txt") + if ret: + self.logger.log_err("verify file failed!", also_print_console) + self.fail_reason.append("verify file failed!") + totalret = E.EUSB1002 + else: + self.logger.log_info("verify file success.", also_print_console) + + ret, log = run_command("rm -rf /tmp/usb_test/usbtest.txt") + if ret: + self.logger.log_err("delete file failed!", also_print_console) + self.fail_reason.append("delete file failed!") + totalret = E.EUSB1003 + else: + self.logger.log_info("delete file success.", also_print_console) + + run_command("umount /tmp/usb_test") + run_command("rm -rf /tmp/usb_test") + run_command("rm -f /tmp/usbtest.txt") + + if totalret == E.OK: + break + + if totalret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return totalret + + def run_test(self, *argv): + ret, path_list = self.get_usb_location() + if ret != E.OK: + self.fail_reason.append("usb not found!") + return ret + for path in path_list: + errocode, info = self.get_usb_info(path) + if errocode != E.OK or info["size"] <= 0: + continue + self.logger.log_info("found usb device: {}, size={}GB".format(info["dev"], info["size"]), True) + ret = self.usb_rw_test(info["dev"], True) + if ret == E.OK: + break + + return ret diff --git a/src/sonic-pit/pit-sysdiag/src/usbstress_tc.py b/src/sonic-pit/pit-sysdiag/src/usbstress_tc.py new file mode 100644 index 00000000000..dfe2fa66100 --- /dev/null +++ b/src/sonic-pit/pit-sysdiag/src/usbstress_tc.py @@ -0,0 +1,128 @@ +import os +import pit_util_common +from errcode import E +from pit_util_common import run_command +from test_case import TestCaseCommon + + +class USBSTRESSTC(TestCaseCommon): + def __init__(self, index, logger, platform_cfg_file, case_cfg_file=None): + MODULE_NAME = "usbstress_tc" + TestCaseCommon.__init__( + self, + index, + MODULE_NAME, + logger, + platform_cfg_file, + case_cfg_file) + self.usb_info_dict = None + try: + if self.platform_cfg_json and "usbstress_info" in self.platform_cfg_json.keys(): + self.usb_info_dict = self.platform_cfg_json["usbstress_info"] + except Exception as e: + self.logger.log_err(str(e), True) + + def search_dir_by_name(self, name, dir): + result = [] + try: + files = os.listdir(dir) + for file in files: + if name in file: + result.append(os.path.join(dir, file)) + except Exception as e: + self.logger.append(str(e)) + return result + + def get_usb_location(self): + dir = "/sys/block/" + spect = "sd" + usbpath = [] + result = self.search_dir_by_name(spect, dir) + if len(result) <= 0: + self.fail_reason.append("no usb found") + return E.EUSB1000, usbpath + for item in result: + with open(os.path.join(item, "removable")) as fd: + value = fd.read() + if value.strip() == "1": + usbpath.append(item) + if not usbpath: + self.fail_reason.append("no usb found") + return E.EUSB1000, usbpath + return E.OK, usbpath + + def get_usb_info(self, path): + size_file = os.path.join(path, "size") + try: + with open(size_file) as f: + value = f.read() + return E.OK, { + "dev": os.path.basename(path), "size": round( + float(value) * 512 / 1024 / 1024 / 1024, 1)} + except IOError as e: + return E.EIO, str(e) + + def usb_stress(self, usb_dev, also_print_console=False): + ret = E.EFAIL + cmd = "fdisk -l |grep %s|grep 'Disk' -v|awk '{print $1}'" % usb_dev + fdisk_ret, fdisk_log = run_command(cmd) + usb_list = fdisk_log.splitlines() + if not usb_list: + usb_list.append("/dev/" + usb_dev) + for usb_disk in usb_list: + self.logger.log_info("[USB STRESS TEST]:", also_print_console) + ret, log = run_command("mount -v | grep /mnt/usb") + if log != '': + run_command("umount /mnt/usb/") + run_command("rm -rf /mnt/usb/") + mount_ret, mount_log = run_command( + "mkdir -p /mnt/usb && mount {} /mnt/usb/".format(usb_disk)) + if mount_ret: + self.fail_reason.append("mount usb failed") + self.logger.log_err("FAIL!", also_print_console) + ret = E.EUSB1001 + run_command("rm -rf /mnt/usb/") + return ret + else: + test_stress_cmd = "dd if={} of={}4G.txt bs=4k count=1048570".format( + usb_disk, "/mnt/usb/") + waiting = pit_util_common.waiting(self.logger,"usbstress testing...") + stress_ret, stress_log = run_command(test_stress_cmd) + waiting.stop("usbstress test stop!") + if stress_ret: + self.fail_reason.append("stress test failed!") + ret = E.EUSB1006 + else: + self.logger.log_info( + "stress test success.", also_print_console) + ret = E.OK + run_command("rm -rf /mnt/usb/4G.txt") + run_command("umount /mnt/usb/") + run_command("rm -rf /mnt/usb/") + + if ret != E.OK: + self.logger.log_err("FAIL!", also_print_console) + else: + self.logger.log_info("PASS.", also_print_console) + return ret + + def run_test(self, *argv): + for index in range(1, self.usb_info_dict['stress_test_time'] + 1): + ret, path_list = self.get_usb_location() + if ret != E.OK: + self.fail_reason.append("usb not found!") + return ret + for path in path_list: + errocode, info = self.get_usb_info(path) + if errocode != E.OK or info["size"] <= 0: + self.fail_reason.append("USB device cannot be used") + return errocode + self.logger.log_info( + "found usb device: {}, size={}GB".format( + info["dev"], info["size"]), True) + ret = self.usb_stress(info["dev"], True) + if ret != E.OK: + self.logger.log_err( + "usb stress {} failed!".format(index), True) + return ret + return ret diff --git a/src/sonic-pit/setup.py b/src/sonic-pit/setup.py new file mode 100644 index 00000000000..b5e9f380ed4 --- /dev/null +++ b/src/sonic-pit/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup, find_packages + +dependencies = [ +] + +setup( + name='sonic-pit', + version='1.0', + description='Platform Integration Test', + license='Apache 2.0', + author='S3IP PIT dev-team', + author_email='guizhao.lh@alibaba-inc.com', + maintainer='S3IP PIT dev-team', + maintainer_email='guizhao.lh@alibaba-inc.com', + install_requires=dependencies, + packages=find_packages(), + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: No Input/Output (Daemon)', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 2.7', + 'Topic :: System :: Utilities', + ], + keywords='SONiC Platform Integration Test' +)