1# Lint as: python2, python3 2# Copyright 2014 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import gzip, logging, os, re 7from autotest_lib.client.bin import utils 8from autotest_lib.client.common_lib import error 9 10class KernelConfig(): 11 """ 12 Parse the kernel config and enable us to query it. 13 Used to verify the kernel config (see kernel_ConfigVerify). 14 """ 15 16 def _passed(self, msg): 17 logging.info('ok: %s', msg) 18 19 def _failed(self, msg): 20 logging.error('FAIL: %s', msg) 21 self._failures.append(msg) 22 23 def failures(self): 24 """Return the list of failures that occured during the test. 25 26 @return a list of string describing errors that occured since 27 initialization. 28 """ 29 return self._failures 30 31 def _fatal(self, msg): 32 logging.error('FATAL: %s', msg) 33 raise error.TestError(msg) 34 35 def get(self, key, default): 36 """Get the value associated to key or default if it does not exist 37 38 @param key: key to look for. 39 @param default: value returned if key is not set in self._config 40 """ 41 return self._config.get(key, default) 42 43 def _config_required(self, name, wanted): 44 value = self._config.get(name, None) 45 if value in wanted: 46 self._passed('"%s" was "%s" in kernel config' % (name, value)) 47 else: 48 states = [] 49 for state in wanted: 50 if state == None: 51 states.append("unset") 52 else: 53 states.append(state) 54 self._failed('"%s" was "%s" (wanted one of "%s") in kernel config' % 55 (name, value, '|'.join(states))) 56 57 def has_value(self, name, value): 58 """Determine if the name config item has a specific value. 59 60 @param name: name of config item to test 61 @param value: value expected for the given config name 62 """ 63 self._config_required('CONFIG_%s' % (name), value) 64 65 def has_builtin(self, name): 66 """Check if the specific config item is built-in (present but not 67 built as a module). 68 69 @param name: name of config item to test 70 """ 71 wanted = ['y'] 72 if name in self._missing_ok: 73 wanted.append(None) 74 self.has_value(name, wanted) 75 76 def has_module(self, name): 77 """Check if the specific config item is a module (present but not 78 built-in). 79 80 @param name: name of config item to test 81 """ 82 wanted = ['m'] 83 if name in self._missing_ok: 84 wanted.append(None) 85 self.has_value(name, wanted) 86 87 def is_enabled(self, name): 88 """Check if the specific config item is present (either built-in or 89 a module). 90 91 @param name: name of config item to test 92 """ 93 wanted = ['y', 'm'] 94 if name in self._missing_ok: 95 wanted.append(None) 96 self.has_value(name, wanted) 97 98 def is_missing(self, name): 99 """Check if the specific config item is not present (neither built-in 100 nor a module). 101 102 @param name: name of config item to test 103 """ 104 self.has_value(name, [None]) 105 106 def is_exclusive(self, exclusive): 107 """Given a config item regex, make sure only the expected items 108 are present in the kernel configs. 109 110 @param exclusive: hash containing "missing", "builtin", "module", 111 "enabled" each to be checked with the corresponding 112 has_* function based on config items matching the 113 "regex" value. 114 """ 115 expected = set() 116 for name in exclusive['missing']: 117 self.is_missing(name) 118 for name in exclusive['builtin']: 119 self.has_builtin(name) 120 expected.add('CONFIG_%s' % (name)) 121 for name in exclusive['module']: 122 self.has_module(name) 123 expected.add('CONFIG_%s' % (name)) 124 for name in exclusive['enabled']: 125 self.is_enabled(name) 126 expected.add('CONFIG_%s' % (name)) 127 128 # Now make sure nothing else with the specified regex exists. 129 regex = r'CONFIG_%s' % (exclusive['regex']) 130 for name in self._config: 131 if not re.match(regex, name): 132 continue 133 if not name in expected: 134 self._failed('"%s" found for "%s" when only "%s" allowed' % 135 (name, regex, "|".join(expected))) 136 137 def _read_config(self): 138 """Open the kernel's build config file. Attempt to use the built-in 139 symbols from /proc first, then fall back to looking for a text file 140 in /boot. 141 142 @return readlines for fileobj 143 """ 144 filename = '/proc/config.gz' 145 if not os.path.exists(filename): 146 utils.system("modprobe configs", ignore_status=True) 147 if os.path.exists(filename): 148 with gzip.open(filename, "r") as rf: 149 return rf.readlines() 150 151 filename = '/boot/config-%s' % utils.system_output('uname -r') 152 if os.path.exists(filename): 153 logging.info('Falling back to reading %s', filename) 154 with open(filename, "r") as rf: 155 return rf.readlines() 156 157 self._fatal("Cannot locate suitable kernel config file") 158 159 def initialize(self, missing_ok=None): 160 """Load the kernel configuration and parse it. 161 """ 162 file_lines = self._read_config() 163 # Import kernel config variables into a dictionary for each searching. 164 config = dict() 165 for item in file_lines: 166 item = item.strip() 167 if not '=' in item: 168 continue 169 key, value = item.split('=', 1) 170 config[key] = value 171 172 # Make sure we actually loaded something sensible. 173 if len(config) == 0: 174 self._fatal('No CONFIG variables found!') 175 176 self._config = config 177 self._failures = [] 178 self._missing_ok = set() 179 if missing_ok: 180 self._missing_ok |= set(missing_ok) 181