#!/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()