Merge lp:~cr3/checkbox/story_384 into lp:checkbox

Proposed by Marc Tardif
Status: Merged
Merged at revision: 1091
Proposed branch: lp:~cr3/checkbox/story_384
Merge into: lp:checkbox
Diff against target: 2872 lines (+1561/-856)
20 files modified
checkbox/dispatcher.py (+191/-0)
checkbox/lib/bit.py (+5/-4)
checkbox/lib/conversion.py (+140/-42)
checkbox/lib/dmi.py (+169/-18)
checkbox/parsers/cpuinfo.py (+30/-41)
checkbox/parsers/cputable (+40/-0)
checkbox/parsers/cputable.py (+42/-0)
checkbox/parsers/deferred.py (+27/-0)
checkbox/parsers/dmidecode.py (+123/-0)
checkbox/parsers/meminfo.py (+46/-0)
checkbox/parsers/submission.py (+474/-383)
checkbox/parsers/udevadm.py (+178/-317)
jobs/info.txt.in (+4/-0)
jobs/resource.txt.in (+5/-0)
scripts/cpuinfo_resource (+13/-17)
scripts/dmi_resource (+55/-0)
scripts/hal_resource (+0/-5)
scripts/meminfo_resource (+15/-27)
scripts/udev_resource (+2/-2)
setup.py (+2/-0)
To merge this branch: bzr merge lp:~cr3/checkbox/story_384
Reviewer Review Type Date Requested Status
Daniel Manrique (community) Approve
Review via email: mp+79147@code.launchpad.net

Description of the change

This branch refactors the parsers significantly so that the generated submission.xml can be processed offline from another system with a different architecture. To test the checkbox and checkbox-certification branches in a Hardware Certification context:

