1*9c5db199SXin Li#!/usr/bin/python3 2*9c5db199SXin Li 3*9c5db199SXin Li# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 4*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 5*9c5db199SXin Li# found in the LICENSE file. 6*9c5db199SXin Li 7*9c5db199SXin Li""" 8*9c5db199SXin LiTool to check DUT usage by querying the Autotest DB. 9*9c5db199SXin Li 10*9c5db199SXin LiSample usage: 11*9c5db199SXin Li 12*9c5db199SXin Liutils/site_check_dut_usage.py 11/1/2011 11/5/2011 netbook_LABEL 13*9c5db199SXin Li""" 14*9c5db199SXin Li 15*9c5db199SXin Lifrom __future__ import absolute_import 16*9c5db199SXin Lifrom __future__ import division 17*9c5db199SXin Lifrom __future__ import print_function 18*9c5db199SXin Li 19*9c5db199SXin Liimport datetime 20*9c5db199SXin Liimport optparse 21*9c5db199SXin Liimport sys 22*9c5db199SXin Li 23*9c5db199SXin Liimport common 24*9c5db199SXin Lifrom autotest_lib.database import database_connection 25*9c5db199SXin Li 26*9c5db199SXin Li_DATE_FORMAT = '%m/%d/%Y' 27*9c5db199SXin Li 28*9c5db199SXin Li 29*9c5db199SXin Liclass CheckDutUsageRunner(object): 30*9c5db199SXin Li """Checks DUT usage for given label or hostname during a time period.""" 31*9c5db199SXin Li 32*9c5db199SXin Li def __init__(self, start_time, end_time, label, hostname, list_hostnames): 33*9c5db199SXin Li """ 34*9c5db199SXin Li Instantiates a CheckDUTUsageRunner. 35*9c5db199SXin Li 36*9c5db199SXin Li @start_time: start date of time period we are interested in. 37*9c5db199SXin Li @end_time: end date of time period we are interested in. Note the 38*9c5db199SXin Li time period is (start_date, end_date]. 39*9c5db199SXin Li @label: If not None, the platform label of the hostnames we are 40*9c5db199SXin Li interested in. 41*9c5db199SXin Li @hostname: If not None, the hostname we are intersted in. 42*9c5db199SXin Li @list_hostnames: If set, print out the list of hostnames found that ran 43*9c5db199SXin Li jobs during the given time period. 44*9c5db199SXin Li """ 45*9c5db199SXin Li self._start_time = start_time 46*9c5db199SXin Li self._end_time = end_time 47*9c5db199SXin Li self._list_hostnames = list_hostnames 48*9c5db199SXin Li self._label = label 49*9c5db199SXin Li self._hostname = hostname 50*9c5db199SXin Li self._database_connection = None 51*9c5db199SXin Li 52*9c5db199SXin Li 53*9c5db199SXin Li def find_all_durations(self): 54*9c5db199SXin Li """ 55*9c5db199SXin Li Returns all list of tuples containing durations. 56*9c5db199SXin Li 57*9c5db199SXin Li A duration is a 4-tuple containing |queued_time|, |started_time|, 58*9c5db199SXin Li |finished_time|, |hostname|. 59*9c5db199SXin Li """ 60*9c5db199SXin Li query = ('select queued_time, started_time, finished_time, ' 61*9c5db199SXin Li ' hostname ' 62*9c5db199SXin Li 'from tko_jobs left join tko_machines on ' 63*9c5db199SXin Li ' tko_jobs.machine_idx=tko_machines.machine_idx ' 64*9c5db199SXin Li 'where tko_jobs.started_time>=DATE(%s) and ' 65*9c5db199SXin Li ' tko_jobs.finished_time<DATE(%s)') 66*9c5db199SXin Li if self._label: 67*9c5db199SXin Li query += ' and tko_machines.machine_group=%s' 68*9c5db199SXin Li filter_value = self._label 69*9c5db199SXin Li else: 70*9c5db199SXin Li query += ' and tko_machines.hostname=%s' 71*9c5db199SXin Li filter_value = self._hostname 72*9c5db199SXin Li 73*9c5db199SXin Li results = self._database_connection.execute( 74*9c5db199SXin Li query, [self._start_time, self._end_time, filter_value]) 75*9c5db199SXin Li return results 76*9c5db199SXin Li 77*9c5db199SXin Li 78*9c5db199SXin Li @staticmethod 79*9c5db199SXin Li def _total_seconds(time_delta): 80*9c5db199SXin Li """ 81*9c5db199SXin Li Returns a float that has the total seconds in a datetime.timedelta. 82*9c5db199SXin Li """ 83*9c5db199SXin Li return float(time_delta.days * 86400 + time_delta.seconds) 84*9c5db199SXin Li 85*9c5db199SXin Li 86*9c5db199SXin Li def calculate_usage(self, durations): 87*9c5db199SXin Li """ 88*9c5db199SXin Li Calculates and prints out usage information given list of durations. 89*9c5db199SXin Li """ 90*9c5db199SXin Li total_run_time = datetime.timedelta() 91*9c5db199SXin Li total_queued_time = datetime.timedelta() 92*9c5db199SXin Li machines = set() 93*9c5db199SXin Li for q_time, s_time, f_time, machine in durations: 94*9c5db199SXin Li total_run_time += f_time - s_time 95*9c5db199SXin Li total_queued_time += s_time - q_time 96*9c5db199SXin Li machines.add(machine) 97*9c5db199SXin Li 98*9c5db199SXin Li num_machines = len(machines) 99*9c5db199SXin Li avg_run_time = total_run_time / num_machines 100*9c5db199SXin Li avg_job_run_time = self._total_seconds(total_run_time) / len(durations) 101*9c5db199SXin Li avg_job_queued_time = (self._total_seconds(total_queued_time) / 102*9c5db199SXin Li len(durations)) 103*9c5db199SXin Li duration = self._end_time - self._start_time 104*9c5db199SXin Li usage = self._total_seconds(avg_run_time) / self._total_seconds( 105*9c5db199SXin Li duration) 106*9c5db199SXin Li 107*9c5db199SXin Li # Print the list of hostnames if the user requested. 108*9c5db199SXin Li if self._list_hostnames: 109*9c5db199SXin Li print('==================================================') 110*9c5db199SXin Li print('Machines with label:') 111*9c5db199SXin Li for machine in machines: 112*9c5db199SXin Li print(machine) 113*9c5db199SXin Li print('==================================================') 114*9c5db199SXin Li 115*9c5db199SXin Li # Print the usage summary. 116*9c5db199SXin Li print('==================================================') 117*9c5db199SXin Li print('Total running time', total_run_time) 118*9c5db199SXin Li print('Total queued time', total_queued_time) 119*9c5db199SXin Li print('Total number of machines', num_machines) 120*9c5db199SXin Li print('Average time spent running tests per machine ', avg_run_time) 121*9c5db199SXin Li print('Average Job Time ', datetime.timedelta(seconds=int( 122*9c5db199SXin Li avg_job_run_time))) 123*9c5db199SXin Li print('Average Time Job Queued ', datetime.timedelta(seconds=int( 124*9c5db199SXin Li avg_job_queued_time))) 125*9c5db199SXin Li print('Total duration ', duration) 126*9c5db199SXin Li print('Usage ', usage) 127*9c5db199SXin Li print('==================================================') 128*9c5db199SXin Li 129*9c5db199SXin Li 130*9c5db199SXin Li def run(self): 131*9c5db199SXin Li """Connects to SQL DB and calculates DUT usage given args.""" 132*9c5db199SXin Li # Force the database connection to use the read the readonly options. 133*9c5db199SXin Li database_connection._GLOBAL_CONFIG_NAMES.update( 134*9c5db199SXin Li {'username': 'readonly_user', 135*9c5db199SXin Li 'password': 'readonly_password', 136*9c5db199SXin Li }) 137*9c5db199SXin Li self._database_connection = database_connection.DatabaseConnection( 138*9c5db199SXin Li global_config_section='AUTOTEST_WEB') 139*9c5db199SXin Li self._database_connection.connect() 140*9c5db199SXin Li 141*9c5db199SXin Li durations = self.find_all_durations() 142*9c5db199SXin Li if not durations: 143*9c5db199SXin Li print('Query returned no results.') 144*9c5db199SXin Li else: 145*9c5db199SXin Li self.calculate_usage(durations) 146*9c5db199SXin Li 147*9c5db199SXin Li self._database_connection.disconnect() 148*9c5db199SXin Li 149*9c5db199SXin Li 150*9c5db199SXin Lidef parse_args(options, args, parser): 151*9c5db199SXin Li """Returns a tuple containing start time, end time, and label, hostname.""" 152*9c5db199SXin Li label, hostname = None, None 153*9c5db199SXin Li 154*9c5db199SXin Li if len(args) != 4: 155*9c5db199SXin Li parser.error('Should have exactly 3 arguments.') 156*9c5db199SXin Li 157*9c5db199SXin Li if options.hostname: 158*9c5db199SXin Li hostname = args[-1] 159*9c5db199SXin Li else: 160*9c5db199SXin Li label = args[-1] 161*9c5db199SXin Li 162*9c5db199SXin Li start_time, end_time = args[1:3] 163*9c5db199SXin Li return (datetime.datetime.strptime(start_time, _DATE_FORMAT).date(), 164*9c5db199SXin Li datetime.datetime.strptime(end_time, _DATE_FORMAT).date(), 165*9c5db199SXin Li label, hostname) 166*9c5db199SXin Li 167*9c5db199SXin Li 168*9c5db199SXin Lidef main(argv): 169*9c5db199SXin Li """Main method. Parses options and runs main program.""" 170*9c5db199SXin Li usage = ('usage: %prog [options] start_date end_date platform_Label|' 171*9c5db199SXin Li 'hostname') 172*9c5db199SXin Li parser = optparse.OptionParser(usage=usage) 173*9c5db199SXin Li parser.add_option('--hostname', action='store_true', default=False, 174*9c5db199SXin Li help='If set, interpret argument as hostname.') 175*9c5db199SXin Li parser.add_option('--list', action='store_true', default=False, 176*9c5db199SXin Li help='If set, print out list of hostnames with ' 177*9c5db199SXin Li 'the given label that ran jobs during this time.') 178*9c5db199SXin Li options, args = parser.parse_args(argv) 179*9c5db199SXin Li 180*9c5db199SXin Li start_time, end_time, label, hostname = parse_args(options, args, parser) 181*9c5db199SXin Li runner = CheckDutUsageRunner(start_time, end_time, label, hostname, 182*9c5db199SXin Li options.list) 183*9c5db199SXin Li runner.run() 184*9c5db199SXin Li 185*9c5db199SXin Li 186*9c5db199SXin Liif __name__ == '__main__': 187*9c5db199SXin Li main(sys.argv) 188