← Back to team overview

opencompute-developers team mailing list archive

[Merge] lp:~nelson-chu/opencompute/add-ocp-certified-jobs into lp:opencompute/checkbox

 

Nelson Chu has proposed merging lp:~nelson-chu/opencompute/add-ocp-certified-jobs into lp:opencompute/checkbox.

Requested reviews:
  Open Compute Developers (opencompute-developers)

For more details, see:
https://code.launchpad.net/~nelson-chu/opencompute/add-ocp-certified-jobs/+merge/196476
-- 
https://code.launchpad.net/~nelson-chu/opencompute/add-ocp-certified-jobs/+merge/196476
Your team Open Compute Developers is requested to review the proposed merge of lp:~nelson-chu/opencompute/add-ocp-certified-jobs into lp:opencompute/checkbox.
=== added file 'data/whitelists/opencompute-certify-local.whitelist'
--- data/whitelists/opencompute-certify-local.whitelist	1970-01-01 00:00:00 +0000
+++ data/whitelists/opencompute-certify-local.whitelist	2013-11-25 07:01:57 +0000
@@ -0,0 +1,100 @@
+# Resource Jobs
+block_device
+cdimage
+cpuinfo
+device
+dmi
+dpkg
+efi
+environment
+gconf
+lsb
+meminfo
+module
+optical_drive
+package
+sleep
+uname
+#Info attachment jobs
+__info__
+cpuinfo_attachment
+dmesg_attachment
+dmi_attachment
+dmidecode_attachment
+efi_attachment
+lspci_attachment
+lshw_attachment
+mcelog_attachment
+meminfo_attachment
+modprobe_attachment
+modules_attachment
+sysctl_attachment
+sysfs_attachment
+udev_attachment
+lsmod_attachment
+acpi_sleep_attachment
+info/hdparm
+info/hdparm_.*.txt
+installer_debug.gz
+info/disk_partitions
+# Actual test cases
+__TC-001-0001-CPU_Memory__
+TC-001-0001-001 CPU information
+TC-001-0001-002 CPU topology
+TC-001-0001-003 Single Processor Mode
+TC-001-0001-004 Intel QPI
+TC-001-0001-005 Memory Information
+
+__TC-001-0002-Platform_Controller_Hub__
+TC-001-0002-001 SATA port
+TC-001-0002-002 USB 2.0
+TC-001-0002-003 Mini-SAS port
+
+__TC-001-0003-BIOS_setup_menu__
+TC-001-0003-001 BIOS setup menu
+
+__TC-001-0004-BIOS_remote_update__
+TC-001-0004-001 Scenario 1
+TC-001-0004-002 Scenario 2
+TC-001-0004-003 Scenario 3
+
+__TC-001-0005-BIOS_event_log__
+TC-001-0005-001 BIOS event log
+
+__TC-002-0001-Initial_machine_provisioning__
+TC-002-0001-001 Inventory information
+TC-002-0001-002 IP lease information
+TC-002-0001-003 Monitor attachment
+
+__TC-002-0004-Boot_order_PXE__
+TC-002-0004-001 Boot Order / PXE
+
+__TC-002-0005-Remote_serial_console__
+TC-002-0005-001 Enable SOL
+TC-002-0005-002 SOL operation
+
+__TC-003-0001-Hardware_information__
+TC-003-0001-001 CPU information
+TC-003-0001-002 Memory information
+TC-003-0001-003 Disk information
+TC-003-0001-004 BIOS information
+TC-003-0001-005 ME information
+TC-003-0001-006 NIC information
+TC-003-0001-007 RAID information
+TC-003-0001-008 HBA information
+
+___TC-003-0002-Idle_test__
+TC-003-0002-001 12 hours idle
+
+__TC-003-0003-Network_performance__
+TC-003-0003-001 Network performance
+
+__TC-003-0004-Disk_performance__
+TC-003-0004-001 Disk performance
+
+__TC-004-0001-Knox_testing__
+TC-004-0001-001-RAID_card_information
+TC-004-0002-001-Get_all_HDD
+TC-004-0003-001-Build_raid_6
+TC-004-0004-001-Read/Write_file
+TC-004-0005-001-Rebuild_Raid

=== added file 'jobs/TC-001-0001-CPU_Memory.txt'
--- jobs/TC-001-0001-CPU_Memory.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-001-0001-CPU_Memory.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,25 @@
+plugin: shell
+name: TC-001-0001-001 CPU information
+requires: package.name == 'lshw'
+command: cpu_info
+description:
+ 1. Gathering CPU information by using lshw command.
+ 2. Output CPU model and L1, L2, L3 cache size.
+ 3. Pass criteria: CPU model should belong to Intel Xeon processor E5-2600 product family and Last Level Cache (LLC) should be larger than 20MB
+
+plugin: shell
+name: TC-001-0001-002 CPU topology
+command: processor_topology
+description:
+ 1. Gathering CPU information by using lscpu command.
+ 2. Output the total number of CPU, the number of Thread per core, the number of Core per Socket, and the number of Socket.
+ 3. Pass criteria: It should be 8 cores per CPU and 2 Threads per core (that is, up to 16 threads with Hyper-Threading Technology per CPU).
+
+plugin: shell
+name: TC-001-0001-003 Memory Information
+requires: package.name == 'lshw'
+command: memory_info
+description:
+ 1. Gathering memory information by using lshw command.
+ 2. Output memory module, vendor, size and slot.
+ 3. Pass criteria: 2 DDR3 slots per channel per processor (total of 16 DIMMs on the motherboard).

