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