1#!/usr/bin/python 2 3# Copyright 2016 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Queries a MySQL database and emits status metrics to Monarch. 8 9Note: confusingly, 'Innodb_buffer_pool_reads' is actually the cache-misses, not 10the number of reads to the buffer pool. 'Innodb_buffer_pool_read_requests' 11corresponds to the number of reads the the buffer pool. 12""" 13import logging 14import sys 15 16import MySQLdb 17import time 18 19import common 20 21from autotest_lib.client.common_lib import global_config 22from autotest_lib.client.common_lib import utils 23 24try: 25 from chromite.lib import metrics 26 from chromite.lib import ts_mon_config 27except ImportError: 28 metrics = utils.metrics_mock 29 ts_mon_config = utils.metrics_mock 30 31 32AT_DIR='/usr/local/autotest' 33DEFAULT_USER = global_config.global_config.get_config_value( 34 'CROS', 'db_backup_user', type=str, default='') 35DEFAULT_PASSWD = global_config.global_config.get_config_value( 36 'CROS', 'db_backup_password', type=str, default='') 37LOOP_INTERVAL = 60 38EMITTED_STATUSES_COUNTERS = [ 39 'bytes_received', 40 'bytes_sent', 41 'connections', 42 'Innodb_buffer_pool_read_requests', 43 'Innodb_buffer_pool_reads', 44 'questions', 45 'slow_queries', 46 'threads_created', 47] 48 49EMITTED_STATUS_GAUGES = ['threads_running', 'threads_connected'] 50 51 52def main(): 53 """Sets up ts_mon and repeatedly queries MySQL stats""" 54 logging.basicConfig(stream=sys.stdout, level=logging.INFO) 55 db = MySQLdb.connect('localhost', DEFAULT_USER, DEFAULT_PASSWD) 56 cursor = db.cursor() 57 58 with ts_mon_config.SetupTsMonGlobalState('mysql_stats', indirect=True): 59 QueryLoop(cursor) 60 61 62def QueryLoop(cursor): 63 """Queries and emits metrics every LOOP_INTERVAL seconds. 64 65 @param cursor: The mysql command line. 66 """ 67 # Get the baselines for cumulative metrics. Otherwise the windowed rate at 68 # the very beginning will be extremely high as it shoots up from 0 to its 69 # current value. 70 baselines = dict((s, GetStatus(cursor, s)) 71 for s in EMITTED_STATUSES_COUNTERS) 72 73 while True: 74 now = time.time() 75 QueryAndEmit(baselines, cursor) 76 time_spent = time.time() - now 77 sleep_duration = LOOP_INTERVAL - time_spent 78 time.sleep(max(0, sleep_duration)) 79 80 81def GetStatus(cursor, s): 82 """Get the status variable from database. 83 84 @param cursor: MySQLdb cursor to query with. 85 @param s: Name of the status variable. 86 @returns The mysql query result. 87 """ 88 cursor.execute('SHOW GLOBAL STATUS LIKE "%s";' % s) 89 output = cursor.fetchone()[1] 90 if not output: 91 logging.error('Cannot find any global status like %s', s) 92 return int(output) 93 94 95def QueryAndEmit(baselines, cursor): 96 """Queries MySQL for important stats and emits Monarch metrics 97 98 @param baselines: A dict containing the initial values for the cumulative 99 metrics. 100 @param cursor: The mysql command line. 101 """ 102 103 for status in EMITTED_STATUSES_COUNTERS: 104 delta = GetStatus(cursor, status) - baselines[status] 105 metric_name = 'chromeos/autotest/afe_db/%s' % status.lower() 106 metrics.Counter(metric_name).set(delta) 107 108 for status in EMITTED_STATUS_GAUGES: 109 metric_name = 'chromeos/autotest/afe_db/%s' % status.lower() 110 metrics.Gauge(metric_name).set(GetStatus(cursor, status)) 111 112 pages_free = GetStatus(cursor, 'Innodb_buffer_pool_pages_free') 113 pages_total = GetStatus(cursor, 'Innodb_buffer_pool_pages_total') 114 115 metrics.Gauge('chromeos/autotest/afe_db/buffer_pool_pages').set( 116 pages_free, fields={'used': False}) 117 118 metrics.Gauge('chromeos/autotest/afe_db/buffer_pool_pages').set( 119 pages_total - pages_free, fields={'used': True}) 120 121 122if __name__ == '__main__': 123 main() 124