Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [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 | |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 7 | """This script is to be run daily to report machine utilization stats across |
| 8 | each board and pool. |
| 9 | """ |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 10 | |
| 11 | |
| 12 | import argparse |
| 13 | from datetime import date |
| 14 | from datetime import datetime |
| 15 | from datetime import timedelta |
| 16 | |
| 17 | import common |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 18 | from autotest_lib.client.common_lib import time_utils |
Dan Shi | 5e2efb7 | 2017-02-07 11:40:23 -0800 | [diff] [blame] | 19 | from autotest_lib.client.common_lib import utils |
Dan Shi | 1d59a67 | 2015-03-31 14:09:03 -0700 | [diff] [blame] | 20 | from autotest_lib.site_utils import gmail_lib |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 21 | from autotest_lib.site_utils import host_history |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 22 | from autotest_lib.site_utils import host_history_utils |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 23 | from autotest_lib.site_utils import host_label_utils |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 24 | |
Dan Shi | 5e2efb7 | 2017-02-07 11:40:23 -0800 | [diff] [blame] | 25 | try: |
| 26 | from chromite.lib import metrics |
| 27 | from chromite.lib import ts_mon_config |
| 28 | except ImportError: |
| 29 | metrics = utils.metrics_mock |
| 30 | ts_mon_config = utils.metrics_mock |
| 31 | |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 32 | |
Dan Shi | 4a7c542 | 2016-08-26 11:42:10 -0700 | [diff] [blame] | 33 | _MACHINE_UTILIZATION_RATE_HOURLY = metrics.Float( |
| 34 | 'chromeos/autotest/host/machine_utilization_rate/hourly') |
| 35 | _MACHINE_AVAILABILITY_RATE_HOURLY = metrics.Float( |
| 36 | 'chromeos/autotest/host/machine_availability_rate/hourly') |
| 37 | _MACHINE_IDLE_RATE_HOURLY = metrics.Float( |
| 38 | 'chromeos/autotest/host/machine_idle_rate/hourly') |
| 39 | _MACHINE_UTILIZATION_RATE_DAILY = metrics.Float( |
| 40 | 'chromeos/autotest/host/machine_utilization_rate/daily') |
| 41 | _MACHINE_AVAILABILITY_RATE_DAILY = metrics.Float( |
| 42 | 'chromeos/autotest/host/machine_availability_rate/daily') |
| 43 | _MACHINE_IDLE_RATE_DAILY = metrics.Float( |
| 44 | 'chromeos/autotest/host/machine_idle_rate/daily') |
| 45 | |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 46 | def report_stats(board, pool, start_time, end_time, span): |
| 47 | """Report machine stats for given board, pool and time period. |
| 48 | |
| 49 | @param board: Name of board. |
| 50 | @param pool: Name of pool. |
| 51 | @param start_time: start time to collect stats. |
| 52 | @param end_time: end time to collect stats. |
| 53 | @param span: Number of hours that the stats should be collected for. |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 54 | @return: Error message collected when calculating the stats. |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 55 | """ |
| 56 | print '================ %-12s %-12s ================' % (board, pool) |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 57 | try: |
| 58 | history = host_history.get_history_details(start_time=start_time, |
| 59 | end_time=end_time, |
| 60 | board=board, |
| 61 | pool=pool) |
| 62 | except host_history_utils.NoHostFoundException as e: |
| 63 | print 'No history found. Error:\n%s' % e |
| 64 | history = None |
| 65 | mur = -1 |
| 66 | mar = -1 |
| 67 | mir = -1 |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 68 | |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 69 | if history: |
| 70 | status_intervals = host_history_utils.get_status_intervals(history) |
| 71 | stats_all, num_hosts = host_history_utils.aggregate_hosts( |
| 72 | status_intervals) |
| 73 | total = 0 |
| 74 | total_time = span*3600*num_hosts |
| 75 | for status, interval in stats_all.iteritems(): |
| 76 | total += interval |
| 77 | if abs(total - total_time) > 10: |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 78 | error = ('Status intervals do not add up. No stats will be ' |
| 79 | 'collected for board: %s, pool: %s, diff: %s' % |
| 80 | (board, pool, total - total_time)) |
| 81 | hosts = [] |
| 82 | for history_for_host in status_intervals: |
| 83 | total = 0 |
| 84 | for interval in history_for_host.keys(): |
| 85 | total += interval[1] - interval[0] |
| 86 | if total > span*3600: |
| 87 | hosts.append(history_for_host.values()[0]['metadata']['hostname']) |
| 88 | error += ' hosts: %s' % ','.join(hosts) |
| 89 | print error |
| 90 | return error |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 91 | |
| 92 | mur = host_history_utils.get_machine_utilization_rate(stats_all) |
| 93 | mar = host_history_utils.get_machine_availability_rate(stats_all) |
| 94 | mir = mar - mur |
| 95 | |
| 96 | for status, interval in stats_all.iteritems(): |
| 97 | print '%-18s %-16s %-10.2f%%' % (status, interval, |
| 98 | 100*interval/total_time) |
| 99 | print 'Machine utilization rate = %-4.2f%%' % (100*mur) |
| 100 | print 'Machine availability rate = %-4.2f%%' % (100*mar) |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 101 | |
Dan Shi | 4a7c542 | 2016-08-26 11:42:10 -0700 | [diff] [blame] | 102 | fields = {'board': board, |
| 103 | 'pool': pool} |
| 104 | if span == 1: |
| 105 | _MACHINE_UTILIZATION_RATE_HOURLY.set(mur, fields=fields) |
| 106 | _MACHINE_AVAILABILITY_RATE_HOURLY.set(mar, fields=fields) |
| 107 | _MACHINE_IDLE_RATE_HOURLY.set(mir, fields=fields) |
| 108 | elif span == 24: |
| 109 | _MACHINE_UTILIZATION_RATE_DAILY.set(mur, fields=fields) |
| 110 | _MACHINE_AVAILABILITY_RATE_DAILY.set(mar, fields=fields) |
| 111 | _MACHINE_IDLE_RATE_DAILY.set(mir, fields=fields) |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 112 | |
| 113 | |
| 114 | def main(): |
| 115 | """main script. """ |
| 116 | parser = argparse.ArgumentParser() |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 117 | parser.add_argument('--span', type=int, dest='span', default=1, |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 118 | help=('Number of hours that stats should be collected. ' |
| 119 | 'If it is set to 24, the end time of stats being ' |
| 120 | 'collected will set to the mid of the night. ' |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 121 | 'Default is set to 1 hour.')) |
| 122 | parser.add_argument('-e', '--email', dest='email', default=None, |
| 123 | help='Email any errors to the given email address.') |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 124 | options = parser.parse_args() |
| 125 | |
Dan Shi | 1c3b0d1 | 2014-09-26 17:15:41 -0700 | [diff] [blame] | 126 | boards = host_label_utils.get_all_boards() |
J. Richard Barnette | 789f87c | 2015-03-23 16:25:25 -0700 | [diff] [blame] | 127 | pools = ['bvt', 'suites', 'cq'] |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 128 | |
| 129 | if options.span == 24: |
| 130 | today = datetime.combine(date.today(), datetime.min.time()) |
| 131 | end_time = time_utils.to_epoch_time(today) |
| 132 | else: |
| 133 | now = datetime.now() |
| 134 | end_time = datetime(year=now.year, month=now.month, day=now.day, |
| 135 | hour=now.hour) |
| 136 | end_time = time_utils.to_epoch_time(end_time) |
| 137 | |
| 138 | start_time = end_time - timedelta(hours=options.span).total_seconds() |
| 139 | print ('Collecting host stats from %s to %s...' % |
| 140 | (time_utils.epoch_time_to_date_string(start_time), |
| 141 | time_utils.epoch_time_to_date_string(end_time))) |
| 142 | |
Dan Shi | 4a7c542 | 2016-08-26 11:42:10 -0700 | [diff] [blame] | 143 | ts_mon_config.SetupTsMonGlobalState('collect_host_stats') |
| 144 | |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 145 | errors = [] |
Dan Shi | 99be217 | 2015-04-06 14:35:10 -0700 | [diff] [blame] | 146 | if not boards: |
| 147 | errors.append('Error! No board found in metadb.') |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 148 | for board in boards: |
| 149 | for pool in pools: |
Dan Shi | 66ba3c9 | 2014-10-28 17:39:30 -0700 | [diff] [blame] | 150 | error = report_stats(board, pool, start_time, end_time, |
| 151 | options.span) |
| 152 | if error: |
| 153 | errors.append(error) |
| 154 | if options.email and errors: |
Dan Shi | 1d59a67 | 2015-03-31 14:09:03 -0700 | [diff] [blame] | 155 | gmail_lib.send_email(options.email, |
| 156 | 'Error occured when collecting host stats.', |
| 157 | '\n'.join(errors)) |
Dan Shi | 1ccb652 | 2014-09-19 17:15:30 -0700 | [diff] [blame] | 158 | |
| 159 | |
| 160 | if __name__ == '__main__': |
| 161 | main() |