Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | |
| 7 | """ |
| 8 | This script provides functions to: |
| 9 | 1. collect: Collect all hosts and their labels to metaDB, can be scheduled |
| 10 | run daily, e.g., |
Dan Shi | 17ecbbf | 2014-10-06 13:56:34 -0700 | [diff] [blame] | 11 | ./site_utils/host_label_utils.py collect |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 12 | 2. query: Query for hosts and their labels information at a given day, e.g., |
Dan Shi | 17ecbbf | 2014-10-06 13:56:34 -0700 | [diff] [blame] | 13 | ./site_utils/host_label_utils.py query -n 172.27.213.193 -l peppy |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 14 | """ |
| 15 | |
| 16 | import argparse |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 17 | import itertools |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 18 | import logging |
| 19 | import pprint |
| 20 | import time |
| 21 | |
| 22 | import common |
| 23 | from autotest_lib.client.common_lib import time_utils |
Gabe Black | b72f4fb | 2015-01-20 16:47:13 -0800 | [diff] [blame] | 24 | from autotest_lib.client.common_lib.cros.graphite import autotest_es |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 25 | from autotest_lib.frontend import setup_django_environment |
| 26 | from autotest_lib.frontend.afe import models |
| 27 | |
| 28 | |
| 29 | # _type used for ES |
| 30 | _HOST_LABEL_TYPE = 'host_labels' |
| 31 | _HOST_LABEL_TIME_INDEX_TYPE = 'host_labels_time_index' |
| 32 | |
David James | 8352bc2 | 2015-05-05 16:37:05 -0700 | [diff] [blame] | 33 | def get_all_boards(labels=None): |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 34 | """Get a list of boards from host labels. |
| 35 | |
| 36 | Scan through all labels of all duts and get all possible boards based on |
| 37 | label of name board:* |
| 38 | |
David James | 8352bc2 | 2015-05-05 16:37:05 -0700 | [diff] [blame] | 39 | @param labels: A list of labels to filter hosts. |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 40 | @return: A list of board names, e.g., ['peppy', 'daisy'] |
| 41 | """ |
David James | 8352bc2 | 2015-05-05 16:37:05 -0700 | [diff] [blame] | 42 | host_labels = get_host_labels(labels=labels) |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 43 | board_labels = [[label[6:] for label in labels |
| 44 | if label.startswith('board:')] |
| 45 | for labels in host_labels.values()] |
| 46 | boards = list(set(itertools.chain.from_iterable(board_labels))) |
| 47 | return boards |
| 48 | |
| 49 | |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 50 | def get_host_labels(days_back=0, hostname=None, labels=None): |
| 51 | """Get the labels for a given host or all hosts. |
| 52 | |
| 53 | @param days_back: Get the label info around that number of days back. The |
| 54 | default is 0, i.e., the latest label information. |
| 55 | @param hostname: Name of the host, if set to None, return labels for all |
| 56 | hosts. Default is None. |
| 57 | @param labels: A list of labels to filter hosts. |
| 58 | @return: A dictionary of host labels, key is the hostname, and value is a |
| 59 | list of labels, e.g., |
| 60 | {'host1': ['board:daisy', 'pool:bvt']} |
| 61 | """ |
| 62 | # Search for the latest logged labels before the given days_back. |
| 63 | # Default is 0, which means the last time host labels were logged. |
| 64 | t_end = time.time() - days_back*24*3600 |
Gabe Black | b72f4fb | 2015-01-20 16:47:13 -0800 | [diff] [blame] | 65 | results = autotest_es.query( |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 66 | fields_returned=['time_index'], |
| 67 | equality_constraints=[('_type', _HOST_LABEL_TIME_INDEX_TYPE),], |
| 68 | range_constraints=[('time_index', None, t_end)], |
| 69 | size=1, |
| 70 | sort_specs=[{'time_index': 'desc'}]) |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 71 | t_end_str = time_utils.epoch_time_to_date_string(t_end) |
Gabe Black | b72f4fb | 2015-01-20 16:47:13 -0800 | [diff] [blame] | 72 | if results.total == 0: |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 73 | logging.error('No label information was logged before %s.', t_end_str) |
| 74 | return |
Dan Shi | f242c20 | 2015-02-20 09:21:49 -0800 | [diff] [blame] | 75 | time_index = results.hits[0]['time_index'] |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 76 | logging.info('Host labels were recorded at %s', |
| 77 | time_utils.epoch_time_to_date_string(time_index)) |
| 78 | |
| 79 | # Search for labels for a given host or all hosts, at time_index. |
| 80 | equality_constraints=[('_type', _HOST_LABEL_TYPE), |
| 81 | ('time_index', time_index),] |
| 82 | if hostname: |
| 83 | equality_constraints.append(('hostname', hostname)) |
| 84 | if labels: |
| 85 | for label in labels: |
| 86 | equality_constraints.append(('labels', label)) |
Gabe Black | b72f4fb | 2015-01-20 16:47:13 -0800 | [diff] [blame] | 87 | results = autotest_es.query( |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 88 | fields_returned=['hostname', 'labels'], |
| 89 | equality_constraints=equality_constraints) |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 90 | |
| 91 | host_labels = {} |
Gabe Black | b72f4fb | 2015-01-20 16:47:13 -0800 | [diff] [blame] | 92 | for hit in results.hits: |
Dan Shi | c6509e6 | 2014-10-20 11:19:51 -0700 | [diff] [blame] | 93 | if 'labels' in hit: |
| 94 | host_labels[hit['hostname']] = hit['labels'] |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 95 | |
| 96 | return host_labels |
| 97 | |
| 98 | |
| 99 | def collect_info(): |
| 100 | """Collect label info and report to metaDB. |
| 101 | """ |
| 102 | # time_index is to index all host labels collected together. It's |
| 103 | # converted to int to make search faster. |
| 104 | time_index = int(time.time()) |
| 105 | hosts = models.Host.objects.filter(invalid=False) |
Dan Shi | 1e290c9 | 2015-05-11 12:54:48 -0700 | [diff] [blame] | 106 | data_list = [] |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 107 | for host in hosts: |
Dan Shi | 1e290c9 | 2015-05-11 12:54:48 -0700 | [diff] [blame] | 108 | info = {'_type': _HOST_LABEL_TYPE, |
| 109 | 'hostname': host.hostname, |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 110 | 'labels': [label.name for label in host.labels.all()], |
| 111 | 'time_index': time_index} |
Dan Shi | 1e290c9 | 2015-05-11 12:54:48 -0700 | [diff] [blame] | 112 | data_list.append(info) |
Dan Shi | f8f45b1 | 2015-09-20 23:37:20 -0700 | [diff] [blame] | 113 | if not autotest_es.bulk_post(data_list, log_time_recorded=False): |
| 114 | raise Exception('Failed to upload host label info.') |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 115 | |
| 116 | # After all host label information is logged, save the time stamp. |
Dan Shi | 1d59a67 | 2015-03-31 14:09:03 -0700 | [diff] [blame] | 117 | autotest_es.post(use_http=True, type_str=_HOST_LABEL_TIME_INDEX_TYPE, |
| 118 | metadata={'time_index': time_index}, |
| 119 | log_time_recorded=False) |
Dan Shi | 54682e7 | 2014-10-08 19:10:56 +0000 | [diff] [blame] | 120 | logging.info('Finished collecting host labels for %d hosts.', len(hosts)) |
| 121 | |
| 122 | |
| 123 | def main(): |
| 124 | """Main script. |
| 125 | """ |
| 126 | parser = argparse.ArgumentParser() |
| 127 | parser.add_argument('action', |
| 128 | help=('collect or query. Action collect will collect ' |
| 129 | 'all hosts and their labels to metaDB. Action ' |
| 130 | 'query will query for hosts and their labels ' |
| 131 | 'information at a given day')) |
| 132 | parser.add_argument('-d', '--days_back', type=int, dest='days_back', |
| 133 | help=('Number of days before current time. Query will ' |
| 134 | 'get host label information collected before that' |
| 135 | ' time. The option is applicable to query only. ' |
| 136 | 'Default to 0, i.e., get the latest label info.'), |
| 137 | default=0) |
| 138 | parser.add_argument('-n', '--hostname', type=str, dest='hostname', |
| 139 | help=('Name of the host to query label information for.' |
| 140 | 'The option is applicable to query only. ' |
| 141 | 'Default to None, i.e., return label info for all' |
| 142 | ' hosts.'), |
| 143 | default=None) |
| 144 | parser.add_argument('-l', '--labels', nargs='+', dest='labels', |
| 145 | help=('A list of labels to filter hosts. The option is ' |
| 146 | 'applicable to query only. Default to None.'), |
| 147 | default=None) |
| 148 | parser.add_argument('-v', '--verbose', action="store_true", dest='verbose', |
| 149 | help='Allow more detail information to be shown.') |
| 150 | options = parser.parse_args() |
| 151 | |
| 152 | logging.getLogger().setLevel(logging.INFO if options.verbose |
| 153 | else logging.WARN) |
| 154 | if options.action == 'collect': |
| 155 | collect_info() |
| 156 | elif options.action == 'query': |
| 157 | host_labels = get_host_labels(options.days_back, options.hostname, |
| 158 | options.labels) |
| 159 | pprint.pprint(host_labels) |
| 160 | else: |
| 161 | logging.error('action %s is not supported, can only be collect or ' |
| 162 | 'query!', options.action) |
| 163 | |
| 164 | |
| 165 | if __name__ == '__main__': |
| 166 | main() |