=== added file 'jobs/TC-001-0002-Platform_Controller_Hub.txt'
--- jobs/TC-001-0002-Platform_Controller_Hub.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-001-0002-Platform_Controller_Hub.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,11 @@
+plugin: shell
+name: TC-001-0002-001 SATA port
+command: echo "manual"; exit 1
+description:
+ individual SATA 6Gps ports from SATA port 0/1.
+
+plugin: shell
+name: TC-001-0002-002 USB 2.0
+command: echo "manual"; exit 1
+description:
+ USB 2.0 ports (2 designed in this board: one front connector, one vertical onboard)

=== added file 'jobs/TC-001-0003-BIOS_setup_menu.txt'
--- jobs/TC-001-0003-BIOS_setup_menu.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-001-0003-BIOS_setup_menu.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,5 @@
+plugin: shell
+name: TC-001-0003-001 BIOS setup menu
+command: echo "manual"; exit 1
+description:
+ BIOS setup menu.

=== added file 'jobs/TC-001-0004-BIOS_remote_update.txt'
--- jobs/TC-001-0004-BIOS_remote_update.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-001-0004-BIOS_remote_update.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,17 @@
+plugin: shell
+name: TC-001-0004-001 Scenario 1
+command: echo "Scenario 1: Sample/Audit BIOS settings."; echo "Not implemented yet"; exit 1
+description:
+ Scenario 1: Sample/Audit BIOS settings.
+
+plugin: shell
+name: TC-001-0004-002 Scenario 2
+command: echo "Scenario 2: Update BIOS with pre-configured set of BIOS settings."; echo "Not implemented yet";exit 1
+description:
+ Scenario 2: Update BIOS with pre-configured set of BIOS settings.
+
+plugin: shell
+name: TC-001-0004-003 Scenario 3
+command: echo "Scenario 3: BIOS/firmware update with a new revision."; echo "Not implemented yet"; exit 1
+description:
+ Scenario 1: BIOS/firmware update with a new revision.

=== added file 'jobs/TC-001-0005-BIOS_event_log.txt'
--- jobs/TC-001-0005-BIOS_event_log.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-001-0005-BIOS_event_log.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,5 @@
+plugin: shell
+name: TC-001-0005-001 BIOS event log
+command: echo "manual"; exit 1
+description:
+ BIOS event log. Logged errors and error threshold setting.

=== added file 'jobs/TC-002-0001-Initial_machine_provisioning.txt'
--- jobs/TC-002-0001-Initial_machine_provisioning.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-002-0001-Initial_machine_provisioning.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,17 @@
+plugin: shell
+name: TC-002-0001-001 Inventory information
+command: echo "MAC address and/or other inventory information feeds/bar codes from the machine's manufacturer."; echo "Not implemented yet"; exit 1
+description:
+ MAC address and/or other inventory information feeds/bar codes from the machine's manufacturer.
+
+plugin: shell
+name: TC-002-0001-002 IP lease information
+command: echo "Searching through IP lease information to find machines that have gotten a DHCP leases for their management interfaces."; echo "Not implemented yet"; exit 1
+description:
+ Searching through IP lease information to find machines that have gotten a DHCP leases for their management interfaces.
+
+plugin: shell
+name: TC-002-0001-003 Monitor attachment
+command: echo "manual"; exit 1
+description:
+ Attach KVM to setup machine.

=== added file 'jobs/TC-002-0004-Boot_order_PXE.txt'
--- jobs/TC-002-0004-Boot_order_PXE.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-002-0004-Boot_order_PXE.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,5 @@
+plugin: shell
+name: TC-002-0004-001 Boot Order / PXE
+command: echo "Not implemented yet"; exit 1
+description:
+ Boot form PXE.

=== added file 'jobs/TC-002-0005-Remote_serial_console.txt'
--- jobs/TC-002-0005-Remote_serial_console.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-002-0005-Remote_serial_console.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,14 @@
+plugin: shell
+name: TC-002-0005-001 Enable SOL
+command: echo "manual"; exit 1
+description:
+ SOL BIOS setup.
+
+plugin: shell
+name: TC-002-0005-002 SOL operation
+command: echo "manual"; exit 1
+description:
+ 1. Remote control the SUT by using IPMI SOL.
+ 2. SOL that will allow to operate the SUT on: pre-boot process, BIOS setup, boot process, access to the installed OS
+ 3. Pass criteria: Successfully operate the SUT in the scenarios listed above.
+

=== added file 'jobs/TC-003-0001-Hardware_information.txt'
--- jobs/TC-003-0001-Hardware_information.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-003-0001-Hardware_information.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,54 @@
+plugin: shell
+name: TC-003-0001-001 CPU information
+command: echo "refer to TC-001-0001-001"
+description:
+ To verify CPU information with specification. Show CPU type and speed.
+
+plugin: shell
+name: TC-003-0001-002 Memory information
+command: echo "refer to TC-001-0001-003"
+description:
+ To verify memory information with specification. Show memory vendor, location and size.
+
+plugin: shell
+name: TC-003-0001-003 Disk information
+requires: package.name == 'lshw'
+command: disk_info
+description:
+ 1. Gathering disk information by using lshw command.
+ 2. Output Disk type, vendor, product, capacity.
+ 3. Pass criteria: If catch the information then pass.
+
+plugin: shell
+name: TC-003-0001-004 BIOS information
+requires: package.name == 'lshw'
+command: bios_info
+description:
+ 1. Gathering BIOS information by using lshw command.
+ 2. Output BIOS vendor, version and release date.
+ 3. Pass criteria: If catch the information then pass.
+
+plugin: shell
+name: TC-003-0001-005 ME information
+command: echo "Show Intel ME information."; echo "Refer to DCMI job."
+description:
+ Show Intel ME information.
+
+plugin: shell
+name: TC-003-0001-006 NIC information
+command: network_device_info
+description:
+ 1. Gathering NIC information by using udevadm command.
+ 2. Output Interface, product, vendor, driver, device path.
+ 3. Pass criteria: If catch the information then pass.
+
+plugin: shell
+name: TC-003-0001-007 RAID card information
+requires:
+ package.name == 'megacli'
+ package.name == 'megactl'
+command: raid_info
+description:
+ 1. Gathering LSI RAID card information by using megasasctl command.
+ 2. Show adapter, product name, memory, BBU, serial no.
+ 3. Pass criteria: If catch the information then pass.

