15952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 25952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang# Use of this source code is governed by a BSD-style license that can be 35952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang# found in the LICENSE file. 45952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 55952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang# This file contains utility functions for host_history. 65952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 75952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangimport collections 81ccb65208c26615096985b5f8b52368f7342a077Dan Shiimport copy 97cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shiimport multiprocessing.pool 1017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shifrom itertools import groupby 115952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 125952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangimport common 13dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shifrom autotest_lib.client.common_lib import time_utils 14b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Blackfrom autotest_lib.client.common_lib.cros.graphite import autotest_es 155952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangfrom autotest_lib.frontend import setup_django_environment 165952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangfrom autotest_lib.frontend.afe import models 1717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shifrom autotest_lib.site_utils import host_label_utils 181ccb65208c26615096985b5f8b52368f7342a077Dan Shifrom autotest_lib.site_utils import job_history 195952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 2117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi_HOST_HISTORY_TYPE = 'host_history' 2217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi_LOCK_HISTORY_TYPE = 'lock_history' 2317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 247cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi# The maximum number of days that the script will lookup for history. 257cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi_MAX_DAYS_FOR_HISTORY = 90 261c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi 271c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shiclass NoHostFoundException(Exception): 281c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi """Exception raised when no host is found to search for history. 291c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi """ 301c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi 311c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi 3217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef get_matched_hosts(board, pool): 3317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """Get duts with matching board and pool labels from metaDB. 3417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 3517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param board: board of DUT, set to None if board doesn't need to match. 3617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param pool: pool of DUT, set to None if pool doesn't need to match. 3717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @return: A list of duts that match the specified board and pool. 3817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """ 3917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi labels = [] 4017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if pool: 4117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi labels.append('pool:%s' % pool) 4217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if board: 4317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi labels.append('board:%s' % board) 4417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi host_labels = host_label_utils.get_host_labels(labels=labels) 4517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi return host_labels.keys() 4617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 4717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 485952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangdef prepopulate_dict(keys, value, extras=None): 495952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Creates a dictionary with val=value for each key. 505952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 515952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param keys: list of keys 525952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param value: the value of each entry in the dict. 535952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param extras: list of additional keys 545952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: dictionary 555952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 565952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result = collections.OrderedDict() 575952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang extra_keys = tuple(extras if extras else []) 585952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang for key in keys + extra_keys: 595952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result[key] = value 605952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return result 615952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 625952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 635952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangdef lock_history_to_intervals(initial_lock_val, t_start, t_end, lock_history): 645952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Converts lock history into a list of intervals of locked times. 655952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 665952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param initial_lock_val: Initial value of the lock (False or True) 675952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param t_start: beginning of the time period we are interested in. 685952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param t_end: end of the time period we are interested in. 695952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param lock_history: Result of querying es for locks (dict) 705952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang This dictionary should contain keys 'locked' and 'time_recorded' 715952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: Returns a list of tuples where the elements of each tuples 725952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang represent beginning and end of intervals of locked, respectively. 735952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 745952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang locked_intervals = [] 755952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_prev = t_start 765952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang state_prev = initial_lock_val 77b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black for entry in lock_history.hits: 78f242c20641311794fbddd022d8dda076eb3e8ea6Dan Shi t_curr = entry['time_recorded'] 795952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 805952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang #If it is locked, then we put into locked_intervals 815952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang if state_prev: 825952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang locked_intervals.append((t_prev, t_curr)) 835952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 845952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # update vars 855952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_prev = t_curr 86f242c20641311794fbddd022d8dda076eb3e8ea6Dan Shi state_prev = entry['locked'] 875952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang if state_prev: 885952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang locked_intervals.append((t_prev, t_end)) 895952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return locked_intervals 905952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 915952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 9217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef find_most_recent_entry_before(t, type_str, hostname, fields): 935952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Returns the fields of the most recent entry before t. 945952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 955952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param t: time we are interested in. 965952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param type_str: _type in esdb, such as 'host_history' (string) 975952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param hostname: hostname of DUT (string) 985952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param fields: list of fields we are interested in 995952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: time, field_value of the latest entry. 1005952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 1017cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi # History older than 90 days are ignored. This helps the ES query faster. 10255bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_epoch = time_utils.to_epoch_time(t) 103b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black result = autotest_es.query( 1045952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang fields_returned=fields, 1055952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang equality_constraints=[('_type', type_str), 1065952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang ('hostname', hostname)], 1077cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi range_constraints=[('time_recorded', 1087cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi t_epoch-3600*24*_MAX_DAYS_FOR_HISTORY, t_epoch)], 1095952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang size=1, 1105952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang sort_specs=[{'time_recorded': 'desc'}]) 111b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black if result.total > 0: 112b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black return result.hits[0] 1135952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return {} 1145952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 1155952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 1167cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shidef get_host_history_intervals(input): 1175952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Gets stats for a host. 1185952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 11917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi This method uses intervals found in metaDB to build a full history of the 12017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi host. The intervals argument contains a list of metadata from querying ES 12117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for records between t_start and t_end. To get the status from t_start to 12217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi the first record logged in ES, we need to look back to the last record 12317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi logged in ES before t_start. 12417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 1257cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi @param input: A dictionary of input args, which including following args: 1267cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi t_start: beginning of time period we are interested in. 1277cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi t_end: end of time period we are interested in. 1287cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi hostname: hostname for the host we are interested in (string) 1297cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi intervals: intervals from ES query. 1305952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: dictionary, num_entries_found 1315952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang dictionary of status: time spent in that status 1325952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang num_entries_found: number of host history entries 1335952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang found in [t_start, t_end] 1345952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 1355952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 1367cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi t_start = input['t_start'] 1377cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi t_end = input['t_end'] 1387cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi hostname = input['hostname'] 1397cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi intervals = input['intervals'] 1405952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang lock_history_recent = find_most_recent_entry_before( 14117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi t=t_start, type_str=_LOCK_HISTORY_TYPE, hostname=hostname, 14217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi fields=['time_recorded', 'locked']) 1435952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # I use [0] and [None] because lock_history_recent's type is list. 1447cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi t_lock = lock_history_recent.get('time_recorded', None) 1457cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi t_lock_val = lock_history_recent.get('locked', None) 146b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black t_metadata = find_most_recent_entry_before( 14717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi t=t_start, type_str=_HOST_HISTORY_TYPE, hostname=hostname, 14817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi fields=None) 149b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black t_host = t_metadata.pop('time_recorded', None) 150b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black t_host_stat = t_metadata.pop('status', None) 151f242c20641311794fbddd022d8dda076eb3e8ea6Dan Shi status_first = t_host_stat if t_host else 'Ready' 1525952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t = min([t for t in [t_lock, t_host, t_start] if t]) 1535952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 15455bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_epoch = time_utils.to_epoch_time(t) 15555bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_end_epoch = time_utils.to_epoch_time(t_end) 156b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black lock_history_entries = autotest_es.query( 1575952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang fields_returned=['locked', 'time_recorded'], 15817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints=[('_type', _LOCK_HISTORY_TYPE), 1595952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang ('hostname', hostname)], 16055bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black range_constraints=[('time_recorded', t_epoch, t_end_epoch)], 1615952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang sort_specs=[{'time_recorded': 'asc'}]) 1625952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 1635d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # Validate lock history. If an unlock event failed to be recorded in metadb, 1645d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # lock history will show the dut being locked while host still has status 1655d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # changed over the time. This check tries to remove the lock event in lock 1665d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # history if: 1675d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # 1. There is only one entry in lock_history_entries (it's a good enough 1685d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # assumption to avoid the code being over complicated. 1695d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # 2. The host status has changes after the lock history starts as locked. 1705d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi if (len(lock_history_entries.hits) == 1 and t_lock_val and 1715d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi len(intervals) >1): 1725d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi locked_intervals = None 1735d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi print ('Lock history of dut %s is ignored, the dut may have missing ' 1745d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi 'data in lock history in metadb. Try to lock and unlock the dut ' 1755d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi 'in AFE will force the lock history to be updated in metadb.' 1765d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi % hostname) 1775d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi else: 1785d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi locked_intervals = lock_history_to_intervals(t_lock_val, t, t_end, 1795d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi lock_history_entries) 18017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi num_entries_found = len(intervals) 1815952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_prev = t_start 1825952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang status_prev = status_first 1837cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi metadata_prev = t_metadata 1845952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang intervals_of_statuses = collections.OrderedDict() 1855952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 18617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for entry in intervals: 187b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black metadata = entry.copy() 188b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black t_curr = metadata.pop('time_recorded') 189b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black status_curr = metadata.pop('status') 1907cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi intervals_of_statuses.update(calculate_status_times( 1917cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi t_prev, t_curr, status_prev, metadata_prev, locked_intervals)) 1925952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # Update vars 1935952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_prev = t_curr 1945952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang status_prev = status_curr 1957cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi metadata_prev = metadata 1965952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 1975952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # Do final as well. 1987cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi intervals_of_statuses.update(calculate_status_times( 1997cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi t_prev, t_end, status_prev, metadata_prev, locked_intervals)) 2007cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi return hostname, intervals_of_statuses, num_entries_found 2015952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2025952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2035952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangdef calculate_total_times(intervals_of_statuses): 2045952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Calculates total times in each status. 2055952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2065952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param intervals_of_statuses: ordereddict where key=(ti, tf) and val=status 2075952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: dictionary where key=status value=time spent in that status 2085952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 2095952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang total_times = prepopulate_dict(models.Host.Status.names, 0.0, 2105952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang extras=['Locked']) 2115952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang for key, status_info in intervals_of_statuses.iteritems(): 2125952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang ti, tf = key 2135952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang total_times[status_info['status']] += tf - ti 2145952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return total_times 2155952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2165952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 2171ccb65208c26615096985b5f8b52368f7342a077Dan Shidef aggregate_hosts(intervals_of_statuses_list): 21893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """Aggregates history of multiple hosts 21993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 22093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param intervals_of_statuses_list: A list of dictionaries where keys 2217cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi are tuple (ti, tf), and value is the status along with other metadata. 22293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @returns: A dictionary where keys are strings, e.g. 'status' and 22393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang value is total time spent in that status among all hosts. 22493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """ 22593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang stats_all = prepopulate_dict(models.Host.Status.names, 0.0, 22693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang extras=['Locked']) 22793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang num_hosts = len(intervals_of_statuses_list) 22893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang for intervals_of_statuses in intervals_of_statuses_list: 22993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang total_times = calculate_total_times(intervals_of_statuses) 23093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang for status, delta in total_times.iteritems(): 23193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang stats_all[status] += delta 23293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang return stats_all, num_hosts 23393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 23493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 23593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liangdef get_stats_string_aggregate(labels, t_start, t_end, aggregated_stats, 23693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang num_hosts): 23793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """Returns string reporting overall host history for a group of hosts. 23893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 23993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param labels: A list of labels useful for describing the group 24093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang of hosts these overall stats represent. 24193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_start: beginning of time period we are interested in. 24293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_end: end of time period we are interested in. 24393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param aggregated_stats: A dictionary where keys are string, e.g. 'status' 24493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang value is total time spent in that status among all hosts. 24593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @returns: string representing the aggregate stats report. 24693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """ 24793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang result = 'Overall stats for hosts: %s \n' % (', '.join(labels)) 248dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi result += ' %s - %s \n' % (time_utils.epoch_time_to_date_string(t_start), 249dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi time_utils.epoch_time_to_date_string(t_end)) 25093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang result += ' Number of total hosts: %s \n' % (num_hosts) 25193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang # This is multiplied by time_spent to get percentage_spent 25293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang multiplication_factor = 100.0 / ((t_end - t_start) * num_hosts) 25393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang for status, time_spent in aggregated_stats.iteritems(): 25493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang # Normalize by the total time we are interested in among ALL hosts. 25593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang spaces = ' ' * (15 - len(status)) 25693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang percent_spent = multiplication_factor * time_spent 25793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang result += ' %s: %s %.2f %%\n' % (status, spaces, percent_spent) 25893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang result += '- -- --- ---- ----- ---- --- -- -\n' 25993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang return result 26093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 26193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 26293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liangdef get_overall_report(label, t_start, t_end, intervals_of_statuses_list): 26393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """Returns string reporting overall host history for a group of hosts. 26493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 26593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param label: A string that can be useful for showing what type group 26693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang of hosts these overall stats represent. 26793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_start: beginning of time period we are interested in. 26893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_end: end of time period we are interested in. 26993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param intervals_of_statuses_list: A list of dictionaries where keys 2707cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi are tuple (ti, tf), and value is the status along with other metadata, 2717cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi e.g., task_id, task_name, job_id etc. 27293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """ 2731ccb65208c26615096985b5f8b52368f7342a077Dan Shi stats_all, num_hosts = aggregate_hosts( 27493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang intervals_of_statuses_list) 27593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang return get_stats_string_aggregate( 2761ccb65208c26615096985b5f8b52368f7342a077Dan Shi label, t_start, t_end, stats_all, num_hosts) 27793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 27893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 27917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef get_intervals_for_host(t_start, t_end, hostname): 28017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """Gets intervals for the given. 28117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 28217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi Query metaDB to return all intervals between given start and end time. 28317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi Note that intervals found in metaDB may miss the history from t_start to 28417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi the first interval found. 28517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 28617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_start: beginning of time period we are interested in. 28717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_end: end of time period we are interested in. 28817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param hosts: A list of hostnames to look for history. 28917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param board: Name of the board to look for history. Default is None. 29017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param pool: Name of the pool to look for history. Default is None. 29117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @returns: A dictionary of hostname: intervals. 29217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """ 29355bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_start_epoch = time_utils.to_epoch_time(t_start) 29455bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_end_epoch = time_utils.to_epoch_time(t_end) 295b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black host_history_entries = autotest_es.query( 29617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi fields_returned=None, 29717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints=[('_type', _HOST_HISTORY_TYPE), 29817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi ('hostname', hostname)], 29955bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black range_constraints=[('time_recorded', t_start_epoch, 30055bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_end_epoch)], 30117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi sort_specs=[{'time_recorded': 'asc'}]) 302f242c20641311794fbddd022d8dda076eb3e8ea6Dan Shi return host_history_entries.hits 30317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 30417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 30517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef get_intervals_for_hosts(t_start, t_end, hosts=None, board=None, pool=None): 30617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """Gets intervals for given hosts or board/pool. 30717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 30817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi Query metaDB to return all intervals between given start and end time. 30917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi If a list of hosts is provided, the board and pool constraints are ignored. 31017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi If hosts is set to None, and board or pool is set, this method will attempt 31117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi to search host history with labels for all hosts, to help the search perform 31217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi faster. 31317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi If hosts, board and pool are all set to None, return intervals for all 31417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts. 31517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi Note that intervals found in metaDB may miss the history from t_start to 31617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi the first interval found. 31717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 31817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_start: beginning of time period we are interested in. 31917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_end: end of time period we are interested in. 32017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param hosts: A list of hostnames to look for history. 32117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param board: Name of the board to look for history. Default is None. 32217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param pool: Name of the pool to look for history. Default is None. 32317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @returns: A dictionary of hostname: intervals. 32417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """ 32517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts_intervals = {} 32617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if hosts: 32717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for host in hosts: 32817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts_intervals[host] = get_intervals_for_host(t_start, t_end, host) 32917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi else: 33017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts = get_matched_hosts(board, pool) 33117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if not hosts: 3321c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi raise NoHostFoundException('No host is found for board:%s, pool:%s.' 3331c3b0d1836ec77e92851d59a0fd24cdefdac44f3Dan Shi % (board, pool)) 33417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints=[('_type', _HOST_HISTORY_TYPE),] 33517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if board: 33617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints.append(('labels', 'board:'+board)) 33717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if pool: 33817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints.append(('labels', 'pool:'+pool)) 33955bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_start_epoch = time_utils.to_epoch_time(t_start) 34055bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_end_epoch = time_utils.to_epoch_time(t_end) 341b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black results = autotest_es.query( 34217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi equality_constraints=equality_constraints, 34355bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black range_constraints=[('time_recorded', t_start_epoch, 34455bfe14a6c2cc2710593ecf4d461af64181915c0Gabe Black t_end_epoch)], 34517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi sort_specs=[{'hostname': 'asc'}]) 34617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi results_group_by_host = {} 347b72f4fbcf1583da27f09f4abb9d8162530bf4559Gabe Black for hostname,intervals_for_host in groupby(results.hits, 34817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi lambda h: h['hostname']): 34917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi results_group_by_host[hostname] = intervals_for_host 35017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for host in hosts: 35117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi intervals = results_group_by_host.get(host, None) 35217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # In case the host's board or pool label was modified after 35317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # the last status change event was reported, we need to run a 35417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # separate query to get its history. That way the host's 35517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # history won't be shown as blank. 35617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if not intervals: 35717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi intervals = get_intervals_for_host(t_start, t_end, host) 35817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts_intervals[host] = intervals 35917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi return hosts_intervals 36017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 36117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 36217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef get_report(t_start, t_end, hosts=None, board=None, pool=None, 36317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi print_each_interval=False): 36417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """Gets history for given hosts or board/pool 36517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 36617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi If a list of hosts is provided, the board and pool constraints are ignored. 36717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 36817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_start: beginning of time period we are interested in. 36917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param t_end: end of time period we are interested in. 37017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param hosts: A list of hostnames to look for history. 37117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param board: Name of the board to look for history. Default is None. 37217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param pool: Name of the pool to look for history. Default is None. 37317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @param print_each_interval: True display all intervals, default is False. 37417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi @returns: stats report for this particular host. The report is a list of 37517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi tuples (stat_string, intervals, hostname), intervals is a sorted 37617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi dictionary. 37717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi """ 37817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi if hosts: 37917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi board=None 38017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi pool=None 38117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 38217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi hosts_intervals = get_intervals_for_hosts(t_start, t_end, hosts, board, 38317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi pool) 38417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi history = {} 385c1a81e55c53729df443180f1763423218dbb5706Dan Shi pool = multiprocessing.pool.ThreadPool(processes=16) 3867cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi args = [] 38717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for hostname,intervals in hosts_intervals.items(): 3887cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi args.append({'t_start': t_start, 3897cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 't_end': t_end, 3907cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 'hostname': hostname, 3917cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 'intervals': intervals}) 3927cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi results = pool.imap_unordered(get_host_history_intervals, args) 3937cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi for hostname, intervals, count in results: 3947cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi history[hostname] = (intervals, count) 39517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi report = [] 39617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi for hostname,intervals in history.items(): 39717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi total_times = calculate_total_times(intervals[0]) 39817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi stats = get_stats_string( 39917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi t_start, t_end, total_times, intervals[0], hostname, 40017ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi intervals[1], print_each_interval) 40117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi report.append((stats, intervals[0], hostname)) 40217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi return report 40317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 40417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 40517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shidef get_report_for_host(t_start, t_end, hostname, print_each_interval): 40693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """Gets stats report for a host 40793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 40893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_start: beginning of time period we are interested in. 40993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param t_end: end of time period we are interested in. 41093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param hostname: hostname for the host we are interested in (string) 41193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @param print_each_interval: True or False, whether we want to 41293b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang display all intervals 41393b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang @returns: stats report for this particular host (string) 41493b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang """ 41517ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # Search for status change intervals during given time range. 41617ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi intervals = get_intervals_for_host(t_start, t_end, hostname) 41717ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi num_entries_found = len(intervals) 41817ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # Update the status change intervals with status before the first entry and 41917ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi # host's lock history. 4207cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi _, intervals_of_statuses = get_host_history_intervals( 4217cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi {'t_start': t_start, 4227cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 't_end': t_end, 4237cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 'hostname': hostname, 4247cde53d430e208139638ee65116c0cd1ae7a7a36Dan Shi 'intervals': intervals}) 42593b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang total_times = calculate_total_times(intervals_of_statuses) 42693b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang return (get_stats_string( 42793b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang t_start, t_end, total_times, intervals_of_statuses, 42893b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang hostname, num_entries_found, print_each_interval), 42993b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang intervals_of_statuses) 43093b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 43193b4ba492c4af35fa025e3f995904fdeb8dbf7c4Michael Liang 4325952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liangdef get_stats_string(t_start, t_end, total_times, intervals_of_statuses, 4335952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang hostname, num_entries_found, print_each_interval): 4345952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Returns string reporting host_history for this host. 4355952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param t_start: beginning of time period we are interested in. 4365952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param t_end: end of time period we are interested in. 4375952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param total_times: dictionary where key=status, 4385952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang value=(time spent in that status) 4395952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param intervals_of_statuses: dictionary where keys is tuple (ti, tf), 4407cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi and value is the status along with other metadata. 4415952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param hostname: hostname for the host we are interested in (string) 4425952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param num_entries_found: Number of entries found for the host in es 4435952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @param print_each_interval: boolean, whether to print each interval 4445952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 4455952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang delta = t_end - t_start 4465952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result = 'usage stats for host: %s \n' % (hostname) 447dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi result += ' %s - %s \n' % (time_utils.epoch_time_to_date_string(t_start), 448dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi time_utils.epoch_time_to_date_string(t_end)) 4495952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result += ' Num entries found in this interval: %s\n' % (num_entries_found) 4505952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang for status, value in total_times.iteritems(): 4515952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang spaces = (15 - len(status)) * ' ' 4525952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result += ' %s: %s %.2f %%\n' % (status, spaces, 100*value/delta) 4535952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang result += '- -- --- ---- ----- ---- --- -- -\n' 4545952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang if print_each_interval: 4555952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang for interval, status_info in intervals_of_statuses.iteritems(): 4565952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t0, t1 = interval 457dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi t0_string = time_utils.epoch_time_to_date_string(t0) 458dfea368e5c830b1d7950ced5ee7b191e3b141ca3Dan Shi t1_string = time_utils.epoch_time_to_date_string(t1) 4595952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang status = status_info['status'] 4605952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang delta = int(t1-t0) 46117ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi id_info = status_info['metadata'].get( 46217ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi 'task_id', status_info['metadata'].get('job_id', '')) 46317ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi result += (' %s : %s %-15s %-10s %ss\n' % 46417ecbbf63319dabd5514827a34f04b5a0b724352Dan Shi (t0_string, t1_string, status, id_info, delta)) 4655952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return result 4665952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 4675952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 4687cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shidef calculate_status_times(t_start, t_end, int_status, metadata, 4697cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi locked_intervals): 4705952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """Returns a list of intervals along w/ statuses associated with them. 4715952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 472f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi If the dut is in status Ready, i.e., int_status==Ready, the lock history 473f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi should be applied so that the time period when dut is locked is considered 474f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi as not available. Any other status is considered that dut is doing something 475f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi and being used. `Repair Failed` and Repairing are not checked with lock 476f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi status, since these two statuses indicate the dut is not available any way. 477f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi 4787cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi @param t_start: start time 4797cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi @param t_end: end time 4807cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi @param int_status: status of [t_start, t_end] if not locked 4817cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi @param metadata: metadata of the status change, e.g., task_id, task_name. 4827cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi @param locked_intervals: list of tuples denoting intervals of locked states 4835952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang @returns: dictionary where key = (t_interval_start, t_interval_end), 4847cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi val = (status, metadata) 4855952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_interval_start: beginning of interval for that status 4865952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang t_interval_end: end of the interval for that status 4875952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang status: string such as 'Repair Failed', 'Locked', etc. 4887cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi metadata: A dictionary of metadata, e.g., 4897cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi {'task_id':123, 'task_name':'Reset'} 4905952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang """ 4915952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang statuses = collections.OrderedDict() 4925952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 4937cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi prev_interval_end = t_start 4945952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang 4955952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # TODO: Put allow more information here in info/locked status 4965952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang status_info = {'status': int_status, 4977cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi 'metadata': metadata} 4985952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang locked_info = {'status': 'Locked', 4997cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi 'metadata': {}} 5005d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi if not locked_intervals: 5017cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi statuses[(t_start, t_end)] = status_info 5025952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang return statuses 5035952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang for lock_start, lock_end in locked_intervals: 5045d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi if prev_interval_end >= t_end: 5055d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi break 5067cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi if lock_start > t_end: 5075952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # optimization to break early 5085952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # case 0 5097cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi # Timeline of status change: t_start t_end 5107cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi # Timeline of lock action: lock_start lock_end 5115952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang break 512f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi elif lock_end < prev_interval_end: 5135952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # case 1 514f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # prev_interval_end t_end 5157cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi # lock_start lock_end 5165952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang continue 517f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi elif lock_end <= t_end and lock_start >= prev_interval_end: 5185952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # case 2 519f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # prev_interval_end t_end 520f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # lock_start lock_end 521f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # Lock happened in the middle, while the host stays in the same 522f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # status, consider the lock has no effect on host history. 5235d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi statuses[(prev_interval_end, lock_end)] = status_info 524f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi prev_interval_end = lock_end 525f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi elif lock_end > prev_interval_end and lock_start < prev_interval_end: 5265952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # case 3 527f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # prev_interval_end t_end 5285d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi # lock_start lock_end (or lock_end) 529f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # If the host status changed in the middle of being locked, consider 530f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # the new status change as part of the host history. 5315d53f70f710af3d552fe1f97720471973a98cbc9Dan Shi statuses[(prev_interval_end, min(lock_end, t_end))] = locked_info 532f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi prev_interval_end = lock_end 5337cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi elif lock_start < t_end and lock_end > t_end: 5345952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # case 4 535f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # prev_interval_end t_end 536f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # lock_start lock_end 537f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # If the lock happens in the middle of host status change, consider 538f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi # the lock has no effect on the host history for that status. 539f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi statuses[(prev_interval_end, t_end)] = status_info 5407cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi statuses[(lock_start, t_end)] = locked_info 541f867da40b1e52ab6ea80b93bf6e3e875d50e0d51Dan Shi prev_interval_end = t_end 5427cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi # Otherwise we are in the case where lock_end < t_start OR 5437cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi # lock_start > t_end, which means the lock doesn't apply. 5447cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi if t_end > prev_interval_end: 5455952fbe77ee7f33a642d19aa1d13ffeae0b92117Michael Liang # This is to avoid logging the same time 5467cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi statuses[(prev_interval_end, t_end)] = status_info 5477cf3d84fda609f6402543bb7e0bf3e3b7f93d539Dan Shi return statuses 5481ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5491ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5501ccb65208c26615096985b5f8b52368f7342a077Dan Shidef get_log_url(hostname, metadata): 5511ccb65208c26615096985b5f8b52368f7342a077Dan Shi """Compile a url to job's debug log from debug string. 5521ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5531ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param hostname: Hostname of the dut. 5541ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param metadata: A dictionary of other metadata, e.g., 5551ccb65208c26615096985b5f8b52368f7342a077Dan Shi {'task_id':123, 'task_name':'Reset'} 5561ccb65208c26615096985b5f8b52368f7342a077Dan Shi @return: Url of the debug log for special task or job url for test job. 5571ccb65208c26615096985b5f8b52368f7342a077Dan Shi """ 5581ccb65208c26615096985b5f8b52368f7342a077Dan Shi log_url = None 5591ccb65208c26615096985b5f8b52368f7342a077Dan Shi if 'task_id' in metadata and 'task_name' in metadata: 5601ccb65208c26615096985b5f8b52368f7342a077Dan Shi log_url = job_history.TASK_URL % {'hostname': hostname, 5611ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'task_id': metadata['task_id'], 5621ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'task_name': metadata['task_name']} 5631ccb65208c26615096985b5f8b52368f7342a077Dan Shi elif 'job_id' in metadata and 'owner' in metadata: 5641ccb65208c26615096985b5f8b52368f7342a077Dan Shi log_url = job_history.JOB_URL % {'hostname': hostname, 5651ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'job_id': metadata['job_id'], 5661ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'owner': metadata['owner']} 5671ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5681ccb65208c26615096985b5f8b52368f7342a077Dan Shi return log_url 5691ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5701ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5711ccb65208c26615096985b5f8b52368f7342a077Dan Shidef build_history(hostname, status_intervals): 5721ccb65208c26615096985b5f8b52368f7342a077Dan Shi """Get host history information from given state intervals. 5731ccb65208c26615096985b5f8b52368f7342a077Dan Shi 5741ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param hostname: Hostname of the dut. 5751ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param status_intervals: A ordered dictionary with 5761ccb65208c26615096985b5f8b52368f7342a077Dan Shi key as (t_start, t_end) and value as (status, metadata) 5771ccb65208c26615096985b5f8b52368f7342a077Dan Shi status = status of the host. e.g. 'Repair Failed' 5781ccb65208c26615096985b5f8b52368f7342a077Dan Shi t_start is the beginning of the interval where the DUT's has 5791ccb65208c26615096985b5f8b52368f7342a077Dan Shi that status 5801ccb65208c26615096985b5f8b52368f7342a077Dan Shi t_end is the end of the interval where the DUT has that 5811ccb65208c26615096985b5f8b52368f7342a077Dan Shi status 5821ccb65208c26615096985b5f8b52368f7342a077Dan Shi metadata: A dictionary of other metadata, e.g., 5831ccb65208c26615096985b5f8b52368f7342a077Dan Shi {'task_id':123, 'task_name':'Reset'} 5841ccb65208c26615096985b5f8b52368f7342a077Dan Shi @return: A list of host history, e.g., 5851ccb65208c26615096985b5f8b52368f7342a077Dan Shi [{'status': 'Resetting' 5861ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'start_time': '2014-08-07 10:02:16', 5871ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'end_time': '2014-08-07 10:03:16', 5881ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'log_url': 'http://autotest/reset-546546/debug', 5891ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'task_id': 546546}, 5901ccb65208c26615096985b5f8b52368f7342a077Dan Shi {'status': 'Running' 5911ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'start_time': '2014-08-07 10:03:18', 5921ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'end_time': '2014-08-07 10:13:00', 5931ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'log_url': 'http://autotest/afe/#tab_id=view_job&object_id=1683', 5941ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'job_id': 1683} 5951ccb65208c26615096985b5f8b52368f7342a077Dan Shi ] 5961ccb65208c26615096985b5f8b52368f7342a077Dan Shi """ 5971ccb65208c26615096985b5f8b52368f7342a077Dan Shi history = [] 5981ccb65208c26615096985b5f8b52368f7342a077Dan Shi for time_interval, status_info in status_intervals.items(): 5991ccb65208c26615096985b5f8b52368f7342a077Dan Shi start_time = time_utils.epoch_time_to_date_string(time_interval[0]) 6001ccb65208c26615096985b5f8b52368f7342a077Dan Shi end_time = time_utils.epoch_time_to_date_string(time_interval[1]) 6011ccb65208c26615096985b5f8b52368f7342a077Dan Shi interval = {'status': status_info['status'], 6021ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'start_time': start_time, 6031ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'end_time': end_time} 6041ccb65208c26615096985b5f8b52368f7342a077Dan Shi interval['log_url'] = get_log_url(hostname, status_info['metadata']) 6051ccb65208c26615096985b5f8b52368f7342a077Dan Shi interval.update(status_info['metadata']) 6061ccb65208c26615096985b5f8b52368f7342a077Dan Shi history.append(interval) 6071ccb65208c26615096985b5f8b52368f7342a077Dan Shi return history 6081ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6091ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6101ccb65208c26615096985b5f8b52368f7342a077Dan Shidef get_status_intervals(history_details): 6111ccb65208c26615096985b5f8b52368f7342a077Dan Shi """Get a list of status interval from history details. 6121ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6131ccb65208c26615096985b5f8b52368f7342a077Dan Shi This is a reverse method of above build_history. Caller gets the history 6141ccb65208c26615096985b5f8b52368f7342a077Dan Shi details from RPC get_host_history, and use this method to get the list of 6151ccb65208c26615096985b5f8b52368f7342a077Dan Shi status interval, which can be used to calculate stats from 6161ccb65208c26615096985b5f8b52368f7342a077Dan Shi host_history_utils.aggregate_hosts. 6171ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6181ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param history_details: A dictionary of host history for each host, e.g., 6191ccb65208c26615096985b5f8b52368f7342a077Dan Shi {'172.22.33.51': [{'status': 'Resetting' 6201ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'start_time': '2014-08-07 10:02:16', 6211ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'end_time': '2014-08-07 10:03:16', 6221ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'log_url': 'http://autotest/reset-546546/debug', 6231ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'task_id': 546546},] 6241ccb65208c26615096985b5f8b52368f7342a077Dan Shi } 6251ccb65208c26615096985b5f8b52368f7342a077Dan Shi @return: A list of dictionaries where keys are tuple (start_time, end_time), 6261ccb65208c26615096985b5f8b52368f7342a077Dan Shi and value is a dictionary containing at least key 'status'. 6271ccb65208c26615096985b5f8b52368f7342a077Dan Shi """ 6281ccb65208c26615096985b5f8b52368f7342a077Dan Shi status_intervals = [] 6291ccb65208c26615096985b5f8b52368f7342a077Dan Shi for host,history in history_details.iteritems(): 6301ccb65208c26615096985b5f8b52368f7342a077Dan Shi intervals = collections.OrderedDict() 6311ccb65208c26615096985b5f8b52368f7342a077Dan Shi for interval in history: 6321ccb65208c26615096985b5f8b52368f7342a077Dan Shi start_time = time_utils.to_epoch_time(interval['start_time']) 6331ccb65208c26615096985b5f8b52368f7342a077Dan Shi end_time = time_utils.to_epoch_time(interval['end_time']) 6341ccb65208c26615096985b5f8b52368f7342a077Dan Shi metadata = copy.deepcopy(interval) 6351ccb65208c26615096985b5f8b52368f7342a077Dan Shi metadata['hostname'] = host 6361ccb65208c26615096985b5f8b52368f7342a077Dan Shi intervals[(start_time, end_time)] = {'status': interval['status'], 6371ccb65208c26615096985b5f8b52368f7342a077Dan Shi 'metadata': metadata} 6381ccb65208c26615096985b5f8b52368f7342a077Dan Shi status_intervals.append(intervals) 6391ccb65208c26615096985b5f8b52368f7342a077Dan Shi return status_intervals 6401ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6411ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6421ccb65208c26615096985b5f8b52368f7342a077Dan Shidef get_machine_utilization_rate(stats): 6431ccb65208c26615096985b5f8b52368f7342a077Dan Shi """Get machine utilization rate from given stats. 6441ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6451ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param stats: A dictionary with a status as key and value is the total 6461ccb65208c26615096985b5f8b52368f7342a077Dan Shi number of seconds spent on the status. 6471ccb65208c26615096985b5f8b52368f7342a077Dan Shi @return: The percentage of time when dut is running test jobs. 6481ccb65208c26615096985b5f8b52368f7342a077Dan Shi """ 6491ccb65208c26615096985b5f8b52368f7342a077Dan Shi not_utilized_status = ['Repairing', 'Repair Failed', 'Ready', 'Verifying'] 6501ccb65208c26615096985b5f8b52368f7342a077Dan Shi excluded_status = ['Locked'] 6511ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time = 0 6521ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time_not_utilized = 0.0 6531ccb65208c26615096985b5f8b52368f7342a077Dan Shi for status, interval in stats.iteritems(): 6541ccb65208c26615096985b5f8b52368f7342a077Dan Shi if status in excluded_status: 6551ccb65208c26615096985b5f8b52368f7342a077Dan Shi continue 6561ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time += interval 6571ccb65208c26615096985b5f8b52368f7342a077Dan Shi if status in not_utilized_status: 6581ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time_not_utilized += interval 6591ccb65208c26615096985b5f8b52368f7342a077Dan Shi if total_time == 0: 6601ccb65208c26615096985b5f8b52368f7342a077Dan Shi # All duts are locked, assume MUR is 0% 6611ccb65208c26615096985b5f8b52368f7342a077Dan Shi return 0 6621ccb65208c26615096985b5f8b52368f7342a077Dan Shi else: 6631ccb65208c26615096985b5f8b52368f7342a077Dan Shi return 1 - total_time_not_utilized/total_time 6641ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6651ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6661ccb65208c26615096985b5f8b52368f7342a077Dan Shidef get_machine_availability_rate(stats): 6671ccb65208c26615096985b5f8b52368f7342a077Dan Shi """Get machine availability rate from given stats. 6681ccb65208c26615096985b5f8b52368f7342a077Dan Shi 6691ccb65208c26615096985b5f8b52368f7342a077Dan Shi @param stats: A dictionary with a status as key and value is the total 6701ccb65208c26615096985b5f8b52368f7342a077Dan Shi number of seconds spent on the status. 6711ccb65208c26615096985b5f8b52368f7342a077Dan Shi @return: The percentage of time when dut is available to run jobs. 6721ccb65208c26615096985b5f8b52368f7342a077Dan Shi """ 6731ccb65208c26615096985b5f8b52368f7342a077Dan Shi not_available_status = ['Repairing', 'Repair Failed', 'Verifying'] 6741ccb65208c26615096985b5f8b52368f7342a077Dan Shi excluded_status = ['Locked'] 6751ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time = 0 6761ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time_not_available = 0.0 6771ccb65208c26615096985b5f8b52368f7342a077Dan Shi for status, interval in stats.iteritems(): 6781ccb65208c26615096985b5f8b52368f7342a077Dan Shi if status in excluded_status: 6791ccb65208c26615096985b5f8b52368f7342a077Dan Shi continue 6801ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time += interval 6811ccb65208c26615096985b5f8b52368f7342a077Dan Shi if status in not_available_status: 6821ccb65208c26615096985b5f8b52368f7342a077Dan Shi total_time_not_available += interval 6831ccb65208c26615096985b5f8b52368f7342a077Dan Shi if total_time == 0: 6841ccb65208c26615096985b5f8b52368f7342a077Dan Shi # All duts are locked, assume MAR is 0% 6851ccb65208c26615096985b5f8b52368f7342a077Dan Shi return 0 6861ccb65208c26615096985b5f8b52368f7342a077Dan Shi else: 6871ccb65208c26615096985b5f8b52368f7342a077Dan Shi return 1 - total_time_not_available/total_time 688