1# Lint as: python2, python3 2# Copyright (c) 2011 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 6"""Host attributes define properties on individual hosts. 7 8Host attributes are specified a strings with the format: 9 <key>{,<value>}? 10 11A machine may have a list of strings for attributes like: 12 13 ['has_80211n,True', 14 'has_ssd,False', 15 'drive_kind,string,ssd,1'] 16 17A legal attribute has the pattern: 18 <name>,<kind>(,<extra>)? 19 20Name can be any legal python identifier. Kind may be any of 'string', 'True', 21or 'False'. Only if kind is string can there be extra data. 22 23Strings which are not legal attributes are ignored. 24 25Given the above list of attributes, you can use the syntax: 26 host_attributes.drive_kind => 'ssd,1' 27 host_attributes.has_80211n => True 28 host_attributes.has_ssd => False 29 host_attributes.unknown_attribute => raise KeyError 30 31Machine attributes can be specified in two ways. 32 33If you create private_host_attributes_config.py and private_host_attributes 34there, we will use it when possible instead of using the server front-end. 35 36Example configuration: 37 private_host_attributes = { 38 "mydevice": ["has_80211n,True", 39 "has_resume_bug,False"] 40 } 41 42We also consult the AFE database for its labels which are all treated as host 43attribute strings defined above. Illegal strings are ignored. 44""" 45 46 47import hashlib, logging, os 48 49from autotest_lib.client.common_lib import utils 50 51private_host_attributes = utils.import_site_symbol( 52 __file__, 53 'autotest_lib.server.private_host_attributes_config', 54 'private_host_attributes', 55 placeholder={}) 56 57try: 58 settings = 'autotest_lib.frontend.settings' 59 os.environ['DJANGO_SETTINGS_MODULE'] = settings 60 has_models = True 61except Exception: 62 has_models = False 63 64 65_DEFAULT_ATTRIBUTES = [ 66 'has_80211n,True', 67 'has_bluetooth,False', 68 'has_chromeos_firmware,True', 69 'has_resume_bug,False', 70 'has_ssd,True' 71 ] 72 73 74class HostAttributes(object): 75 """Host attribute class for site specific attributes.""" 76 77 def __init__(self, host): 78 """Create an instance of HostAttribute for the given hostname. 79 80 We look up the host in both the hardcoded configuration and the AFE 81 models if they can be found. 82 83 Args: 84 host: Host name to find attributes for. 85 """ 86 self._add_attributes(_DEFAULT_ATTRIBUTES) 87 if host in private_host_attributes: 88 logging.info('Including private_host_attributes file for %s', host) 89 self._add_attributes(private_host_attributes[host]) 90 if has_models: 91 logging.info("Including labels for %s from database", host) 92 host_obj = models.Host.valid_objects.get(hostname=host) 93 self._add_attributes([label.name for label in 94 host_obj.labels.all()]) 95 for key, value in self.__dict__.items(): 96 logging.info('Host attribute: %s => %s', key, value) 97 98 def _add_attributes(self, attributes): 99 for attribute in attributes: 100 splitnames = attribute.split(',') 101 if len(splitnames) == 1: 102 if 'netbook_' in attribute: 103 # Hash board names to prevent any accidental leaks. 104 splitnames = ['netbook_' + hashlib.sha256( 105 attribute.split('netbook_')[1] 106 .encode('utf-8')).hexdigest()[:8], 'True'] 107 else: 108 splitnames = attribute.split(':') 109 if len(splitnames) == 2: 110 setattr(self, splitnames[0], splitnames[1]) 111 continue 112 value = ','.join(splitnames[1:]) 113 if value == 'True': 114 value = True 115 elif value == 'False': 116 value = False 117 elif splitnames[1] == 'string' and len(splitnames) > 2: 118 value = ','.join(splitnames[2:]) 119 else: 120 logging.info('Non-attribute string "%s" is ignored', attribute) 121 continue 122 setattr(self, splitnames[0], value) 123 124 def get_attributes(self): 125 """Return a list of non-False attributes for this host.""" 126 return [key for key, value in self.__dict__.items() if value] 127