=== added file 'jobs/TC-003-0002-Idle_test.txt'
--- jobs/TC-003-0002-Idle_test.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-003-0002-Idle_test.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,5 @@
+plugin: shell
+name: TC-003-0002-001 12 hours idle
+command: echo "Leave server sitting idle for 12 hours. Verify after 12 hours that the system is still stable."; echo "Not implemented yet"; exit 1
+description:
+ Leave server sitting idle for 12 hours. Verify after 12 hours that the system is still stable.

=== added file 'jobs/TC-003-0003-Network_performance.txt'
--- jobs/TC-003-0003-Network_performance.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-003-0003-Network_performance.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,5 @@
+plugin: shell
+name: TC-003-0003-001 Network performance
+command: echo "Tx/Rx Gbps for 10G NIC spec. >9.5 Gbps"; echo "Not implemented yet"; exit 1
+description:
+ Tx/Rx Gbps for 10G NIC spec. > 9.5 Gbps

=== added file 'jobs/TC-003-0004-Disk_performance.txt'
--- jobs/TC-003-0004-Disk_performance.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-003-0004-Disk_performance.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,6 @@
+plugin: shell
+name: TC-003-0004-001 Disk performance
+command: echo "SMART threshold, grown defect list glist = reallocated sectors = 200 for 4TB, glist = 100 for 2TB for all vendors.
+SMART HDD temp = 60C."; echo "Not implemented yet"; exit 1
+description:
+ Test disk performance.

=== added file 'jobs/TC-004-0001-Knox_testing.txt'
--- jobs/TC-004-0001-Knox_testing.txt	1970-01-01 00:00:00 +0000
+++ jobs/TC-004-0001-Knox_testing.txt	2013-11-25 07:01:57 +0000
@@ -0,0 +1,45 @@
+plugin: shell
+name: TC-004-0001-001-RAID_card_information
+command: echo "refer to TC-003-0001-007"
+description:
+ RAID card information
+
+plugin: shell
+name: TC-004-0002-001-Get_all_HDD
+requires:
+ package.name == 'megacli'
+ package.name == 'megactl'
+depends: TC-004-0001-001 RAID card information
+command: raid_hdd_info
+description:
+ Get knox all HDD.
+
+plugin: shell
+name: TC-004-0003-001-Build_raid_6
+requires:
+ package.name == 'megacli'
+ package.name == 'megactl'
+depends: TC-004-0002-001-Get_all_HDD
+command: create_raid6
+description:
+ Build Raid 6
+
+plugin: shell
+name: TC-004-0004-001-Read/Write_file
+depends: TC-004-0003-001-Build_raid_6
+description: Test RAID Read/Write file
+user: root
+command:
+ exit 1
+
+plugin: shell
+name: TC-004-0005-001-Rebuild_Raid
+requires:
+ package.name == 'megacli'
+ package.name == 'megactl'
+depends: TC-004-0004-001-Read/Write_file
+environ: CHECKBOX_DATA
+user: root
+command: rebulid_raid -l $CHECKBOX_DATA/rebulid_raid.log
+description:
+ Re-build Raid

