| # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Host attributes define properties on individual hosts. |
| |
| Host attributes are specified a strings with the format: |
| <key>{,<value>}? |
| |
| A machine may have a list of strings for attributes like: |
| |
| ['has_80211n,True', |
| 'has_ssd,False', |
| 'drive_kind,string,ssd,1'] |
| |
| A legal attribute has the pattern: |
| <name>,<kind>(,<extra>)? |
| |
| Name can be any legal python identifier. Kind may be any of 'string', 'True', |
| or 'False'. Only if kind is string can there be extra data. |
| |
| Strings which are not legal attributes are ignored. |
| |
| Given the above list of attributes, you can use the syntax: |
| host_attributes.drive_kind => 'ssd,1' |
| host_attributes.has_80211n => True |
| host_attributes.has_ssd => False |
| host_attributes.unknown_attribute => raise KeyError |
| |
| Machine attributes can be specified in two ways. |
| |
| If you create private_host_attributes_config.py and private_host_attributes |
| there, we will use it when possible instead of using the server front-end. |
| |
| Example configuration: |
| private_host_attributes = { |
| "mydevice": ["has_80211n,True", |
| "has_resume_bug,False"] |
| } |
| |
| We also consult the AFE database for its labels which are all treated as host |
| attribute strings defined above. Illegal strings are ignored. |
| """ |
| |
| |
| import hashlib, logging, os, utils |
| |
| |
| private_host_attributes = utils.import_site_symbol( |
| __file__, |
| 'autotest_lib.server.private_host_attributes_config', |
| 'private_host_attributes', dummy={}) |
| |
| try: |
| settings = 'autotest_lib.frontend.settings' |
| os.environ['DJANGO_SETTINGS_MODULE'] = settings |
| from autotest_lib.frontend.afe import models |
| has_models = True |
| except Exception: |
| has_models = False |
| |
| |
| _DEFAULT_ATTRIBUTES = [ |
| 'has_80211n,True', |
| 'has_bluetooth,False', |
| 'has_chromeos_firmware,True', |
| 'has_resume_bug,False', |
| 'has_ssd,True' |
| ] |
| |
| |
| class HostAttributes(object): |
| """Host attribute class for site specific attributes.""" |
| |
| def __init__(self, host): |
| """Create an instance of HostAttribute for the given hostname. |
| |
| We look up the host in both the hardcoded configuration and the AFE |
| models if they can be found. |
| |
| Args: |
| host: Host name to find attributes for. |
| """ |
| self._add_attributes(_DEFAULT_ATTRIBUTES) |
| if host in private_host_attributes: |
| logging.info('Including private_host_attributes file for %s', host) |
| self._add_attributes(private_host_attributes[host]) |
| if has_models: |
| logging.info("Including labels for %s from database", host) |
| host_obj = models.Host.valid_objects.get(hostname=host) |
| self._add_attributes([label.name for label in |
| host_obj.labels.all()]) |
| for key, value in self.__dict__.items(): |
| logging.info('Host attribute: %s => %s', key, value) |
| |
| def _add_attributes(self, attributes): |
| for attribute in attributes: |
| splitnames = attribute.split(',') |
| if len(splitnames) == 1: |
| if 'netbook_' in attribute: |
| # Hash board names to prevent any accidental leaks. |
| splitnames = ['netbook_' + hashlib.sha256( |
| attribute.split('netbook_')[1]).hexdigest()[:8], 'True'] |
| else: |
| splitnames = attribute.split(':') |
| if len(splitnames) == 2: |
| setattr(self, splitnames[0], splitnames[1]) |
| continue |
| value = ','.join(splitnames[1:]) |
| if value == 'True': |
| value = True |
| elif value == 'False': |
| value = False |
| elif splitnames[1] == 'string' and len(splitnames) > 2: |
| value = ','.join(splitnames[2:]) |
| else: |
| logging.info('Non-attribute string "%s" is ignored', attribute) |
| continue |
| setattr(self, splitnames[0], value) |
| |
| def get_attributes(self): |
| """Return a list of non-False attributes for this host.""" |
| return [key for key, value in self.__dict__.items() if value] |