Home | History | Annotate | Download | only in server
      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