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

Proposed by Nelson Chu
Status: Rejected
Rejected by: Jeff Lane 
Proposed branch: lp:~nelson-chu/opencompute/add-ocp-certified-jobs
Merge into: lp:opencompute/checkbox
Diff against target: 1510 lines (+1391/-0)
24 files modified
data/whitelists/opencompute-certify-local.whitelist (+100/-0)
jobs/TC-001-0001-CPU_Memory.txt (+25/-0)
jobs/TC-001-0002-Platform_Controller_Hub.txt (+11/-0)
jobs/TC-001-0003-BIOS_setup_menu.txt (+5/-0)
jobs/TC-001-0004-BIOS_remote_update.txt (+17/-0)
jobs/TC-001-0005-BIOS_event_log.txt (+5/-0)
jobs/TC-002-0001-Initial_machine_provisioning.txt (+17/-0)
jobs/TC-002-0004-Boot_order_PXE.txt (+5/-0)
jobs/TC-002-0005-Remote_serial_console.txt (+14/-0)
jobs/TC-003-0001-Hardware_information.txt (+54/-0)
jobs/TC-003-0002-Idle_test.txt (+5/-0)
jobs/TC-003-0003-Network_performance.txt (+5/-0)
jobs/TC-003-0004-Disk_performance.txt (+6/-0)
jobs/TC-004-0001-Knox_testing.txt (+45/-0)
scripts/bios_info (+63/-0)
scripts/cpu_info (+77/-0)
scripts/create_raid6 (+121/-0)
scripts/disk_info (+66/-0)
scripts/memory_info (+65/-0)
scripts/processor_topology (+123/-0)
scripts/raid_hdd_info (+59/-0)
scripts/raid_info (+49/-0)
scripts/read_write_file (+177/-0)
scripts/rebulid_raid (+277/-0)
To merge this branch: bzr merge lp:~nelson-chu/opencompute/add-ocp-certified-jobs
Reviewer Review Type Date Requested Status
Jeff Lane  Disapprove
Review via email: mp+196476@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jeff Lane  (bladernr) wrote :

Hi Nelson...

I really hate to do this, but I'm going to reject this Merge Proposal. Here's why:

Please, PLEASE make several, smaller, targeted merge requests rather than one large 1500 line merge. The only time a merge should have that many lines of changed code is if it adds or changes a very large chunk of code, not adding so many small, individual files.

Smaller merge requests make it a lot easier to review, make suggestions and get things merged in a more timely fashion.

I would suggest breaking this merge request up into smaller chunks divided based on testing topic. So, for example submit one merge request with your proposed changes for:
TC-001-0001-CPU_Memory
whcih should include the whitelist changes, the added scripts, added/modified job files, etc.

Then do the same in different merge requests for:
TC-001-0002-Platform_Controller_Hub
TC-001-0003-BIOS_setup_menu
TC-001-0004-BIOS_remote_update
TC-001-0005-BIOS_event_log
and so on.

Now, a word about job naming and whitelists that apply across all these changes:

1: Dont put whitespace (space, tab) in the job name.
    Rather than TC-001-0001-001 CPU information, you should use something like these:
        TC-001-0001-001-CPU_information
        TC-001-0001/001_CPU_information
2: I don't know that it will hurt, but try to remove any blank lines in the whitelist file, the sections are identifiable by the __SUITENAME__ items on the list, so blank lines are not necessary.
3: You CAN, however, add comments preceding them with the # character
4: remember that every job called __SUITENAME__ must exist in the local.txt.in file (see that file for examples)
    That job is a special job that causes the suitename.txt.in file to be read in when checkbox launches.

Beyond those suggestions, We'll deal with individual problems in the smaller merge requests I've asked for.

Also, given that you are new to this process, using smaller merge requests will make it a LOT easier for me to help show you through the process as well as acquaint you with the way checkbox works and how the code needs to look.

review: Disapprove

Unmerged revisions

2169. By Nelson Chu

