| Hirokazu Honda | 0591a9f | 2017-12-01 17:37:10 +0900 | [diff] [blame] | 1 | # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | import importlib |
| 6 | import logging |
| 7 | import os |
| 8 | import re |
| 9 | |
| 10 | import yaml |
| 11 | |
| 12 | from autotest_lib.client.common_lib import error |
| 13 | |
| 14 | class DeviceCapability(object): |
| 15 | """ |
| 16 | Generate capabilities status on DUT from yaml files in a given path. |
| 17 | Answer from the capabilities whether some capability is satisfied on DUT. |
| 18 | """ |
| 19 | |
| Hirokazu Honda | 24c394c | 2018-02-16 13:13:02 +0900 | [diff] [blame] | 20 | def __init__(self, settings_path='/usr/local/etc/autotest-capability'): |
| Hirokazu Honda | 0591a9f | 2017-12-01 17:37:10 +0900 | [diff] [blame] | 21 | """ |
| 22 | @param settings_path: string, the base directory for autotest |
| 23 | capability. There should be yaml files. |
| 24 | """ |
| 25 | self.capabilities = self.__get_autotest_capability(settings_path) |
| 26 | logging.info("Capabilities:\n%r", self.capabilities) |
| 27 | |
| 28 | |
| 29 | def __get_autotest_capability(self, settings_path): |
| 30 | """ |
| 31 | Generate and summarize capabilities from yaml files in |
| 32 | settings_path with detectors. |
| 33 | |
| 34 | @param settings_path: string, the base directory for autotest |
| 35 | capability. There should be yaml files. |
| 36 | @returns dict: |
| 37 | The capabilities on DUT. |
| 38 | Its key is string denoting a capability. Its value is 'yes', 'no' or |
| 39 | 'disable.' |
| 40 | """ |
| 41 | |
| 42 | def run_detector(name): |
| 43 | """ |
| 44 | Run a detector in the detector directory. (i.e. |
| 45 | autotest/files/client/cros/video/detectors) |
| 46 | Return the result of the detector. |
| 47 | |
| 48 | @param name: string, the name of running detector. |
| 49 | @returns string, a result of detect() in the detector script. |
| 50 | """ |
| 51 | if name not in detect_results: |
| 52 | detector = importlib.import_module( |
| 53 | "autotest_lib.client.cros.video.detectors.%s" |
| 54 | % name) |
| 55 | detect_results[name] = detector.detect() |
| 56 | logging.info("Detector result (%s): %s", |
| 57 | name, detect_results[name]) |
| 58 | return detect_results[name] |
| 59 | |
| 60 | managed_cap_fpath = os.path.join(settings_path, |
| 61 | 'managed-capabilities.yaml') |
| 62 | if not os.path.exists(managed_cap_fpath): |
| 63 | raise error.TestFail("%s is not installed" % managed_cap_fpath) |
| 64 | managed_caps = yaml.load(file(managed_cap_fpath)) |
| 65 | |
| 66 | cap_files = [f for f in os.listdir(settings_path) |
| 67 | if re.match(r'^[0-9]+-.*\.yaml$', f)] |
| 68 | cap_files.sort(key=lambda f: int(f.split('-', 1)[0])) |
| 69 | |
| 70 | detect_results = {} |
| 71 | autotest_caps = dict.fromkeys(managed_caps, 'no') |
| 72 | for fname in cap_files: |
| 73 | logging.debug('Processing caps: %s', fname) |
| 74 | fname = os.path.join(settings_path, fname) |
| 75 | for rule in yaml.load(file(fname)): |
| 76 | # The type of rule is string or dict |
| 77 | # If the type is a string, it is a capability (e.g. webcam). |
| 78 | # If a specific condition (e.g. kepler, cpu type) is required, |
| 79 | # rule would be dict, for example, |
| 80 | # {'detector': 'intel_cpu', |
| 81 | # 'match': ['intel_celeron_1007U'], |
| 82 | # 'capabilities': ['no hw_h264_enc_1080_30'] }. |
| 83 | logging.debug("%r", rule) |
| 84 | caps = [] |
| 85 | if isinstance(rule, dict): |
| 86 | if run_detector(rule['detector']) in rule['match']: |
| 87 | caps = rule['capabilities'] |
| 88 | else: |
| 89 | caps = [rule] |
| 90 | |
| 91 | for capability in caps: |
| Hirokazu Honda | cf8789d | 2018-02-21 18:25:50 +0900 | [diff] [blame] | 92 | m = re.match(r'(?:(disable|no)\s+)?([\w\-]+)$', capability) |
| Hirokazu Honda | 0591a9f | 2017-12-01 17:37:10 +0900 | [diff] [blame] | 93 | prefix, capability = m.groups() |
| 94 | if capability in managed_caps: |
| 95 | autotest_caps[capability] = prefix or 'yes' |
| 96 | else: |
| 97 | raise error.TestFail( |
| 98 | "Unexpected capability: %s" % capability) |
| 99 | |
| 100 | return autotest_caps |
| 101 | |
| 102 | |
| 103 | def get_managed_caps(self): |
| 104 | return self.capabilities.keys() |
| 105 | |
| 106 | |
| 107 | def get_capability_results(self): |
| 108 | return self.capabilities |
| 109 | |
| 110 | |
| 111 | def get_capability(self, cap): |
| 112 | """ |
| 113 | Decide if a device satisfies a required capability for an autotest. |
| 114 | |
| 115 | @param cap: string, denoting one capability. It must be one in |
| 116 | settings_path + 'managed-capabilities.yaml.' |
| 117 | @returns 'yes', 'no', or 'disable.' |
| 118 | """ |
| 119 | try: |
| 120 | return self.capabilities[cap] |
| 121 | except KeyError: |
| 122 | raise error.TestFail("Unexpected capability: %s" % capability) |
| 123 | |
| 124 | |
| 125 | def ensure_capability(self, cap): |
| 126 | """ |
| 127 | Raise TestNAError if a device doesn't satisfy cap. |
| 128 | """ |
| 129 | if self.get_capability(cap) != 'yes': |
| 130 | raise error.TestNAError("Missing Capability: %s" % cap) |