=== added file 'scripts/bios_info'
--- scripts/bios_info	1970-01-01 00:00:00 +0000
+++ scripts/bios_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+bios_info
+  Gathering BIOS information by using lshw command.
+  And output BIOS vendor, version and release date.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import os
+import sys
+import xml.etree.ElementTree as ET
+from subprocess import check_output
+
+
+def main():
+    attribute = ['vendor', 'version', 'date']
+    command = 'lshw -xml'
+    with open(os.devnull, "w") as NULL:
+       hwinfo_xml = check_output(command, stderr=NULL, shell=True)
+    root = ET.fromstring(hwinfo_xml)
+
+    # Parser lshw XML for gather BIOS information.
+    bios_info = root.findall(".//node[@id='firmware']")
+
+    if not bios_info:
+        print("Can not parser any BIOS information.")
+        return 10
+
+    for bios in bios_info:
+        for attr in attribute:
+            if bios.find(attr) is None:
+                print(("Can not found BIOS %s") %attr)
+                return 20
+
+        for attr in attribute:
+            if attr == 'date':
+                print(('release date=%s.') %(bios.find(attr).text)),
+                continue
+            print(('%s=%s,') %(attr, bios.find(attr).text)),
+        print()
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/cpu_info'
--- scripts/cpu_info	1970-01-01 00:00:00 +0000
+++ scripts/cpu_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications 
+Industrial Technology Research Institute
+
+cpu_info
+  Gathering CPU information by using lshw command.
+  And output CPU model and L1, L2, L3 cache size.
+  L3 Cache should be larger than 20MB.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import os
+import re
+import sys
+import xml.etree.ElementTree as ET
+from subprocess import check_output
+
+def main():
+    command = 'lshw -xml'
+    with open(os.devnull, "w") as NULL:
+       hwinfo_xml = check_output(command, stderr=NULL, shell=True)
+    root = ET.fromstring(hwinfo_xml)
+
+    # Parser lshw XML for gather processor information.
+    processor = root.findall(".//product/..[@class='processor']")
+
+    if not processor:
+        print("Can not parser any processor information.")
+        return 10
+
+    for cpu in processor:
+        if cpu.find('product') is None:
+            print("Can not found processor product.")
+            return 20
+        print(cpu.find('product').text)
+
+        cache_list = cpu.findall(".//size/..[@class='memory']")
+        if not cache_list:
+            print("Can not found any CPU cache.")
+            return 30
+
+        for cache in cache_list:
+            if cache.find('size') is None or cache.find('slot') is None:
+                print("Can not access Last Level Cache (LLC).")
+                return 40
+
+            cache_size = int(cache.find('size').text) / 1024
+            if cache_size > 1024:
+                cache_size = cache_size / 1024
+                print(('%s %d MB') %(cache.find('slot').text, cache_size))
+                if re.search('L3', cache.find('slot').text):
+                    if cache_size < 20:
+                        print('L3 cache size less than 20MB.')
+                        return 50
+            else:
+                print(('%s %d KB') %(cache.find('slot').text, cache_size))
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/create_raid6'
--- scripts/create_raid6	1970-01-01 00:00:00 +0000
+++ scripts/create_raid6	2013-11-25 07:01:57 +0000
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+create_raid6
+  Using megacli command build RAID 6 on knox.
+
+  Warning: Make sure there is no RAID configuration in LSI RAID card 
+           before this program is executed.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import os
+import re
+import sys
+from subprocess import check_output, check_call
+
+
+def get_adapter():
+    """
+    Gather all adapter number if there are multiple RAID card.
+    """
+
+    command = 'megacli -CfgDsply -Aall|grep Adapter'
+    adapter_list = []
+
+    adapter_info = check_output(command, shell=True)
+    adapter_info = adapter_info.decode('utf-8')
+
+    for adapter in adapter_info.strip().split('\n'):
+        adapter_list.append(adapter.strip().split(' ')[-1])
+    return adapter_list
+
+
+def get_all_disk(adapter):
+    """
+    Gather all disks Enclosure and Slot number, and make a 
+    Enclosure:Slot pair list.
+    """
+
+    command = 'megacli -PDList -A{0}'.format(adapter) \
+        + '|grep -E "Enclosure Device ID|Slot Number"'
+
+    disk_list = []
+    disk_info = check_output(command, shell=True)
+    disk_info = disk_info.decode('utf-8')
+
+    for line in disk_info.strip().split('\n'):
+        if line.startswith('Enclosure'):
+            match = re.search(r'\d+', line)
+            disk_list.append(match.group(0))
+        if line.startswith('Slot'):
+            match = re.search(r'\d+', line)
+            enclosure = disk_list.pop()
+            E_S = '%s:%s' %(enclosure, match.group(0))
+            disk_list.append(E_S)
+    return disk_list
+
+
+def build_raid(adapter, disk_list):
+    """
+    Use all disk creatd RAID 6 and set last disk as hot spare.
+    """
+
+    # use the last disk as hot spare
+    spare = disk_list.pop()
+
+    disk = ','.join(disk_list)
+    command = 'megacli -CfgLDadd -r6 [{0}] WB Direct -Hsp[{1}] -a{2}'.format(
+        disk, spare, adapter)
+
+    check_call(command, shell=True)
+
+
+def main():
+    knox_dict = {}
+
+    # get RAID card adapter number
+    try:
+        adapter_list = get_adapter()
+    except Exception as e:
+        print('Error: %s' %e)
+        return 10
+
+    # get all disk in RAID card
+    try:
+        for adapter in adapter_list:
+            knox_dict[adapter] = get_all_disk(adapter)
+    except Exception as e:
+        print('Error: %s' %e)
+        return 20
+
+    # use all HDD build RAID
+    try:
+        for adapter, disk_list in knox_dict.items():
+            build_raid(adapter, disk_list)
+    except Exception as e:
+        print('Error: Knox build raid 6 failed.')
+        return 30
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/disk_info'
--- scripts/disk_info	1970-01-01 00:00:00 +0000
+++ scripts/disk_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+disk_info
+  Gathering disk information by using lshw command.
+  And output disk type, vendor, product, capacity.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import sys
+import xml.etree.ElementTree as ET
+from subprocess import Popen, PIPE
+
+def main():
+    attribute = ['description', 'vendor', 'product', 'size']
+    command = 'lshw -xml'
+    hwinfo_xml = Popen(command, stdout=PIPE, stderr=PIPE,
+                       shell=True).communicate()[0]
+    root = ET.fromstring(hwinfo_xml)
+
+    # Parser lshw XML for gather disk information.
+    disk_list = root.findall(".//node[@class='disk']")
+
+    if not disk_list:
+        print("Can not parser any disk information.")
+        return 10
+
+    for disk in disk_list:
+        for attr in attribute:
+            if disk.find(attr) is None:
+                print(("Can not found disk %s") %attr)
+                return 20
+
+        disk_size = int(disk.find('size').text) / (1000**3)
+        for attr in attribute:
+            if attr == 'description':
+                print(('Type=%s,') %disk.find(attr).text),
+                continue
+            elif attr == 'size':
+                print(('%s=%dGB.') %(attr, disk_size)),
+                continue
+            else:
+                print(('%s=%s,') %(attr, disk.find(attr).text)),
+        print()
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/memory_info'
--- scripts/memory_info	1970-01-01 00:00:00 +0000
+++ scripts/memory_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+memory_info
+  Gathering memory information by using lshw command.
+  And output memory module, vendor, size and slot.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import sys
+import xml.etree.ElementTree as ET
+from subprocess import Popen, PIPE
+
+def main():
+    attribute = ['description', 'vendor', 'slot', 'size']
+    command = 'lshw -xml'
+    hwinfo_xml = Popen(command, stdout=PIPE, stderr=PIPE,
+                       shell=True).communicate()[0]
+    root = ET.fromstring(hwinfo_xml)
+
+    # Parser lshw XML for gather memory information.
+    memory_list = root.findall(".//clock/..[@class='memory']")
+
+    if not memory_list:
+        print("Can not parser any memory information.")
+        return 10
+
+    for dimm in memory_list:
+        for attr in attribute:
+            if dimm.find(attr) is None:
+                print(("Can not found memory %s") %attr)
+                return 20
+
+        memory_size = int(dimm.find('size').text) / (1024**3)
+        for attr in attribute:
+            if attr == 'description':
+                print(('%s,') %(dimm.find(attr).text)),
+                continue
+            elif attr == 'size':
+                print(('%s=%dGB.') %(attr, memory_size)),
+                continue
+            print(('%s=%s,') %(attr, dimm.find(attr).text)),
+        print()
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/processor_topology'
--- scripts/processor_topology	1970-01-01 00:00:00 +0000
+++ scripts/processor_topology	2013-11-25 07:01:57 +0000
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+'''
+cpu_topology
+Written by Jeffrey Lane <jeffrey.lane@xxxxxxxxxxxxx>
+'''
+import sys
+import os
+from subprocess import check_output
+
+class proc_cpuinfo():
+    '''
+    Class to get and handle information from /proc/cpuinfo
+    Creates a dictionary of data gleaned from that file.
+    '''
+    def __init__(self):
+        self.cpuinfo = {}
+        cpu_fh = open('/proc/cpuinfo', 'r')
+        try:
+            temp = cpu_fh.readlines()
+        finally:
+            cpu_fh.close()
+
+        for i in temp:
+            if i.startswith('processor'):
+                key = 'cpu' + (i.split(':')[1].strip())
+                self.cpuinfo[key] = {'core_id':'', 'physical_package_id':''}
+            elif i.startswith('core id'):
+                self.cpuinfo[key].update({'core_id': i.split(':')[1].strip()})
+            elif i.startswith('physical id'):
+                self.cpuinfo[key].update({'physical_package_id':
+                                          i.split(':')[1].strip()})
+            else:
+                continue
+
+
+class sysfs_cpu():
+    '''
+    Class to get and handle information from sysfs as relates to CPU topology
+    Creates an informational class to present information on various CPUs
+    '''
+
+    def __init__(self, proc):
+        self.syscpu = {}
+        self.path = '/sys/devices/system/cpu/' + proc + '/topology'
+        items = ['core_id', 'physical_package_id']
+        for i in items:
+            syscpu_fh = open(os.path.join(self.path, i), 'r')
+            try:
+                self.syscpu[i] = syscpu_fh.readline().strip()
+            finally:
+                syscpu_fh.close()
+
+
+def compare(proc_cpu, sys_cpu):
+    cpu_map = {}
+    '''
+    If there is only 1 CPU the test don't look for core_id
+    and physical_package_id because those information are absent in
+    /proc/cpuinfo on singlecore system
+    '''
+    for key in proc_cpu.keys():
+        if 'cpu1' not in proc_cpu:
+            cpu_map[key] = True
+        else:
+            for subkey in proc_cpu[key].keys():
+                if proc_cpu[key][subkey] == sys_cpu[key][subkey]:
+                    cpu_map[key] = True
+                else:
+                    cpu_map[key] = False
+    return cpu_map
+
+
+def main():
+    cpuinfo = proc_cpuinfo()
+    sys_cpu = {}
+    keys = cpuinfo.cpuinfo.keys()
+    for k in keys:
+        sys_cpu[k] = sysfs_cpu(k).syscpu
+    cpu_map = compare(cpuinfo.cpuinfo, sys_cpu)
+    if False in cpu_map.values() or len(cpu_map) < 1:
+        print("FAIL: CPU Topology is incorrect", file=sys.stderr)
+        print("-" * 52, file=sys.stderr)
+        print("{0}{1}".format("/proc/cpuinfo".center(30), "sysfs".center(25)),
+                file=sys.stderr)
+        print("{0}{1}{2}{3}{1}{2}".format(
+                "CPU".center(6),
+                "Physical ID".center(13),
+                "Core ID".center(9),
+                "|".center(3)), file=sys.stderr)
+        for key in sorted(sys_cpu.keys()):
+            print("{0}{1}{2}{3}{4}{5}".format(
+                  key.center(6),
+                  cpuinfo.cpuinfo[key]['physical_package_id'].center(13),
+                  cpuinfo.cpuinfo[key]['core_id'].center(9),
+                  "|".center(3),
+                  sys_cpu[key]['physical_package_id'].center(13),
+                  sys_cpu[key]['core_id'].center(9)), file=sys.stderr)
+        return 1
+    else:
+        # Output the total number of CPU, the number of Thread per core, 
+        # the number of Core per Socket, and the number of Socket.
+        # Revised by Nelson Chu <nelson.chu@xxxxxxxxxxx>
+        command = 'lscpu'
+
+        with open(os.devnull, "w") as NULL:
+            cpu_info = check_output(command, stderr=NULL, shell=True)
+
+        cpu_info = cpu_info.decode()
+
+        for cpu in cpu_info.split('\n'):
+            if cpu.startswith("CPU(s)"):
+                print(cpu + ',')
+            if cpu.startswith("Thread(s) per core"):
+                print(cpu + ',')
+            if cpu.startswith("Core(s) per socket"):
+                print(cpu + ',')
+            if cpu.startswith("Socket(s)"):
+                print(cpu + '.')
+
+        return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/raid_hdd_info'
--- scripts/raid_hdd_info	1970-01-01 00:00:00 +0000
+++ scripts/raid_hdd_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+raid_hdd_info
+  Get all knox HDD information by using megasasctl command.
+  Show total HDD number in knox and check these disks status is ready or not.
+  Pass criteria: All knox disks information and status are correct.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import sys
+from subprocess import Popen, PIPE
+
+def main():
+    command = 'megasasctl |grep -E "a[0-9]+e[0-9]+s[0-9]+ +[0-9]+GiB"'
+    hdd_info = Popen(command, stdout=PIPE, stderr=PIPE,
+                     shell=True).communicate()[0]
+    hdd_info = hdd_info.decode('utf-8')
+    disk_number = 0
+    fail = 0
+
+    # Check is there has any disk.
+    if not hdd_info:
+        print('There is no disk in knox.')
+        return 10
+
+    for disk in hdd_info.strip().split('\n'):
+        disk_number = disk_number+1
+        # Check disk status is ready or not
+        if disk.strip().split(' ')[-1] != 'ready':
+            fail = fail+1
+
+    print(('Total %d HDDs in knox.') %disk_number)
+
+    if fail:
+        print(('There are %d disk status not in ready!') %fail)
+        return 20
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/raid_info'
--- scripts/raid_info	1970-01-01 00:00:00 +0000
+++ scripts/raid_info	2013-11-25 07:01:57 +0000
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+# Industrial Technology Research Institute
+#
+# raid_info
+#   Gathering LSI RAID card information by using megasasctl command.
+#   Show adapter, product name, memory, BBU, serial no.
+#
+# Authors
+#   Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+check_return_code() {
+    if [ "${1}" -ne "0" ]; then
+        echo "ERROR: ${2}" >&2
+        exit ${1}
+    fi
+}
+
+show=`megacli -CfgDsply -Aall|grep -E \
+    'Adapter:|Product Name:|Memory:|BBU:|Serial No:'`
+check_return_code $? "There is no LSI MegaRAID SAS cards found."
+
+n=0
+echo "$show" | {
+    while IFS= read -r line; do 
+	echo $line"."
+	n=$(($n + 1))
+    done
+
+    if [ $n -ne "5" ]; then
+	check_return_code 10 "LSI RAID attributes are not correct."
+    fi
+
+    exit 0
+}

