summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/runtime/parselogs.py
diff options
context:
space:
mode:
authorLucian Musat <georgex.l.musat@intel.com>2014-08-18 17:12:28 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-08-29 23:44:37 +0100
commitc730c94a21f67f675b99df0e6bcbdac90801b79b (patch)
tree4073a71400e62948b0f0b8cc2a04aaa7c6309aa5 /meta/lib/oeqa/runtime/parselogs.py
parent00317221bf462e99d3f30e0b075dfe894d80acf1 (diff)
downloadpoky-c730c94a21f67f675b99df0e6bcbdac90801b79b.tar.gz
oeqa/runtime: Automatic test for parsing the logs on a machine and search for certain error keywords.
This adds a common new qa test for general processing of log files. One significant improvement is machine dependent ignore filters. This can be used to replace several weaker individual QA tests that are currently used. (From OE-Core rev: a14d076a401397b6773d5d1b99e49126261f1eb4) Signed-off-by: Lucian Musat <georgex.l.musat@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa/runtime/parselogs.py')
-rw-r--r--meta/lib/oeqa/runtime/parselogs.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/meta/lib/oeqa/runtime/parselogs.py b/meta/lib/oeqa/runtime/parselogs.py
new file mode 100644
index 0000000000..4d4a21b12f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/parselogs.py
@@ -0,0 +1,128 @@
1import os
2import unittest
3from oeqa.oetest import oeRuntimeTest
4from oeqa.utils.decorators import *
5
6#in the future these lists could be moved outside of module
7errors = ["error", "cannot", "can\'t", "failed"]
8
9ignore_errors = { 'genericx86-64': ['mmci-pl18x', 'error changing net interface name', 'dma timeout'], \
10 'genericx86': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS'], \
11 'emenlow': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS', '\[drm:psb_do_init\] \*ERROR\* Debug is'], \
12 'crownbay': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS', 'Could not enable PowerButton event', 'probe of LNXPWRBN:00 failed with error -22'], \
13 'qemuarm': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'mmci-pl18x: probe of fpga:[0-f][0-f] failed with error -38', 'wrong ELF class', 'Fast TSC calibration', 'AE_NOT_FOUND', 'Open ACPI failed', 'Failed to load module "glx"', '\(EE\) error', 'perfctr msr \(MSR c1 is 0\)', 'MMCONFIG information'], \
14 'qemux86-64': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'wrong ELF class', 'Fast TSC calibration', 'AE_NOT_FOUND', 'Open ACPI failed', 'Failed to load module "glx"', '\(EE\) error', 'perfctr msr \(MSR c1 is 0\)', 'MMCONFIG information'] }
15
16log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"]
17
18class ParseLogsTest(oeRuntimeTest):
19
20 @classmethod
21 def setUpClass(self):
22 self.errors = errors
23 self.ignore_errors = ignore_errors
24 self.log_locations = log_locations
25 self.msg = ""
26
27 def getMachine(self):
28 (status, output) = self.target.run("uname -n")
29 return output
30
31 #get some information on the CPU of the machine to display at the beginning of the output. This info might be useful in some cases.
32 def getHardwareInfo(self):
33 hwi = ""
34 (status, cpu_name) = self.target.run("cat /proc/cpuinfo | grep \"model name\" | head -n1 | awk 'BEGIN{FS=\":\"}{print $2}'")
35 (status, cpu_physical_cores) = self.target.run("cat /proc/cpuinfo | grep \"cpu cores\" | head -n1 | awk {'print $4'}")
36 (status, cpu_logical_cores) = self.target.run("cat /proc/cpuinfo | grep \"processor\" | wc -l")
37 (status, cpu_arch) = self.target.run("uname -m")
38 hwi += "Machine information: \n"
39 hwi += "*******************************\n"
40 hwi += "Machine name: "+self.getMachine()+"\n"
41 hwi += "CPU: "+str(cpu_name)+"\n"
42 hwi += "Arch: "+str(cpu_arch)+"\n"
43 hwi += "Physical cores: "+str(cpu_physical_cores)+"\n"
44 hwi += "Logical cores: "+str(cpu_logical_cores)+"\n"
45 hwi += "*******************************\n"
46 return hwi
47
48 #go through the log locations provided and if it's a folder create a list with all the .log files in it, if it's a file just add
49 #it to that list
50 def getLogList(self, log_locations):
51 logs = []
52 for location in log_locations:
53 (status, output) = self.target.run("test -f "+str(location))
54 if (status == 0):
55 logs.append(str(location))
56 else:
57 (status, output) = self.target.run("test -d "+str(location))
58 if (status == 0):
59 (status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f")
60 output = output.splitlines()
61 for logfile in output:
62 logs.append(os.path.join(location,str(logfile)))
63 return logs
64
65 #build the grep command to be used with filters and exclusions
66 def build_grepcmd(self, errors, ignore_errors, log):
67 grepcmd = "grep "
68 grepcmd +="-Ei \""
69 for error in errors:
70 grepcmd += error+"|"
71 grepcmd = grepcmd[:-1]
72 grepcmd += "\" "+str(log)+" | grep -Eiv \'"
73 try:
74 errorlist = ignore_errors[self.getMachine()]
75 except KeyError:
76 self.msg += "No ignore list found for this machine, using generic\n"
77 errorlist = ignore_errors['genericx86']
78 for ignore_error in errorlist:
79 grepcmd += ignore_error+"|"
80 grepcmd = grepcmd[:-1]
81 grepcmd += "\'"
82 return grepcmd
83
84 #grep only the errors so that their context could be collected. Default context is 10 lines before and after the error itself
85 def parse_logs(self, errors, ignore_errors, logs, lines_before = 10, lines_after = 10):
86 results = {}
87 rez = []
88 for log in logs:
89 thegrep = self.build_grepcmd(errors, ignore_errors, log)
90 try:
91 (status, result) = self.target.run(thegrep)
92 except:
93 pass
94 if result:
95 results[log] = {}
96 rez = result.splitlines()
97 for xrez in rez:
98 command = "grep \"\\"+str(xrez)+"\" -B "+str(lines_before)+" -A "+str(lines_after)+" "+str(log)
99 try:
100 (status, yrez) = self.target.run(command)
101 except:
102 pass
103 results[log][xrez]=yrez
104 return results
105
106 #get the output of dmesg and write it in a file. This file is added to log_locations.
107 def write_dmesg(self):
108 (status, dmesg) = self.target.run("dmesg")
109 (status, dmesg2) = self.target.run("echo \""+str(dmesg)+"\" > /tmp/dmesg_output.log")
110
111 @skipUnlessPassed('test_ssh')
112 def test_parselogs(self):
113 self.write_dmesg()
114 log_list = self.getLogList(self.log_locations)
115 result = self.parse_logs(self.errors, self.ignore_errors, log_list)
116 print self.getHardwareInfo()
117 errcount = 0
118 for log in result:
119 self.msg += "Log: "+log+"\n"
120 self.msg += "-----------------------\n"
121 for error in result[log]:
122 errcount += 1
123 self.msg += "Central error: "+str(error)+"\n"
124 self.msg += "***********************\n"
125 self.msg += result[str(log)][str(error)]+"\n"
126 self.msg += "***********************\n"
127 self.msg += "%s errors found in logs." % errcount
128 self.assertEqual(errcount, 0, msg=self.msg) \ No newline at end of file