Add OCP certified jobs

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/whitelists/opencompute-certify-local.whitelist'
2--- data/whitelists/opencompute-certify-local.whitelist 1970-01-01 00:00:00 +0000
3+++ data/whitelists/opencompute-certify-local.whitelist 2013-11-25 07:01:57 +0000
4@@ -0,0 +1,100 @@
5+# Resource Jobs
6+block_device
7+cdimage
8+cpuinfo
9+device
10+dmi
11+dpkg
12+efi
13+environment
14+gconf
15+lsb
16+meminfo
17+module
18+optical_drive
19+package
20+sleep
21+uname
22+#Info attachment jobs
23+__info__
24+cpuinfo_attachment
25+dmesg_attachment
26+dmi_attachment
27+dmidecode_attachment
28+efi_attachment
29+lspci_attachment
30+lshw_attachment
31+mcelog_attachment
32+meminfo_attachment
33+modprobe_attachment
34+modules_attachment
35+sysctl_attachment
36+sysfs_attachment
37+udev_attachment
38+lsmod_attachment
39+acpi_sleep_attachment
40+info/hdparm
41+info/hdparm_.*.txt
42+installer_debug.gz
43+info/disk_partitions
44+# Actual test cases
45+__TC-001-0001-CPU_Memory__
46+TC-001-0001-001 CPU information
47+TC-001-0001-002 CPU topology
48+TC-001-0001-003 Single Processor Mode
49+TC-001-0001-004 Intel QPI
50+TC-001-0001-005 Memory Information
51+
52+__TC-001-0002-Platform_Controller_Hub__
53+TC-001-0002-001 SATA port
54+TC-001-0002-002 USB 2.0
55+TC-001-0002-003 Mini-SAS port
56+
57+__TC-001-0003-BIOS_setup_menu__
58+TC-001-0003-001 BIOS setup menu
59+
60+__TC-001-0004-BIOS_remote_update__
61+TC-001-0004-001 Scenario 1
62+TC-001-0004-002 Scenario 2
63+TC-001-0004-003 Scenario 3
64+
65+__TC-001-0005-BIOS_event_log__
66+TC-001-0005-001 BIOS event log
67+
68+__TC-002-0001-Initial_machine_provisioning__
69+TC-002-0001-001 Inventory information
70+TC-002-0001-002 IP lease information
71+TC-002-0001-003 Monitor attachment
72+
73+__TC-002-0004-Boot_order_PXE__
74+TC-002-0004-001 Boot Order / PXE
75+
76+__TC-002-0005-Remote_serial_console__
77+TC-002-0005-001 Enable SOL
78+TC-002-0005-002 SOL operation
79+
80+__TC-003-0001-Hardware_information__
81+TC-003-0001-001 CPU information
82+TC-003-0001-002 Memory information
83+TC-003-0001-003 Disk information
84+TC-003-0001-004 BIOS information
85+TC-003-0001-005 ME information
86+TC-003-0001-006 NIC information
87+TC-003-0001-007 RAID information
88+TC-003-0001-008 HBA information
89+
90+___TC-003-0002-Idle_test__
91+TC-003-0002-001 12 hours idle
92+
93+__TC-003-0003-Network_performance__
94+TC-003-0003-001 Network performance
95+
96+__TC-003-0004-Disk_performance__
97+TC-003-0004-001 Disk performance
98+
99+__TC-004-0001-Knox_testing__
100+TC-004-0001-001-RAID_card_information
101+TC-004-0002-001-Get_all_HDD
102+TC-004-0003-001-Build_raid_6
103+TC-004-0004-001-Read/Write_file
104+TC-004-0005-001-Rebuild_Raid
105
106=== added file 'jobs/TC-001-0001-CPU_Memory.txt'
107--- jobs/TC-001-0001-CPU_Memory.txt 1970-01-01 00:00:00 +0000
108+++ jobs/TC-001-0001-CPU_Memory.txt 2013-11-25 07:01:57 +0000
109@@ -0,0 +1,25 @@
110+plugin: shell
111+name: TC-001-0001-001 CPU information
112+requires: package.name == 'lshw'
113+command: cpu_info
114+description:
115+ 1. Gathering CPU information by using lshw command.
116+ 2. Output CPU model and L1, L2, L3 cache size.
117+ 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
118+
119+plugin: shell
120+name: TC-001-0001-002 CPU topology
121+command: processor_topology
122+description:
123+ 1. Gathering CPU information by using lscpu command.
124+ 2. Output the total number of CPU, the number of Thread per core, the number of Core per Socket, and the number of Socket.
125+ 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).
126+
127+plugin: shell
128+name: TC-001-0001-003 Memory Information
129+requires: package.name == 'lshw'
130+command: memory_info
131+description:
132+ 1. Gathering memory information by using lshw command.
133+ 2. Output memory module, vendor, size and slot.
134+ 3. Pass criteria: 2 DDR3 slots per channel per processor (total of 16 DIMMs on the motherboard).
135
136=== added file 'jobs/TC-001-0002-Platform_Controller_Hub.txt'
137--- jobs/TC-001-0002-Platform_Controller_Hub.txt 1970-01-01 00:00:00 +0000
138+++ jobs/TC-001-0002-Platform_Controller_Hub.txt 2013-11-25 07:01:57 +0000
139@@ -0,0 +1,11 @@
140+plugin: shell
141+name: TC-001-0002-001 SATA port
142+command: echo "manual"; exit 1
143+description:
144+ individual SATA 6Gps ports from SATA port 0/1.
145+
146+plugin: shell
147+name: TC-001-0002-002 USB 2.0
148+command: echo "manual"; exit 1
149+description:
150+ USB 2.0 ports (2 designed in this board: one front connector, one vertical onboard)
151
152=== added file 'jobs/TC-001-0003-BIOS_setup_menu.txt'
153--- jobs/TC-001-0003-BIOS_setup_menu.txt 1970-01-01 00:00:00 +0000
154+++ jobs/TC-001-0003-BIOS_setup_menu.txt 2013-11-25 07:01:57 +0000
155@@ -0,0 +1,5 @@
156+plugin: shell
157+name: TC-001-0003-001 BIOS setup menu
158+command: echo "manual"; exit 1
159+description:
160+ BIOS setup menu.
161
162=== added file 'jobs/TC-001-0004-BIOS_remote_update.txt'
163--- jobs/TC-001-0004-BIOS_remote_update.txt 1970-01-01 00:00:00 +0000
164+++ jobs/TC-001-0004-BIOS_remote_update.txt 2013-11-25 07:01:57 +0000
165@@ -0,0 +1,17 @@
166+plugin: shell
167+name: TC-001-0004-001 Scenario 1
168+command: echo "Scenario 1: Sample/Audit BIOS settings."; echo "Not implemented yet"; exit 1
169+description:
170+ Scenario 1: Sample/Audit BIOS settings.
171+
172+plugin: shell
173+name: TC-001-0004-002 Scenario 2
174+command: echo "Scenario 2: Update BIOS with pre-configured set of BIOS settings."; echo "Not implemented yet";exit 1
175+description:
176+ Scenario 2: Update BIOS with pre-configured set of BIOS settings.
177+
178+plugin: shell
179+name: TC-001-0004-003 Scenario 3
180+command: echo "Scenario 3: BIOS/firmware update with a new revision."; echo "Not implemented yet"; exit 1
181+description:
182+ Scenario 1: BIOS/firmware update with a new revision.
183
184=== added file 'jobs/TC-001-0005-BIOS_event_log.txt'
185--- jobs/TC-001-0005-BIOS_event_log.txt 1970-01-01 00:00:00 +0000
186+++ jobs/TC-001-0005-BIOS_event_log.txt 2013-11-25 07:01:57 +0000
187@@ -0,0 +1,5 @@
188+plugin: shell
189+name: TC-001-0005-001 BIOS event log
190+command: echo "manual"; exit 1
191+description:
192+ BIOS event log. Logged errors and error threshold setting.
193
194=== added file 'jobs/TC-002-0001-Initial_machine_provisioning.txt'
195--- jobs/TC-002-0001-Initial_machine_provisioning.txt 1970-01-01 00:00:00 +0000
196+++ jobs/TC-002-0001-Initial_machine_provisioning.txt 2013-11-25 07:01:57 +0000
197@@ -0,0 +1,17 @@
198+plugin: shell
199+name: TC-002-0001-001 Inventory information
200+command: echo "MAC address and/or other inventory information feeds/bar codes from the machine's manufacturer."; echo "Not implemented yet"; exit 1
201+description:
202+ MAC address and/or other inventory information feeds/bar codes from the machine's manufacturer.
203+
204+plugin: shell
205+name: TC-002-0001-002 IP lease information
206+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
207+description:
208+ Searching through IP lease information to find machines that have gotten a DHCP leases for their management interfaces.
209+
210+plugin: shell
211+name: TC-002-0001-003 Monitor attachment
212+command: echo "manual"; exit 1
213+description:
214+ Attach KVM to setup machine.
215
216=== added file 'jobs/TC-002-0004-Boot_order_PXE.txt'
217--- jobs/TC-002-0004-Boot_order_PXE.txt 1970-01-01 00:00:00 +0000
218+++ jobs/TC-002-0004-Boot_order_PXE.txt 2013-11-25 07:01:57 +0000
219@@ -0,0 +1,5 @@
220+plugin: shell
221+name: TC-002-0004-001 Boot Order / PXE
222+command: echo "Not implemented yet"; exit 1
223+description:
224+ Boot form PXE.
225
226=== added file 'jobs/TC-002-0005-Remote_serial_console.txt'
227--- jobs/TC-002-0005-Remote_serial_console.txt 1970-01-01 00:00:00 +0000
228+++ jobs/TC-002-0005-Remote_serial_console.txt 2013-11-25 07:01:57 +0000
229@@ -0,0 +1,14 @@
230+plugin: shell
231+name: TC-002-0005-001 Enable SOL
232+command: echo "manual"; exit 1
233+description:
234+ SOL BIOS setup.
235+
236+plugin: shell
237+name: TC-002-0005-002 SOL operation
238+command: echo "manual"; exit 1
239+description:
240+ 1. Remote control the SUT by using IPMI SOL.
241+ 2. SOL that will allow to operate the SUT on: pre-boot process, BIOS setup, boot process, access to the installed OS
242+ 3. Pass criteria: Successfully operate the SUT in the scenarios listed above.
243+
244
245=== added file 'jobs/TC-003-0001-Hardware_information.txt'
246--- jobs/TC-003-0001-Hardware_information.txt 1970-01-01 00:00:00 +0000
247+++ jobs/TC-003-0001-Hardware_information.txt 2013-11-25 07:01:57 +0000
248@@ -0,0 +1,54 @@
249+plugin: shell
250+name: TC-003-0001-001 CPU information
251+command: echo "refer to TC-001-0001-001"
252+description:
253+ To verify CPU information with specification. Show CPU type and speed.
254+
255+plugin: shell
256+name: TC-003-0001-002 Memory information
257+command: echo "refer to TC-001-0001-003"
258+description:
259+ To verify memory information with specification. Show memory vendor, location and size.
260+
261+plugin: shell
262+name: TC-003-0001-003 Disk information
263+requires: package.name == 'lshw'
264+command: disk_info
265+description:
266+ 1. Gathering disk information by using lshw command.
267+ 2. Output Disk type, vendor, product, capacity.
268+ 3. Pass criteria: If catch the information then pass.
269+
270+plugin: shell
271+name: TC-003-0001-004 BIOS information
272+requires: package.name == 'lshw'
273+command: bios_info
274+description:
275+ 1. Gathering BIOS information by using lshw command.
276+ 2. Output BIOS vendor, version and release date.
277+ 3. Pass criteria: If catch the information then pass.
278+
279+plugin: shell
280+name: TC-003-0001-005 ME information
281+command: echo "Show Intel ME information."; echo "Refer to DCMI job."
282+description:
283+ Show Intel ME information.
284+
285+plugin: shell
286+name: TC-003-0001-006 NIC information
287+command: network_device_info
288+description:
289+ 1. Gathering NIC information by using udevadm command.
290+ 2. Output Interface, product, vendor, driver, device path.
291+ 3. Pass criteria: If catch the information then pass.
292+
293+plugin: shell
294+name: TC-003-0001-007 RAID card information
295+requires:
296+ package.name == 'megacli'
297+ package.name == 'megactl'
298+command: raid_info
299+description:
300+ 1. Gathering LSI RAID card information by using megasasctl command.
301+ 2. Show adapter, product name, memory, BBU, serial no.
302+ 3. Pass criteria: If catch the information then pass.
303
304=== added file 'jobs/TC-003-0002-Idle_test.txt'
305--- jobs/TC-003-0002-Idle_test.txt 1970-01-01 00:00:00 +0000
306+++ jobs/TC-003-0002-Idle_test.txt 2013-11-25 07:01:57 +0000
307@@ -0,0 +1,5 @@
308+plugin: shell
309+name: TC-003-0002-001 12 hours idle
310+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
311+description:
312+ Leave server sitting idle for 12 hours. Verify after 12 hours that the system is still stable.
313
314=== added file 'jobs/TC-003-0003-Network_performance.txt'
315--- jobs/TC-003-0003-Network_performance.txt 1970-01-01 00:00:00 +0000
316+++ jobs/TC-003-0003-Network_performance.txt 2013-11-25 07:01:57 +0000
317@@ -0,0 +1,5 @@
318+plugin: shell
319+name: TC-003-0003-001 Network performance
320+command: echo "Tx/Rx Gbps for 10G NIC spec. >9.5 Gbps"; echo "Not implemented yet"; exit 1
321+description:
322+ Tx/Rx Gbps for 10G NIC spec. > 9.5 Gbps
323
324=== added file 'jobs/TC-003-0004-Disk_performance.txt'
325--- jobs/TC-003-0004-Disk_performance.txt 1970-01-01 00:00:00 +0000
326+++ jobs/TC-003-0004-Disk_performance.txt 2013-11-25 07:01:57 +0000
327@@ -0,0 +1,6 @@
328+plugin: shell
329+name: TC-003-0004-001 Disk performance
330+command: echo "SMART threshold, grown defect list glist = reallocated sectors = 200 for 4TB, glist = 100 for 2TB for all vendors.
331+SMART HDD temp = 60C."; echo "Not implemented yet"; exit 1
332+description:
333+ Test disk performance.
334
335=== added file 'jobs/TC-004-0001-Knox_testing.txt'
336--- jobs/TC-004-0001-Knox_testing.txt 1970-01-01 00:00:00 +0000
337+++ jobs/TC-004-0001-Knox_testing.txt 2013-11-25 07:01:57 +0000
338@@ -0,0 +1,45 @@
339+plugin: shell
340+name: TC-004-0001-001-RAID_card_information
341+command: echo "refer to TC-003-0001-007"
342+description:
343+ RAID card information
344+
345+plugin: shell
346+name: TC-004-0002-001-Get_all_HDD
347+requires:
348+ package.name == 'megacli'
349+ package.name == 'megactl'
350+depends: TC-004-0001-001 RAID card information
351+command: raid_hdd_info
352+description:
353+ Get knox all HDD.
354+
355+plugin: shell
356+name: TC-004-0003-001-Build_raid_6
357+requires:
358+ package.name == 'megacli'
359+ package.name == 'megactl'
360+depends: TC-004-0002-001-Get_all_HDD
361+command: create_raid6
362+description:
363+ Build Raid 6
364+
365+plugin: shell
366+name: TC-004-0004-001-Read/Write_file
367+depends: TC-004-0003-001-Build_raid_6
368+description: Test RAID Read/Write file
369+user: root
370+command:
371+ exit 1
372+
373+plugin: shell
374+name: TC-004-0005-001-Rebuild_Raid
375+requires:
376+ package.name == 'megacli'
377+ package.name == 'megactl'
378+depends: TC-004-0004-001-Read/Write_file
379+environ: CHECKBOX_DATA
380+user: root
381+command: rebulid_raid -l $CHECKBOX_DATA/rebulid_raid.log
382+description:
383+ Re-build Raid
384
385=== added file 'scripts/bios_info'
386--- scripts/bios_info 1970-01-01 00:00:00 +0000
387+++ scripts/bios_info 2013-11-25 07:01:57 +0000
388@@ -0,0 +1,63 @@
389+#!/usr/bin/env python3
390+"""
391+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
392+Industrial Technology Research Institute
393+
394+bios_info
395+ Gathering BIOS information by using lshw command.
396+ And output BIOS vendor, version and release date.
397+
398+Authors
399+ Nelson Chu <Nelson.Chu@itri.org.tw>
400+
401+This program is free software: you can redistribute it and/or modify
402+it under the terms of the GNU General Public License version 3,
403+as published by the Free Software Foundation.
404+
405+This program is distributed in the hope that it will be useful,
406+but WITHOUT ANY WARRANTY; without even the implied warranty of
407+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
408+GNU General Public License for more details.
409+
410+You should have received a copy of the GNU General Public License
411+along with this program. If not, see <http://www.gnu.org/licenses/>.
412+
413+"""
414+
415+import os
416+import sys
417+import xml.etree.ElementTree as ET
418+from subprocess import check_output
419+
420+
421+def main():
422+ attribute = ['vendor', 'version', 'date']
423+ command = 'lshw -xml'
424+ with open(os.devnull, "w") as NULL:
425+ hwinfo_xml = check_output(command, stderr=NULL, shell=True)
426+ root = ET.fromstring(hwinfo_xml)
427+
428+ # Parser lshw XML for gather BIOS information.
429+ bios_info = root.findall(".//node[@id='firmware']")
430+
431+ if not bios_info:
432+ print("Can not parser any BIOS information.")
433+ return 10
434+
435+ for bios in bios_info:
436+ for attr in attribute:
437+ if bios.find(attr) is None:
438+ print(("Can not found BIOS %s") %attr)
439+ return 20
440+
441+ for attr in attribute:
442+ if attr == 'date':
443+ print(('release date=%s.') %(bios.find(attr).text)),
444+ continue
445+ print(('%s=%s,') %(attr, bios.find(attr).text)),
446+ print()
447+
448+ return 0
449+
450+if __name__ == '__main__':
451+ sys.exit(main())
452
453=== added file 'scripts/cpu_info'
454--- scripts/cpu_info 1970-01-01 00:00:00 +0000
455+++ scripts/cpu_info 2013-11-25 07:01:57 +0000
456@@ -0,0 +1,77 @@
457+#!/usr/bin/env python3
458+"""
459+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
460+Industrial Technology Research Institute
461+
462+cpu_info
463+ Gathering CPU information by using lshw command.
464+ And output CPU model and L1, L2, L3 cache size.
465+ L3 Cache should be larger than 20MB.
466+
467+Authors
468+ Nelson Chu <Nelson.Chu@itri.org.tw>
469+
470+This program is free software: you can redistribute it and/or modify
471+it under the terms of the GNU General Public License version 3,
472+as published by the Free Software Foundation.
473+
474+This program is distributed in the hope that it will be useful,
475+but WITHOUT ANY WARRANTY; without even the implied warranty of
476+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
477+GNU General Public License for more details.
478+
479+You should have received a copy of the GNU General Public License
480+along with this program. If not, see <http://www.gnu.org/licenses/>.
481+
482+"""
483+
484+import os
485+import re
486+import sys
487+import xml.etree.ElementTree as ET
488+from subprocess import check_output
489+
490+def main():
491+ command = 'lshw -xml'
492+ with open(os.devnull, "w") as NULL:
493+ hwinfo_xml = check_output(command, stderr=NULL, shell=True)
494+ root = ET.fromstring(hwinfo_xml)
495+
496+ # Parser lshw XML for gather processor information.
497+ processor = root.findall(".//product/..[@class='processor']")
498+
499+ if not processor:
500+ print("Can not parser any processor information.")
501+ return 10
502+
503+ for cpu in processor:
504+ if cpu.find('product') is None:
505+ print("Can not found processor product.")
506+ return 20
507+ print(cpu.find('product').text)
508+
509+ cache_list = cpu.findall(".//size/..[@class='memory']")
510+ if not cache_list:
511+ print("Can not found any CPU cache.")
512+ return 30
513+
514+ for cache in cache_list:
515+ if cache.find('size') is None or cache.find('slot') is None:
516+ print("Can not access Last Level Cache (LLC).")
517+ return 40
518+
519+ cache_size = int(cache.find('size').text) / 1024
520+ if cache_size > 1024:
521+ cache_size = cache_size / 1024
522+ print(('%s %d MB') %(cache.find('slot').text, cache_size))
523+ if re.search('L3', cache.find('slot').text):
524+ if cache_size < 20:
525+ print('L3 cache size less than 20MB.')
526+ return 50
527+ else:
528+ print(('%s %d KB') %(cache.find('slot').text, cache_size))
529+
530+ return 0
531+
532+if __name__ == '__main__':
533+ sys.exit(main())
534
535=== added file 'scripts/create_raid6'
536--- scripts/create_raid6 1970-01-01 00:00:00 +0000
537+++ scripts/create_raid6 2013-11-25 07:01:57 +0000
538@@ -0,0 +1,121 @@
539+#!/usr/bin/env python3
540+"""
541+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
542+Industrial Technology Research Institute
543+
544+create_raid6
545+ Using megacli command build RAID 6 on knox.
546+
547+ Warning: Make sure there is no RAID configuration in LSI RAID card
548+ before this program is executed.
549+
550+Authors
551+ Nelson Chu <Nelson.Chu@itri.org.tw>
552+
553+This program is free software: you can redistribute it and/or modify
554+it under the terms of the GNU General Public License version 3,
555+as published by the Free Software Foundation.
556+
557+This program is distributed in the hope that it will be useful,
558+but WITHOUT ANY WARRANTY; without even the implied warranty of
559+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
560+GNU General Public License for more details.
561+
562+You should have received a copy of the GNU General Public License
563+along with this program. If not, see <http://www.gnu.org/licenses/>.
564+
565+"""
566+
567+import os
568+import re
569+import sys
570+from subprocess import check_output, check_call
571+
572+
573+def get_adapter():
574+ """
575+ Gather all adapter number if there are multiple RAID card.
576+ """
577+
578+ command = 'megacli -CfgDsply -Aall|grep Adapter'
579+ adapter_list = []
580+
581+ adapter_info = check_output(command, shell=True)
582+ adapter_info = adapter_info.decode('utf-8')
583+
584+ for adapter in adapter_info.strip().split('\n'):
585+ adapter_list.append(adapter.strip().split(' ')[-1])
586+ return adapter_list
587+
588+
589+def get_all_disk(adapter):
590+ """
591+ Gather all disks Enclosure and Slot number, and make a
592+ Enclosure:Slot pair list.
593+ """
594+
595+ command = 'megacli -PDList -A{0}'.format(adapter) \
596+ + '|grep -E "Enclosure Device ID|Slot Number"'
597+
598+ disk_list = []
599+ disk_info = check_output(command, shell=True)
600+ disk_info = disk_info.decode('utf-8')
601+
602+ for line in disk_info.strip().split('\n'):
603+ if line.startswith('Enclosure'):
604+ match = re.search(r'\d+', line)
605+ disk_list.append(match.group(0))
606+ if line.startswith('Slot'):
607+ match = re.search(r'\d+', line)
608+ enclosure = disk_list.pop()
609+ E_S = '%s:%s' %(enclosure, match.group(0))
610+ disk_list.append(E_S)
611+ return disk_list
612+
613+
614+def build_raid(adapter, disk_list):
615+ """
616+ Use all disk creatd RAID 6 and set last disk as hot spare.
617+ """
618+
619+ # use the last disk as hot spare
620+ spare = disk_list.pop()
621+
622+ disk = ','.join(disk_list)
623+ command = 'megacli -CfgLDadd -r6 [{0}] WB Direct -Hsp[{1}] -a{2}'.format(
624+ disk, spare, adapter)
625+
626+ check_call(command, shell=True)
627+
628+
629+def main():
630+ knox_dict = {}
631+
632+ # get RAID card adapter number
633+ try:
634+ adapter_list = get_adapter()
635+ except Exception as e:
636+ print('Error: %s' %e)
637+ return 10
638+
639+ # get all disk in RAID card
640+ try:
641+ for adapter in adapter_list:
642+ knox_dict[adapter] = get_all_disk(adapter)
643+ except Exception as e:
644+ print('Error: %s' %e)
645+ return 20
646+
647+ # use all HDD build RAID
648+ try:
649+ for adapter, disk_list in knox_dict.items():
650+ build_raid(adapter, disk_list)
651+ except Exception as e:
652+ print('Error: Knox build raid 6 failed.')
653+ return 30
654+
655+ return 0
656+
657+
658+if __name__ == '__main__':
659+ sys.exit(main())
660
661=== added file 'scripts/disk_info'
662--- scripts/disk_info 1970-01-01 00:00:00 +0000
663+++ scripts/disk_info 2013-11-25 07:01:57 +0000
664@@ -0,0 +1,66 @@
665+#!/usr/bin/env python3
666+"""
667+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
668+Industrial Technology Research Institute
669+
670+disk_info
671+ Gathering disk information by using lshw command.
672+ And output disk type, vendor, product, capacity.
673+
674+Authors
675+ Nelson Chu <Nelson.Chu@itri.org.tw>
676+
677+This program is free software: you can redistribute it and/or modify
678+it under the terms of the GNU General Public License version 3,
679+as published by the Free Software Foundation.
680+
681+This program is distributed in the hope that it will be useful,
682+but WITHOUT ANY WARRANTY; without even the implied warranty of
683+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
684+GNU General Public License for more details.
685+
686+You should have received a copy of the GNU General Public License
687+along with this program. If not, see <http://www.gnu.org/licenses/>.
688+
689+"""
690+
691+import sys
692+import xml.etree.ElementTree as ET
693+from subprocess import Popen, PIPE
694+
695+def main():
696+ attribute = ['description', 'vendor', 'product', 'size']
697+ command = 'lshw -xml'
698+ hwinfo_xml = Popen(command, stdout=PIPE, stderr=PIPE,
699+ shell=True).communicate()[0]
700+ root = ET.fromstring(hwinfo_xml)
701+
702+ # Parser lshw XML for gather disk information.
703+ disk_list = root.findall(".//node[@class='disk']")
704+
705+ if not disk_list:
706+ print("Can not parser any disk information.")
707+ return 10
708+
709+ for disk in disk_list:
710+ for attr in attribute:
711+ if disk.find(attr) is None:
712+ print(("Can not found disk %s") %attr)
713+ return 20
714+
715+ disk_size = int(disk.find('size').text) / (1000**3)
716+ for attr in attribute:
717+ if attr == 'description':
718+ print(('Type=%s,') %disk.find(attr).text),
719+ continue
720+ elif attr == 'size':
721+ print(('%s=%dGB.') %(attr, disk_size)),
722+ continue
723+ else:
724+ print(('%s=%s,') %(attr, disk.find(attr).text)),
725+ print()
726+
727+ return 0
728+
729+if __name__ == '__main__':
730+ sys.exit(main())
731
732=== added file 'scripts/memory_info'
733--- scripts/memory_info 1970-01-01 00:00:00 +0000
734+++ scripts/memory_info 2013-11-25 07:01:57 +0000
735@@ -0,0 +1,65 @@
736+#!/usr/bin/env python3
737+"""
738+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
739+Industrial Technology Research Institute
740+
741+memory_info
742+ Gathering memory information by using lshw command.
743+ And output memory module, vendor, size and slot.
744+
745+Authors
746+ Nelson Chu <Nelson.Chu@itri.org.tw>
747+
748+This program is free software: you can redistribute it and/or modify
749+it under the terms of the GNU General Public License version 3,
750+as published by the Free Software Foundation.
751+
752+This program is distributed in the hope that it will be useful,
753+but WITHOUT ANY WARRANTY; without even the implied warranty of
754+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
755+GNU General Public License for more details.
756+
757+You should have received a copy of the GNU General Public License
758+along with this program. If not, see <http://www.gnu.org/licenses/>.
759+
760+"""
761+
762+import sys
763+import xml.etree.ElementTree as ET
764+from subprocess import Popen, PIPE
765+
766+def main():
767+ attribute = ['description', 'vendor', 'slot', 'size']
768+ command = 'lshw -xml'
769+ hwinfo_xml = Popen(command, stdout=PIPE, stderr=PIPE,
770+ shell=True).communicate()[0]
771+ root = ET.fromstring(hwinfo_xml)
772+
773+ # Parser lshw XML for gather memory information.
774+ memory_list = root.findall(".//clock/..[@class='memory']")
775+
776+ if not memory_list:
777+ print("Can not parser any memory information.")
778+ return 10
779+
780+ for dimm in memory_list:
781+ for attr in attribute:
782+ if dimm.find(attr) is None:
783+ print(("Can not found memory %s") %attr)
784+ return 20
785+
786+ memory_size = int(dimm.find('size').text) / (1024**3)
787+ for attr in attribute:
788+ if attr == 'description':
789+ print(('%s,') %(dimm.find(attr).text)),
790+ continue
791+ elif attr == 'size':
792+ print(('%s=%dGB.') %(attr, memory_size)),
793+ continue
794+ print(('%s=%s,') %(attr, dimm.find(attr).text)),
795+ print()
796+
797+ return 0
798+
799+if __name__ == '__main__':
800+ sys.exit(main())
801
802=== added file 'scripts/processor_topology'
803--- scripts/processor_topology 1970-01-01 00:00:00 +0000
804+++ scripts/processor_topology 2013-11-25 07:01:57 +0000
805@@ -0,0 +1,123 @@
806+#!/usr/bin/env python3
807+'''
808+cpu_topology
809+Written by Jeffrey Lane <jeffrey.lane@canonical.com>
810+'''
811+import sys
812+import os
813+from subprocess import check_output
814+
815+class proc_cpuinfo():
816+ '''
817+ Class to get and handle information from /proc/cpuinfo
818+ Creates a dictionary of data gleaned from that file.
819+ '''
820+ def __init__(self):
821+ self.cpuinfo = {}
822+ cpu_fh = open('/proc/cpuinfo', 'r')
823+ try:
824+ temp = cpu_fh.readlines()
825+ finally:
826+ cpu_fh.close()
827+
828+ for i in temp:
829+ if i.startswith('processor'):
830+ key = 'cpu' + (i.split(':')[1].strip())
831+ self.cpuinfo[key] = {'core_id':'', 'physical_package_id':''}
832+ elif i.startswith('core id'):
833+ self.cpuinfo[key].update({'core_id': i.split(':')[1].strip()})
834+ elif i.startswith('physical id'):
835+ self.cpuinfo[key].update({'physical_package_id':
836+ i.split(':')[1].strip()})
837+ else:
838+ continue
839+
840+
841+class sysfs_cpu():
842+ '''
843+ Class to get and handle information from sysfs as relates to CPU topology
844+ Creates an informational class to present information on various CPUs
845+ '''
846+
847+ def __init__(self, proc):
848+ self.syscpu = {}
849+ self.path = '/sys/devices/system/cpu/' + proc + '/topology'
850+ items = ['core_id', 'physical_package_id']
851+ for i in items:
852+ syscpu_fh = open(os.path.join(self.path, i), 'r')
853+ try:
854+ self.syscpu[i] = syscpu_fh.readline().strip()
855+ finally:
856+ syscpu_fh.close()
857+
858+
859+def compare(proc_cpu, sys_cpu):
860+ cpu_map = {}
861+ '''
862+ If there is only 1 CPU the test don't look for core_id
863+ and physical_package_id because those information are absent in
864+ /proc/cpuinfo on singlecore system
865+ '''
866+ for key in proc_cpu.keys():
867+ if 'cpu1' not in proc_cpu:
868+ cpu_map[key] = True
869+ else:
870+ for subkey in proc_cpu[key].keys():
871+ if proc_cpu[key][subkey] == sys_cpu[key][subkey]:
872+ cpu_map[key] = True
873+ else:
874+ cpu_map[key] = False
875+ return cpu_map
876+
877+
878+def main():
879+ cpuinfo = proc_cpuinfo()
880+ sys_cpu = {}
881+ keys = cpuinfo.cpuinfo.keys()
882+ for k in keys:
883+ sys_cpu[k] = sysfs_cpu(k).syscpu
884+ cpu_map = compare(cpuinfo.cpuinfo, sys_cpu)
885+ if False in cpu_map.values() or len(cpu_map) < 1:
886+ print("FAIL: CPU Topology is incorrect", file=sys.stderr)
887+ print("-" * 52, file=sys.stderr)
888+ print("{0}{1}".format("/proc/cpuinfo".center(30), "sysfs".center(25)),
889+ file=sys.stderr)
890+ print("{0}{1}{2}{3}{1}{2}".format(
891+ "CPU".center(6),
892+ "Physical ID".center(13),
893+ "Core ID".center(9),
894+ "|".center(3)), file=sys.stderr)
895+ for key in sorted(sys_cpu.keys()):
896+ print("{0}{1}{2}{3}{4}{5}".format(
897+ key.center(6),
898+ cpuinfo.cpuinfo[key]['physical_package_id'].center(13),
899+ cpuinfo.cpuinfo[key]['core_id'].center(9),
900+ "|".center(3),
901+ sys_cpu[key]['physical_package_id'].center(13),
902+ sys_cpu[key]['core_id'].center(9)), file=sys.stderr)
903+ return 1
904+ else:
905+ # Output the total number of CPU, the number of Thread per core,
906+ # the number of Core per Socket, and the number of Socket.
907+ # Revised by Nelson Chu <nelson.chu@itri.org.tw>
908+ command = 'lscpu'
909+
910+ with open(os.devnull, "w") as NULL:
911+ cpu_info = check_output(command, stderr=NULL, shell=True)
912+
913+ cpu_info = cpu_info.decode()
914+
915+ for cpu in cpu_info.split('\n'):
916+ if cpu.startswith("CPU(s)"):
917+ print(cpu + ',')
918+ if cpu.startswith("Thread(s) per core"):
919+ print(cpu + ',')
920+ if cpu.startswith("Core(s) per socket"):
921+ print(cpu + ',')
922+ if cpu.startswith("Socket(s)"):
923+ print(cpu + '.')
924+
925+ return 0
926+
927+if __name__ == '__main__':
928+ sys.exit(main())
929
930=== added file 'scripts/raid_hdd_info'
931--- scripts/raid_hdd_info 1970-01-01 00:00:00 +0000
932+++ scripts/raid_hdd_info 2013-11-25 07:01:57 +0000
933@@ -0,0 +1,59 @@
934+#!/usr/bin/env python3
935+"""
936+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
937+Industrial Technology Research Institute
938+
939+raid_hdd_info
940+ Get all knox HDD information by using megasasctl command.
941+ Show total HDD number in knox and check these disks status is ready or not.
942+ Pass criteria: All knox disks information and status are correct.
943+
944+Authors
945+ Nelson Chu <Nelson.Chu@itri.org.tw>
946+
947+This program is free software: you can redistribute it and/or modify
948+it under the terms of the GNU General Public License version 3,
949+as published by the Free Software Foundation.
950+
951+This program is distributed in the hope that it will be useful,
952+but WITHOUT ANY WARRANTY; without even the implied warranty of
953+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
954+GNU General Public License for more details.
955+
956+You should have received a copy of the GNU General Public License
957+along with this program. If not, see <http://www.gnu.org/licenses/>.
958+
959+"""
960+
961+import sys
962+from subprocess import Popen, PIPE
963+
964+def main():
965+ command = 'megasasctl |grep -E "a[0-9]+e[0-9]+s[0-9]+ +[0-9]+GiB"'
966+ hdd_info = Popen(command, stdout=PIPE, stderr=PIPE,
967+ shell=True).communicate()[0]
968+ hdd_info = hdd_info.decode('utf-8')
969+ disk_number = 0
970+ fail = 0
971+
972+ # Check is there has any disk.
973+ if not hdd_info:
974+ print('There is no disk in knox.')
975+ return 10
976+
977+ for disk in hdd_info.strip().split('\n'):
978+ disk_number = disk_number+1
979+ # Check disk status is ready or not
980+ if disk.strip().split(' ')[-1] != 'ready':
981+ fail = fail+1
982+
983+ print(('Total %d HDDs in knox.') %disk_number)
984+
985+ if fail:
986+ print(('There are %d disk status not in ready!') %fail)
987+ return 20
988+
989+ return 0
990+
991+if __name__ == '__main__':
992+ sys.exit(main())
993
994=== added file 'scripts/raid_info'
995--- scripts/raid_info 1970-01-01 00:00:00 +0000
996+++ scripts/raid_info 2013-11-25 07:01:57 +0000
997@@ -0,0 +1,49 @@
998+#!/bin/bash
999+#
1000+# Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
1001+# Industrial Technology Research Institute
1002+#
1003+# raid_info
1004+# Gathering LSI RAID card information by using megasasctl command.
1005+# Show adapter, product name, memory, BBU, serial no.
1006+#
1007+# Authors
1008+# Nelson Chu <Nelson.Chu@itri.org.tw>
1009+#
1010+# This program is free software: you can redistribute it and/or modify
1011+# it under the terms of the GNU General Public License version 3,
1012+# as published by the Free Software Foundation.
1013+#
1014+# This program is distributed in the hope that it will be useful,
1015+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1016+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1017+# GNU General Public License for more details.
1018+#
1019+# You should have received a copy of the GNU General Public License
1020+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1021+#
1022+
1023+check_return_code() {
1024+ if [ "${1}" -ne "0" ]; then
1025+ echo "ERROR: ${2}" >&2
1026+ exit ${1}
1027+ fi
1028+}
1029+
1030+show=`megacli -CfgDsply -Aall|grep -E \
1031+ 'Adapter:|Product Name:|Memory:|BBU:|Serial No:'`
1032+check_return_code $? "There is no LSI MegaRAID SAS cards found."
1033+
1034+n=0
1035+echo "$show" | {
1036+ while IFS= read -r line; do
1037+ echo $line"."
1038+ n=$(($n + 1))
1039+ done
1040+
1041+ if [ $n -ne "5" ]; then
1042+ check_return_code 10 "LSI RAID attributes are not correct."
1043+ fi
1044+
1045+ exit 0
1046+}
1047
1048=== added file 'scripts/read_write_file'
1049--- scripts/read_write_file 1970-01-01 00:00:00 +0000
1050+++ scripts/read_write_file 2013-11-25 07:01:57 +0000
1051@@ -0,0 +1,177 @@
1052+#!/usr/bin/env python3
1053+"""
1054+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
1055+Industrial Technology Research Institute
1056+
1057+read_write_file
1058+ Create one partition and format on device then test disk I/O.
1059+
1060+Authors
1061+ Nelson Chu <Nelson.Chu@itri.org.tw>
1062+
1063+This program is free software: you can redistribute it and/or modify
1064+it under the terms of the GNU General Public License version 3,
1065+as published by the Free Software Foundation.
1066+
1067+This program is distributed in the hope that it will be useful,
1068+but WITHOUT ANY WARRANTY; without even the implied warranty of
1069+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1070+GNU General Public License for more details.
1071+
1072+You should have received a copy of the GNU General Public License
1073+along with this program. If not, see <http://www.gnu.org/licenses/>.
1074+"""
1075+
1076+import os
1077+import re
1078+import sys
1079+import logging
1080+from subprocess import check_output, check_call, STDOUT
1081+from argparse import ArgumentParser, RawTextHelpFormatter
1082+
1083+log_formatter = '%(asctime)s [%(levelname)-4s] %(funcName)s: %(message)s'
1084+logging.basicConfig(level=logging.DEBUG,
1085+ format=log_formatter,
1086+ datefmt='%Y-%m-%d %H:%M:%S')
1087+
1088+
1089+def read_file_test(mount_point, file_size):
1090+ command = "dd if={0}/test_file of=/dev/null ".format(mount_point) \
1091+ + "bs=1M count={0} iflag=direct".format(file_size)
1092+
1093+ output = check_output(command, stderr=STDOUT, shell=True)
1094+ output = output.decode("utf-8")
1095+
1096+ match = re.search(r'\((.*)\)\D*(\d+\.\d+)\D*(\d+.*)', output)
1097+ if not match:
1098+ logging.error('Does not match patterns of read file test.')
1099+ return False
1100+
1101+ logging.info('Read file successful: %s in %s seconds, speed= %s.'
1102+ %match.group(1,2,3))
1103+ return True
1104+
1105+
1106+def write_file_test(mount_point, file_size):
1107+ command = "dd if=/dev/zero of={0}/test_file ".format(mount_point) \
1108+ + "bs=1M count={0} oflag=direct".format(file_size)
1109+
1110+ output = check_output(command, stderr=STDOUT, shell=True)
1111+ output = output.decode("utf-8")
1112+
1113+ match = re.search(r'\((.*)\)\D*(\d+\.\d+)\D*(\d+.*)', output)
1114+ if not match:
1115+ logging.error('Does not match patterns of write file test.')
1116+ return False
1117+
1118+ logging.info('Write file successful: %s in %s seconds, speed= %s.'
1119+ %match.group(1,2,3))
1120+ return True
1121+
1122+
1123+def umount_filesystem(mount_point):
1124+ umount_cmd = "umount {0}".format(mount_point)
1125+ with open(os.devnull, "w") as NULL:
1126+ check_call(umount_cmd, stdout=NULL, stderr=NULL, shell=True)
1127+ logging.debug('Umount %s directory successful.' %mount_point)
1128+
1129+
1130+def mount_filesystem(device):
1131+ mount_point = '/mnt/' + os.path.basename(device)
1132+ if not os.path.exists(mount_point):
1133+ os.mkdir(mount_point)
1134+ logging.debug('Create %s directory successful!' %mount_point)
1135+ else:
1136+ if os.path.ismount(mount_point):
1137+ logging.debug('The %s directory is already mounted.' %mount_point)
1138+ umount_filesystem(mount_point)
1139+
1140+ mount_cmd = "mount {0} {1}".format(device, mount_point)
1141+ with open(os.devnull, "w") as NULL:
1142+ check_call(mount_cmd, stdout=NULL, stderr=NULL, shell=True)
1143+ logging.debug('Mount %s on %s directory successful.'
1144+ %(device, mount_point))
1145+ return mount_point
1146+
1147+
1148+def format_device(device):
1149+ command = "mkfs.ext4 -F {0}".format(device)
1150+ logging.debug('Format %s device beginning.' %device)
1151+ with open(os.devnull, "w") as NULL:
1152+ check_call(command, stdout=NULL, stderr=NULL, shell=True)
1153+ logging.debug('Format %s completed successfully.' %device)
1154+
1155+
1156+def run(device, file_size):
1157+
1158+ # format device
1159+ try:
1160+ format_device(device)
1161+ except Exception as e:
1162+ logging.error('%s' %e)
1163+ return 20
1164+
1165+ # mount device on system
1166+ try:
1167+ mount_point = mount_filesystem(device)
1168+ except Exception as e:
1169+ logging.error('%s' %e)
1170+ return 30
1171+
1172+ # write file test
1173+ try:
1174+ if not write_file_test(mount_point, file_size):
1175+ return 35
1176+ except Exception as e:
1177+ logging.error('%s' %e)
1178+ return 40
1179+
1180+ # read file test
1181+ try:
1182+ if not read_file_test(mount_point, file_size):
1183+ return 45
1184+ except Exception as e:
1185+ logging.error('%s' %e)
1186+ return 50
1187+
1188+ # umount device
1189+ try:
1190+ umount_filesystem(mount_point)
1191+ except Exception as e:
1192+ logging.error('%s' %e)
1193+ return 60
1194+
1195+ return 0
1196+
1197+
1198+def main():
1199+ description_text = 'read_write_file\n\tTest the device read/write file ' \
1200+ + 'correctly.\n\n\n\tWarning: This program will create one partition' \
1201+ + ' on device and format it.\n\t\t Please make sure what you are ' \
1202+ + 'doing!\n\n\n\tRequirement: Give a device name on the system.'
1203+
1204+ parser = ArgumentParser(description=description_text,
1205+ formatter_class=RawTextHelpFormatter)
1206+
1207+ parser.add_argument('-d', '--device', type=str, required=True,
1208+ help=('The device name which used to read write test.\n'
1209+ '[Example: sda]'))
1210+ parser.add_argument('-m', '--megabyte', type=int,
1211+ default=5000,
1212+ help=('The test file size. [Default: 5GB]'))
1213+
1214+ args = parser.parse_args()
1215+
1216+ device = args.device
1217+ if not device.startswith('/dev/'):
1218+ device = '/dev/' + device
1219+
1220+ if not os.path.exists(device):
1221+ parser.print_help()
1222+ return 10
1223+
1224+ return run(device, args.megabyte)
1225+
1226+
1227+if __name__ == '__main__':
1228+ sys.exit(main())
1229
1230=== added file 'scripts/rebulid_raid'
1231--- scripts/rebulid_raid 1970-01-01 00:00:00 +0000
1232+++ scripts/rebulid_raid 2013-11-25 07:01:57 +0000
1233@@ -0,0 +1,277 @@
1234+#!/usr/bin/env python3
1235+"""
1236+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
1237+Industrial Technology Research Institute
1238+
1239+rebulid_raid
1240+ Test RAID 6 auto rebuild feature in hot spare mode.
1241+
1242+Authors
1243+ Nelson Chu <Nelson.Chu@itri.org.tw>
1244+
1245+This program is free software: you can redistribute it and/or modify
1246+it under the terms of the GNU General Public License version 3,
1247+as published by the Free Software Foundation.
1248+
1249+This program is distributed in the hope that it will be useful,
1250+but WITHOUT ANY WARRANTY; without even the implied warranty of
1251+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1252+GNU General Public License for more details.
1253+
1254+You should have received a copy of the GNU General Public License
1255+along with this program. If not, see <http://www.gnu.org/licenses/>.
1256+
1257+"""
1258+
1259+import os
1260+import re
1261+import sys
1262+import time
1263+import random
1264+import logging
1265+from subprocess import check_output, check_call
1266+from argparse import ArgumentParser, RawTextHelpFormatter
1267+
1268+logFormatter = logging.Formatter(
1269+ "%(asctime)s [%(levelname)-5.5s] %(funcName)s: %(message)s",
1270+ datefmt='%Y-%m-%d %H:%M:%S')
1271+logger = logging.getLogger()
1272+logger.setLevel(logging.DEBUG)
1273+
1274+
1275+def get_adapter():
1276+ """
1277+ Gather all adapter number if there are multiple RAID card.
1278+ """
1279+
1280+ command = 'megacli -CfgDsply -Aall|grep Adapter'
1281+ adapter_list = []
1282+
1283+ adapter_info = check_output(command, shell=True)
1284+ adapter_info = adapter_info.decode('utf-8')
1285+
1286+ for adapter in adapter_info.strip().split('\n'):
1287+ adapter_list.append(adapter.strip().split(' ')[-1])
1288+ logger.debug('adapter_list: %s' %adapter_list)
1289+ return adapter_list
1290+
1291+
1292+def get_all_disk(adapter):
1293+ """
1294+ Gather all disks Enclosure and Slot number, and make a
1295+ Enclosure:Slot pair list.
1296+ """
1297+
1298+ command = ('megacli -PDList -A%s|grep -E "Enclosure Device ID|Slot Numbe"'
1299+ %adapter)
1300+ disk_list = []
1301+ disk_info = check_output(command, shell=True)
1302+ disk_info = disk_info.decode('utf-8')
1303+
1304+ for line in disk_info.strip().split('\n'):
1305+ if line.startswith('Enclosure'):
1306+ match = re.search(r'\d+', line)
1307+ disk_list.append(match.group(0))
1308+ if line.startswith('Slot'):
1309+ match = re.search(r'\d+', line)
1310+ enclosure = disk_list.pop()
1311+ E_S = '%s:%s' %(enclosure, match.group(0))
1312+ disk_list.append(E_S)
1313+
1314+ logger.debug('adapter: %s, disk_list: %s' %(adapter, disk_list))
1315+ return disk_list
1316+
1317+
1318+def set_disk_offline(adapter, disk_list):
1319+ """
1320+ Use all disk creatd RAID 6 and set last disk as hot spare.
1321+ """
1322+
1323+ # Random offline one disk in disk list.
1324+ disk = disk_list[random.randint(0, len(disk_list)-1)]
1325+ command = ('megacli -PDOffline -PhysDrv [%s] -a%s' %(disk, adapter))
1326+ with open(os.devnull, "w") as NULL:
1327+ check_call(command, stdout=NULL, stderr=NULL, shell=True)
1328+ logger.info('Random offline one disk in disk list, Enclosure: %s, Slot: %s'
1329+ %(disk.split(':')[0], disk.split(':')[1]))
1330+
1331+
1332+def find_hotspare_disk(adapter):
1333+ """
1334+ Find hotspare disk in adapter
1335+ """
1336+ command = "megasasctl |grep -E '^a%s.*hotspare'" %adapter
1337+ output = check_output(command, shell=True)
1338+ output = output.decode('utf-8')
1339+ hotspare = output.strip()
1340+
1341+ if len(hotspare.split('\n')) != 1:
1342+ logger.error('Hotspare disk number is not correct.' \
1343+ + 'It can be only one hotspare in a ' \
1344+ + 'adapter.')
1345+ return False
1346+
1347+ match = re.match(r'a(\d+)e(\d+)s(\d+)',hotspare)
1348+ if not match:
1349+ logger.error('Can not found enclosure and slot number of hotspare.')
1350+ return False
1351+
1352+ hotspare_E_S = "%s:%s" %(match.group(2), match.group(3))
1353+ logger.info('Found hotspare, Enclosure: %s, Slot: %s'
1354+ %(match.group(2), match.group(3)))
1355+ return hotspare_E_S
1356+
1357+
1358+def check_rebuild_status(adapter, spare):
1359+ command = 'megacli -PDRbld -ShowProg -PhysDrv [%s] -A%s|grep -i rebuild' \
1360+ %(spare, adapter)
1361+ output = check_output(command, shell=True)
1362+ output = output.decode('utf-8')
1363+ logger.debug('%s' %output.strip())
1364+
1365+ match = re.search(r'Completed (\d+)% in (\d+) Minutes', output)
1366+ if not match:
1367+ return True
1368+ return False
1369+
1370+
1371+def all_rebuild_status(adapter_list, spare_dict):
1372+ for adapter in adapter_list:
1373+ if not check_rebuild_status(adapter, spare_dict[adapter]):
1374+ return False
1375+ return True
1376+
1377+
1378+def confirm_rebuild_status(adapter, spare):
1379+ enclosure, slot = spare.split(':')
1380+ command = ("megasasctl|grep -E '^a%se%ss%s'" %(adapter, enclosure, slot))
1381+ output = check_output(command, shell=True)
1382+ output = output.decode('utf-8')
1383+
1384+ match = re.search(r'online', output)
1385+ if not match:
1386+ logger.error('Hotspare status did not change to online. Adapter: %s, ' \
1387+ +'Hotspare: [$s:$s]' %(adapter, enclosure, slot))
1388+ return False
1389+ return True
1390+
1391+
1392+def run(Adapter= None, Hsp=None, disk_list=None):
1393+ disk_dict = {}
1394+ spare_dict = {}
1395+ adapter_list = []
1396+ rebuild_status = {}
1397+
1398+ try:
1399+ if Adapter:
1400+ adapter_list.append(Adapter)
1401+ else:
1402+ adapter_list = get_adapter()
1403+ except Exception as e:
1404+ logger.error('%s' %e)
1405+ return 10
1406+
1407+ try:
1408+ if Adapter and disk_list:
1409+ disk_dict[Adapter] = disk_list
1410+ else:
1411+ for adapter in adapter_list:
1412+ disk_dict[adapter] = get_all_disk(adapter)
1413+ except Exception as e:
1414+ logger.error('%s' %e)
1415+ return 20
1416+
1417+ try:
1418+ if Adapter and Hsp:
1419+ spare_dict[Adapter] = Hsp
1420+ else:
1421+ for adapter in adapter_list:
1422+ spare_dict[adapter] = find_hotspare_disk(adapter)
1423+ if not spare_dict[adapter]:
1424+ return 30
1425+ except Exception as e:
1426+ logger.error('%s' %e)
1427+ return 40
1428+
1429+ try:
1430+ for adapter in adapter_list:
1431+ # Remove hotspare disk from disk dict.
1432+ if spare_dict[adapter] in disk_dict[adapter]:
1433+ disk_dict[adapter].remove(spare_dict[adapter])
1434+ set_disk_offline(adapter, disk_dict[adapter])
1435+ except Exception as e:
1436+ logger.error('%s' %e)
1437+ return 50
1438+
1439+ # Wait for rebuild process
1440+ time.sleep(5)
1441+
1442+ try:
1443+ while(True):
1444+ if not all_rebuild_status(adapter_list, spare_dict):
1445+ # Check rebuild RAID status every 15 min.
1446+ time.sleep(900)
1447+ continue
1448+ break
1449+ except Exception as e:
1450+ logger.error('%s' %e)
1451+ return 60
1452+
1453+ check = 0
1454+ try:
1455+ for adapter in adapter_list:
1456+ if not confirm_rebuild_status(adapter, spare_dict[adapter]):
1457+ logger.error('Rebuild RAID failed, Adapter: %s, Hotspare: %s'
1458+ %(adapter, spare_dict[adapter]))
1459+ check = 1
1460+ if check != 0:
1461+ return 70
1462+ except Exception as e:
1463+ logger.error('%s' %e)
1464+ return 80
1465+
1466+ logger.info('All adapter rebuild RAID successful!')
1467+ return 0
1468+
1469+
1470+def main():
1471+ description_text = 'rebulit_raid\n\tTests LSI RAID card auto rebuild ' \
1472+ + 'function.\n\n\n\tWarning: This program will rebuild RAID ' \
1473+ + 'automaticity.\n\t\t Please make sure what you are doing!\n\n\n' \
1474+ + '\tRequirement: It can be only one hotspare in a adapter.'
1475+
1476+ parser = ArgumentParser(description=description_text,
1477+ formatter_class=RawTextHelpFormatter)
1478+
1479+ parser.add_argument('-l', '--log', type=str,
1480+ default='/tmp/rebulid_raid.log',
1481+ help=('Specify the location and name of the log file.\n'
1482+ '[Default: %(default)s]'))
1483+ parser.add_argument('-P', '--PhysDrv', type=str,
1484+ help=('The physical drive enclosure and slot of RAID.'
1485+ '\n[Example: --PhysDrv E0:S0,E1:S1,...] '))
1486+ parser.add_argument('-H', '--Hsp', type=str,
1487+ help=('The hotspare\'s enclosure and slot of RAID.'
1488+ '\n[Example: --Hsp E0:S0] '))
1489+ parser.add_argument('-A', '--Adapter', type=str,
1490+ help=('The adapter number of RAID card.'
1491+ '\n[Example: 0'))
1492+ args = parser.parse_args()
1493+
1494+ file_handler = logging.FileHandler(args.log)
1495+ file_handler.setFormatter(logFormatter)
1496+ console_handler = logging.StreamHandler()
1497+ console_handler.setFormatter(logFormatter)
1498+ console_handler.setLevel(logging.INFO)
1499+ logger.addHandler(file_handler)
1500+ logger.addHandler(console_handler)
1501+
1502+ disk_list = None
1503+ if args.PhysDrv:
1504+ disk_list = args.PhysDrv.split(',')
1505+
1506+ logger.info('Rebuild process beginning.')
1507+ return run(args.Adapter, args.Hsp, disk_list)
1508+
1509+if __name__ == '__main__':
1510+ sys.exit(main())

Subscribers

People subscribed via source and target branches