Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: gft_hwcomp.py

Issue 6839009: factory_test_tools: merge latest improvements from ToT to R12 factory branch (Closed) Base URL: ssh://gitrw.chromium.org:9222/factory_test_tools.git@0.12.369.B
Patch Set: Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gft_common.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 import glob 7 import glob
8 import os 8 import os
9 import pprint 9 import pprint
10 import re 10 import re
11 import sys 11 import sys
12 import threading
12 13
13 import flashrom_util 14 import flashrom_util
14 import gft_common 15 import gft_common
15 import gft_fwhash 16 import gft_fwhash
16 import vblock 17 import vblock
17 18
18 from gft_common import DebugMsg, VerboseMsg, WarningMsg, ErrorMsg, ErrorDie 19 from gft_common import DebugMsg, VerboseMsg, WarningMsg, ErrorMsg, ErrorDie
19 20
20 21
21 def Memorize(f): 22 def Memorize(f):
22 """ Decorator for functions that need memorization.""" 23 return gft_common.ThreadSafe(gft_common.Memorize(f))
23 memorize_data = {}
24 def memorize_call(*args):
25 index = repr(args)
26 if index in memorize_data:
27 value = memorize_data[index]
28 # DebugMsg('Memorize: using cached value for: %s %s' % (repr(f), index))
29 return value
30 value = f(*args)
31 memorize_data[index] = value
32 return value
33 return memorize_call
34 24
35 25
36 class HardwareComponents(object): 26 class HardwareComponents(object):
37 """ Hardware Components Scanner """ 27 """ Hardware Components Scanner """
38 28
39 # Function names in this class are used for reflection, so please don't change 29 # Function names in this class are used for reflection, so please don't change
40 # the function names even if they are not comliant to coding style guide. 30 # the function names even if they are not comliant to coding style guide.
41 31
42 version = 5 32 version = 6
43 33
44 # We divide all component IDs (cids) into 5 categories: 34 # We divide all component IDs (cids) into 5 categories:
45 # - enumerable: able to get the results by running specific commands; 35 # - enumerable: able to get the results by running specific commands;
46 # - probable: returns existed or not by given some pre-defined choices; 36 # - probable: returns existed or not by given some pre-defined choices;
47 # - pure data: data for some special purpose, can't be tested; 37 # - pure data: data for some special purpose, can't be tested;
48 38
49 _enumerable_cids = [ 39 _enumerable_cids = [
50 'data_display_geometry', 40 'data_display_geometry',
51 'hash_ec_firmware', 41 'hash_ec_firmware',
52 'hash_ro_firmware', 42 'hash_ro_firmware',
(...skipping 28 matching lines...) Expand all
81 _probable_cids = [ 71 _probable_cids = [
82 'key_recovery', 72 'key_recovery',
83 'key_root', 73 'key_root',
84 'part_id_cardreader', 74 'part_id_cardreader',
85 ] 75 ]
86 _pure_data_cids = [ 76 _pure_data_cids = [
87 'data_bitmap_fv', 77 'data_bitmap_fv',
88 'data_recovery_url', 78 'data_recovery_url',
89 ] 79 ]
90 80
81 # list of cids that should not be fetched asynchronously.
82 _non_async_cids = [
83 # Reading EC will become very slow and cause inaccurate results if we try to
84 # probe components that also fires EC command at the same time.
85 'part_id_ec_flash_chip',
86 ]
87
91 # _not_test_cids and _to_be_tested_cids will be re-created for each match. 88 # _not_test_cids and _to_be_tested_cids will be re-created for each match.
92 _not_test_cids = [] 89 _not_test_cids = []
93 _to_be_tested_cids = [] 90 _to_be_tested_cids = []
94 91
95 # TODO(hungte) unify the 'not available' style messages 92 # TODO(hungte) unify the 'not available' style messages
96 _not_present = '' 93 _not_present = ''
97 _no_match = 'No match' 94 _no_match = 'No match'
98 95
99 def __init__(self, verbose=False): 96 def __init__(self, verbose=False):
100 self._initialized = False 97 self._initialized = False
(...skipping 27 matching lines...) Expand all
128 @Memorize 125 @Memorize
129 def load_module(self, name): 126 def load_module(self, name):
130 grep_cmd = ('lsmod 2>/dev/null | grep -q %s' % name) 127 grep_cmd = ('lsmod 2>/dev/null | grep -q %s' % name)
131 loaded = (os.system(grep_cmd) == 0) 128 loaded = (os.system(grep_cmd) == 0)
132 if not loaded: 129 if not loaded:
133 if os.system('modprobe %s >/dev/null 2>&1' % name) != 0: 130 if os.system('modprobe %s >/dev/null 2>&1' % name) != 0:
134 ErrorMsg("Cannot load module: %s" % name) 131 ErrorMsg("Cannot load module: %s" % name)
135 return loaded 132 return loaded
136 133
137 @Memorize 134 @Memorize
135 def is_legacy_device_record(self, record):
136 """ Returns if a matching record looks like a legacy device. """
137 # Current format: [0-9a-f]{4}:[0-9a-f]{4}
138 return True if re.match('[0-9a-f]{4}:[0-9a-f]{4}', record) else False
139
140 @Memorize
141 def _get_legacy_device_list(self):
142 # pci: cat /proc/bus/pci/devices | cut -f 2 # 0.004s < lspci=0.012s
143 device_list = []
144 pci_device_file = '/proc/bus/pci/devices'
145 if os.path.exists(pci_device_file):
146 with open(pci_device_file) as handle:
147 pci_list = [data.split('\t', 2)[1]
148 for data in handle.readlines()]
149 device_list += ['%s:%s' % (entry[:4], entry[4:])
150 for entry in pci_list]
151 else:
152 DebugMsg('Failed to read %s. Execute lspci.' % pci_device_list)
153 pci_list = [entry.split()[2:4]
154 for entry in
155 gft_common.SystemOutput('lspci -n -mm').splitlines()]
156 device_list += ['%s:%s' % (vendor.strip('"'), product.strip('"'))
157 for (vendor, product) in pci_list]
158 # usb: realpath(/sys/bus/usb/devices/*:*)/../id* # 0.05s < lspci=0.078s
159 usb_devs = glob.glob('/sys/bus/usb/devices/*:*')
160 for dev in usb_devs:
161 path = os.path.join(os.path.realpath(dev), '..')
162 device_list += ['%s:%s' %
163 (gft_common.ReadOneLine(os.path.join(path, 'idVendor')),
164 gft_common.ReadOneLine(os.path.join(path, 'idProduct')))]
165 DebugMsg('Legacy device list: ' + ', '.join(device_list))
166 return device_list
167
168 @Memorize
138 def _get_all_connection_info(self): 169 def _get_all_connection_info(self):
139 """ Probes available connectivity and device information """ 170 """ Probes available connectivity and device information """
140 connection_info = { 171 connection_info = {
141 'wireless': '/sys/class/net/wlan0/device', 172 'wireless': '/sys/class/net/wlan0/device',
142 'ethernet': '/sys/class/net/eth0/device', 173 'ethernet': '/sys/class/net/eth0/device',
143 '3g': '/sys/class/tty/ttyUSB0/device', 174 '3g': '/sys/class/tty/ttyUSB0/device',
144 } 175 }
145 flimflam_scripts_base = '/usr/local/lib/flimflam/test' 176 flimflam_scripts_base = '/usr/local/lib/flimflam/test'
146 if os.path.exists(flimflam_scripts_base): 177 if os.path.exists(flimflam_scripts_base):
147 # TODO(hungte) use flimflam to update the device list. 178 # TODO(hungte) use flimflam to update the device list.
(...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after
626 def read_approved_from_file(self, filename): 657 def read_approved_from_file(self, filename):
627 approved = gft_common.LoadComponentsDatabaseFile(filename) 658 approved = gft_common.LoadComponentsDatabaseFile(filename)
628 for cid in self._to_be_tested_cids + self._not_test_cids: 659 for cid in self._to_be_tested_cids + self._not_test_cids:
629 if cid not in approved: 660 if cid not in approved:
630 # If we don't have any listing for this type 661 # If we don't have any listing for this type
631 # of part in HWID, it's not required. 662 # of part in HWID, it's not required.
632 WarningMsg('gft_hwcomp: Bypassing unlisted cid %s' % cid) 663 WarningMsg('gft_hwcomp: Bypassing unlisted cid %s' % cid)
633 approved[cid] = '*' 664 approved[cid] = '*'
634 return approved 665 return approved
635 666
636 def get_all_enumerable_components(self): 667 def get_all_enumerable_components(self, async):
637 results = {} 668 results = {}
638 for cid in self._enumerable_cids: 669 thread_pools = []
670
671 def fetch_enumerable_component(cid):
672 """ Fetch an enumerable component and update the results """
639 if self._verbose: 673 if self._verbose:
640 sys.stdout.flush() 674 sys.stdout.flush()
641 sys.stderr.write('<Fetching property %s>\n' % cid) 675 sys.stderr.write('<Fetching property %s>\n' % cid)
642 components = self.force_get_property('get_' + cid) 676 components = self.force_get_property('get_' + cid)
643 if not isinstance(components, list): 677 if not isinstance(components, list):
644 components = [components] 678 components = [components]
645 results[cid] = components 679 results[cid] = components
680
681 class FetchThread(threading.Thread):
682 """ Thread object for parallel enumerating """
683 def __init__(self, cid):
684 threading.Thread.__init__(self)
685 self.cid = cid
686
687 def run(self):
688 fetch_enumerable_component(self.cid)
689
690 for cid in self._enumerable_cids:
691 if async and cid not in self._non_async_cids:
692 thread_pools.append(FetchThread(cid))
693 else:
694 fetch_enumerable_component(cid)
695
696 # Complete the threads
697 for thread in thread_pools:
698 thread.start()
699 for thread in thread_pools:
700 thread.join()
646 return results 701 return results
647 702
648 def check_enumerable_component(self, cid, exact_values, approved_values): 703 def check_enumerable_component(self, cid, exact_values, approved_values):
649 if '*' in approved_values: 704 if '*' in approved_values:
650 return 705 return
651 706
652 for value in exact_values: 707 unmatched = [value for value in exact_values
653 if value not in approved_values: 708 if value not in approved_values]
654 if cid in self._failures: 709 if not unmatched:
655 self._failures[cid].append(value) 710 return
656 else: 711
657 self._failures[cid] = [value] 712 # there's some error, let's try to match them in legacy list
713 legacy_approved = filter(self.is_legacy_device_record, approved_values)
714 if set(legacy_approved) == set(approved_values):
715 DebugMsg('Start legacy search for cid: ' + cid)
716 # safe for legacy match
717 # TODO(hungte) prefetch this list in async batch process.
718 legacy_list = self._get_legacy_device_list()
719 matched = list(set(legacy_list).intersection(set(approved_values)))
720 if matched:
721 DebugMsg('Changed detected list: %s->%s' % (self._system[cid], matched))
722 self._system[cid] = matched
723 return
724 # update with remaining error.
725 self._failures[cid] = unmatched
658 726
659 @Memorize 727 @Memorize
660 def verify_probable_component(self, cid, approved_values): 728 def verify_probable_component(self, cid, approved_values):
661 if '*' in approved_values: 729 if '*' in approved_values:
662 return (True, ['*']) 730 return (True, ['*'])
663 731
664 for value in approved_values: 732 for value in approved_values:
665 present = getattr(self, 'probe_' + cid)(value) 733 present = getattr(self, 'probe_' + cid)(value)
666 if present: 734 if present:
667 return (True, [value]) 735 return (True, [value])
668 return (False, [self._no_match]) 736 return (False, [self._no_match])
669 737
670 def check_probable_component(self, cid, approved_values): 738 def check_probable_component(self, cid, approved_values):
671 (probed, value) = self.verify_probable_component(cid, approved_values) 739 (probed, value) = self.verify_probable_component(cid, approved_values)
672 if probed: 740 if probed:
673 self._system[cid] = value 741 self._system[cid] = value
674 else: 742 else:
675 self._failures[cid] = value 743 self._failures[cid] = value
676 744
677 def pformat(self, obj): 745 def pformat(self, obj):
678 return '\n' + self._pp.pformat(obj) + '\n' 746 return '\n' + self._pp.pformat(obj) + '\n'
679 747
680 def initialize(self, force=False): 748 def initialize(self, force=False, async=False):
681 if self._initialized and not force: 749 if self._initialized and not force:
682 return 750 return
683 # probe current system components 751 # probe current system components
684 DebugMsg('Starting to detect system components...') 752 DebugMsg('Starting to detect system components...')
685 self._enumerable_system = self.get_all_enumerable_components() 753 self._enumerable_system = self.get_all_enumerable_components(async)
686 self._initialized = True 754 self._initialized = True
687 755
688 def match_current_system(self, filename, ignored_cids=[]): 756 def match_current_system(self, filename, ignored_cids=[]):
689 """ Matches a given component list to current system. 757 """ Matches a given component list to current system.
690 Returns: (current, failures) 758 Returns: (current, failures)
691 """ 759 """
692 760
693 # assert self._initialized, 'Not initialized.' 761 # assert self._initialized, 'Not initialized.'
694 self._to_be_tested_cids = (self._enumerable_cids + 762 self._to_be_tested_cids = (self._enumerable_cids +
695 self._probable_cids) 763 self._probable_cids)
(...skipping 18 matching lines...) Expand all
714 else: 782 else:
715 self.check_probable_component(cid, approved[cid]) 783 self.check_probable_component(cid, approved[cid])
716 784
717 return (self._system, self._failures) 785 return (self._system, self._failures)
718 786
719 787
720 ############################################################################# 788 #############################################################################
721 # Console main entry 789 # Console main entry
722 @gft_common.GFTConsole 790 @gft_common.GFTConsole
723 def _main(self_path, args): 791 def _main(self_path, args):
724 if not args: 792 do_async = True
725 print 'Usage: %s components_db_files...\n' % self_path 793
726 sys.exit(1) 794 # preprocess args
795 compdb_list = []
796 for arg in args:
797 if arg == '--sync':
798 do_async = False
799 elif arg == '--async':
800 do_async = True
801 elif not os.path.exists(arg):
802 print 'ERROR: unknown parameter: ' + arg
803 print 'Usage: %s [--sync|--async] [components_db_files...]\n' % self_path
804 sys.exit(1)
805 else:
806 compdb_list.append(arg)
807
727 components = HardwareComponents(verbose=True) 808 components = HardwareComponents(verbose=True)
728 print 'Starting to probe system...' 809 print 'Starting to detect%s...' % (' asynchrounously' if do_async else '')
729 components.initialize() 810 components.initialize(async=do_async)
811
812 if not compdb_list:
813 print 'Enumerable properties:'
814 print components.pformat(components._enumerable_system)
815 sys.exit(0)
816
730 print 'Starting to match system...' 817 print 'Starting to match system...'
731 818 for arg in compdb_list:
732 for arg in args:
733 (matched, failures) = components.match_current_system(arg) 819 (matched, failures) = components.match_current_system(arg)
734 print 'Probed (%s):' % arg 820 print 'Probed (%s):' % arg
735 print components.pformat(matched) 821 print components.pformat(matched)
736 print 'Failures (%s):' % arg 822 print 'Failures (%s):' % arg
737 print components.pformat(failures) 823 print components.pformat(failures)
738 824
739 if __name__ == '__main__': 825 if __name__ == '__main__':
740 _main(sys.argv[0], sys.argv[1:]) 826 _main(sys.argv[0], sys.argv[1:])
OLDNEW
« no previous file with comments | « gft_common.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698