=== added file 'scripts/read_write_file'
--- scripts/read_write_file	1970-01-01 00:00:00 +0000
+++ scripts/read_write_file	2013-11-25 07:01:57 +0000
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+read_write_file
+ Create one partition and format on device then test disk I/O.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import os
+import re
+import sys
+import logging
+from subprocess import check_output, check_call, STDOUT
+from argparse import ArgumentParser, RawTextHelpFormatter
+
+log_formatter = '%(asctime)s [%(levelname)-4s] %(funcName)s: %(message)s'
+logging.basicConfig(level=logging.DEBUG,
+                    format=log_formatter,
+                    datefmt='%Y-%m-%d %H:%M:%S')
+
+
+def read_file_test(mount_point, file_size):
+    command = "dd if={0}/test_file of=/dev/null ".format(mount_point) \
+        + "bs=1M count={0} iflag=direct".format(file_size)
+
+    output = check_output(command, stderr=STDOUT, shell=True)
+    output = output.decode("utf-8")
+
+    match = re.search(r'\((.*)\)\D*(\d+\.\d+)\D*(\d+.*)', output)
+    if not match:
+        logging.error('Does not match patterns of read file test.')
+        return False
+
+    logging.info('Read file successful: %s in %s seconds, speed= %s.'
+                %match.group(1,2,3))
+    return True
+
+
+def write_file_test(mount_point, file_size):
+    command = "dd if=/dev/zero of={0}/test_file ".format(mount_point) \
+        + "bs=1M count={0} oflag=direct".format(file_size)
+
+    output = check_output(command, stderr=STDOUT, shell=True)
+    output = output.decode("utf-8")
+
+    match = re.search(r'\((.*)\)\D*(\d+\.\d+)\D*(\d+.*)', output)
+    if not match:
+        logging.error('Does not match patterns of write file test.')
+        return False
+
+    logging.info('Write file successful: %s in %s seconds, speed= %s.'
+                %match.group(1,2,3))
+    return True
+
+
+def umount_filesystem(mount_point):
+    umount_cmd = "umount {0}".format(mount_point)
+    with open(os.devnull, "w") as NULL:
+        check_call(umount_cmd, stdout=NULL, stderr=NULL, shell=True)
+    logging.debug('Umount %s directory successful.' %mount_point)
+    
+
+def mount_filesystem(device):
+    mount_point = '/mnt/' + os.path.basename(device)
+    if not os.path.exists(mount_point):
+        os.mkdir(mount_point)
+        logging.debug('Create %s directory successful!' %mount_point)
+    else:
+        if os.path.ismount(mount_point):
+            logging.debug('The %s directory is already mounted.' %mount_point)
+            umount_filesystem(mount_point)
+    
+    mount_cmd = "mount {0} {1}".format(device, mount_point)
+    with open(os.devnull, "w") as NULL:
+        check_call(mount_cmd, stdout=NULL, stderr=NULL, shell=True)
+    logging.debug('Mount %s on %s directory successful.' 
+                %(device, mount_point))
+    return mount_point
+
+
+def format_device(device):
+    command = "mkfs.ext4 -F {0}".format(device)
+    logging.debug('Format %s device beginning.' %device)
+    with open(os.devnull, "w") as NULL:
+        check_call(command, stdout=NULL, stderr=NULL, shell=True)
+    logging.debug('Format %s completed successfully.' %device)
+
+
+def run(device, file_size):
+
+    # format device
+    try:
+        format_device(device)
+    except Exception as e:
+        logging.error('%s' %e)
+        return 20
+
+    # mount device on system
+    try:
+        mount_point = mount_filesystem(device)
+    except Exception as e:
+        logging.error('%s' %e)
+        return 30
+
+    # write file test
+    try:
+        if not write_file_test(mount_point, file_size):
+            return 35
+    except Exception as e:
+        logging.error('%s' %e)
+        return 40
+
+    # read file test
+    try:
+        if not read_file_test(mount_point, file_size):
+            return 45
+    except Exception as e:
+        logging.error('%s' %e)
+        return 50
+
+    # umount device
+    try:
+        umount_filesystem(mount_point)
+    except Exception as e:
+        logging.error('%s' %e)
+        return 60
+
+    return 0
+
+
+def main():
+    description_text = 'read_write_file\n\tTest the device read/write file ' \
+        + 'correctly.\n\n\n\tWarning: This program will create one partition' \
+        + ' on device and format it.\n\t\t  Please make sure what you are ' \
+        + 'doing!\n\n\n\tRequirement: Give a device name on the system.'
+
+    parser = ArgumentParser(description=description_text,
+                            formatter_class=RawTextHelpFormatter)
+
+    parser.add_argument('-d', '--device', type=str, required=True,
+                      help=('The device name which used to read write test.\n'
+                            '[Example: sda]'))
+    parser.add_argument('-m', '--megabyte', type=int,
+                        default=5000,
+                        help=('The test file size. [Default: 5GB]'))
+
+    args = parser.parse_args()
+
+    device = args.device
+    if not device.startswith('/dev/'):
+        device = '/dev/' + device
+
+    if not os.path.exists(device):
+        parser.print_help()
+        return 10
+
+    return run(device, args.megabyte)
+
+
+if __name__ == '__main__':
+    sys.exit(main())

