Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 1 | # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 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 | |
Dale Curtis | 7b2bd4d | 2011-05-02 12:39:02 -0700 | [diff] [blame] | 46 | import hashlib, logging, os, utils |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 47 | |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 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 |
Chris Sosa | f4fa49b | 2013-01-11 17:41:57 -0800 | [diff] [blame] | 59 | except Exception: |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 60 | has_models = False |
| 61 | |
| 62 | |
| 63 | _DEFAULT_ATTRIBUTES = [ |
| 64 | 'has_80211n,True', |
Dale Curtis | 3c0b6fd | 2011-06-13 18:38:44 -0700 | [diff] [blame] | 65 | 'has_bluetooth,False', |
Simon Glass | 1232f28 | 2011-02-28 09:16:28 -0800 | [diff] [blame] | 66 | 'has_chromeos_firmware,True', |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 67 | 'has_resume_bug,False', |
Dale Curtis | 3c0b6fd | 2011-06-13 18:38:44 -0700 | [diff] [blame] | 68 | 'has_ssd,True' |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 69 | ] |
| 70 | |
| 71 | |
| 72 | class HostAttributes(object): |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 73 | """Host attribute class for site specific attributes.""" |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 74 | |
| 75 | def __init__(self, host): |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 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. |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 83 | """ |
| 84 | self._add_attributes(_DEFAULT_ATTRIBUTES) |
| 85 | if host in private_host_attributes: |
J. Richard Barnette | ed46058 | 2012-08-01 16:36:40 -0700 | [diff] [blame] | 86 | logging.info('Including private_host_attributes file for %s', host) |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 87 | self._add_attributes(private_host_attributes[host]) |
| 88 | if has_models: |
J. Richard Barnette | ed46058 | 2012-08-01 16:36:40 -0700 | [diff] [blame] | 89 | logging.info("Including labels for %s from database", host) |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 90 | host_obj = models.Host.valid_objects.get(hostname=host) |
Eric Li | f87edc8 | 2010-05-03 10:59:21 -0700 | [diff] [blame] | 91 | self._add_attributes([label.name for label in |
| 92 | host_obj.labels.all()]) |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 93 | for key, value in self.__dict__.items(): |
| 94 | logging.info('Host attribute: %s => %s', key, value) |
| 95 | |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 96 | def _add_attributes(self, attributes): |
| 97 | for attribute in attributes: |
| 98 | splitnames = attribute.split(',') |
Eric Li | f87edc8 | 2010-05-03 10:59:21 -0700 | [diff] [blame] | 99 | if len(splitnames) == 1: |
Dale Curtis | 7b2bd4d | 2011-05-02 12:39:02 -0700 | [diff] [blame] | 100 | if 'netbook_' in attribute: |
| 101 | # Hash board names to prevent any accidental leaks. |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 102 | splitnames = ['netbook_' + hashlib.sha256( |
Dale Curtis | 7b2bd4d | 2011-05-02 12:39:02 -0700 | [diff] [blame] | 103 | attribute.split('netbook_')[1]).hexdigest()[:8], 'True'] |
| 104 | else: |
J. Richard Barnette | ed46058 | 2012-08-01 16:36:40 -0700 | [diff] [blame] | 105 | splitnames = attribute.split(':') |
| 106 | if len(splitnames) == 2: |
| 107 | setattr(self, splitnames[0], splitnames[1]) |
Dale Curtis | 7b2bd4d | 2011-05-02 12:39:02 -0700 | [diff] [blame] | 108 | continue |
| 109 | value = ','.join(splitnames[1:]) |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 110 | if value == 'True': |
| 111 | value = True |
| 112 | elif value == 'False': |
| 113 | value = False |
Paul Stewart | cd61a4f | 2010-07-19 10:49:35 -0700 | [diff] [blame] | 114 | elif splitnames[1] == 'string' and len(splitnames) > 2: |
| 115 | value = ','.join(splitnames[2:]) |
| 116 | else: |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 117 | logging.info('Non-attribute string "%s" is ignored', attribute) |
Ken Mixter | 768fe64 | 2010-04-30 17:12:04 -0700 | [diff] [blame] | 118 | continue |
| 119 | setattr(self, splitnames[0], value) |
Paul Pendlebury | 7c1fdcf | 2011-05-04 12:39:15 -0700 | [diff] [blame] | 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] |