1. Get the latest source either from the branches, or the packages currently building in ppa:cr3 (might not be ready for a few hours), or use the packages by on chinstrap:~cr3/public_html/checkbox (might be more convenient here https://chinstrap.canonical.com/~cr3/checkbox/);
2. Run the command: checkbox-certification-gtk -W /usr/share/checkbox-certification/data/certification.whitelist
3. All tests can be unselected, finish clicking on the Next button and don't worry about the invalid submission.
4. Open the URL of the submission that was created, like this one for example: https://certification.canonical.com/hardware/200910-4253/submission/QhVLZKfT8jX34a3

To test that the generated submission.xml can be processed offline:

5. Run the command: checkbox-to-certification <hardware secure ID> ~/.checkbox/submission.xml
6. Open the URL of the submission that was created, like this one for example: https://certification.canonical.com/hardware/200910-4253/submission/FkfBqEGrFcTDD5O

Finally, compare that both submissions have the same details for: release, architecture, attachments, devices, packages, processor and memory.

To post a comment you must log in.
Revision history for this message
Daniel Manrique (roadmr) wrote :

Looks OK and works as advertised. Merging, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'checkbox/dispatcher.py'
2--- checkbox/dispatcher.py 1970-01-01 00:00:00 +0000
3+++ checkbox/dispatcher.py 2011-10-12 15:56:08 +0000
4@@ -0,0 +1,191 @@
5+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+__metaclass__ = type
9+
10+__all__ = [
11+ "Dispatcher",
12+ "DispatcherList",
13+ "DispatcherQueue",
14+ ]
15+
16+import logging
17+
18+from itertools import product
19+
20+
21+class Event:
22+ """Event payload containing the positional and keywoard arguments
23+ passed to the handler in the event listener."""
24+
25+ def __init__(self, type, *args, **kwargs):
26+ self.type = type
27+ self.args = args
28+ self.kwargs = kwargs
29+
30+
31+class Listener:
32+ """Event listener notified when events are published by the dispatcher."""
33+
34+ def __init__(self, event_type, handler, count):
35+ self.event_type = event_type
36+ self.handler = handler
37+ self.count = count
38+
39+ def notify(self, event):
40+ """Notify the handler with the payload of the event.
41+
42+ :param event: The event containint the payload for the handler.
43+ """
44+ if self.count is None or self.count:
45+ self.handler(*event.args, **event.kwargs)
46+ if self.count:
47+ self.count -= 1
48+
49+
50+class ListenerList(Listener):
51+ """Event listener notified for lists of events."""
52+
53+ def __init__(self, *args, **kwargs):
54+ super(ListenerList, self).__init__(*args, **kwargs)
55+ self.event_types = set(self.event_type)
56+ self.kwargs = {}
57+
58+ def notify(self, event):
59+ """Only notify the handler when all the events for this listener
60+ have been published by the dispatcher. When duplicate events
61+ occur, the latest event is preserved and the previous one are
62+ overwritten until all events have been published.
63+ """
64+ if self.count is None or self.count:
65+ self.kwargs[event.type] = event.args[0]
66+ if self.event_types.issubset(self.kwargs):
67+ self.handler(**self.kwargs)
68+ if self.count:
69+ self.count -= 1
70+
71+
72+class ListenerQueue(ListenerList):
73+
74+ def notify(self, event):
75+ """Only notify the handler when all the events for this listener
76+ have been published by the dispatcher. Duplicate events are enqueued
77+ and dequeued only when all events have been published.
78+ """
79+ arg = event.args[0]
80+ queue = self.kwargs.setdefault(event.type, [])
81+
82+ # Strip duplicates from the queue.
83+ if arg not in queue:
84+ queue.append(arg)
85+
86+ # Once the queue has handler has been called, the queue
87+ # then behaves like a list using the latest events.
88+ if self.event_types.issubset(self.kwargs):
89+ self.notify = notify = super(ListenerQueue, self).notify
90+ keys = self.kwargs.keys()
91+ for values in product(*self.kwargs.values()):
92+ self.kwargs = dict(zip(keys, values))
93+ notify(event)
94+
95+
96+class Dispatcher:
97+ """Register handlers and publish events for them identified by strings."""
98+
99+ listener_factory = Listener
100+
101+ def __init__(self, listener_factory=None):
102+ self._event_listeners = {}
103+
104+ if listener_factory is not None:
105+ self.listener_factory = listener_factory
106+
107+ def registerHandler(self, event_type, handler, count=None):
108+ """Register an event handler and return its listener.
109+
110+ :param event_type: The name of the event type to handle.
111+ :param handler: The function handling the given event type.
112+ :param count: Optionally, the number times to call the handler.
113+ """
114+ listener = self.listener_factory(event_type, handler, count)
115+
116+ listeners = self._event_listeners.setdefault(event_type, [])
117+ listeners.append(listener)
118+
119+ return listener
120+
121+ def unregisterHandler(self, handler):
122+ """Unregister a handler.
123+
124+ :param handler: The handler to unregister.
125+ """
126+ for event_type, listeners in self._event_listeners.items():
127+ listeners = [
128+ listener for listener in listeners
129+ if listener.handler == handler]
130+ if listeners:
131+ self._event_listeners[event_type] = listeners
132+ else:
133+ del self._event_listeners[event_type]
134+
135+ def unregisterListener(self, listener):
136+ """Unregister a listener.
137+
138+ :param listener: The listener of the handler to unregister.
139+ """
140+ self._event_listeners[listener.event_type].remove(listener)
141+
142+ def publishEvent(self, event_type, *args, **kwargs):
143+ """Publish an event of a given type and notify all listeners.
144+
145+ :param event_type: The name of the event type to publish.
146+ :param args: Positional arguments to pass to the registered handlers.
147+ :param kwargs: Keyword arguments to pass to the registered handlers.
148+ """
149+ event = Event(event_type, *args, **kwargs)
150+ for listener in self._event_listeners.get(event_type, ()):
151+ try:
152+ listener.notify(event)
153+ except:
154+ logging.exception(
155+ "Error running event handler for %r with args %r %r",
156+ event_type, args, kwargs)
157+
158+
159+class DispatcherList(Dispatcher):
160+ """
161+ Register handlers and publish events for them identified by lists
162+ of strings.
163+ """
164+
165+ listener_factory = ListenerList
166+
167+ def registerHandler(self, event_types, handler, count=None):
168+ """See Dispatcher."""
169+ if not isinstance(event_types, (list, tuple,)):
170+ event_types = (event_types,)
171+
172+ listener = self.listener_factory(event_types, handler, count)
173+ for event_type in event_types:
174+ listeners = self._event_listeners.setdefault(event_type, [])
175+ listeners.append(listener)
176+
177+ return listener
178+
179+ def unregisterListener(self, listener):
180+ """See Dispatcher."""
181+ for event_type in listener.event_types:
182+ self._event_listeners[event_type].remove(listener)
183+
184+ def publishEvent(self, event_type, arg):
185+ """See Dispatcher."""
186+ super(DispatcherList, self).publishEvent(event_type, arg)
187+
188+
189+class DispatcherQueue(DispatcherList):
190+ """
191+ Register handlers and publish events for them identified by lists
192+ of strings in queue order.
193+ """
194+
195+ listener_factory = ListenerQueue
196
197=== modified file 'checkbox/lib/bit.py'
198--- checkbox/lib/bit.py 2011-01-27 19:35:50 +0000
199+++ checkbox/lib/bit.py 2011-10-12 15:56:08 +0000
200@@ -36,10 +36,11 @@
201
202 return bitcount
203
204-def test_bit(bit, bitmask):
205- bits_per_long = calcsize("l") * 8
206- offset = bit % bits_per_long
207- long = int(bit / bits_per_long)
208+def test_bit(bit, bitmask, bits=None):
209+ if bits is None:
210+ bits = calcsize("l") * 8
211+ offset = bit % bits
212+ long = int(bit / bits)
213 if long >= len(bitmask):
214 return 0
215 return (bitmask[long] >> offset) & 1
216
217=== modified file 'checkbox/lib/conversion.py'
218--- checkbox/lib/conversion.py 2009-02-16 15:48:24 +0000
219+++ checkbox/lib/conversion.py 2011-10-12 15:56:08 +0000
220@@ -18,48 +18,110 @@
221 #
222 import re
223
224-
225-def string_to_type(value):
226- conversion_table = (
227- ("(yes|true)", lambda v: True),
228- ("(no|false)", lambda v: False),
229- ("\d+", lambda v: int(v.group(0))),
230- ("\d+\.\d+", lambda v: float(v.group(0))),
231- ("(\d+) ?([kmgt]?b?)", lambda v: int(v.group(1))),
232- ("(\d+\.\d+) ?([kmgt]?b?)", lambda v: float(v.group(1))),
233- ("(\d+) ?([kmgt]?hz?)", lambda v: int(v.group(1))),
234- ("(\d+\.\d+) ?([kmgt]?hz?)", lambda v: float(v.group(1))))
235-
236- multiplier_table = (
237- ("b", 1),
238- ("kb?", 1024),
239- ("mb?", 1024 * 1024),
240- ("gb?", 1024 * 1024 * 1024),
241- ("tb?", 1024 * 1024 * 1024 * 1024),
242- ("hz", 1),
243- ("khz?", 1024),
244- ("mhz?", 1024 * 1024),
245- ("ghz?", 1024 * 1024 * 1024),
246- ("thz?", 1024 * 1024 * 1024 * 1024))
247-
248- if isinstance(value, basestring):
249- for regex, conversion in conversion_table:
250- match = re.match("^%s$" % regex, value, re.IGNORECASE)
251- if match:
252- value = conversion(match)
253- if len(match.groups()) < 2:
254- return value
255-
256- unit = match.group(2)
257- for regex, multiplier in multiplier_table:
258- match = re.match("^%s$" % regex, unit, re.IGNORECASE)
259- if match:
260- value *= multiplier
261- return value
262- else:
263- raise Exception, "Unknown multiplier: %s" % unit
264-
265- return value
266+from dateutil import tz
267+from datetime import (
268+ datetime,
269+ timedelta,
270+ )
271+
272+
273+DATETIME_RE = re.compile(r"""
274+ ^(?P<year>\d\d\d\d)-?(?P<month>\d\d)-?(?P<day>\d\d)
275+ T(?P<hour>\d\d):?(?P<minute>\d\d):?(?P<second>\d\d)
276+ (?:\.(?P<second_fraction>\d{0,6}))?
277+ (?P<tz>
278+ (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
279+ | Z)?$
280+ """, re.VERBOSE)
281+
282+TYPE_FORMATS = (
283+ (r"(yes|true)", lambda v: True),
284+ (r"(no|false)", lambda v: False),
285+ (r"-?\d+", lambda v: int(v.group(0))),
286+ (r"-?\d+\.\d+", lambda v: float(v.group(0))),
287+ (r"(-?\d+) ?([kmgt]?b?)", lambda v: int(v.group(1))),
288+ (r"(-?\d+\.\d+) ?([kmgt]?b?)", lambda v: float(v.group(1))),
289+ (r"(-?\d+) ?([kmgt]?hz)", lambda v: int(v.group(1))),
290+ (r"(-?\d+\.\d+) ?([kmgt]?hz)", lambda v: float(v.group(1))))
291+TYPE_FORMATS = tuple(
292+ (re.compile(r"^%s$" % pattern, re.IGNORECASE), format)
293+ for pattern, format in TYPE_FORMATS)
294+
295+TYPE_MULTIPLIERS = (
296+ (r"b", 1),
297+ (r"kb?", 1024),
298+ (r"mb?", 1024 * 1024),
299+ (r"gb?", 1024 * 1024 * 1024),
300+ (r"tb?", 1024 * 1024 * 1024 * 1024),
301+ (r"hz", 1),
302+ (r"khz?", 1024),
303+ (r"mhz?", 1024 * 1024),
304+ (r"ghz?", 1024 * 1024 * 1024),
305+ (r"thz?", 1024 * 1024 * 1024 * 1024))
306+TYPE_MULTIPLIERS = tuple(
307+ (re.compile(r"^%s$" % pattern, re.IGNORECASE), multiplier)
308+ for pattern, multiplier in TYPE_MULTIPLIERS)
309+
310+
311+def datetime_to_string(dt):
312+ """Return a consistent string representation for a given datetime.
313+
314+ :param dt: The datetime object.
315+ """
316+ return dt.isoformat()
317+
318+def string_to_datetime(string):
319+ """Return a datetime object from a consistent string representation.
320+
321+ :param string: The string representation.
322+ """
323+ # we cannot use time.strptime: this function accepts neither fractions
324+ # of a second nor a time zone given e.g. as '+02:30'.
325+ match = DATETIME_RE.match(string)
326+
327+ # The Relax NG schema allows a leading minus sign and year numbers
328+ # with more than four digits, which are not "covered" by _time_regex.
329+ if not match:
330+ raise ValueError("Datetime with unreasonable value: %s" % string)
331+
332+ time_parts = match.groupdict()
333+
334+ year = int(time_parts['year'])
335+ month = int(time_parts['month'])
336+ day = int(time_parts['day'])
337+ hour = int(time_parts['hour'])
338+ minute = int(time_parts['minute'])
339+ second = int(time_parts['second'])
340+ second_fraction = time_parts['second_fraction']
341+ if second_fraction is not None:
342+ milliseconds = second_fraction + '0' * (6 - len(second_fraction))
343+ milliseconds = int(milliseconds)
344+ else:
345+ milliseconds = 0
346+
347+ # The Relax NG validator accepts leap seconds, but the datetime
348+ # constructor rejects them. The time values submitted by the HWDB
349+ # client are not necessarily very precise, hence we can round down
350+ # to 59.999999 seconds without losing any real precision.
351+ if second > 59:
352+ second = 59
353+ milliseconds = 999999
354+
355+ dt = datetime(
356+ year, month, day, hour, minute, second, milliseconds,
357+ tzinfo=tz.tzutc())
358+
359+ tz_sign = time_parts['tz_sign']
360+ tz_hour = time_parts['tz_hour']
361+ tz_minute = time_parts['tz_minute']
362+ if tz_sign in ('-', '+'):
363+ delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
364+ if tz_sign == '-':
365+ dt = dt + delta
366+ else:
367+ dt = dt - delta
368+
369+ return dt
370
371 def sizeof_bytes(bytes):
372 for x in ["bytes", "KB", "MB", "GB", "TB"]:
373@@ -78,3 +140,39 @@
374 hertz /= 1000.0
375
376 return string
377+
378+def string_to_type(string, string_type=None):
379+ """Return a typed representation for the given string.
380+
381+ The result might be a bool, int or float. The string might also be
382+ supplemented by a multiplier like KB which would return an int or
383+ float multiplied by 1024 for example.
384+
385+ :param string: The string representation.
386+ :param string_type: Optional type to cast the result.
387+ """
388+ if not isinstance(string, basestring):
389+ raise TypeError(
390+ "string_to_type() argument 1 must be string, not %s"
391+ % type(string))
392+
393+ for regex, formatter in TYPE_FORMATS:
394+ match = regex.match(string)
395+ if match:
396+ string = formatter(match)
397+ if len(match.groups()) > 1:
398+ unit = match.group(2)
399+ for regex, multiplier in TYPE_MULTIPLIERS:
400+ match = regex.match(unit)
401+ if match:
402+ string *= multiplier
403+ break
404+ else:
405+ raise ValueError("Unknown multiplier: %s" % unit)
406+
407+ break
408+
409+ if string_type is not None:
410+ string = string_type(string)
411+
412+ return string
413
414=== modified file 'checkbox/lib/dmi.py'
415--- checkbox/lib/dmi.py 2009-09-06 14:19:16 +0000
416+++ checkbox/lib/dmi.py 2011-10-12 15:56:08 +0000
417@@ -16,13 +16,15 @@
418 # You should have received a copy of the GNU General Public License
419 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
420 #
421+import os
422+
423
424 # See also 3.3.4.1 of the "System Management BIOS Reference Specification,
425 # Version 2.6.1" (Preliminary Standard) document, available from
426 # http://www.dmtf.org/standards/smbios.
427 class Dmi:
428 chassis = (
429- ("Undefined", "unknown"), # 0x00
430+ ("Undefined", "unknown"), # 0x00
431 ("Other", "unknown"),
432 ("Unknown", "unknown"),
433 ("Desktop", "desktop"),
434@@ -53,22 +55,171 @@
435 ("Blade", "server"),
436 ("Blade Enclosure", "unknown"))
437
438- chassis_names = [c[0] for c in chassis]
439- chassis_types = [c[1] for c in chassis]
440+ chassis_names = tuple(c[0] for c in chassis)
441+ chassis_types = tuple(c[1] for c in chassis)
442 chassis_name_to_type = dict(chassis)
443
444-
445-class DmiNotAvailable(object):
446- def __init__(self, function):
447- self._function = function
448-
449- def __get__(self, instance, cls=None):
450- self._instance = instance
451- return self
452-
453- def __call__(self, *args, **kwargs):
454- name = self._function(self._instance, *args, **kwargs)
455- if name == "Not Available":
456- name = None
457-
458- return name
459+ type_names = (
460+ "BIOS", # 0x00
461+ "System",
462+ "Base Board",
463+ "Chassis",
464+ "Processor",
465+ "Memory Controller",
466+ "Memory Module",
467+ "Cache",
468+ "Port Connector",
469+ "System Slots",
470+ "On Board Devices",
471+ "OEM Strings",
472+ "System Configuration Options",
473+ "BIOS Language",
474+ "Group Associations",
475+ "System Event Log",
476+ "Physical Memory Array",
477+ "Memory Device",
478+ "32-bit Memory Error",
479+ "Memory Array Mapped Address",
480+ "Memory Device Mapped Address",
481+ "Built-in Pointing Device",
482+ "Portable Battery",
483+ "System Reset",
484+ "Hardware Security",
485+ "System Power Controls",
486+ "Voltage Probe",
487+ "Cooling Device",
488+ "Temperature Probe",
489+ "Electrical Current Probe",
490+ "Out-of-band Remote Access",
491+ "Boot Integrity Services",
492+ "System Boot",
493+ "64-bit Memory Error",
494+ "Management Device",
495+ "Management Device Component",
496+ "Management Device Threshold Data",
497+ "Memory Channel",
498+ "IPMI Device",
499+ "Power Supply",
500+ )
501+
502+
503+class DmiDevice:
504+
505+ bus = "dmi"
506+ driver = None
507+ product_id = None
508+ vendor_id = None
509+
510+ _product_blacklist = (
511+ "<BAD INDEX>",
512+ "N/A",
513+ "Not Available",
514+ "INVALID",
515+ "OEM",
516+ "Product Name",
517+ "System Product Name",
518+ "To be filled by O.E.M.",
519+ "To Be Filled By O.E.M.",
520+ "To Be Filled By O.E.M. by More String",
521+ "Unknown",
522+ "Uknown",
523+ "Unknow",
524+ "xxxxxxxxxxxxxx",
525+ )
526+ _vendor_blacklist = (
527+ "<BAD INDEX>",
528+ "Not Available",
529+ "OEM",
530+ "OEM Manufacturer",
531+ "System manufacturer",
532+ "System Manufacturer",
533+ "System Name",
534+ "To be filled by O.E.M.",
535+ "To Be Filled By O.E.M.",
536+ "To Be Filled By O.E.M. by More String",
537+ "Unknow", # XXX This is correct mispelling
538+ "Unknown",
539+ )
540+ _serial_blacklist = (
541+ "0",
542+ "00000000",
543+ "00 00 00 00 00 00 00 00",
544+ "0123456789",
545+ "Base Board Serial Number",
546+ "Chassis Serial Number",
547+ "N/A",
548+ "None",
549+ "Not Applicable",
550+ "Not Available",
551+ "Not Specified",
552+ "OEM",
553+ "System Serial Number",
554+ )
555+ _version_blacklist = (
556+ "-1",
557+ "<BAD INDEX>",
558+ "N/A",
559+ "None",
560+ "Not Applicable",
561+ "Not Available",
562+ "Not Specified",
563+ "OEM",
564+ "System Version",
565+ "Unknown",
566+ "x.x",
567+ )
568+
569+ def __init__(self, attributes, category):
570+ self._attributes = attributes
571+ self.category = category
572+
573+ @property
574+ def path(self):
575+ path = "/devices/virtual/dmi/id"
576+ return os.path.join(path, self.category.lower())
577+
578+ @property
579+ def product(self):
580+ if self.category == "CHASSIS":
581+ type_string = self._attributes.get("chassis_type", "0")
582+ try:
583+ type_index = int(type_string)
584+ return Dmi.chassis_names[type_index]
585+ except ValueError:
586+ return type_string
587+
588+ for name in "name", "version":
589+ attribute = "%s_%s" % (self.category.lower(), name)
590+ product = self._attributes.get(attribute)
591+ if product and product not in self._product_blacklist:
592+ return product
593+
594+ return None
595+
596+ @property
597+ def vendor(self):
598+ for name in "manufacturer", "vendor":
599+ attribute = "%s_%s" % (self.category.lower(), name)
600+ vendor = self._attributes.get(attribute)
601+ if vendor and vendor not in self._vendor_blacklist:
602+ return vendor
603+
604+ return None
605+
606+ @property
607+ def serial(self):
608+ attribute = "%s_serial" % self.category.lower()
609+ serial = self._attributes.get(attribute)
610+ if serial and serial not in self._serial_blacklist:
611+ return serial
612+
613+ return None
614+
615+ @property
616+ def version(self):
617+ attribute = "%s_version" % self.category.lower()
618+ version = self._attributes.get(attribute)
619+ if version and version not in self._version_blacklist:
620+ return version
621+
622+ return None
623
624=== modified file 'checkbox/parsers/cpuinfo.py'
625--- checkbox/parsers/cpuinfo.py 2011-05-30 20:42:48 +0000
626+++ checkbox/parsers/cpuinfo.py 2011-10-12 15:56:08 +0000
627@@ -16,24 +16,26 @@
628 # You should have received a copy of the GNU General Public License
629 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
630 #
631-import os
632 import re
633
634+from os import uname
635+
636 from checkbox.lib.conversion import string_to_type
637-from checkbox.parsers.utils import implement_from_dict
638-
639-
640-class CpuinfoParser(object):
641-
642- def __init__(self, stream, uname=None):
643+
644+
645+class CpuinfoParser:
646+ """Parser for the /proc/cpuinfo file."""
647+
648+ def __init__(self, stream, machine=None):
649 self.stream = stream
650- self.uname = uname or os.uname()[4].lower()
651+ self.machine = machine or uname()[4].lower()
652
653 def getAttributes(self):
654 count = 0
655 attributes = {}
656 cpuinfo = self.stream.read()
657- for block in cpuinfo.split("\n\n"):
658+ for block in re.split(r"\n{2,}", cpuinfo):
659+ block = block.strip()
660 if not block:
661 continue
662
663@@ -54,19 +56,23 @@
664
665 attributes[key.lower()] = value
666
667- attributes["count"] = count
668+ if attributes:
669+ attributes["count"] = count
670+
671 return attributes
672
673 def run(self, result):
674- uname = self.uname
675 attributes = self.getAttributes()
676+ if not attributes:
677+ return
678
679 # Default values
680+ machine = self.machine
681 processor = {
682- "platform": uname,
683+ "platform": machine,
684 "count": 1,
685- "type": self.uname,
686- "model": self.uname,
687+ "type": machine,
688+ "model": machine,
689 "model_number": "",
690 "model_version": "",
691 "model_revision": "",
692@@ -113,7 +119,6 @@
693 "type": "platform",
694 "model": "cpu",
695 "model_version": "revision",
696- "vendor": "machine",
697 "speed": "clock"},
698 ("sparc64", "sparc",): {
699 "count": "ncpus probed",
700@@ -126,7 +131,7 @@
701 bogompips_string = attributes.get("bogomips", "0.0")
702 processor["bogomips"] = int(round(float(bogompips_string)))
703 for platform, conversion in platform_to_conversion.iteritems():
704- if uname in platform:
705+ if machine in platform:
706 for pkey, ckey in conversion.iteritems():
707 if isinstance(ckey, (list, tuple)):
708 processor[pkey] = "/".join([attributes[k]
709@@ -134,13 +139,11 @@
710 elif ckey in attributes:
711 processor[pkey] = attributes[ckey]
712
713- # Adjust platform and vendor
714- if uname[0] == "i" and uname[-2:] == "86":
715- processor["platform"] = "i386"
716- elif uname[:5] == "alpha":
717- processor["platform"] = "alpha"
718- elif uname[:5] == "sparc":
719- processor["vendor"] = "sun"
720+ # Adjust platform
721+ if machine[0] == "i" and machine[-2:] == "86":
722+ processor["platform"] = u"i386"
723+ elif machine[:5] == "alpha":
724+ processor["platform"] = u"alpha"
725
726 # Adjust cache
727 if processor["cache"]:
728@@ -148,14 +151,14 @@
729
730 # Adjust speed
731 try:
732- if uname[:5] == "alpha":
733+ if machine[:5] == "alpha":
734 speed = processor["speed"].split()[0]
735 processor["speed"] = int(round(float(speed))) / 1000000
736- elif uname[:5] == "sparc":
737+ elif machine[:5] == "sparc":
738 speed = processor["speed"]
739 processor["speed"] = int(round(float(speed))) / 2
740 else:
741- if uname[:3] == "ppc":
742+ if machine[:3] == "ppc":
743 # String is appended with "mhz"
744 speed = processor["speed"][:-3]
745 else:
746@@ -174,18 +177,4 @@
747 if processor["count"] == 0:
748 processor["count"] = 1
749
750- for key, value in processor.iteritems():
751- camel_case = key.replace("_", " ").title().replace(" ", "")
752- method_name = "set%s" % camel_case
753- method = getattr(result, method_name)
754- method(value)
755-
756-
757-class CpuinfoResult(dict):
758-
759- def __getattr__(self, name):
760- name = re.sub(r"([A-Z])", "_\\1", name)[4:].lower()
761- def setAll(value):
762- self[name] = value
763-
764- return setAll
765+ result.setProcessor(processor)
766
767=== added file 'checkbox/parsers/cputable'
768--- checkbox/parsers/cputable 1970-01-01 00:00:00 +0000
769+++ checkbox/parsers/cputable 2011-10-12 15:56:08 +0000
770@@ -0,0 +1,40 @@
771+# This file contains the table of known CPU names.
772+#
773+# Architecture names are formed as a combination of the system name
774+# (from ostable) and CPU name (from this table) after mapping from
775+# the Debian triplet (from triplettable). A list of architecture
776+# names in the Debian ‘sid’ distribution can be found in the archtable
777+# file.
778+#
779+# Column 1 is the Debian name for the CPU, used to form the cpu part in
780+# the Debian triplet.
781+# Column 2 is the GNU name for the CPU, used to output build and host
782+# targets in ‘dpkg-architecture’.
783+# Column 3 is an extended regular expression used to match against the
784+# CPU part of the output of the GNU config.guess script.
785+# Column 4 is the size (in bits) of the integers/pointers
786+# Column 5 is the endianness (byte ordering in numbers)
787+#
788+# <Debian name> <GNU name> <config.guess regex> <Bits> <Endianness>
789+i386 i686 (i[3456]86|pentium) 32 little
790+ia64 ia64 ia64 64 little
791+alpha alpha alpha.* 64 little
792+amd64 x86_64 x86_64 64 little
793+armeb armeb arm.*b 32 big
794+arm arm arm.* 32 little
795+avr32 avr32 avr32 32 big
796+hppa hppa hppa.* 32 big
797+m32r m32r m32r 32 big
798+m68k m68k m68k 32 big
799+mips mips mips(eb)? 32 big
800+mipsel mipsel mipsel 32 little
801+powerpc powerpc (powerpc|ppc) 32 big
802+ppc64 powerpc64 (powerpc|ppc)64 64 big
803+s390 s390 s390 32 big
804+s390x s390x s390x 64 big
805+sh3 sh3 sh3 32 little
806+sh3eb sh3eb sh3eb 32 big
807+sh4 sh4 sh4 32 little
808+sh4eb sh4eb sh4eb 32 big
809+sparc sparc sparc 32 big
810+sparc64 sparc64 sparc64 64 big
811
812=== added file 'checkbox/parsers/cputable.py'
813--- checkbox/parsers/cputable.py 1970-01-01 00:00:00 +0000
814+++ checkbox/parsers/cputable.py 2011-10-12 15:56:08 +0000
815@@ -0,0 +1,42 @@
816+#
817+# This file is part of Checkbox.
818+#
819+# Copyright 2011 Canonical Ltd.
820+#
821+# Checkbox is free software: you can redistribute it and/or modify
822+# it under the terms of the GNU General Public License as published by
823+# the Free Software Foundation, either version 3 of the License, or
824+# (at your option) any later version.
825+#
826+# Checkbox is distributed in the hope that it will be useful,
827+# but WITHOUT ANY WARRANTY; without even the implied warranty of
828+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
829+# GNU General Public License for more details.
830+#
831+# You should have received a copy of the GNU General Public License
832+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
833+#
834+import re
835+
836+
837+CPUTABLE_RE = re.compile(
838+ r"^(?!\#)(?P<debian_name>\S+)"
839+ r"\s+(?P<gnu_name>\S+)"
840+ r"\s+(?P<regex>\S+)"
841+ r"\s+(?P<bits>\d+)"
842+ r"\s+(?P<endianness>big|little)")
843+
844+
845+class CputableParser:
846+ """Parser for the /usr/share/dpkg/cputable file."""
847+
848+ def __init__(self, stream):
849+ self.stream = stream
850+
851+ def run(self, result):
852+ for line in self.stream.readlines():
853+ match = CPUTABLE_RE.match(line)
854+ if match:
855+ cpu = match.groupdict()
856+ cpu["bits"] = int(cpu["bits"])
857+ result.addCpu(cpu)
858
859=== added file 'checkbox/parsers/deferred.py'
860--- checkbox/parsers/deferred.py 1970-01-01 00:00:00 +0000
861+++ checkbox/parsers/deferred.py 2011-10-12 15:56:08 +0000
862@@ -0,0 +1,27 @@
863+#
864+# This file is part of Checkbox.
865+#
866+# Copyright 2011 Canonical Ltd.
867+#
868+# Checkbox is free software: you can redistribute it and/or modify
869+# it under the terms of the GNU General Public License as published by
870+# the Free Software Foundation, either version 3 of the License, or
871+# (at your option) any later version.
872+#
873+# Checkbox is distributed in the hope that it will be useful,
874+# but WITHOUT ANY WARRANTY; without even the implied warranty of
875+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
876+# GNU General Public License for more details.
877+#
878+# You should have received a copy of the GNU General Public License
879+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
880+#
881+class DeferredParser:
882+ """Parser for deferred dispatching of events."""
883+
884+ def __init__(self, dispatcher, event_type="result"):
885+ self.dispatcher = dispatcher
886+ self.event_type = event_type
887+
888+ def run(self, result):
889+ self.dispatcher.publishEvent(self.event_type, result)
890
891=== added file 'checkbox/parsers/dmidecode.py'
892--- checkbox/parsers/dmidecode.py 1970-01-01 00:00:00 +0000
893+++ checkbox/parsers/dmidecode.py 2011-10-12 15:56:08 +0000
894@@ -0,0 +1,123 @@
895+#
896+# This file is part of Checkbox.
897+#
898+# Copyright 2011 Canonical Ltd.
899+#
900+# Checkbox is free software: you can redistribute it and/or modify
901+# it under the terms of the GNU General Public License as published by
902+# the Free Software Foundation, either version 3 of the License, or
903+# (at your option) any later version.
904+#
905+# Checkbox is distributed in the hope that it will be useful,
906+# but WITHOUT ANY WARRANTY; without even the implied warranty of
907+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
908+# GNU General Public License for more details.
909+#
910+# You should have received a copy of the GNU General Public License
911+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
912+#
913+import re
914+
915+from string import (
916+ hexdigits,
917+ uppercase,
918+ )
919+
920+from checkbox.lib.dmi import (
921+ Dmi,
922+ DmiDevice,
923+ )
924+
925+
926+HANDLE_RE = re.compile(
927+ r"^Handle (?P<handle>0x[%s]{4}), "
928+ r"DMI type (?P<type>\d+), "
929+ r"(?P<size>\d+) bytes$"
930+ % hexdigits)
931+KEY_VALUE_RE = re.compile(
932+ r"^\t(?P<key>[%s].+):( (?P<value>.+))?$"
933+ % uppercase)
934+
935+
936+class DmidecodeParser:
937+ """Parser for the dmidecode command."""
938+
939+ _key_map = {
940+ "ID": "serial",
941+ "Manufacturer": "vendor",
942+ "Product Name": "name",
943+ "Serial Number": "serial",
944+ "Type": "type",
945+ "Vendor": "vendor",
946+ "Version": "version",
947+ }
948+
949+ def __init__(self, stream):
950+ self.stream = stream
951+
952+ def _parseKey(self, key):
953+ return self._key_map.get(key)
954+
955+ def _parseValue(self, value):
956+ if value is not None:
957+ value = value.strip()
958+ if not value:
959+ value = None
960+
961+ return value
962+
963+ def run(self, result):
964+ output = self.stream.read()
965+ for record in re.split(r"\n{2,}", output):
966+ record = record.strip()
967+ # Skip empty records
968+ if not record:
969+ continue
970+
971+ # Skip header record
972+ lines = record.split("\n")
973+ line = lines.pop(0)
974+ if line.startswith("#"):
975+ continue
976+
977+ # Skip records with an unsupported handle
978+ match = HANDLE_RE.match(line)
979+ if not match:
980+ continue
981+
982+ # Skip records that are empty or inactive
983+ if not lines or lines.pop(0) == "Inactive":
984+ continue
985+
986+ # Skip disabled entries and end-of-table marker
987+ type_index = int(match.group("type"))
988+ if type_index >= len(Dmi.type_names):
989+ continue
990+
991+ category = Dmi.type_names[type_index]
992+ category = category.upper().split(" ")[-1]
993+ if category not in (
994+ "BOARD", "BIOS", "CHASSIS", "PROCESSOR", "SYSTEM"):
995+ continue
996+
997+ # Parse attributes
998+ attributes = {}
999+ for line in lines:
1000+ # Skip lines with an unsupported key/value pair
1001+ match = KEY_VALUE_RE.match(line)
1002+ if not match:
1003+ continue
1004+
1005+ # Skip lines with an unsupported key
1006+ key = self._parseKey(match.group("key"))
1007+ if not key:
1008+ continue
1009+
1010+ key = "%s_%s" % (category.lower(), key)
1011+ value = self._parseValue(match.group("value"))
1012+ attributes[key] = value
1013+
1014+ device = DmiDevice(attributes, category)
1015+ result.addDmiDevice(device)
1016+
1017+ return result
1018
1019=== added file 'checkbox/parsers/meminfo.py'
1020--- checkbox/parsers/meminfo.py 1970-01-01 00:00:00 +0000
1021+++ checkbox/parsers/meminfo.py 2011-10-12 15:56:08 +0000
1022@@ -0,0 +1,46 @@
1023+#
1024+# This file is part of Checkbox.
1025+#
1026+# Copyright 2011 Canonical Ltd.
1027+#
1028+# Checkbox is free software: you can redistribute it and/or modify
1029+# it under the terms of the GNU General Public License as published by
1030+# the Free Software Foundation, either version 3 of the License, or
1031+# (at your option) any later version.
1032+#
1033+# Checkbox is distributed in the hope that it will be useful,
1034+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1035+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1036+# GNU General Public License for more details.
1037+#
1038+# You should have received a copy of the GNU General Public License
1039+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1040+#
1041+import re
1042+
1043+
1044+class MeminfoParser:
1045+ """Parser for the /proc/meminfo file."""
1046+
1047+ def __init__(self, stream):
1048+ self.stream = stream
1049+
1050+ def run(self, result):
1051+ key_value_pattern = re.compile(r"(?P<key>.*):\s+(?P<value>.*)")
1052+ meminfo_map = {
1053+ "MemTotal": "total",
1054+ "SwapTotal": "swap"}
1055+
1056+ meminfo = {}
1057+ for line in self.stream.readlines():
1058+ line = line.strip()
1059+ match = key_value_pattern.match(line)
1060+ if match:
1061+ key = match.group("key")
1062+ if key in meminfo_map:
1063+ key = meminfo_map[key]
1064+ value = match.group("value")
1065+ (integer, factor) = value.split()
1066+ meminfo[key] = int(integer) * 1024
1067+
1068+ result.setMemory(meminfo)
1069
1070=== modified file 'checkbox/parsers/submission.py'
1071--- checkbox/parsers/submission.py 2011-06-08 15:16:37 +0000
1072+++ checkbox/parsers/submission.py 2011-10-12 15:56:08 +0000
1073@@ -16,430 +16,521 @@
1074 # You should have received a copy of the GNU General Public License
1075 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1076 #
1077-import re
1078-
1079-import logging
1080-
1081-from datetime import (
1082- datetime,
1083- timedelta,
1084- )
1085-from dateutil import tz
1086-
1087-from checkbox.parsers.device import DeviceResult
1088-from checkbox.parsers.udev import UdevParser
1089-from checkbox.parsers.utils import implement_from_dict
1090-
1091 try:
1092 import xml.etree.cElementTree as etree
1093 except ImportError:
1094 import cElementTree as etree
1095
1096-
1097-_time_regex = re.compile(r"""
1098- ^(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)
1099- T(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)
1100- (?:\.(?P<second_fraction>\d{0,6}))?
1101- (?P<tz>
1102- (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
1103- | Z)?$
1104- """,
1105- re.VERBOSE)
1106-
1107-_xml_illegal_regex = re.compile(
1108- u"([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])"
1109- + u"|([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])" % (
1110- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
1111- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
1112- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff)))
1113-
1114-
1115-class HALDevice(object):
1116-
1117- def __init__(self, id, udi, properties):
1118- self.id = id
1119- self.udi = udi
1120- self.properties = properties
1121-
1122-
1123-class SubmissionStream(object):
1124-
1125- default_size = 4096
1126-
1127- def __init__(self, stream):
1128- self.stream = stream
1129- self._buffer = ""
1130- self._buffers = []
1131-
1132- def read(self, size=None):
1133- if size is None:
1134- size = self.default_size
1135-
1136- info_start_regex = re.compile("^<info .*>$")
1137- info_end_regex = re.compile("^</info>$")
1138-
1139- in_info = False
1140- length = sum(len(buffer) for buffer in self._buffers)
1141-
1142- while length < size:
1143- try:
1144- buffer = self.stream.next()
1145- except StopIteration:
1146- break
1147-
1148- if not in_info:
1149- if info_start_regex.match(buffer):
1150- in_info = True
1151- self._buffer += "".join(self._buffers)
1152- self._buffers = [buffer]
1153- else:
1154- length += len(buffer)
1155- self._buffers.append(buffer)
1156- else:
1157- self._buffers.append(buffer)
1158- if info_end_regex.match(buffer):
1159- in_info = False
1160-
1161- buffer = "".join(self._buffers)
1162- self._buffers = []
1163-
1164- if not _xml_illegal_regex.search(buffer):
1165- length += len(buffer)
1166- self._buffer += buffer
1167-
1168- if self._buffers:
1169- self._buffer += "".join(self._buffers)
1170- self._buffers = []
1171-
1172- if not self._buffer:
1173- return None
1174-
1175- data = self._buffer[:size]
1176- self._buffers = [self._buffer[size:]]
1177- self._buffer = ""
1178-
1179- return data
1180-
1181-
1182-class SubmissionParser(object):
1183-
1184- default_name = "unknown"
1185-
1186- def __init__(self, stream, name=None):
1187- self.stream = SubmissionStream(stream)
1188- self.name = name or self.default_name
1189+from StringIO import StringIO
1190+from logging import getLogger
1191+from pkg_resources import resource_string
1192+
1193+from checkbox.lib.conversion import string_to_datetime
1194+
1195+from checkbox import parsers
1196+from checkbox.dispatcher import DispatcherQueue
1197+from checkbox.parsers.cpuinfo import CpuinfoParser
1198+from checkbox.parsers.cputable import CputableParser
1199+from checkbox.parsers.deferred import DeferredParser
1200+from checkbox.parsers.dmidecode import DmidecodeParser
1201+from checkbox.parsers.meminfo import MeminfoParser
1202+from checkbox.parsers.udevadm import UdevadmParser
1203+from checkbox.job import (FAIL, PASS, UNINITIATED, UNRESOLVED,
1204+ UNSUPPORTED, UNTESTED)
1205+
1206+
1207+class SubmissionResult:
1208+
1209+ def __init__(self, test_run_factory, **kwargs):
1210+ self.test_run_factory = test_run_factory
1211+ self.test_run_kwargs = kwargs
1212+ self.dispatcher = DispatcherQueue()
1213+
1214+ # Register handlers to incrementally add information
1215+ register = self.dispatcher.registerHandler
1216+ register(("cpu", "architecture",), self.addCpuArchitecture)
1217+ register(("identifier",), self.addIdentifier)
1218+ register(("test_run", "attachment",), self.addAttachment)
1219+ register(("test_run", "device",), self.addDeviceState)
1220+ register(("test_run", "distribution",), self.setDistribution)
1221+ register(("test_run", "package_version",), self.addPackageVersion)
1222+ register(("test_run", "test_result",), self.addTestResult)
1223+
1224+ # Register handlers to set information once
1225+ register(("architecture",), self.setArchitecture, count=1)
1226+ register(
1227+ ("cpuinfo", "machine", "cpuinfo_result",),
1228+ self.setCpuinfo, count=1)
1229+ register(
1230+ ("meminfo", "meminfo_result",),
1231+ self.setMeminfo, count=1)
1232+ register(
1233+ ("project", "series",),
1234+ self.setTestRun, count=1)
1235+ register(
1236+ ("test_run", "architecture",),
1237+ self.setArchitectureState, count=1)
1238+ register(
1239+ ("test_run", "memory",),
1240+ self.setMemoryState, count=1)
1241+ register(
1242+ ("test_run", "processor",),
1243+ self.setProcessorState, count=1)
1244+ register(
1245+ ("udevadm", "bits", "udevadm_result",),
1246+ self.setUdevadm, count=1)
1247+
1248+ # Publish events passed as keyword arguments
1249+ if "project" in kwargs:
1250+ self.dispatcher.publishEvent("project", kwargs.pop("project"))
1251+ self.dispatcher.publishEvent("series", kwargs.pop("series", None))
1252+
1253+ def addAttachment(self, test_run, attachment):
1254+ test_run.addAttachment(**attachment)
1255+
1256+ def addContext(self, text, command=None):
1257+ if text.strip() == "Command not found.":
1258+ return
1259+
1260+ self.dispatcher.publishEvent(
1261+ "attachment", {"name": command, "content": text })
1262+
1263+ parsers = {
1264+ "cat /proc/cpuinfo": self.parseCpuinfo,
1265+ "cat /proc/meminfo": self.parseMeminfo,
1266+ "dmidecode": DmidecodeParser,
1267+ "udevadm info --export-db": self.parseUdevadm,
1268+ }
1269+ parser = parsers.get(command)
1270+ if parser:
1271+ if not isinstance(text, unicode):
1272+ text = text.decode("utf-8")
1273+ stream = StringIO(text)
1274+ p = parser(stream)
1275+ p.run(self)
1276+
1277+ def addCpu(self, cpu):
1278+ self.dispatcher.publishEvent("cpu", cpu)
1279+
1280+ def addCpuArchitecture(self, cpu, architecture):
1281+ if cpu["debian_name"] == architecture:
1282+ self.dispatcher.publishEvent("machine", cpu["gnu_name"])
1283+ self.dispatcher.publishEvent("bits", cpu["bits"])
1284+
1285+ def addDevice(self, device):
1286+ self.dispatcher.publishEvent("device", device)
1287+
1288+ def addDeviceState(self, test_run, device):
1289+ test_run.addDeviceState(
1290+ bus_name=device.bus, category_name=device.category,
1291+ product_name=device.product, vendor_name=device.vendor,
1292+ product_id=device.product_id, vendor_id=device.vendor_id,
1293+ subproduct_id=device.subproduct_id,
1294+ subvendor_id=device.subvendor_id,
1295+ driver_name=device.driver, path=device.path)
1296+
1297+ def addDmiDevice(self, device):
1298+ if device.serial:
1299+ self.dispatcher.publishEvent("identifier", device.serial)
1300+
1301+ if device.category in ("BOARD", "SYSTEM") \
1302+ and device.vendor != device.product \
1303+ and device.product is not None:
1304+ self.dispatcher.publishEvent("model", device.product)
1305+ self.dispatcher.publishEvent("make", device.vendor)
1306+ self.dispatcher.publishEvent("version", device.version)
1307+
1308+ def addIdentifier(self, identifier):
1309+ try:
1310+ self.identifiers.append(identifier)
1311+ except AttributeError:
1312+ self.identifiers = [identifier]
1313+ self.dispatcher.publishEvent("identifiers", self.identifiers)
1314+
1315+ def addPackage(self, package):
1316+ package_version = {
1317+ "name": package["name"],
1318+ "version": package["properties"]["version"],
1319+ }
1320+ self.dispatcher.publishEvent("package_version", package_version)
1321+
1322+ def addPackageVersion(self, test_run, package_version):
1323+ test_run.addPackageVersion(**package_version)
1324+
1325+ def addQuestion(self, question):
1326+ answer_to_status = {
1327+ "fail": FAIL,
1328+ "no": FAIL,
1329+ "pass": PASS,
1330+ "skip": UNTESTED,
1331+ "uninitiated": UNINITIATED,
1332+ "unresolved": UNRESOLVED,
1333+ "unsupported": UNSUPPORTED,
1334+ "untested": UNTESTED,
1335+ "yes": PASS,
1336+ }
1337+
1338+ test_result = dict(
1339+ name=question["name"],
1340+ output=question["comment"],
1341+ status=answer_to_status[question["answer"]["value"]],
1342+ )
1343+ test_result.update(self.test_run_kwargs)
1344+ self.dispatcher.publishEvent("test_result", test_result)
1345+
1346+ def addTestResult(self, test_run, test_result):
1347+ test_run.addTestResult(**test_result)
1348+
1349+ def addSummary(self, name, value):
1350+ if name == "architecture":
1351+ self.dispatcher.publishEvent("architecture", value)
1352+ elif name == "distribution":
1353+ self.dispatcher.publishEvent("project", value)
1354+ elif name == "distroseries":
1355+ self.dispatcher.publishEvent("series", value)
1356+
1357+ def parseCpuinfo(self, cpuinfo):
1358+ self.dispatcher.publishEvent("cpuinfo", cpuinfo)
1359+ return DeferredParser(self.dispatcher, "cpuinfo_result")
1360+
1361+ def parseMeminfo(self, meminfo):
1362+ self.dispatcher.publishEvent("meminfo", meminfo)
1363+ return DeferredParser(self.dispatcher, "meminfo_result")
1364+
1365+ def parseUdevadm(self, udevadm):
1366+ self.dispatcher.publishEvent("udevadm", udevadm)
1367+ return DeferredParser(self.dispatcher, "udevadm_result")
1368+
1369+ def setArchitecture(self, architecture):
1370+ string = resource_string(parsers.__name__, "cputable")
1371+ stream = StringIO(string.decode("utf-8"))
1372+ parser = CputableParser(stream)
1373+ parser.run(self)
1374+
1375+ def setArchitectureState(self, test_run, architecture):
1376+ test_run.setArchitectureState(architecture)
1377+
1378+ def setCpuinfo(self, cpuinfo, machine, cpuinfo_result):
1379+ parser = CpuinfoParser(cpuinfo, machine)
1380+ parser.run(cpuinfo_result)
1381+
1382+ def setMeminfo(self, meminfo, meminfo_result):
1383+ parser = MeminfoParser(meminfo)
1384+ parser.run(meminfo_result)
1385+
1386+ def setDistribution(self, test_run, distribution):
1387+ test_run.setDistribution(**distribution)
1388+
1389+ def setLSBRelease(self, lsb_release):
1390+ self.dispatcher.publishEvent("distribution", lsb_release)
1391+
1392+ def setMemory(self, memory):
1393+ self.dispatcher.publishEvent("memory", memory)
1394+
1395+ def setMemoryState(self, test_run, memory):
1396+ test_run.setMemoryState(**memory)
1397+
1398+ def setProcessor(self, processor):
1399+ self.dispatcher.publishEvent("processor", processor)
1400+
1401+ def setProcessorState(self, test_run, processor):
1402+ test_run.setProcessorState(
1403+ platform_name=processor["platform"],
1404+ make=processor["type"], model=processor["model"],
1405+ model_number=processor["model_number"],
1406+ model_version=processor["model_version"],
1407+ model_revision=processor["model_revision"],
1408+ cache=processor["cache"], other=processor["other"],
1409+ bogomips=processor["bogomips"], speed=processor["speed"],
1410+ count=processor["count"])
1411+
1412+ def setTestRun(self, project, series):
1413+ test_run = self.test_run_factory(
1414+ **self.test_run_kwargs)
1415+ self.dispatcher.publishEvent("test_run", test_run)
1416+
1417+ def setUdevadm(self, udevadm, bits, udevadm_result):
1418+ parser = UdevadmParser(udevadm, bits)
1419+ parser.run(udevadm_result)
1420+
1421+
1422+class SubmissionParser:
1423+
1424+ def __init__(self, file):
1425+ self.file = file
1426+ self.logger = getLogger()
1427
1428 def _getClient(self, node):
1429- return "_".join([node.get('name'), node.get('version')])
1430+ """Return a dictionary with the name and version of the client."""
1431+ return {
1432+ "name": node.get("name"),
1433+ "version": node.get("version"),
1434+ }
1435
1436 def _getProperty(self, node):
1437- """Parse a <property> node.
1438-
1439- :return: (name, (value, type)) of a property.
1440- """
1441- return (node.get('name'), self._getValueAttribute(node))
1442+ """Return the (name, value) of a property."""
1443+ return (node.get("name"), self._getValueAsType(node))
1444
1445 def _getProperties(self, node):
1446- """Parse <property> sub-nodes of node.
1447-
1448- :return: A dictionary, where each key is the name of a property;
1449- the values are the tuples (value, type) of a property.
1450- """
1451+ """Return a dictionary of properties."""
1452 properties = {}
1453 for child in node.getchildren():
1454+ assert child.tag == "property", \
1455+ "Unexpected tag <%s>, expected <property>" % child.tag
1456 name, value = self._getProperty(child)
1457- if name in properties:
1458- raise ValueError(
1459- '<property name="%s"> found more than once in <%s>'
1460- % (name, node.tag))
1461 properties[name] = value
1462
1463 return properties
1464
1465- def _getValueAttribute(self, node):
1466- """Return (value, type) of a <property> or <value> node."""
1467- type_ = node.get('type')
1468- if type_ in ('dbus.Boolean', 'bool'):
1469- value = node.text.strip() == 'True'
1470-
1471- elif type_ in ('str', 'dbus.String', 'dbus.UTF8String'):
1472+ def _getValueAsType(self, node):
1473+ """Return value of a node as the type attribute."""
1474+ type_ = node.get("type")
1475+ if type_ in ("bool",):
1476 value = node.text.strip()
1477-
1478- elif type_ in ('dbus.Byte', 'dbus.Int16', 'dbus.Int32', 'dbus.Int64',
1479- 'dbus.UInt16', 'dbus.UInt32', 'dbus.UInt64', 'int',
1480- 'long'):
1481- value = int(node.text.strip())
1482-
1483- elif type_ in ('dbus.Double', 'float'):
1484- value = float(node.text.strip())
1485-
1486- elif type_ in ('dbus.Array', 'list'):
1487- value = [self._getValueAttribute(child)
1488- for child in node.getchildren()]
1489-
1490- elif type_ in ('dbus.Dictionary', 'dict'):
1491- value = dict((child.get('name'), self._getValueAttribute(child))
1492- for child in node.getchildren())
1493-
1494+ assert value in ("True", "False",), \
1495+ "Unexpected boolean value '%s' in <%s>" % (value, node.tag)
1496+ return value == "True"
1497+ elif type_ in ("str",):
1498+ return unicode(node.text.strip())
1499+ elif type_ in ("int", "long",):
1500+ return int(node.text.strip())
1501+ elif type_ in ("float",):
1502+ return float(node.text.strip())
1503+ elif type_ in ("list",):
1504+ return list(self._getValueAsType(child)
1505+ for child in node.getchildren())
1506+ elif type_ in ("dict",):
1507+ return dict((child.get("name"), self._getValueAsType(child))
1508+ for child in node.getchildren())
1509 else:
1510- # This should not happen.
1511 raise AssertionError(
1512- 'Unexpected <property> or <value> type: %s' % type_)
1513-
1514- return value
1515-
1516- def _getValueAttributeAsBoolean(self, node):
1517+ "Unexpected type '%s' in <%s>" % (type_, node.tag))
1518+
1519+ def _getValueAsBoolean(self, node):
1520 """Return the value of the attribute "value" as a boolean."""
1521- return node.attrib['value'] == "True"
1522-
1523- def _getValueAttributeAsString(self, node):
1524+ value = node.attrib["value"]
1525+ assert value in ("True", "False",), \
1526+ "Unexpected boolean value '%s' in tag <%s>" % (value, node.tag)
1527+ return value == "True"
1528+
1529+ def _getValueAsDatetime(self, node):
1530+ """Return the value of the attribute "value" as a datetime."""
1531+ string = node.attrib["value"]
1532+ return string_to_datetime(string)
1533+
1534+ def _getValueAsString(self, node):
1535 """Return the value of the attribute "value"."""
1536- return node.attrib['value']
1537-
1538- def _getValueAttributeAsDateTime(self, node):
1539- """Convert a "value" attribute into a datetime object."""
1540- time_text = node.get('value')
1541-
1542- # we cannot use time.strptime: this function accepts neither fractions
1543- # of a second nor a time zone given e.g. as '+02:30'.
1544- match = _time_regex.search(time_text)
1545-
1546- if match is None:
1547- raise ValueError(
1548- 'Timestamp with unreasonable value: %s' % time_text)
1549-
1550- time_parts = match.groupdict()
1551-
1552- year = int(time_parts['year'])
1553- month = int(time_parts['month'])
1554- day = int(time_parts['day'])
1555- hour = int(time_parts['hour'])
1556- minute = int(time_parts['minute'])
1557- second = int(time_parts['second'])
1558- second_fraction = time_parts['second_fraction']
1559- if second_fraction is not None:
1560- milliseconds = second_fraction + '0' * (6 - len(second_fraction))
1561- milliseconds = int(milliseconds)
1562- else:
1563- milliseconds = 0
1564-
1565- if second > 59:
1566- second = 59
1567- milliseconds = 999999
1568-
1569- timestamp = datetime(year, month, day, hour, minute, second,
1570- milliseconds, tzinfo=tz.tzutc())
1571-
1572- tz_sign = time_parts['tz_sign']
1573- tz_hour = time_parts['tz_hour']
1574- tz_minute = time_parts['tz_minute']
1575- if tz_sign in ('-', '+'):
1576- delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
1577- if tz_sign == '-':
1578- timestamp = timestamp + delta
1579- else:
1580- timestamp = timestamp - delta
1581-
1582- return timestamp
1583-
1584- def _parseHAL(self, result, node):
1585- result.startDevices()
1586- for child in node.getchildren():
1587- id = int(child.get('id'))
1588- udi = child.get('udi')
1589- properties = self._getProperties(child)
1590- device = HALDevice(id, udi, properties)
1591- result.addDevice(device)
1592-
1593- result.endDevices()
1594-
1595- def _parseInfo(self, result, node):
1596- command = node.attrib['command']
1597- if command == "udevadm info --export-db":
1598- self._parseUdev(result, node)
1599-
1600- result.addInfo(command, node.text)
1601-
1602- def _parseUdev(self, result, node):
1603- result.startDevices()
1604-
1605- stream = StringIO(node.text)
1606- udev = UdevParser(stream)
1607- udev.run(result)
1608-
1609- result.endDevices()
1610-
1611- def _parseProcessors(self, result, node):
1612- result.startProcessors()
1613-
1614- for child in node.getchildren():
1615- id = int(child.get('id'))
1616- name = child.get('name')
1617- properties = self._getProperties(child)
1618- result.addProcessor(id, name, properties)
1619-
1620- result.endProcessors()
1621-
1622- def _parseRoot(self, result, node):
1623- parsers = {
1624- "summary": self._parseSummary,
1625- "hardware": self._parseHardware,
1626- "software": self._parseSoftware,
1627- "questions": self._parseQuestions,
1628- "context": self._parseContext,
1629- }
1630-
1631- for child in node.getchildren():
1632- parser = parsers.get(child.tag, self._parseNone)
1633- parser(result, child)
1634-
1635- def _parseSummary(self, result, node):
1636- parsers = {
1637- 'live_cd': self._getValueAttributeAsBoolean,
1638- 'system_id': self._getValueAttributeAsString,
1639- 'distribution': self._getValueAttributeAsString,
1640- 'distroseries': self._getValueAttributeAsString,
1641- 'architecture': self._getValueAttributeAsString,
1642- 'private': self._getValueAttributeAsBoolean,
1643- 'contactable': self._getValueAttributeAsBoolean,
1644- 'date_created': self._getValueAttributeAsDateTime,
1645- 'client': self._getClient,
1646- 'kernel-release': self._getValueAttributeAsString,
1647- }
1648-
1649- for child in node.getchildren():
1650- parser = parsers.get(child.tag, self._parseNone)
1651- value = parser(child)
1652- result.addSummary(child.tag, value)
1653-
1654- def _parseHardware(self, result, node):
1655- parsers = {
1656- 'hal': self._parseHAL,
1657- 'processors': self._parseProcessors,
1658- 'udev': self._parseUdev,
1659- }
1660-
1661- for child in node.getchildren():
1662- parser = parsers.get(child.tag, self._parseNone)
1663- parser(result, child)
1664-
1665- def _parseLSBRelease(self, result, node):
1666+ return unicode(node.attrib["value"])
1667+
1668+ def parseContext(self, result, node):
1669+ """Parse the <context> part of a submission."""
1670+ duplicates = set()
1671+ for child in node.getchildren():
1672+ assert child.tag == "info", \
1673+ "Unexpected tag <%s>, expected <info>" % child.tag
1674+ command = child.get("command")
1675+ if command not in duplicates:
1676+ duplicates.add(command)
1677+ result.addContext(child.text, command)
1678+ else:
1679+ self.logger.debug(
1680+ "Duplicate command found in tag <info>: %s" % command)
1681+
1682+ def parseHardware(self, result, node):
1683+ """Parse the <hardware> section of a submission."""
1684+ parsers = {
1685+ "dmi": DmidecodeParser,
1686+ "processors": self.parseProcessors,
1687+ "udev": result.parseUdevadm,
1688+ }
1689+
1690+ for child in node.getchildren():
1691+ parser = parsers.get(child.tag)
1692+ if parser:
1693+ if child.getchildren():
1694+ parser(result, child)
1695+ else:
1696+ text = child.text
1697+ if not isinstance(text, unicode):
1698+ text = text.decode("utf-8")
1699+ stream = StringIO(text)
1700+ p = parser(stream)
1701+ p.run(result)
1702+ else:
1703+ self.logger.debug(
1704+ "Unsupported tag <%s> in <hardware>" % child.tag)
1705+
1706+ def parseLSBRelease(self, result, node):
1707+ """Parse the <lsbrelease> part of a submission."""
1708 properties = self._getProperties(node)
1709- result.setDistribution(**properties)
1710-
1711- def _parsePackages(self, result, node):
1712- result.startPackages()
1713-
1714- for child in node.getchildren():
1715- id = int(child.get('id'))
1716- name = child.get('name')
1717+ result.setLSBRelease(properties)
1718+
1719+ def parsePackages(self, result, node):
1720+ """Parse the <packages> part of a submission."""
1721+ for child in node.getchildren():
1722+ assert child.tag == "package", \
1723+ "Unexpected tag <%s>, expected <package>" % child.tag
1724+
1725+ package = {
1726+ "name": child.get("name"),
1727+ "properties": self._getProperties(child),
1728+ }
1729+ result.addPackage(package)
1730+
1731+ def parseProcessors(self, result, node):
1732+ """Parse the <processors> part of a submission."""
1733+ processors = []
1734+ for child in node.getchildren():
1735+ assert child.tag == "processor", \
1736+ "Unexpected tag <%s>, expected <processor>" % child.tag
1737+
1738+ # Convert lists to space separated strings.
1739 properties = self._getProperties(child)
1740-
1741- result.addPackage(id, name, properties)
1742-
1743- result.endPackages()
1744-
1745- def _parseXOrg(self, result, node):
1746- drivers = {}
1747- for child in node.getchildren():
1748- info = dict(child.attrib)
1749- if 'device' in info:
1750- info['device'] = int(info['device'])
1751-
1752- name = info['name']
1753- if name in drivers:
1754- raise ValueError(
1755- '<driver name="%s"> appears more than once in <xorg>'
1756- % name)
1757-
1758- drivers[name] = info
1759-
1760- version = node.get('version')
1761- result.addXorg(version, drivers)
1762-
1763- def _parseSoftware(self, result, node):
1764- parsers = {
1765- 'lsbrelease': self._parseLSBRelease,
1766- 'packages': self._parsePackages,
1767- 'xorg': self._parseXOrg,
1768- }
1769-
1770- for child in node.getchildren():
1771- parser = parsers.get(child.tag, self._parseNone)
1772- parser(result, child)
1773-
1774- def _parseQuestions(self, result, node):
1775- result.startQuestions()
1776-
1777- for child in node.getchildren():
1778- question = {'name': child.get('name')}
1779- plugin = child.get('plugin', None)
1780+ for key, value in properties.iteritems():
1781+ if key in ("bogomips", "cache", "count", "speed",):
1782+ properties[key] = int(value)
1783+ elif isinstance(value, list):
1784+ properties[key] = u" ".join(value)
1785+ processors.append(properties)
1786+
1787+ # Check if /proc/cpuinfo was parsed already.
1788+ if any("platform" in processor for processor in processors):
1789+ result.setProcessor(processors[0])
1790+ else:
1791+ lines = []
1792+ for processor in processors:
1793+ # Convert some keys with underscores to spaces instead.
1794+ for key, value in processor.iteritems():
1795+ if "_" in key and key != "vendor_id":
1796+ key = key.replace("_", " ")
1797+
1798+ lines.append(u"%s: %s" % (key, value))
1799+
1800+ lines.append(u"")
1801+
1802+ stream = StringIO(u"\n".join(lines))
1803+ parser = result.parseCpuinfo(stream)
1804+ parser.run(result)
1805+
1806+ def parseQuestions(self, result, node):
1807+ """Parse the <questions> part of a submission."""
1808+ for child in node.getchildren():
1809+ assert child.tag == "question", \
1810+ "Unexpected tag <%s>, expected <question>" % child.tag
1811+ question = {
1812+ "name": child.get("name"),
1813+ "targets": [],
1814+ }
1815+ plugin = child.get("plugin", None)
1816 if plugin is not None:
1817- question['plugin'] = plugin
1818- question['targets'] = targets = []
1819+ question["plugin"] = plugin
1820+
1821 answer_choices = []
1822-
1823 for sub_node in child.getchildren():
1824 sub_tag = sub_node.tag
1825- if sub_tag == 'answer':
1826- question['answer'] = answer = {}
1827- answer['type'] = sub_node.get('type')
1828- if answer['type'] == 'multiple_choice':
1829- question['answer_choices'] = answer_choices
1830- unit = sub_node.get('unit', None)
1831+ if sub_tag == "answer":
1832+ question["answer"] = answer = {}
1833+ answer["type"] = sub_node.get("type")
1834+ if answer["type"] == "multiple_choice":
1835+ question["answer_choices"] = answer_choices
1836+ unit = sub_node.get("unit", None)
1837 if unit is not None:
1838- answer['unit'] = unit
1839- answer['value'] = sub_node.text.strip()
1840- elif sub_tag == 'answer_choices':
1841+ answer["unit"] = unit
1842+ answer["value"] = sub_node.text.strip()
1843+
1844+ elif sub_tag == "answer_choices":
1845 for value_node in sub_node.getchildren():
1846 answer_choices.append(
1847- self._getValueAttribute(value_node))
1848- elif sub_tag == 'target':
1849- target = {'id': int(sub_node.get('id'))}
1850- target['drivers'] = drivers = []
1851+ self._getValueAsType(value_node))
1852+
1853+ elif sub_tag == "target":
1854+ # The Relax NG schema ensures that the attribute
1855+ # id exists and that it is an integer
1856+ target = {"id": int(sub_node.get("id"))}
1857+ target["drivers"] = drivers = []
1858 for driver_node in sub_node.getchildren():
1859 drivers.append(driver_node.text.strip())
1860- targets.append(target)
1861- elif sub_tag in('comment', 'command'):
1862+ question["targets"].append(target)
1863+
1864+ elif sub_tag in ("comment", "command",):
1865 data = sub_node.text
1866 if data is not None:
1867 question[sub_tag] = data.strip()
1868
1869+ else:
1870+ raise AssertionError(
1871+ "Unexpected tag <%s> in <question>" % sub_tag)
1872+
1873 result.addQuestion(question)
1874
1875- result.endQuestions()
1876-
1877- def _parseContext(self, result, node):
1878- parsers = {
1879- 'info': self._parseInfo,
1880- }
1881-
1882- for child in node.getchildren():
1883- parser = parsers.get(child.tag, self._parseNone)
1884- parser(result, child)
1885-
1886- def _parseNone(self, result, node):
1887- pass
1888-
1889- def run(self, result):
1890+ def parseSoftware(self, result, node):
1891+ """Parse the <software> section of a submission."""
1892+ parsers = {
1893+ "lsbrelease": self.parseLSBRelease,
1894+ "packages": self.parsePackages,
1895+ }
1896+
1897+ for child in node.getchildren():
1898+ parser = parsers.get(child.tag)
1899+ if parser:
1900+ parser(result, child)
1901+ else:
1902+ self.logger.debug(
1903+ "Unsupported tag <%s> in <software>" % child.tag)
1904+
1905+ def parseSummary(self, result, node):
1906+ """Parse the <summary> section of a submission."""
1907+ parsers = {
1908+ "architecture": self._getValueAsString,
1909+ "client": self._getClient,
1910+ "contactable": self._getValueAsBoolean,
1911+ "date_created": self._getValueAsDatetime,
1912+ "distribution": self._getValueAsString,
1913+ "distroseries": self._getValueAsString,
1914+ "kernel-release": self._getValueAsString,
1915+ "live_cd": self._getValueAsBoolean,
1916+ "private": self._getValueAsBoolean,
1917+ "system_id": self._getValueAsString,
1918+ }
1919+
1920+ for child in node.getchildren():
1921+ parser = parsers.get(child.tag)
1922+ if parser:
1923+ value = parser(child)
1924+ result.addSummary(child.tag, value)
1925+ else:
1926+ self.logger.debug(
1927+ "Unsupported tag <%s> in <summary>" % child.tag)
1928+
1929+ def parseRoot(self, result, node):
1930+ """Parse the <system> root of a submission."""
1931+ parsers = {
1932+ "context": self.parseContext,
1933+ "hardware": self.parseHardware,
1934+ "questions": self.parseQuestions,
1935+ "software": self.parseSoftware,
1936+ "summary": self.parseSummary,
1937+ }
1938+
1939+ # Iterate over the root children, "summary" first
1940+ for child in node.getchildren():
1941+ parser = parsers.get(child.tag)
1942+ if parser:
1943+ parser(result, child)
1944+ else:
1945+ self.logger.debug(
1946+ "Unsupported tag <%s> in <system>" % child.tag)
1947+
1948+ def run(self, test_run_factory, **kwargs):
1949 parser = etree.XMLParser()
1950
1951- try:
1952- tree = etree.parse(self.stream, parser=parser)
1953- except SyntaxError, error:
1954- logging.error(error)
1955- return
1956-
1957+ tree = etree.parse(self.file, parser=parser)
1958 root = tree.getroot()
1959 if root.tag != "system":
1960- logging.error("Root node is not '<system>'")
1961- return
1962-
1963- self._parseRoot(result, root)
1964-
1965-
1966-SubmissionResult = implement_from_dict("SubmissionResult", [
1967- "startDevices", "endDevices", "addDevice", "startPackages",
1968- "endPackages", "addPackage", "startProcessors", "endProcessors",
1969- "addProcessor", "startQuestions", "endQuestions", "addQuestion",
1970- "addInfo", "addSummary", "addXorg", "setDistribution"], DeviceResult)
1971+ raise AssertionError(
1972+ "Unexpected tag <%s> at root, expected <system>" % root.tag)
1973+
1974+ result = SubmissionResult(test_run_factory, **kwargs)
1975+ self.parseRoot(result, root)
1976+
1977+ return result
1978
1979=== renamed file 'checkbox/parsers/udev.py' => 'checkbox/parsers/udevadm.py'
1980--- checkbox/parsers/udev.py 2011-09-09 19:24:58 +0000
1981+++ checkbox/parsers/udevadm.py 2011-10-12 15:56:08 +0000
1982@@ -16,38 +16,78 @@
1983 # You should have received a copy of the GNU General Public License
1984 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1985 #
1986-import os
1987 import re
1988 import string
1989-import posixpath
1990-
1991-from curses.ascii import isprint
1992-
1993-from checkbox.lib.bit import get_bitmask, test_bit
1994-from checkbox.lib.dmi import Dmi, DmiNotAvailable
1995+
1996+from checkbox.lib.bit import (
1997+ get_bitmask,
1998+ test_bit,
1999+ )
2000 from checkbox.lib.input import Input
2001 from checkbox.lib.pci import Pci
2002 from checkbox.lib.usb import Usb
2003
2004
2005-class UdevDevice(object):
2006- __slots__ = ("_environment", "_attributes", "_stack")
2007-
2008- def __init__(self, environment, attributes, stack=[]):
2009- super(UdevDevice, self).__init__()
2010+PCI_RE = re.compile(
2011+ r"^pci:"
2012+ r"v(?P<vendor_id>[%(hex)s]{8})"
2013+ r"d(?P<product_id>[%(hex)s]{8})"
2014+ r"sv(?P<subvendor_id>[%(hex)s]{8})"
2015+ r"sd(?P<subproduct_id>[%(hex)s]{8})"
2016+ r"bc(?P<class>[%(hex)s]{2})"
2017+ r"sc(?P<subclass>[%(hex)s]{2})"
2018+ r"i(?P<interface>[%(hex)s]{2})"
2019+ % {"hex": string.hexdigits})
2020+PNP_RE = re.compile(
2021+ r"^acpi:"
2022+ r"(?P<vendor_name>[%(upper)s]{3})"
2023+ r"(?P<product_id>[%(hex)s]{4}):"
2024+ % {"upper": string.uppercase, "hex": string.hexdigits})
2025+USB_RE = re.compile(
2026+ r"^usb:"
2027+ r"v(?P<vendor_id>[%(hex)s]{4})"
2028+ r"p(?P<product_id>[%(hex)s]{4})"
2029+ r"d(?P<revision>[%(hex)s]{4})"
2030+ r"dc(?P<class>[%(hex)s]{2})"
2031+ r"dsc(?P<subclass>[%(hex)s]{2})"
2032+ r"dp(?P<protocol>[%(hex)s]{2})"
2033+ r"ic(?P<interface_class>[%(hex)s]{2})"
2034+ r"isc(?P<interface_subclass>[%(hex)s]{2})"
2035+ r"ip(?P<interface_protocol>[%(hex)s]{2})"
2036+ % {"hex": string.hexdigits})
2037+SCSI_RE = re.compile(
2038+ r"^scsi:"
2039+ r"t-0x(?P<type>[%(hex)s]{2})"
2040+ % {"hex": string.hexdigits})
2041+
2042+
2043+class UdevadmDevice:
2044+ __slots__ = ("_environment", "_bits", "_stack",)
2045+
2046+ def __init__(self, environment, bits=None, stack=[]):
2047 self._environment = environment
2048- self._attributes = attributes
2049+ self._bits = bits
2050 self._stack = stack
2051
2052 @property
2053 def bus(self):
2054- return self._environment.get("SUBSYSTEM")
2055+ # Change the bus from 'acpi' to 'pnp' for some devices
2056+ if PNP_RE.match(self._environment.get("MODALIAS", "")) \
2057+ and self.path.endswith(":00"):
2058+ return "pnp"
2059+
2060+ # Change the bus from 'block' to parent
2061+ if self._environment.get("DEVTYPE") == "disk" and self._stack:
2062+ return self._stack[-1]._environment.get("SUBSYSTEM")
2063+
2064+ bus = self._environment.get("SUBSYSTEM")
2065+ if bus == "pnp":
2066+ return None
2067+
2068+ return bus
2069
2070 @property
2071 def category(self):
2072- if "sys_vendor" in self._attributes:
2073- return "SYSTEM"
2074-
2075 if "IFINDEX" in self._environment:
2076 return "NETWORK"
2077
2078@@ -69,7 +109,10 @@
2079 return "NETWORK"
2080
2081 if class_id == Pci.BASE_CLASS_DISPLAY:
2082- return "VIDEO"
2083+ if subclass_id == Pci.CLASS_DISPLAY_VGA:
2084+ return "VIDEO"
2085+ else:
2086+ return "OTHER"
2087
2088 if class_id == Pci.BASE_CLASS_SERIAL \
2089 and subclass_id == Pci.CLASS_SERIAL_USB:
2090@@ -113,13 +156,9 @@
2091 or subclass_id == Pci.CLASS_BRIDGE_CARDBUS):
2092 return "SOCKET"
2093
2094- if "bInterfaceClass" in self._attributes:
2095- interface_class = int(
2096- self._attributes["bInterfaceClass"], 16)
2097- interface_subclass = int(
2098- self._attributes["bInterfaceSubClass"], 16)
2099- interface_protocol = int(
2100- self._attributes["bInterfaceProtocol"], 16)
2101+ if "TYPE" in self._environment and "INTERFACE" in self._environment:
2102+ interface_class, interface_subclass, interface_protocol = (
2103+ int(i) for i in self._environment["INTERFACE"].split("/"))
2104
2105 if interface_class == Usb.BASE_CLASS_AUDIO:
2106 return "AUDIO"
2107@@ -143,6 +182,25 @@
2108 else:
2109 return "WIRELESS"
2110
2111+ if "KEY" in self._environment:
2112+ key = self._environment["KEY"].strip("=")
2113+ bitmask = get_bitmask(key)
2114+
2115+ for i in range(Input.KEY_Q, Input.KEY_P + 1):
2116+ if not test_bit(i, bitmask, self._bits):
2117+ break
2118+ else:
2119+ return "KEYBOARD"
2120+
2121+ if test_bit(Input.KEY_CAMERA, bitmask, self._bits):
2122+ return "CAPTURE"
2123+
2124+ if test_bit(Input.BTN_TOUCH, bitmask, self._bits):
2125+ return "TOUCH"
2126+
2127+ if test_bit(Input.BTN_MOUSE, bitmask, self._bits):
2128+ return "MOUSE"
2129+
2130 if "ID_TYPE" in self._environment:
2131 id_type = self._environment["ID_TYPE"]
2132
2133@@ -155,22 +213,6 @@
2134 if id_type == "video":
2135 return "VIDEO"
2136
2137- if "KEY" in self._environment:
2138- key = self._environment["KEY"].strip("=")
2139- bitmask = get_bitmask(key)
2140-
2141- for i in range(Input.KEY_Q, Input.KEY_P + 1):
2142- if not test_bit(i, bitmask):
2143- break
2144- else:
2145- return "KEYBOARD"
2146-
2147- if test_bit(Input.BTN_TOUCH, bitmask):
2148- return "TOUCH"
2149-
2150- if test_bit(Input.BTN_MOUSE, bitmask):
2151- return "MOUSE"
2152-
2153 if "DEVTYPE" in self._environment:
2154 devtype = self._environment["DEVTYPE"]
2155 if devtype == "disk":
2156@@ -181,8 +223,10 @@
2157 return "FLOPPY"
2158
2159 if devtype == "scsi_device":
2160- type = int(self._attributes.get("type", "-1"))
2161- # Check for FLASH drives, see /lib/udev/rules.d/80-udisks.rules
2162+ match = SCSI_RE.match(self._environment.get("MODALIAS", ""))
2163+ type = int(match.group("type"), 16) if match else -1
2164+
2165+ # Check FLASH drives, see /lib/udev/rules.d/80-udisks.rules
2166 if type in (0, 7, 14) \
2167 and not any(d.driver == "rts_pstor" for d in self._stack):
2168 return "DISK"
2169@@ -216,8 +260,11 @@
2170 if "DRIVER" in self._environment:
2171 return self._environment["DRIVER"]
2172
2173- if "ID_USB_DRIVER" in self._environment:
2174- return self._environment["ID_USB_DRIVER"]
2175+ # Check parent device for driver
2176+ if self._stack:
2177+ parent = self._stack[-1]
2178+ if "DRIVER" in parent._environment:
2179+ return parent._environment["DRIVER"]
2180
2181 return None
2182
2183@@ -228,48 +275,36 @@
2184 @property
2185 def product_id(self):
2186 # pci
2187- if "PCI_ID" in self._environment:
2188- vendor_id, product_id = self._environment["PCI_ID"].split(":")
2189- return int(product_id, 16)
2190-
2191- # usb interface
2192- if "PRODUCT" in self._environment \
2193- and self._environment.get("DEVTYPE") == "usb_interface":
2194- vendor_id, product_id, revision \
2195- = self._environment["PRODUCT"].split("/")
2196- return int(product_id, 16)
2197-
2198- # usb device and ieee1394
2199- for attribute in "idProduct", "model_id":
2200- if attribute in self._attributes:
2201- return int(self._attributes[attribute], 16)
2202+ match = PCI_RE.match(self._environment.get("MODALIAS", ""))
2203+ if match:
2204+ return int(match.group("product_id"), 16)
2205+
2206+ # usb
2207+ match = USB_RE.match(self._environment.get("MODALIAS", ""))
2208+ if match:
2209+ return int(match.group("product_id"), 16)
2210
2211 # pnp
2212- if "id" in self._attributes:
2213- match = re.match(r"^(?P<vendor_name>.*)(?P<product_id>[%s]{4})$"
2214- % string.hexdigits, self._attributes["id"])
2215- if match:
2216- return int(match.group("product_id"), 16)
2217+ match = PNP_RE.match(self._environment.get("MODALIAS", ""))
2218+ if match:
2219+ product_id = int(match.group("product_id"), 16)
2220+ # Ignore interrupt controllers
2221+ if product_id > 0x0100:
2222+ return product_id
2223
2224 return None
2225
2226 @property
2227 def vendor_id(self):
2228 # pci
2229- if "PCI_ID" in self._environment:
2230- vendor_id, product_id = self._environment["PCI_ID"].split(":")
2231- return int(vendor_id, 16)
2232-
2233- # usb interface
2234- if "PRODUCT" in self._environment \
2235- and self._environment.get("DEVTYPE") == "usb_interface":
2236- vendor_id, product_id, revision \
2237- = self._environment["PRODUCT"].split("/")
2238- return int(vendor_id, 16)
2239-
2240- # usb device
2241- if "idVendor" in self._attributes:
2242- return int(self._attributes["idVendor"], 16)
2243+ match = PCI_RE.match(self._environment.get("MODALIAS", ""))
2244+ if match:
2245+ return int(match.group("vendor_id"), 16)
2246+
2247+ # usb
2248+ match = USB_RE.match(self._environment.get("MODALIAS", ""))
2249+ if match:
2250+ return int(match.group("vendor_id"), 16)
2251
2252 return None
2253
2254@@ -299,16 +334,19 @@
2255 if element in self._environment:
2256 return self._environment[element].strip('"')
2257
2258- for attribute in ("description",
2259- "model_name_kv",
2260- "model",
2261- "product_name"):
2262- if attribute in self._attributes:
2263- return self._attributes[attribute]
2264+ # disk
2265+ if self._environment.get("DEVTYPE") == "scsi_device":
2266+ for device in reversed(self._stack):
2267+ if device._environment.get("ID_BUS") == "usb":
2268+ return decode_id(device._environment["ID_MODEL_ENC"])
2269+
2270+ if self._environment.get("DEVTYPE") == "disk" \
2271+ and self._environment.get("ID_BUS") == "ata":
2272+ return decode_id(self._environment["ID_MODEL_ENC"])
2273
2274 # floppy
2275 if self.driver == "floppy":
2276- return "Platform Device"
2277+ return u"Platform Device"
2278
2279 return None
2280
2281@@ -320,168 +358,28 @@
2282 if "POWER_SUPPLY_MANUFACTURER" in self._environment:
2283 return self._environment["POWER_SUPPLY_MANUFACTURER"]
2284
2285- if "vendor" in self._attributes:
2286- vendor = self._attributes["vendor"]
2287- if not re.match(r"^0x[%s]{4}$" % string.hexdigits, vendor):
2288- return vendor
2289-
2290- # dmi
2291- if "sys_vendor" in self._attributes:
2292- return self._attributes["sys_vendor"]
2293-
2294 # pnp
2295- if "id" in self._attributes:
2296- match = re.match(r"^(?P<vendor_name>.*)(?P<product_id>[%s]{4})$"
2297- % string.hexdigits, self._attributes["id"])
2298- if match:
2299- return match.group("vendor_name")
2300-
2301- return None
2302-
2303-
2304-class UdevLocalDevice(UdevDevice):
2305-
2306- @property
2307- def bus(self):
2308- sys_path = posixpath.join(
2309- "/sys%s" % self._environment["DEVPATH"], "subsystem")
2310- if posixpath.islink(sys_path):
2311- link = os.readlink(sys_path)
2312- if "/" in link:
2313- return posixpath.basename(link)
2314-
2315- return None
2316-
2317- @property
2318- def vendor_id(self):
2319- vendor_id = super(UdevLocalDevice, self).vendor_id
2320- if vendor_id is not None:
2321- return vendor_id
2322-
2323- # ieee1394
2324- vendor_id_path = posixpath.join(self.path, "../vendor_id")
2325- if posixpath.exists(vendor_id_path):
2326- vendor_id = open(vendor_id_path, "r").read().strip()
2327- return int(vendor_id, 16)
2328-
2329- return None
2330-
2331- @property
2332- def product(self):
2333- product = super(UdevLocalDevice, self).product
2334- if product is not None:
2335- return product
2336-
2337- # sound
2338- bus = self.bus
2339- if bus == "sound":
2340- device = posixpath.basename(self._environment["DEVPATH"])
2341- match = re.match(
2342- r"(card|controlC|hwC|midiC)(?P<card>\d+)", device)
2343- if match:
2344- card = match.group("card")
2345- in_card = False
2346- file = open("/proc/asound/cards", "r")
2347- for line in file.readlines():
2348- line = line.strip()
2349- match = re.match(r"(?P<card>\d+) \[", line)
2350- if match:
2351- in_card = match.group("card") == card
2352-
2353- if in_card:
2354- match = re.match(r"""(?P<name>.*) """
2355- """at (?P<address>0x[%s]{8}) """
2356- """irq (?P<irq>\d+)""" % string.hexdigits, line)
2357- if match:
2358- return match.group("name")
2359-
2360- path = None
2361- match = re.match(
2362- r"pcmC(?P<card>\d+)D(?P<device>\d+)(?P<type>\w)", device)
2363- if match:
2364- path = "/proc/asound/card%s/pcm%s%c/info" % match.groups()
2365-
2366- match = re.match(
2367- r"(dsp|adsp|midi|amidi|audio|mixer)(?P<card>\d+)?", device)
2368- if match:
2369- card = match.group("card") or 0
2370- path = "/proc/asound/card%s/pcm0p/info" % card
2371-
2372- if path and posixpath.exists(path):
2373- file = open(path, "r")
2374- for line in file.readlines():
2375- match = re.match(r"name: (?P<name>.*)", line)
2376- if match:
2377- return match.group("name")
2378-
2379- return None
2380-
2381- @property
2382- def vendor(self):
2383- vendor = super(UdevLocalDevice, self).vendor
2384- if vendor is not None:
2385- return vendor
2386-
2387- # ieee1394
2388- vendor_path = posixpath.join(self.path, "../vendor_oui")
2389- if posixpath.exists(vendor_path):
2390- return open(vendor_path, "r").read().strip()
2391-
2392- return None
2393-
2394-
2395-class UdevDmiDevice(UdevDevice):
2396-
2397- def __init__(self, environment, attributes, category):
2398- super(UdevDmiDevice, self).__init__(environment, attributes)
2399- self._category = category
2400-
2401- @property
2402- def category(self):
2403- return self._category
2404-
2405- @property
2406- def path(self):
2407- path = super(UdevDmiDevice, self).path
2408- return posixpath.join(path, self._category.lower())
2409-
2410- @property
2411- def product(self):
2412- if self._category == "CHASSIS":
2413- type_string = self._attributes.get("chassis_type", "0")
2414- type_index = int(type_string)
2415- return Dmi.chassis_names[type_index]
2416-
2417- for name in "name", "version":
2418- attribute = "%s_%s" % (self._category.lower(), name)
2419- product = self._attributes.get(attribute)
2420- if product and product != "Not Available":
2421- return product
2422-
2423- return None
2424-
2425- @DmiNotAvailable
2426- def _getVendor(self):
2427- attribute = "%s_vendor" % self._category.lower()
2428- if attribute in self._attributes:
2429- return self._attributes[attribute]
2430-
2431- return None
2432-
2433- @property
2434- def vendor(self):
2435- return self._getVendor()
2436-
2437-
2438-class UdevParser(object):
2439- """udevadm parser."""
2440-
2441- device_factory = UdevDevice
2442- dmi_device_factory = UdevDmiDevice
2443-
2444- def __init__(self, stream):
2445+ match = PNP_RE.match(self._environment.get("MODALIAS", ""))
2446+ if match:
2447+ return match.group("vendor_name")
2448+
2449+ # disk
2450+ if self._environment.get("DEVTYPE") == "scsi_device":
2451+ for device in reversed(self._stack):
2452+ if device._environment.get("ID_BUS") == "usb":
2453+ return decode_id(device._environment["ID_VENDOR_ENC"])
2454+
2455+ return None
2456+
2457+
2458+class UdevadmParser:
2459+ """Parser for the udevadm command."""
2460+
2461+ device_factory = UdevadmDevice
2462+
2463+ def __init__(self, stream, bits=None):
2464 self.stream = stream
2465- self.stack = []
2466+ self.bits = bits
2467
2468 def _ignoreDevice(self, device):
2469 # Ignore devices without bus information
2470@@ -499,9 +397,8 @@
2471 and device.subvendor_id is None)):
2472 return True
2473
2474- # Ignore virtual devices except for dmi information
2475- if device.bus != "dmi" \
2476- and "virtual" in device.path.split(posixpath.sep):
2477+ # Ignore ACPI devices
2478+ if device.bus == "acpi":
2479 return True
2480
2481 return False
2482@@ -510,96 +407,60 @@
2483 return {}
2484
2485 def run(self, result):
2486- line_pattern = re.compile(r"(?P<key>\w):\s*(?P<value>.*)")
2487+ # Some attribute lines have a space character after the
2488+ # ':', others don't have it (see udevadm-info.c).
2489+ line_pattern = re.compile(r"(?P<key>[A-Z]):\s*(?P<value>.*)")
2490 multi_pattern = re.compile(r"(?P<key>[^=]+)=(?P<value>.*)")
2491
2492+ stack = []
2493 output = self.stream.read()
2494- for record in output.split("\n\n"):
2495+ for record in re.split("\n{2,}", output):
2496+ record = record.strip()
2497 if not record:
2498 continue
2499
2500 # Determine path and environment
2501 path = None
2502+ element = None
2503 environment = {}
2504 for line in record.split("\n"):
2505- if not line:
2506+ line_match = line_pattern.match(line)
2507+ if not line_match:
2508+ if environment:
2509+ # Append to last environment element
2510+ environment[element] += line
2511 continue
2512
2513- match = line_pattern.match(line)
2514- if not match:
2515- raise Exception(
2516- "Device line not supported: %s" % line)
2517-
2518- key = match.group("key")
2519- value = match.group("value")
2520+ key = line_match.group("key")
2521+ value = line_match.group("value")
2522
2523 if key == "P":
2524 path = value
2525 elif key == "E":
2526- match = multi_pattern.match(value)
2527- if not match:
2528+ key_match = multi_pattern.match(value)
2529+ if not key_match:
2530 raise Exception(
2531 "Device property not supported: %s" % value)
2532- environment[match.group("key")] = match.group("value")
2533+ element = key_match.group("key")
2534+ environment[element] = key_match.group("value")
2535
2536 # Update stack
2537- while self.stack:
2538- if self.stack[-1].path + "/" in path:
2539+ while stack:
2540+ if stack[-1].path + "/" in path:
2541 break
2542- self.stack.pop()
2543+ stack.pop()
2544
2545 # Set default DEVPATH
2546 environment.setdefault("DEVPATH", path)
2547
2548- # Determine attributes
2549- attributes = self.getAttributes(path)
2550-
2551- if path == "/devices/virtual/dmi/id":
2552- device = self.device_factory(environment, attributes)
2553- if not self._ignoreDevice(device):
2554- result.addDevice(device)
2555- for category in "BIOS", "BOARD", "CHASSIS":
2556- device = self.dmi_device_factory(
2557- environment, attributes, category)
2558- if not self._ignoreDevice(device):
2559- result.addDevice(device)
2560- else:
2561- device = self.device_factory(environment, attributes, self.stack)
2562- if not self._ignoreDevice(device):
2563- result.addDevice(device)
2564-
2565- self.stack.append(device)
2566-
2567-
2568-class UdevLocalParser(UdevParser):
2569-
2570- device_factory = UdevLocalDevice
2571-
2572- def getAttributes(self, path):
2573- attributes = {}
2574- sys_path = "/sys%s" % path
2575- try:
2576- names = os.listdir(sys_path)
2577- except OSError:
2578- return attributes
2579-
2580- for name in names:
2581- name_path = posixpath.join(sys_path, name)
2582- if name[0] == "." \
2583- or name in ["dev", "uevent"] \
2584- or posixpath.isdir(name_path) \
2585- or posixpath.islink(name_path):
2586- continue
2587-
2588- try:
2589- value = open(name_path, "r").read().strip()
2590- except IOError:
2591- continue
2592-
2593- value = value.split("\n")[0]
2594- if [c for c in value if not isprint(c)]:
2595- continue
2596-
2597- attributes[name] = value
2598-
2599- return attributes
2600+ device = self.device_factory(environment, self.bits, list(stack))
2601+ if not self._ignoreDevice(device):
2602+ result.addDevice(device)
2603+
2604+ stack.append(device)
2605+
2606+
2607+def decode_id(id):
2608+ encoded_id = id.encode("utf-8")
2609+ decoded_id = encoded_id.decode("string-escape").decode("utf-8")
2610+ return decoded_id.strip()
2611
2612=== modified file 'jobs/info.txt.in'
2613--- jobs/info.txt.in 2011-09-08 14:52:33 +0000
2614+++ jobs/info.txt.in 2011-10-12 15:56:08 +0000
2615@@ -25,6 +25,10 @@
2616 plugin: attachment
2617 command: lspci -vvnn
2618
2619+name: info/meminfo_attachment
2620+plugin: attachment
2621+command: cat /proc/meminfo
2622+
2623 name: info/modprobe_attachment
2624 plugin: attachment
2625 command: find /etc/modprobe.* -name \*.conf | xargs cat
2626
2627=== modified file 'jobs/resource.txt.in'
2628--- jobs/resource.txt.in 2010-04-08 21:07:43 +0000
2629+++ jobs/resource.txt.in 2011-10-12 15:56:08 +0000
2630@@ -35,6 +35,11 @@
2631 plugin: resource
2632 command: udev_resource
2633
2634+name: dmi
2635+plugin: resource
2636+user: root
2637+command: dmi_resource
2638+
2639 name: uname
2640 plugin: resource
2641 command: uname_resource
2642
2643=== modified file 'scripts/cpuinfo_resource'
2644--- scripts/cpuinfo_resource 2011-05-30 20:42:48 +0000
2645+++ scripts/cpuinfo_resource 2011-10-12 15:56:08 +0000
2646@@ -20,10 +20,7 @@
2647 import sys
2648 import posixpath
2649
2650-from checkbox.parsers.cpuinfo import (
2651- CpuinfoParser,
2652- CpuinfoResult,
2653- )
2654+from checkbox.parsers.cpuinfo import CpuinfoParser
2655
2656 # Filename where cpuinfo is stored.
2657 CPUINFO_FILENAME = "/proc/cpuinfo"
2658@@ -32,25 +29,24 @@
2659 FREQUENCY_FILENAME = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
2660
2661
2662-class ResourceResult(CpuinfoResult):
2663-
2664- def __setitem__(self, key, value):
2665- print "%s: %s" % (key, value)
2666-
2667- def setSpeed(self, speed):
2668- # Check for frequency scaling
2669- if posixpath.exists(FREQUENCY_FILENAME):
2670- speed = open(FREQUENCY_FILENAME).read().strip()
2671- speed = int(speed) / 1000
2672-
2673- self["speed"] = speed
2674+class CpuinfoResult:
2675+
2676+ def setProcessor(self, processor):
2677+ for key, value in processor.iteritems():
2678+ if key == "speed":
2679+ # Check for frequency scaling
2680+ if posixpath.exists(FREQUENCY_FILENAME):
2681+ value = open(FREQUENCY_FILENAME).read().strip()
2682+ value = int(value) / 1000
2683+
2684+ print "%s: %s" % (key, value)
2685
2686
2687 def main():
2688 stream = open(CPUINFO_FILENAME)
2689 parser = CpuinfoParser(stream)
2690
2691- result = ResourceResult()
2692+ result = CpuinfoResult()
2693 parser.run(result)
2694
2695 return 0
2696
2697=== added file 'scripts/dmi_resource'
2698--- scripts/dmi_resource 1970-01-01 00:00:00 +0000
2699+++ scripts/dmi_resource 2011-10-12 15:56:08 +0000
2700@@ -0,0 +1,55 @@
2701+#!/usr/bin/python
2702+#
2703+# This file is part of Checkbox.
2704+#
2705+# Copyright 2011 Canonical Ltd.
2706+#
2707+# Checkbox is free software: you can redistribute it and/or modify
2708+# it under the terms of the GNU General Public License as published by
2709+# the Free Software Foundation, either version 3 of the License, or
2710+# (at your option) any later version.
2711+#
2712+# Checkbox is distributed in the hope that it will be useful,
2713+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2714+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2715+# GNU General Public License for more details.
2716+#
2717+# You should have received a copy of the GNU General Public License
2718+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2719+#
2720+import os
2721+import sys
2722+
2723+from checkbox.parsers.dmidecode import DmidecodeParser
2724+
2725+
2726+# Command to retrieve dmi information.
2727+COMMAND = "dmidecode"
2728+
2729+
2730+class DmiResult(object):
2731+
2732+ attributes = ("path", "category", "product", "vendor", "serial",
2733+ "version",)
2734+
2735+ def addDmiDevice(self, device):
2736+ for attribute in self.attributes:
2737+ value = getattr(device, attribute)
2738+ if value is not None:
2739+ print "%s: %s" % (attribute, value)
2740+
2741+ print
2742+
2743+
2744+def main():
2745+ stream = os.popen(COMMAND)
2746+ udev = DmidecodeParser(stream)
2747+
2748+ result = DmiResult()
2749+ udev.run(result)
2750+
2751+ return 0
2752+
2753+
2754+if __name__ == "__main__":
2755+ sys.exit(main())
2756
2757=== modified file 'scripts/hal_resource'
2758--- scripts/hal_resource 2010-04-22 21:12:51 +0000
2759+++ scripts/hal_resource 2011-10-12 15:56:08 +0000
2760@@ -23,7 +23,6 @@
2761 import string
2762 import posixpath
2763
2764-from checkbox.lib.dmi import DmiNotAvailable
2765 from checkbox.lib.pci import Pci
2766 from checkbox.lib.usb import Usb
2767
2768@@ -332,10 +331,6 @@
2769
2770 @property
2771 def vendor(self):
2772- return self._get_vendor()
2773-
2774- @DmiNotAvailable
2775- def _get_vendor(self):
2776 for subproperty in "vendor", "manufacturer":
2777 property = "%s.%s" % (self._property, subproperty)
2778 if property in self._properties:
2779
2780=== modified file 'scripts/meminfo_resource'
2781--- scripts/meminfo_resource 2010-02-25 20:58:11 +0000
2782+++ scripts/meminfo_resource 2011-10-12 15:56:08 +0000
2783@@ -17,39 +17,27 @@
2784 # You should have received a copy of the GNU General Public License
2785 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2786 #
2787-import re
2788 import sys
2789
2790+from checkbox.parsers.meminfo import MeminfoParser
2791
2792 # Filename where meminfo is stored.
2793-FILENAME = "/proc/meminfo"
2794-
2795-
2796-def get_meminfo():
2797- key_value_pattern = re.compile(r"(?P<key>.*):\s+(?P<value>.*)")
2798- meminfo_map = {
2799- "MemTotal": "total",
2800- "SwapTotal": "swap"}
2801-
2802- meminfo = {}
2803- file = open(FILENAME, "r")
2804- for line in file.readlines():
2805- line = line.strip()
2806- match = key_value_pattern.match(line)
2807- if match:
2808- key = match.group("key")
2809- if key in meminfo_map:
2810- key = meminfo_map[key]
2811- value = match.group("value")
2812- (integer, factor) = value.split()
2813- meminfo[key] = int(integer) * 1024
2814-
2815- return meminfo
2816+MEMINFO_FILENAME = "/proc/meminfo"
2817+
2818+
2819+class MeminfoResult:
2820+
2821+ def setMemory(self, memory):
2822+ for key, value in memory.iteritems():
2823+ print "%s: %s" % (key, value)
2824+
2825
2826 def main():
2827- meminfo = get_meminfo()
2828- for key, value in meminfo.iteritems():
2829- print "%s: %s" % (key, value)
2830+ stream = open(MEMINFO_FILENAME)
2831+ parser = MeminfoParser(stream)
2832+
2833+ result = MeminfoResult()
2834+ parser.run(result)
2835
2836 return 0
2837
2838
2839=== modified file 'scripts/udev_resource'
2840--- scripts/udev_resource 2011-05-30 20:42:48 +0000
2841+++ scripts/udev_resource 2011-10-12 15:56:08 +0000
2842@@ -20,7 +20,7 @@
2843 import os
2844 import sys
2845
2846-from checkbox.parsers.udev import UdevLocalParser
2847+from checkbox.parsers.udevadm import UdevadmParser
2848
2849
2850 # Command to retrieve udev information.
2851@@ -43,7 +43,7 @@
2852
2853 def main():
2854 stream = os.popen(COMMAND)
2855- udev = UdevLocalParser(stream)
2856+ udev = UdevadmParser(stream)
2857
2858 result = UdevResult()
2859 udev.run(result)
2860
2861=== modified file 'setup.py'
2862--- setup.py 2011-08-26 10:29:35 +0000
2863+++ setup.py 2011-10-12 15:56:08 +0000
2864@@ -233,6 +233,8 @@
2865 scripts = ["bin/checkbox-cli", "bin/checkbox-gtk", "bin/checkbox-urwid"],
2866 packages = ["checkbox", "checkbox.contrib", "checkbox.lib", "checkbox.parsers",
2867 "checkbox.reports", "checkbox_cli", "checkbox_gtk", "checkbox_urwid"],
2868+ package_data = {
2869+ "": ["cputable"]},
2870 cmdclass = {
2871 "build": checkbox_build,
2872 "build_i18n": build_i18n,

Subscribers

People subscribed via source and target branches