=== added file 'scripts/rebulid_raid'
--- scripts/rebulid_raid	1970-01-01 00:00:00 +0000
+++ scripts/rebulid_raid	2013-11-25 07:01:57 +0000
@@ -0,0 +1,277 @@
+#!/usr/bin/env python3
+"""
+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
+Industrial Technology Research Institute
+
+rebulid_raid
+  Test RAID 6 auto rebuild feature in hot spare mode.
+
+Authors
+  Nelson Chu <Nelson.Chu@xxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3,
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+import os
+import re
+import sys
+import time
+import random
+import logging
+from subprocess import check_output, check_call
+from argparse import ArgumentParser, RawTextHelpFormatter
+
+logFormatter = logging.Formatter(
+    "%(asctime)s [%(levelname)-5.5s] %(funcName)s: %(message)s", 
+    datefmt='%Y-%m-%d %H:%M:%S')
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def get_adapter():
+    """
+    Gather all adapter number if there are multiple RAID card.
+    """
+
+    command = 'megacli -CfgDsply -Aall|grep Adapter'
+    adapter_list = []
+
+    adapter_info = check_output(command, shell=True)
+    adapter_info = adapter_info.decode('utf-8')
+
+    for adapter in adapter_info.strip().split('\n'):
+        adapter_list.append(adapter.strip().split(' ')[-1])
+    logger.debug('adapter_list: %s' %adapter_list)
+    return adapter_list
+
+
+def get_all_disk(adapter):
+    """
+    Gather all disks Enclosure and Slot number, and make a 
+    Enclosure:Slot pair list.
+    """
+
+    command = ('megacli -PDList -A%s|grep -E "Enclosure Device ID|Slot Numbe"' 
+               %adapter)
+    disk_list = []
+    disk_info = check_output(command, shell=True)
+    disk_info = disk_info.decode('utf-8')
+
+    for line in disk_info.strip().split('\n'):
+        if line.startswith('Enclosure'):
+            match = re.search(r'\d+', line)
+            disk_list.append(match.group(0))
+        if line.startswith('Slot'):
+            match = re.search(r'\d+', line)
+            enclosure = disk_list.pop()
+            E_S = '%s:%s' %(enclosure, match.group(0))
+            disk_list.append(E_S)
+
+    logger.debug('adapter: %s, disk_list: %s' %(adapter, disk_list))
+    return disk_list
+
+
+def set_disk_offline(adapter, disk_list):
+    """
+    Use all disk creatd RAID 6 and set last disk as hot spare.
+    """
+
+    # Random offline one disk in disk list.
+    disk = disk_list[random.randint(0, len(disk_list)-1)]
+    command = ('megacli -PDOffline -PhysDrv [%s] -a%s' %(disk, adapter))
+    with open(os.devnull, "w") as NULL:
+        check_call(command, stdout=NULL, stderr=NULL, shell=True)
+    logger.info('Random offline one disk in disk list, Enclosure: %s, Slot: %s' 
+                %(disk.split(':')[0], disk.split(':')[1]))
+
+
+def find_hotspare_disk(adapter):
+    """
+    Find hotspare disk in adapter
+    """
+    command = "megasasctl |grep -E '^a%s.*hotspare'" %adapter
+    output = check_output(command, shell=True)
+    output = output.decode('utf-8')
+    hotspare = output.strip()
+
+    if len(hotspare.split('\n')) != 1:
+        logger.error('Hotspare disk number is not correct.' \
+                         + 'It can be only one hotspare in a ' \
+                         + 'adapter.')
+        return False
+
+    match = re.match(r'a(\d+)e(\d+)s(\d+)',hotspare)
+    if not match:
+        logger.error('Can not found enclosure and slot number of hotspare.')
+        return False
+
+    hotspare_E_S = "%s:%s" %(match.group(2), match.group(3))
+    logger.info('Found hotspare, Enclosure: %s, Slot: %s' 
+                %(match.group(2), match.group(3)))
+    return hotspare_E_S
+
+
+def check_rebuild_status(adapter, spare):
+    command = 'megacli -PDRbld -ShowProg -PhysDrv [%s] -A%s|grep -i rebuild' \
+        %(spare, adapter)
+    output = check_output(command, shell=True)
+    output = output.decode('utf-8')
+    logger.debug('%s' %output.strip())
+
+    match = re.search(r'Completed (\d+)% in (\d+) Minutes', output)
+    if not match:
+        return True
+    return False
+
+
+def all_rebuild_status(adapter_list, spare_dict):
+    for adapter in adapter_list:
+        if not check_rebuild_status(adapter, spare_dict[adapter]):
+            return False
+    return True
+
+
+def confirm_rebuild_status(adapter, spare):
+    enclosure, slot = spare.split(':')
+    command = ("megasasctl|grep -E '^a%se%ss%s'" %(adapter, enclosure, slot))
+    output = check_output(command, shell=True)
+    output = output.decode('utf-8')
+
+    match = re.search(r'online', output)
+    if not match:
+        logger.error('Hotspare status did not change to online. Adapter: %s, ' \
+                         +'Hotspare: [$s:$s]' %(adapter, enclosure, slot))
+        return False
+    return True
+
+
+def run(Adapter= None, Hsp=None, disk_list=None):
+    disk_dict = {}
+    spare_dict = {}
+    adapter_list = []
+    rebuild_status = {}
+
+    try:
+        if Adapter:
+            adapter_list.append(Adapter)
+        else:
+            adapter_list = get_adapter()
+    except Exception as e:
+        logger.error('%s' %e)
+        return 10
+
+    try:
+        if Adapter and disk_list:
+            disk_dict[Adapter] = disk_list
+        else:
+            for adapter in adapter_list:
+                disk_dict[adapter] = get_all_disk(adapter)
+    except Exception as e:
+        logger.error('%s' %e)
+        return 20
+
+    try:
+        if Adapter and Hsp:
+            spare_dict[Adapter] = Hsp
+        else:
+            for adapter in adapter_list:
+                spare_dict[adapter] = find_hotspare_disk(adapter)
+                if not spare_dict[adapter]:
+                    return 30
+    except Exception as e:
+        logger.error('%s' %e)
+        return 40
+
+    try:
+        for adapter in adapter_list:
+            # Remove hotspare disk from disk dict.
+            if spare_dict[adapter] in disk_dict[adapter]:
+                disk_dict[adapter].remove(spare_dict[adapter])
+            set_disk_offline(adapter, disk_dict[adapter])
+    except Exception as e:
+        logger.error('%s' %e)
+        return 50
+
+    # Wait for rebuild process
+    time.sleep(5)
+
+    try:
+        while(True):
+            if not all_rebuild_status(adapter_list, spare_dict):
+                # Check rebuild RAID status every 15 min.
+                time.sleep(900)
+                continue
+            break
+    except Exception as e:
+        logger.error('%s' %e)
+        return 60
+
+    check = 0
+    try:
+        for adapter in adapter_list:
+            if not confirm_rebuild_status(adapter, spare_dict[adapter]):
+                logger.error('Rebuild RAID failed, Adapter: %s, Hotspare: %s' 
+                             %(adapter, spare_dict[adapter]))
+                check = 1
+        if check != 0:
+            return 70
+    except Exception as e:
+        logger.error('%s' %e)
+        return 80
+
+    logger.info('All adapter rebuild RAID successful!')
+    return 0
+
+
+def main():
+    description_text = 'rebulit_raid\n\tTests LSI RAID card auto rebuild ' \
+        + 'function.\n\n\n\tWarning: This program will rebuild RAID ' \
+        + 'automaticity.\n\t\t Please make sure what you are doing!\n\n\n' \
+        + '\tRequirement: It can be only one hotspare in a adapter.'
+
+    parser = ArgumentParser(description=description_text,
+                            formatter_class=RawTextHelpFormatter)
+
+    parser.add_argument('-l', '--log', type=str,
+                      default='/tmp/rebulid_raid.log',
+                      help=('Specify the location and name of the log file.\n'
+                            '[Default: %(default)s]'))
+    parser.add_argument('-P', '--PhysDrv', type=str,
+                        help=('The physical drive enclosure and slot of RAID.'
+                              '\n[Example: --PhysDrv E0:S0,E1:S1,...] '))
+    parser.add_argument('-H', '--Hsp', type=str,
+                        help=('The hotspare\'s enclosure and slot of RAID.'
+                              '\n[Example: --Hsp E0:S0] '))
+    parser.add_argument('-A', '--Adapter', type=str,
+                        help=('The adapter number of RAID card.'
+                              '\n[Example: 0'))
+    args = parser.parse_args()
+
+    file_handler = logging.FileHandler(args.log)
+    file_handler.setFormatter(logFormatter)
+    console_handler = logging.StreamHandler()
+    console_handler.setFormatter(logFormatter)
+    console_handler.setLevel(logging.INFO)
+    logger.addHandler(file_handler)
+    logger.addHandler(console_handler)
+
+    disk_list = None
+    if args.PhysDrv:
+       disk_list = args.PhysDrv.split(',')
+
+    logger.info('Rebuild process beginning.')
+    return run(args.Adapter, args.Hsp, disk_list)
+
+if __name__ == '__main__':
+    sys.exit(main())


Follow ups