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# This file defines script for getting host_history for DUTs in Autotest.
8
9"""Script for checking host history for a selected group of hosts.
10
11Currently only supports aggregating stats for each host.
12
13Example usage:
14    python host_history.py -n 10000 -l 24 --board=daisy
15
16Output:
17
18    trying to get all duts...
19    making the query...
20    found all duts. Time to get host_history.
21    usage stats for host: chromeos2-row5-rack1-host6
22        2014-07-24 10:24:07 - 2014-07-25 10:24:07
23            Verifying:        0.00 %
24            Running:          0.00 %
25            Ready:            100.00 %
26            Repairing:        0.00 %
27            Repair Failed:    0.00 %
28            Cleaning:         0.00 %
29            Pending:          0.00 %
30            Resetting:        0.00 %
31            Provisioning:     0.00 %
32            Locked:           0.00 %
33    - -- --- ---- ----- ---- --- -- -
34
35Example usage2: more than one host:
36    python host_history.py -n 1000 -l 2 \
37    --hosts chromeos2-row5-rack4-host6 chromeos4-row12-rack11-host2
38
39    ['chromeos2-row5-rack4-host6', 'chromeos4-row12-rack11-host2']
40    found all duts. Time to get host_history.
41    usage stats for host: chromeos2-row5-rack4-host6
42     2014-07-25 13:02:22 - 2014-07-25 15:02:22
43     Num entries found in this interval: 0
44            Verifying:        0.00 %
45            Running:          0.00 %
46            Ready:            100.00 %
47            Repairing:        0.00 %
48            Repair Failed:    0.00 %
49            Cleaning:         0.00 %
50            Pending:          0.00 %
51            Resetting:        0.00 %
52            Provisioning:     0.00 %
53            Locked:           0.00 %
54    - -- --- ---- ----- ---- --- -- -
55
56    usage stats for host: chromeos4-row12-rack11-host2
57     2014-07-25 13:02:22 - 2014-07-25 15:02:22
58     Num entries found in this interval: 138
59            Verifying:        0.00 %
60            Running:          70.45 %
61            Ready:            17.79 %
62            Repairing:        0.00 %
63            Repair Failed:    0.00 %
64            Cleaning:         0.00 %
65            Pending:          1.24 %
66            Resetting:        10.78 %
67            Provisioning:     0.00 %
68            Locked:           0.00 %
69    - -- --- ---- ----- ---- --- -- -
70"""
71
72import argparse
73import time
74import traceback
75
76import common
77from autotest_lib.client.common_lib import time_utils
78from autotest_lib.site_utils import host_history_utils
79
80
81def print_all_stats(results, labels, t_start, t_end):
82    """Prints overall stats followed by stats for each host.
83
84    @param results: A list of tuples of three elements.
85            1st element: String representing report for individual host.
86            2nd element: An ordered dictionary with
87                    key as (t_start, t_end) and value as (status, metadata)
88                    status = status of the host. e.g. 'Repair Failed'
89                    t_start is the beginning of the interval where the DUT's has
90                            that status
91                    t_end is the end of the interval where the DUT has that
92                            status
93                    metadata: A dictionary of other metadata, e.g.,
94                              {'task_id':123, 'task_name':'Reset'}
95            3rd element: hostname of the dut.
96    @param labels: A list of labels useful for describing the group
97                   of hosts these overall stats represent.
98    @param t_start: beginning of time period we are interested in.
99    @param t_end: end of time period we are interested in.
100    """
101    result_strs, stat_intervals_lst, hostname = zip(*results)
102    overall_report_str = host_history_utils.get_overall_report(
103            labels, t_start, t_end, stat_intervals_lst)
104    # Print the overall stats
105    print overall_report_str
106    # Print the stats for each individual host.
107    for result_str in result_strs:
108        print result_str
109
110
111def get_host_history(input):
112    """Gets the host history.
113
114    @param input: A dictionary of input arguments to
115                  host_history_utils.host_history_stats.
116                  Must contain these keys:
117                      't_start',
118                      't_end',
119                      'hostname',
120                      'size,'
121                      'print_each_interval'
122    @returns:
123            result_str: String reporting history for specific host.
124            stat_intervals: A ordered dictionary with
125                    key as (t_start, t_end) and value as (status, metadata)
126                    status = status of the host. e.g. 'Repair Failed'
127                    t_start is the beginning of the interval where the DUT's has
128                            that status
129                    t_end is the end of the interval where the DUT has that
130                            status
131                    metadata: A dictionary of other metadata, e.g.,
132                              {'task_id':123, 'task_name':'Reset'}
133    """
134    try:
135        result_str, stat_intervals = host_history_utils.get_report_for_host(
136                **input)
137        return result_str, stat_intervals, input['hostname']
138    except Exception as e:
139        # In case any process throws an Exception, we want to see it.
140        print traceback.print_exc()
141        return None, None, None
142
143
144def get_results(start_time, end_time, hosts=None, board=None, pool=None,
145                verbose=False):
146    """Get history results of specified hosts or board/pool.
147
148    If hosts is set to None, all hosts are used, filtered by the board and pool
149    constraints. If board is not provided, all boards are included. If pool is
150    not provided, all pools are included.
151    If a list of hosts is provided, the board and pool constraints are ignored.
152
153    @param hosts: A list of hosts to search for history. Default is None.
154    @param board: board type of hosts. Default is None.
155    @param pool: pool type of hosts. Default is None.
156    @param start_time: start time to search for history, can be string value or
157                       epoch time.
158    @param end_time: end time to search for history, can be string value or
159                     epoch time.
160    @param verbose: True to print out detail intervals of host history.
161
162    @returns: A dictionary of host history.
163    """
164    assert start_time and end_time
165    start_time = time_utils.to_epoch_time(start_time)
166    end_time = time_utils.to_epoch_time(end_time)
167    assert start_time < end_time
168
169    return host_history_utils.get_report(t_start=start_time, t_end=end_time,
170                                          hosts=hosts, board=board, pool=pool,
171                                          print_each_interval=verbose)
172
173
174def get_history_details(start_time, end_time, hosts=None, board=None,
175                        pool=None):
176    """Get the details of host history.
177
178    The return is a dictionary of host history for each host, for example,
179    {'172.22.33.51': [{'status': 'Resetting'
180                       'start_time': '2014-08-07 10:02:16',
181                       'end_time': '2014-08-07 10:03:16',
182                       'log_url': 'http://autotest/reset-546546/debug',
183                       'task_id': 546546},
184                      {'status': 'Running'
185                       'start_time': '2014-08-07 10:03:18',
186                       'end_time': '2014-08-07 10:13:00',
187                       'log_url': ('http://%s/tko/retrieve_logs.cgi?job=/'
188                                   'results/16853-debug/172.22.33.51'),
189                       'job_id': 16853}
190                     ]
191    }
192    @param start_time: start time to search for history, can be string value or
193                       epoch time.
194    @param end_time: end time to search for history, can be string value or
195                     epoch time.
196    @param hosts: A list of hosts to search for history. Default is None.
197    @param board: board type of hosts. Default is None.
198    @param pool: pool type of hosts. Default is None.
199    @returns: A dictionary of the host history details.
200    """
201    results = get_results(start_time=start_time, end_time=end_time, hosts=hosts,
202                          board=board, pool=pool)
203    if not results:
204        # No host found.
205        return None
206    all_history = {}
207    for result_str, status_intervals, hostname in results:
208        if hostname:
209            all_history[hostname] = host_history_utils.build_history(
210                    hostname, status_intervals)
211    return all_history
212
213
214def main():
215    """main script. """
216    t_now = time.time()
217    t_now_minus_one_day = t_now - 3600 * 24
218    parser = argparse.ArgumentParser()
219    parser.add_argument('-v', action='store_true', dest='verbose',
220                        default=False,
221                        help='-v to print out ALL entries.')
222    parser.add_argument('-l', type=float, dest='last',
223                        help='last hours to search results across',
224                        default=None)
225    parser.add_argument('--board', type=str, dest='board',
226                        help='restrict query by board, not implemented yet',
227                        default=None)
228    parser.add_argument('--pool', type=str, dest='pool',
229                        help='restrict query by pool, not implemented yet',
230                        default=None)
231    parser.add_argument('--hosts', nargs='+', dest='hosts',
232                        help='Enter space deliminated hostnames',
233                        default=[])
234    parser.add_argument('--start', type=str, dest='start',
235                        help=('Enter start time as: yyyy-mm-dd hh:mm:ss,'
236                              'defualts to 24h ago.'),
237                        default=time_utils.epoch_time_to_date_string(
238                                t_now_minus_one_day))
239    parser.add_argument('--end', type=str, dest='end',
240                        help=('Enter end time in as: yyyy-mm-dd hh:mm:ss,'
241                              'defualts to current time.'),
242                        default=time_utils.epoch_time_to_date_string(t_now))
243    options = parser.parse_args()
244
245    if options.last:
246        start_time = t_now - 3600 * options.last
247        end_time = t_now
248    else:
249        start_time = time_utils.to_epoch_time(options.start)
250        end_time = time_utils.to_epoch_time(options.end)
251
252    results = get_results(hosts=options.hosts,
253                          board=options.board,
254                          pool=options.pool,
255                          start_time=start_time,
256                          end_time=end_time,
257                          verbose=options.verbose)
258    labels = []
259    if options.board:
260        labels.append('board:%s' % (options.board))
261    if options.pool:
262        labels.append('pool:%s' % (options.pool))
263    print_all_stats(results, labels, start_time, end_time)
264
265
266if __name__ == '__main__':
267    main()
268