#!/usr/bin/python # Copyright (c) 2011 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. """ Tool to check DUT usage by querying the Autotest DB. Sample usage: utils/site_check_dut_usage.py 11/1/2011 11/5/2011 netbook_LABEL """ import datetime import optparse import sys import common from autotest_lib.database import database_connection _DATE_FORMAT = '%m/%d/%Y' class CheckDutUsageRunner(object): """Checks DUT usage for given label or hostname during a time period.""" def __init__(self, start_time, end_time, label, hostname, list_hostnames): """ Instantiates a CheckDUTUsageRunner. @start_time: start date of time period we are interested in. @end_time: end date of time period we are interested in. Note the time period is (start_date, end_date]. @label: If not None, the platform label of the hostnames we are interested in. @hostname: If not None, the hostname we are intersted in. @list_hostnames: If set, print out the list of hostnames found that ran jobs during the given time period. """ self._start_time = start_time self._end_time = end_time self._list_hostnames = list_hostnames self._label = label self._hostname = hostname self._database_connection = None def find_all_durations(self): """ Returns all list of tuples containing durations. A duration is a 4-tuple containing |queued_time|, |started_time|, |finished_time|, |hostname|. """ query = ('select queued_time, started_time, finished_time, ' ' hostname ' 'from tko_jobs left join tko_machines on ' ' tko_jobs.machine_idx=tko_machines.machine_idx ' 'where tko_jobs.started_time>=DATE(%s) and ' ' tko_jobs.finished_time<DATE(%s)') if self._label: query += ' and tko_machines.machine_group=%s' filter_value = self._label else: query += ' and tko_machines.hostname=%s' filter_value = self._hostname results = self._database_connection.execute( query, [self._start_time, self._end_time, filter_value]) return results @staticmethod def _total_seconds(time_delta): """ Returns a float that has the total seconds in a datetime.timedelta. """ return float(time_delta.days * 86400 + time_delta.seconds) def calculate_usage(self, durations): """ Calculates and prints out usage information given list of durations. """ total_run_time = datetime.timedelta() total_queued_time = datetime.timedelta() machines = set() for q_time, s_time, f_time, machine in durations: total_run_time += f_time - s_time total_queued_time += s_time - q_time machines.add(machine) num_machines = len(machines) avg_run_time = total_run_time / num_machines avg_job_run_time = self._total_seconds(total_run_time) / len(durations) avg_job_queued_time = (self._total_seconds(total_queued_time) / len(durations)) duration = self._end_time - self._start_time usage = self._total_seconds(avg_run_time) / self._total_seconds( duration) # Print the list of hostnames if the user requested. if self._list_hostnames: print '==================================================' print 'Machines with label:' for machine in machines: print machine print '==================================================' # Print the usage summary. print '==================================================' print 'Total running time', total_run_time print 'Total queued time', total_queued_time print 'Total number of machines', num_machines print 'Average time spent running tests per machine ', avg_run_time print 'Average Job Time ', datetime.timedelta(seconds=int( avg_job_run_time)) print 'Average Time Job Queued ', datetime.timedelta(seconds=int( avg_job_queued_time)) print 'Total duration ', duration print 'Usage ', usage print '==================================================' def run(self): """Connects to SQL DB and calculates DUT usage given args.""" # Force the database connection to use the read the readonly options. database_connection._GLOBAL_CONFIG_NAMES.update( {'username': 'readonly_user', 'password': 'readonly_password', }) self._database_connection = database_connection.DatabaseConnection( global_config_section='AUTOTEST_WEB') self._database_connection.connect() durations = self.find_all_durations() if not durations: print 'Query returned no results.' else: self.calculate_usage(durations) self._database_connection.disconnect() def parse_args(options, args, parser): """Returns a tuple containing start time, end time, and label, hostname.""" label, hostname = None, None if len(args) != 4: parser.error('Should have exactly 3 arguments.') if options.hostname: hostname = args[-1] else: label = args[-1] start_time, end_time = args[1:3] return (datetime.datetime.strptime(start_time, _DATE_FORMAT).date(), datetime.datetime.strptime(end_time, _DATE_FORMAT).date(), label, hostname) def main(argv): """Main method. Parses options and runs main program.""" usage = ('usage: %prog [options] start_date end_date platform_Label|' 'hostname') parser = optparse.OptionParser(usage=usage) parser.add_option('--hostname', action='store_true', default=False, help='If set, interpret argument as hostname.') parser.add_option('--list', action='store_true', default=False, help='If set, print out list of hostnames with ' 'the given label that ran jobs during this time.') options, args = parser.parse_args(argv) start_time, end_time, label, hostname = parse_args(options, args, parser) runner = CheckDutUsageRunner(start_time, end_time, label, hostname, options.list) runner.run() if __name__ == '__main__': main(sys.argv)