X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=python.d%2Fmysql.chart.py;h=0e3a032998c2535f10a0d46a08408f0519254f09;hb=6b4ed885fc3e86ed009a8ea877f8a8ef3fc6610a;hp=7c3931acf75d1721fcba11fbf406a3dbb1e631ed;hpb=65791a0dab4e0a88ffa46385d6c8afbd1e968158;p=netdata.git diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py index 7c3931ac..0e3a0329 100644 --- a/python.d/mysql.chart.py +++ b/python.d/mysql.chart.py @@ -40,6 +40,7 @@ retries = 60 # query executed on MySQL server QUERY = "SHOW GLOBAL STATUS;" +QUERY_SLAVE = "SHOW SLAVE STATUS;" ORDER = ['net', 'queries', @@ -55,7 +56,7 @@ ORDER = ['net', 'innodb_buffer_pool_read_ahead', 'innodb_buffer_pool_reqs', 'innodb_buffer_pool_ops', 'qcache_ops', 'qcache', 'qcache_freemem', 'qcache_memblocks', 'key_blocks', 'key_requests', 'key_disk_ops', - 'files', 'files_rate'] + 'files', 'files_rate', 'slave_behind', 'slave_status'] CHARTS = { 'net': { @@ -298,8 +299,18 @@ CHARTS = { ["Connection_errors_peer_address", "peer_addr", "incremental"], ["Connection_errors_select", "select", "incremental"], ["Connection_errors_tcpwrap", "tcpwrap", "incremental"] + ]}, + 'slave_behind': { + 'options': [None, 'Slave Behind Seconds', 'seconds', 'slave', 'mysql.slave_behind', 'line'], + 'lines': [ + ["slave_behind", "seconds", "absolute"] + ]}, + 'slave_status': { + 'options': [None, 'Slave Status', 'status', 'slave', 'mysql.slave_status', 'line'], + 'lines': [ + ["slave_sql", "sql_running", "absolute"], + ["slave_io", "io_running", "absolute"] ]} - } @@ -310,6 +321,7 @@ class Service(SimpleService): self.order = ORDER self.definitions = CHARTS self.connection = None + self.do_slave = -1 def _parse_config(self, configuration): """ @@ -317,40 +329,29 @@ class Service(SimpleService): :param configuration: dict :return: dict """ + parameters = {} if self.name is None: self.name = 'local' - if 'user' not in configuration: - self.configuration['user'] = 'root' - if 'pass' not in configuration: - self.configuration['pass'] = '' + if 'user' in configuration: + parameters['user'] = self.configuration['user'] + if 'pass' in configuration: + parameters['passwd'] = self.configuration['pass'] if 'my.cnf' in configuration: - self.configuration['socket'] = '' - self.configuration['host'] = '' - self.configuration['port'] = 0 + parameters['read_default_file'] = self.configuration['my.cnf'] elif 'socket' in configuration: - self.configuration['my.cnf'] = '' - self.configuration['host'] = '' - self.configuration['port'] = 0 + parameters['unix_socket'] = self.configuration['socket'] elif 'host' in configuration: - self.configuration['my.cnf'] = '' - self.configuration['socket'] = '' + parameters['host'] = self.configuration['host'] if 'port' in configuration: - self.configuration['port'] = int(configuration['port']) - else: - self.configuration['port'] = 3306 + parameters['port'] = int(self.configuration['port']) + self.connection_parameters = parameters def _connect(self): """ Try to connect to MySQL server """ try: - self.connection = MySQLdb.connect(user=self.configuration['user'], - passwd=self.configuration['pass'], - read_default_file=self.configuration['my.cnf'], - unix_socket=self.configuration['socket'], - host=self.configuration['host'], - port=self.configuration['port'], - connect_timeout=self.update_every) + self.connection = MySQLdb.connect(connect_timeout=self.update_every, **self.connection_parameters) except MySQLdb.OperationalError as e: self.error("Cannot establish connection to MySQL.") self.debug(str(e)) @@ -359,6 +360,67 @@ class Service(SimpleService): self.error("problem connecting to server:", e) raise RuntimeError + def _get_data_slave(self): + """ + Get slave raw data from MySQL server + :return: dict + """ + if self.connection is None: + try: + self._connect() + except RuntimeError: + return None + + slave_data = None + slave_raw_data = None + try: + cursor = self.connection.cursor() + if cursor.execute(QUERY_SLAVE): + slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) + + except MySQLdb.OperationalError as e: + self.debug("Reconnecting for query", QUERY_SLAVE, ":", str(e)) + try: + self._connect() + cursor = self.connection.cursor() + if cursor.execute(QUERY_SLAVE): + slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) + except Exception as e: + self.error("retried, but cannot execute query", QUERY_SLAVE, ":", str(e)) + self.connection.close() + self.connection = None + return None + + except Exception as e: + self.error("cannot execute query", QUERY_SLAVE, ":", str(e)) + self.connection.close() + self.connection = None + return None + + if slave_raw_data is not None: + slave_data = { + 'slave_behind': None, + 'slave_sql': None, + 'slave_io': None + } + + try: + slave_data['slave_behind'] = int(slave_raw_data.setdefault('Seconds_Behind_Master', -1)) + except: + slave_data['slave_behind'] = None + + try: + slave_data['slave_sql'] = 1 if slave_raw_data.get('Slave_SQL_Running') == 'Yes' else -1 + except: + slave_data['slave_sql'] = None + + try: + slave_data['slave_io'] = 1 if slave_raw_data.get('Slave_IO_Running') == 'Yes' else -1 + except: + slave_data['slave_io'] = None + + return slave_data + def _get_data(self): """ Get raw data from MySQL server @@ -373,23 +435,47 @@ class Service(SimpleService): cursor = self.connection.cursor() cursor.execute(QUERY) raw_data = cursor.fetchall() + except MySQLdb.OperationalError as e: - self.debug("Reconnecting due to", str(e)) - self._connect() - cursor = self.connection.cursor() - cursor.execute(QUERY) - raw_data = cursor.fetchall() + self.debug("Reconnecting for query", QUERY, ":", str(e)) + try: + self._connect() + cursor = self.connection.cursor() + cursor.execute(QUERY) + raw_data = cursor.fetchall() + except Exception as e: + self.error("retried, but cannot execute query", QUERY, ":", str(e)) + self.connection.close() + self.connection = None + return None + except Exception as e: - self.error("cannot execute query.", e) + self.error("cannot execute query", QUERY, ":", str(e)) self.connection.close() self.connection = None return None data = dict(raw_data) + + # check for slave data + # the first time is -1 (so we do it) + # then it is set to 1 or 0 and we keep it like that + if self.do_slave != 0: + slave_data = self._get_data_slave() + if slave_data is not None: + data.update(slave_data) + if self.do_slave == -1: + self.do_slave = 1 + else: + if self.do_slave == -1: + self.error("replication metrics will be disabled - please allow netdata to collect them.") + self.do_slave = 0 + + # do calculations try: - data["Thread_cache_misses"] = int(data["Threads_created"] * 10000 / float(data["Connections"])) + data["Thread_cache_misses"] = round(float(data["Threads_created"]) / float(data["Connections"]) * 10000) except: - data["Thread_cache_misses"] = 0 + data["Thread_cache_misses"] = None return data