Merge lp:~nelson-chu/opencompute/add-ocp-cpu-memory-job into lp:opencompute/checkbox

Proposed by Nelson Chu
Status: Superseded
Proposed branch: lp:~nelson-chu/opencompute/add-ocp-cpu-memory-job
Merge into: lp:opencompute/checkbox
Diff against target: 418 lines (+386/-0)
6 files modified
data/whitelists/opencompute-certify-local.whitelist (+44/-0)
jobs/TC-001-0001-CPU_Memory.txt.in (+31/-0)
jobs/local.txt.in (+7/-0)
scripts/cpu_info (+97/-0)
scripts/memory_info (+77/-0)
scripts/processor_topology (+130/-0)
To merge this branch: bzr merge lp:~nelson-chu/opencompute/add-ocp-cpu-memory-job
Reviewer Review Type Date Requested Status
Nelson Chu Needs Resubmitting
Jeff Lane  Needs Fixing
Review via email: mp+196651@code.launchpad.net

This proposal has been superseded by a proposal from 2014-02-12.

To post a comment you must log in.
Revision history for this message
Jeff Lane  (bladernr) wrote :
Download full text (4.6 KiB)

Hi Nelson,

Thank you for breaking this into smaller pieces... it makes review a LOT easier. Now, there are some things that need fixing.

I'll take them one file at a time:
data/whitelists/opencompute-certify-local.whitelist looks fine.

jobs/TC-001-0001-CPU_Memory.txt.in:
1: any job that calls a script that needs root permissions to run must include the 'user: root' definition. See the file 'jobs/cpu.txt.in' for some examples where this is necessary. When I run the script cpu_info without root, the cache data is not returned and the test will fail. So this job needs 'user:root'
2: TC-001-0001-003-Memory_Information also needs 'user: root' added to properly run.

jobs/local.txt.in
1: The word "Verified" should be "Verify" in the description field.

scripts/cpu_info:
1: When I manually run the cpu_info script for the test TC-001-0001-001-CPU_information with and without root permissions, I get the following different outputs:
bladernr@klaatu:~/development/ocp-nelson-memory-cpu-test$ ./scripts/cpu_infoIntel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz
Can not found any CPU cache.
bladernr@klaatu:~/development/ocp-nelson-memory-cpu-test$ echo $?
30
bladernr@klaatu:~/development/ocp-nelson-memory-cpu-test$ sudo ./scripts/cpu_info
Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz
L1 Cache 32 KB
L2 Cache 256 KB
L3 Cache 6 MB
L3 cache size less than 20MB.
bladernr@klaatu:~/development/ocp-nelson-memory-cpu-test$ echo $?
50

I don't have a system that meets the test criteria, so it will always fail for me. First, the explanation for failure should be more explicit. Example: 'FAIL: Can not find any CPU cache.' for the first example. This is more important in the second example where 'L3 cache size less than 20MB' looks like part of the lshw output. It would be better more explicitly stated as 'FAIL: L3 cache size less than 20MB.'

2: You don't need all those explicit error codes. Checkbox only knows and stores 0 and Not 0 exit codes. A 0 exit code indicates a test passed. A non-zero exit code indicates a failure. Checkbox does not store the actual exit codes. This is not necessarily something you need to change, but you DO need to be aware of the behaviour in case a script behaves differently than expected when you run it via checkbox.

3: The description and spec for this test says: "CPU model should belong to Intel Xeon processor E5-2600 family..." but your test does not fail on non-Xeon processors. For example, when I comment out the error code return for my cache limit on my laptop, I get this output:
bladernr@klaatu:~/development/ocp-nelson-memory-cpu-test$ sudo ./scripts/cpu_info; echo $?
Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz
L1 Cache 32 KB
L2 Cache 256 KB
L3 Cache 6 MB
L3 cache size less than 20MB.
0

but my laptop should clearly fail the test case since it's not up to OCP spec.

