| #!/usr/bin/env python |
| |
| # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """This script is to be run daily to report machine utilization stats across |
| each board and pool. |
| """ |
| |
| |
| import argparse |
| from datetime import date |
| from datetime import datetime |
| from datetime import timedelta |
| |
| import common |
| from autotest_lib.client.common_lib import time_utils |
| from autotest_lib.client.common_lib import utils |
| from autotest_lib.site_utils import gmail_lib |
| from autotest_lib.site_utils import host_history |
| from autotest_lib.site_utils import host_history_utils |
| from autotest_lib.site_utils import host_label_utils |
| |
| try: |
| from chromite.lib import metrics |
| from chromite.lib import ts_mon_config |
| except ImportError: |
| metrics = utils.metrics_mock |
| ts_mon_config = utils.metrics_mock |
| |
| |
| _MACHINE_UTILIZATION_RATE_HOURLY = metrics.Float( |
| 'chromeos/autotest/host/machine_utilization_rate/hourly') |
| _MACHINE_AVAILABILITY_RATE_HOURLY = metrics.Float( |
| 'chromeos/autotest/host/machine_availability_rate/hourly') |
| _MACHINE_IDLE_RATE_HOURLY = metrics.Float( |
| 'chromeos/autotest/host/machine_idle_rate/hourly') |
| _MACHINE_UTILIZATION_RATE_DAILY = metrics.Float( |
| 'chromeos/autotest/host/machine_utilization_rate/daily') |
| _MACHINE_AVAILABILITY_RATE_DAILY = metrics.Float( |
| 'chromeos/autotest/host/machine_availability_rate/daily') |
| _MACHINE_IDLE_RATE_DAILY = metrics.Float( |
| 'chromeos/autotest/host/machine_idle_rate/daily') |
| |
| def report_stats(board, pool, start_time, end_time, span): |
| """Report machine stats for given board, pool and time period. |
| |
| @param board: Name of board. |
| @param pool: Name of pool. |
| @param start_time: start time to collect stats. |
| @param end_time: end time to collect stats. |
| @param span: Number of hours that the stats should be collected for. |
| @return: Error message collected when calculating the stats. |
| """ |
| print '================ %-12s %-12s ================' % (board, pool) |
| try: |
| history = host_history.get_history_details(start_time=start_time, |
| end_time=end_time, |
| board=board, |
| pool=pool) |
| except host_history_utils.NoHostFoundException as e: |
| print 'No history found. Error:\n%s' % e |
| history = None |
| mur = -1 |
| mar = -1 |
| mir = -1 |
| |
| if history: |
| status_intervals = host_history_utils.get_status_intervals(history) |
| stats_all, num_hosts = host_history_utils.aggregate_hosts( |
| status_intervals) |
| total = 0 |
| total_time = span*3600*num_hosts |
| for status, interval in stats_all.iteritems(): |
| total += interval |
| if abs(total - total_time) > 10: |
| error = ('Status intervals do not add up. No stats will be ' |
| 'collected for board: %s, pool: %s, diff: %s' % |
| (board, pool, total - total_time)) |
| hosts = [] |
| for history_for_host in status_intervals: |
| total = 0 |
| for interval in history_for_host.keys(): |
| total += interval[1] - interval[0] |
| if total > span*3600: |
| hosts.append(history_for_host.values()[0]['metadata']['hostname']) |
| error += ' hosts: %s' % ','.join(hosts) |
| print error |
| return error |
| |
| mur = host_history_utils.get_machine_utilization_rate(stats_all) |
| mar = host_history_utils.get_machine_availability_rate(stats_all) |
| mir = mar - mur |
| |
| for status, interval in stats_all.iteritems(): |
| print '%-18s %-16s %-10.2f%%' % (status, interval, |
| 100*interval/total_time) |
| print 'Machine utilization rate = %-4.2f%%' % (100*mur) |
| print 'Machine availability rate = %-4.2f%%' % (100*mar) |
| |
| fields = {'board': board, |
| 'pool': pool} |
| if span == 1: |
| _MACHINE_UTILIZATION_RATE_HOURLY.set(mur, fields=fields) |
| _MACHINE_AVAILABILITY_RATE_HOURLY.set(mar, fields=fields) |
| _MACHINE_IDLE_RATE_HOURLY.set(mir, fields=fields) |
| elif span == 24: |
| _MACHINE_UTILIZATION_RATE_DAILY.set(mur, fields=fields) |
| _MACHINE_AVAILABILITY_RATE_DAILY.set(mar, fields=fields) |
| _MACHINE_IDLE_RATE_DAILY.set(mir, fields=fields) |
| |
| |
| def main(): |
| """main script. """ |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--span', type=int, dest='span', default=1, |
| help=('Number of hours that stats should be collected. ' |
| 'If it is set to 24, the end time of stats being ' |
| 'collected will set to the mid of the night. ' |
| 'Default is set to 1 hour.')) |
| parser.add_argument('-e', '--email', dest='email', default=None, |
| help='Email any errors to the given email address.') |
| options = parser.parse_args() |
| |
| boards = host_label_utils.get_all_boards() |
| pools = ['bvt', 'suites', 'cq'] |
| |
| if options.span == 24: |
| today = datetime.combine(date.today(), datetime.min.time()) |
| end_time = time_utils.to_epoch_time(today) |
| else: |
| now = datetime.now() |
| end_time = datetime(year=now.year, month=now.month, day=now.day, |
| hour=now.hour) |
| end_time = time_utils.to_epoch_time(end_time) |
| |
| start_time = end_time - timedelta(hours=options.span).total_seconds() |
| print ('Collecting host stats from %s to %s...' % |
| (time_utils.epoch_time_to_date_string(start_time), |
| time_utils.epoch_time_to_date_string(end_time))) |
| |
| ts_mon_config.SetupTsMonGlobalState('collect_host_stats') |
| |
| errors = [] |
| if not boards: |
| errors.append('Error! No board found in metadb.') |
| for board in boards: |
| for pool in pools: |
| error = report_stats(board, pool, start_time, end_time, |
| options.span) |
| if error: |
| errors.append(error) |
| if options.email and errors: |
| gmail_lib.send_email(options.email, |
| 'Error occured when collecting host stats.', |
| '\n'.join(errors)) |
| |
| |
| if __name__ == '__main__': |
| main() |