| #!/usr/bin/python2 |
| |
| # 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) |