4: The output needs to be cleaned up a bit. Sorry, I know English is a second language for you, so I'll try to help as much as I can.
    "Can not parser" should be "Can not parse"
    "Can not found" should be "Can not find"
    "# Parser lshw XML for gather" should be "# Parse lshw XML for gathering"

scripts/memory_info:
1: Needs to be run as ...

Read more...

review: Needs Fixing
Revision history for this message
Nelson Chu (nelson-chu) wrote :

OK, Thank you for your suggestion.
I will revise it accordingly.

Revision history for this message
Nelson Chu (nelson-chu) wrote :

Hi Jeff,

I have modified scripts. Please help me review them.
Any suggestion will be appreciated.

Thanks,
Nelson

review: Needs Resubmitting
2169. By Nelson Chu

Merges Nelson Chu's PCH tests with a couple minor typo corrections in the scripts.

2170. By Nelson Chu

Merge add-ocp-cpu-memory-job to lp:opencompute/checkbox and fixed some conflicts

2171. By Nelson Chu

Modify cpu_info and memory_info scripts. Revise debian/changelog file.

2172. By Nelson Chu

debian/changelog file and memory_info script

Unmerged revisions

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 2014-02-07 09:50:29 +0000
4@@ -0,0 +1,44 @@
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-Processor_Topology
48+TC-001-0001-003-Memory_Information
49
50=== added file 'jobs/TC-001-0001-CPU_Memory.txt.in'
51--- jobs/TC-001-0001-CPU_Memory.txt.in 1970-01-01 00:00:00 +0000
52+++ jobs/TC-001-0001-CPU_Memory.txt.in 2014-02-07 09:50:29 +0000
53@@ -0,0 +1,31 @@
54+plugin: shell
55+name: TC-001-0001-001-CPU_Information
56+requires: package.name == 'lshw'
57+user: root
58+command: cpu_info -p Xeon -f E5
59+description:
60+ 1. Use lshw command to gather CPU information.
61+ 2. The program will output CPU model and L1, L2, L3 cache size.
62+ 3. Criteria: CPU model must be Intel Xeon processor E5-2600 product family and L3 cache size must be up to 20MB.
63+
64+plugin: shell
65+name: TC-001-0001-002-Processor_Topology
66+command: processor_topology
67+description:
68+ 1. This test checks CPU topology for accuracy.
69+ 2. Use lscpu command to gather CPU information.
70+ 3. The program will output the total number of CPUs, the number of threads per core, the number of cores per socket, and the number of sockets.
71+ 4. Criteria: It should be 8-12 cores per CPU and 2 threads per core.
72+
73+plugin: shell
74+name: TC-001-0001-003-Memory_Information
75+requires: package.name == 'lshw'
76+user: root
77+command: memory_info
78+description:
79+ 1. Use lshw command to gather memory information.
80+ 2. Testing prerequisites:
81+ 4 channels DDR3 registered memory interface on each processor 0 and processor 1.
82+ 2 DDR3 slots per channel per processor. (total of 16 DIMMs on the motherboard)
83+ 3. The program will output memory module, vendor, size and slot.
84+ 4. Criteria: Total of 16 DIMMs on the motherboard.
85
86=== modified file 'jobs/local.txt.in'
87--- jobs/local.txt.in 2013-10-01 00:55:26 +0000
88+++ jobs/local.txt.in 2014-02-07 09:50:29 +0000
89@@ -109,3 +109,10 @@
90 command:
91 shopt -s extglob
92 cat $CHECKBOX_SHARE/jobs/sniff.txt?(.in)
93+
94+name: __TC-001-0001-CPU_Memory__
95+plugin: local
96+_description: Verify CPU and memory
97+command:
98+ shopt -s extglob
99+ cat $CHECKBOX_SHARE/jobs/TC-001-0001-CPU_Memory.txt?(.in)
100
101=== added file 'scripts/cpu_info'
102--- scripts/cpu_info 1970-01-01 00:00:00 +0000
103+++ scripts/cpu_info 2014-02-07 09:50:29 +0000
104@@ -0,0 +1,97 @@
105+#!/usr/bin/env python3
106+"""
107+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
108+Industrial Technology Research Institute
109+
110+cpu_info
111+ Use lshw command to gather CPU information.
112+ The program will output CPU model and L1, L2, L3 cache size.
113+ Criteria: CPU model and product family must match user's input
114+ and L3 cache should be larger than 20MB.
115+
116+Authors
117+ Nelson Chu <Nelson.Chu@itri.org.tw>
118+
119+This program is free software: you can redistribute it and/or modify
120+it under the terms of the GNU General Public License version 3,
121+as published by the Free Software Foundation.
122+
123+This program is distributed in the hope that it will be useful,
124+but WITHOUT ANY WARRANTY; without even the implied warranty of
125+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
126+GNU General Public License for more details.
127+
128+You should have received a copy of the GNU General Public License
129+along with this program. If not, see <http://www.gnu.org/licenses/>.
130+
131+"""
132+
133+import os
134+import re
135+import sys
136+import xml.etree.ElementTree as ET
137+from subprocess import check_output
138+from argparse import ArgumentParser, RawTextHelpFormatter
139+
140+def run(product, family):
141+ command = "lshw -xml"
142+ with open(os.devnull, 'w') as NULL:
143+ hwinfo_xml = check_output(command, stderr=NULL, shell=True)
144+ root = ET.fromstring(hwinfo_xml)
145+
146+ # Parse lshw XML for gathering processor information.
147+ processor = root.findall(".//product/..[@class='processor']")
148+
149+ if not processor:
150+ print("Fail: Cannot parse any processor information.")
151+ return 10
152+
153+ for cpu in processor:
154+ if cpu.find('product') is None:
155+ print("Fail: Cannot find processor product.")
156+ return 20
157+ print(cpu.find('product').text)
158+ match = re.search(product + '.*' + family, cpu.find('product').text)
159+ if not match:
160+ print("Fail: Cannot match CPU %s %s family." %(product, family))
161+ return 25
162+
163+ cache_list = cpu.findall(".//size/..[@class='memory']")
164+ if not cache_list:
165+ print("Fail: Cannot find any CPU cache.")
166+ return 30
167+
168+ for cache in cache_list:
169+ if cache.find('size') is None or cache.find('slot') is None:
170+ print("Fail: Cannot access Last Level Cache (LLC).")
171+ return 40
172+
173+ cache_size = int(cache.find('size').text) / 1024
174+ if cache_size > 1024:
175+ cache_size = cache_size / 1024
176+ print(('%s %d MB') %(cache.find('slot').text, cache_size))
177+ if re.search('L3', cache.find('slot').text):
178+ if cache_size < 20:
179+ print('Fail: L3 cache size less than 20MB.')
180+ return 50
181+ else:
182+ print(('%s %d KB') %(cache.find('slot').text, cache_size))
183+
184+ return 0
185+
186+def main():
187+ parser = ArgumentParser(formatter_class=RawTextHelpFormatter)
188+
189+ parser.add_argument('-p', '--product', type=str, required=True,
190+ help=("The CPU product name. [Example: Xeon]"))
191+ parser.add_argument('-f', '--family', type=str, required=True,
192+ help=("Processor family. [Example: E5]"))
193+
194+ args = parser.parse_args()
195+
196+ product = args.product.title()
197+ family = args.family.title()
198+ return run(product, family)
199+
200+if __name__ == '__main__':
201+ sys.exit(main())
202
203=== added file 'scripts/memory_info'
204--- scripts/memory_info 1970-01-01 00:00:00 +0000
205+++ scripts/memory_info 2014-02-07 09:50:29 +0000
206@@ -0,0 +1,77 @@
207+#!/usr/bin/env python3
208+"""
209+Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications
210+Industrial Technology Research Institute
211+
212+memory_info
213+ 1. Use lshw command to gather memory information.
214+ 2. Testing prerequisites:
215+ 4 channels DDR3 registered memory interface on each processor 0
216+ and processor 1.
217+ 2 DDR3 slots per channel per processor. (total of 16 DIMMs
218+ on the motherboard)
219+ 3. The program will output memory module, vendor, size and slot.
220+ 4. Criteria: Total of 16 DIMMs on the motherboard.
221+
222+Authors
223+ Nelson Chu <Nelson.Chu@itri.org.tw>
224+
225+This program is free software: you can redistribute it and/or modify
226+it under the terms of the GNU General Public License version 3,
227+as published by the Free Software Foundation.
228+
229+This program is distributed in the hope that it will be useful,
230+but WITHOUT ANY WARRANTY; without even the implied warranty of
231+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
232+GNU General Public License for more details.
233+
234+You should have received a copy of the GNU General Public License
235+along with this program. If not, see <http://www.gnu.org/licenses/>.
236+
237+"""
238+
239+import sys
240+import xml.etree.ElementTree as ET
241+from subprocess import Popen, PIPE
242+
243+def main():
244+ attribute = ['description', 'vendor', 'slot', 'size']
245+ command = 'lshw -xml'
246+ hwinfo_xml = Popen(command, stdout=PIPE, stderr=PIPE,
247+ shell=True).communicate()[0]
248+ root = ET.fromstring(hwinfo_xml)
249+
250+ # Parse lshw XML for gathering memory information.
251+ memory_list = root.findall(".//clock/..[@class='memory']")
252+
253+ if not memory_list:
254+ print("Fail: Cannot parse any memory information.")
255+ return 10
256+
257+ count = 0
258+ for dimm in memory_list:
259+ count = count +1
260+ for attr in attribute:
261+ if dimm.find(attr) is None:
262+ print(("Fail: Cannot find memory %s") %attr)
263+ return 20
264+
265+ memory_size = int(dimm.find('size').text) / (1024**3)
266+ for attr in attribute:
267+ if attr == 'description':
268+ print('%s' %(dimm.find(attr).text), end=" ")
269+ continue
270+ elif attr == 'size':
271+ print('%s=%dGB.' %(attr, memory_size))
272+ continue
273+ print('%s=%s' %(attr, dimm.find(attr).text), end=" ")
274+
275+ print("Total number of DIMMs is %s." %(count))
276+ if count != 16:
277+ print("Fail: Memory DIMM number is not meet the requirement.")
278+ return 30
279+
280+ return 0
281+
282+if __name__ == '__main__':
283+ sys.exit(main())
284
285=== added file 'scripts/processor_topology'
286--- scripts/processor_topology 1970-01-01 00:00:00 +0000
287+++ scripts/processor_topology 2014-02-07 09:50:29 +0000
288@@ -0,0 +1,130 @@
289+#!/usr/bin/env python3
290+'''
291+cpu_topology
292+Written by Jeffrey Lane <jeffrey.lane@canonical.com>
293+'''
294+import sys
295+import os
296+from subprocess import check_output
297+
298+class proc_cpuinfo():
299+ '''
300+ Class to get and handle information from /proc/cpuinfo
301+ Creates a dictionary of data gleaned from that file.
302+ '''
303+ def __init__(self):
304+ self.cpuinfo = {}
305+ cpu_fh = open('/proc/cpuinfo', 'r')
306+ try:
307+ temp = cpu_fh.readlines()
308+ finally:
309+ cpu_fh.close()
310+
311+ for i in temp:
312+ if i.startswith('processor'):
313+ key = 'cpu' + (i.split(':')[1].strip())
314+ self.cpuinfo[key] = {'core_id':'', 'physical_package_id':''}
315+ elif i.startswith('core id'):
316+ self.cpuinfo[key].update({'core_id': i.split(':')[1].strip()})
317+ elif i.startswith('physical id'):
318+ self.cpuinfo[key].update({'physical_package_id':
319+ i.split(':')[1].strip()})
320+ else:
321+ continue
322+
323+
324+class sysfs_cpu():
325+ '''
326+ Class to get and handle information from sysfs as relates to CPU topology
327+ Creates an informational class to present information on various CPUs
328+ '''
329+
330+ def __init__(self, proc):
331+ self.syscpu = {}
332+ self.path = '/sys/devices/system/cpu/' + proc + '/topology'
333+ items = ['core_id', 'physical_package_id']
334+ for i in items:
335+ syscpu_fh = open(os.path.join(self.path, i), 'r')
336+ try:
337+ self.syscpu[i] = syscpu_fh.readline().strip()
338+ finally:
339+ syscpu_fh.close()
340+
341+
342+def compare(proc_cpu, sys_cpu):
343+ cpu_map = {}
344+ '''
345+ If there is only 1 CPU the test don't look for core_id
346+ and physical_package_id because those information are absent in
347+ /proc/cpuinfo on singlecore system
348+ '''
349+ for key in proc_cpu.keys():
350+ if 'cpu1' not in proc_cpu:
351+ cpu_map[key] = True
352+ else:
353+ for subkey in proc_cpu[key].keys():
354+ if proc_cpu[key][subkey] == sys_cpu[key][subkey]:
355+ cpu_map[key] = True
356+ else:
357+ cpu_map[key] = False
358+ return cpu_map
359+
360+
361+def main():
362+ cpuinfo = proc_cpuinfo()
363+ sys_cpu = {}
364+ keys = cpuinfo.cpuinfo.keys()
365+ for k in keys:
366+ sys_cpu[k] = sysfs_cpu(k).syscpu
367+ cpu_map = compare(cpuinfo.cpuinfo, sys_cpu)
368+ if False in cpu_map.values() or len(cpu_map) < 1:
369+ print("FAIL: CPU Topology is incorrect", file=sys.stderr)
370+ print("-" * 52, file=sys.stderr)
371+ print("{0}{1}".format("/proc/cpuinfo".center(30), "sysfs".center(25)),
372+ file=sys.stderr)
373+ print("{0}{1}{2}{3}{1}{2}".format(
374+ "CPU".center(6),
375+ "Physical ID".center(13),
376+ "Core ID".center(9),
377+ "|".center(3)), file=sys.stderr)
378+ for key in sorted(sys_cpu.keys()):
379+ print("{0}{1}{2}{3}{4}{5}".format(
380+ key.center(6),
381+ cpuinfo.cpuinfo[key]['physical_package_id'].center(13),
382+ cpuinfo.cpuinfo[key]['core_id'].center(9),
383+ "|".center(3),
384+ sys_cpu[key]['physical_package_id'].center(13),
385+ sys_cpu[key]['core_id'].center(9)), file=sys.stderr)
386+ return 1
387+ else:
388+ # Use lscpu command to gather CPU information.
389+ # Output the total number of CPUs, the number of threads per core,
390+ # the number of cores per socket, and the number of sockets.
391+ # Criteria: It must be 8-12 cores per CPU and 2 threads per core.
392+ # Revised by Nelson Chu <nelson.chu@itri.org.tw>
393+ command = 'lscpu'
394+ return_code = 0
395+
396+ with open(os.devnull, "w") as NULL:
397+ cpu_info = check_output(command, stderr=NULL, shell=True)
398+
399+ cpu_info = cpu_info.decode('utf-8')
400+
401+ for cpu in cpu_info.split('\n'):
402+ if cpu.startswith("CPU(s)"):
403+ print(cpu)
404+ if cpu.startswith("Thread(s) per core"):
405+ print(cpu)
406+ if cpu.startswith("Core(s) per socket"):
407+ cores = int(cpu.split(":")[1])
408+ print(cpu)
409+ # It should be 8-12 cores per CPU.
410+ if cores < 8 or cores > 12:
411+ return_code = 1
412+ if cpu.startswith("Socket(s)"):
413+ print(cpu)
414+
415+ return return_code
416+
417+if __name__ == '__main__':
418+ sys.exit(main())

Subscribers

People subscribed via source and target branches