]> arthur.barton.de Git - netdata.git/commitdiff
Merge pull request #555 from paulfantom/master
authorCosta Tsaousis <costa@tsaousis.gr>
Wed, 15 Jun 2016 21:22:56 +0000 (00:22 +0300)
committerGitHub <noreply@github.com>
Wed, 15 Jun 2016 21:22:56 +0000 (00:22 +0300)
Python plugin for MySQL

conf.d/Makefile.am
conf.d/python.d.conf
conf.d/python.d/mysql.conf [new file with mode: 0644]
plugins.d/python.d.plugin
python.d/Makefile.am
python.d/README.md
python.d/mysql.chart.py [new file with mode: 0644]

index c8c518fabd244c3edf95b5639afb5612a6e0d1a1..f60faaa570a65af29ec793592c5a10714f6f4319 100644 (file)
@@ -19,7 +19,8 @@ dist_nodeconfig_DATA = \
 
 pythonconfigdir=$(configdir)/python.d
 dist_pythonconfig_DATA = \
-       python.d/example.conf
+       python.d/example.conf \
+       python.d/mysql.conf \
        $(NULL)
 
 
index 4803e893c005484a4d0b0c9bd7755565b8957b89..020ba36cc02189feeff9c00f30d272ee725f0666 100644 (file)
@@ -9,3 +9,4 @@
 # By default python.d.plugin enables all modules stored in python.d
 # Modules can be disabled with setting "module_name = no"
 example=no
+mysql=no
diff --git a/conf.d/python.d/mysql.conf b/conf.d/python.d/mysql.conf
new file mode 100644 (file)
index 0000000..b36c60c
--- /dev/null
@@ -0,0 +1,21 @@
+# Example configuration of mysql.chart.py
+# Indentation is important
+
+#update_every=5
+
+#config=[
+#    {
+#        'name'     : 'local'
+#        'my.cnf'   : '/etc/mysql/my.cnf'
+#    },{
+#        'name'     : 'local_s',
+#        'user'     : 'root',
+#        'password' : 'blablablabla',
+#        'socket'   : '/var/run/mysqld/mysqld.sock'
+#    },{
+#        'name'     : 'remote',
+#        'user'     : 'admin',
+#        'password' : 'bla',
+#        'host'     : 'example.org',
+#        'port'     : '3306'
+#    }]
index bb7468d8f6648fdd1fba6a643d038a76ace3a5d3..07426dce106a097deea00032e7dc8e57606afa80 100755 (executable)
@@ -27,12 +27,10 @@ class PythonCharts(object):
         self.configs = modules_configs
 
         # load modules
-        self.modules = self._load_modules(modules_path,modules)
+        modules = self._load_modules(modules_path,modules)
         
         # check if loaded modules are on disabled modules list
-        for mod in self.modules:
-            if mod.__name__ in modules_disabled:
-                self.modules.remove(mod)
+        self.modules = [ m for m in modules if m.__name__ not in modules_disabled ]
         
         # load configuration files
         self._load_configs()
@@ -102,7 +100,10 @@ class PythonCharts(object):
             if os.path.isfile(configfile):
                 debug("loading chart options: '" + configfile + "'")
                 for k, v in read_config(configfile).items():
-                    setattr(m, k, v)
+                    try:
+                        setattr(m, k, v)
+                    except AttributeError:
+                        self._disable_module(m,"misbehaving having bad configuration")
             else:
                 debug(m.__name__ +
                       ": configuration file '" +
@@ -123,9 +124,9 @@ class PythonCharts(object):
                   "() function. Disabling it.")
         elif reason[:7] == "failed ":
             debug("chart '" +
-                  mod.__name__ +
-                  reason[3:] +
-                  "() function. reports failure.")
+                  mod.__name__ + "' " +
+                  reason[7:] +
+                  "() function reports failure.")
         elif reason[:13] == "configuration":
             debug(mod.__name__,
                   "configuration file '" +
@@ -272,6 +273,8 @@ def parse_cmdline(directory, *commands):
                 pass
 
     debug("started from", commands[0], "with options:", *commands[1:])
+    if len(mods) == 0 and DEBUG_FLAG is False:
+        interval = None
 
     return {'interval': interval,
             'modules': mods}
@@ -292,9 +295,8 @@ def run():
     # read configuration file
     disabled = []
     if config_dir[-1] != '/':
-        configfile = config_dir + '/' + "python.d.conf"
-    else:
-        configfile = config_dir + "python.d.conf"
+        config_dir += '/'
+    configfile = config_dir + "python.d.conf"
 
     try:
         conf = read_config(configfile)
index 8c84254b8f93af846b8367939938e9d56b782add..fa39c8fe58163d7683aabff7a08ce3844f7d9f5c 100644 (file)
@@ -2,6 +2,7 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
 
 dist_python_SCRIPTS = \
        example.chart.py \
+       mysql.chart.py \
        $(NULL)
 
 dist_python_DATA = \
@@ -10,4 +11,6 @@ dist_python_DATA = \
 
 pythonmodulesdir=$(pythondir)/python_modules
 dist_pythonmodules_DATA = \
+       python_modules/__init__.py
        $(NULL)
+
index b4407977103da210cc6813b0fafda326d44c43a5..0a8e032e8f2d7db6ea7820e45476fe5edbea5df4 100644 (file)
@@ -1,6 +1,99 @@
+# Disclaimer
+
+Currently every plugin must be written in python3.
+All third party libraries should be installed system-wide or in `python_modules` directory.
+Also plugins support changing their data collection frequency by setting `update_every` variable in their configuration file.
+
+
 The following python.d plugins are supported:
 
-# None
+# mysql
+
+The plugin will monitor one or more mysql servers
+
+**Requirements:**
+ * python module [MySQLdb](https://github.com/PyMySQL/mysqlclient-python) (faster) or [PyMySQL](https://github.com/PyMySQL/PyMySQL) (slower)
+
+It will produce following charts (if data is available):
+
+1. **Bandwidth** in kbps
+ * in
+ * out
+
+2. **Queries** in queries/sec
+ * queries
+ * questions
+ * slow queries
+
+3. **Operations** in operations/sec
+ * opened tables
+ * flush
+ * commit
+ * delete
+ * prepare
+ * read first
+ * read key
+ * read next
+ * read prev
+ * read random
+ * read random next
+ * rollback
+ * save point
+ * update
+ * write
+
+4. **Table Locks** in locks/sec
+ * immediate
+ * waited
+
+5. **Select Issues** in issues/sec
+ * full join
+ * full range join
+ * range
+ * range check
+ * scan
+
+6. **Sort Issues** in issues/sec
+ * merge passes
+ * range
+ * scan
+
+### configuration
+
+You can provide, per server, the following:
+
+1. a name, anything you like, but keep it short
+2. username which have access to database (deafults to 'root')
+3. password (defaults to none)
+4. mysql my.cnf configuration file
+5. mysql socket (optional)
+6. mysql host (ip or hostname)
+7. mysql port (defaults to 3306)
+
+Here is an example for 3 servers updating data every 10 seconds
+
+```js
+update_every = 10
+
+config=[
+    {
+        'name'     : 'local',
+        'my.cnf'   : '/etc/mysql/my.cnf'
+    },{
+       'name'     : 'local_2',
+        'user'     : 'root',
+        'password' : 'blablablabla',
+        'socket'   : '/var/run/mysqld/mysqld.sock'
+    },{
+        'name'     : 'remote',
+        'user'     : 'admin',
+        'password' : 'bla',
+        'host'     : 'example.org',
+        'port'     : '9000'
+    }]
+```
+
+If no configuration is given, the plugin will attempt to connect to mysql server via unix socket at `/var/run/mysqld/mysqld.sock` without password and username `root`
 
-Currently we don't have any python plugins, but we are counting on you to add some new ones.
+---
 
diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py
new file mode 100644 (file)
index 0000000..1660013
--- /dev/null
@@ -0,0 +1,448 @@
+#!/usr/bin/python3 -u
+
+NAME = "mysql.chart.py"
+from sys import stderr
+try:
+    import MySQLdb
+    stderr.write(NAME + ": using MySQLdb")
+    # https://github.com/PyMySQL/mysqlclient-python
+except ImportError:
+    try:
+        import pymysql as MySQLdb
+        # https://github.com/PyMySQL/PyMySQL
+        stderr.write(NAME + ": using pymysql")
+    except ImportError:
+        stderr.write(NAME + ": You need to install PyMySQL module to use mysql.chart.py plugin\n")
+
+config = [
+    {
+        'name'     : 'local',
+        'user'     : 'root',
+        'password' : '',
+        'socket'   : '/var/run/mysqld/mysqld.sock'
+    }
+]
+
+update_every = 3
+priority = 90000
+
+#query = "SHOW GLOBAL STATUS WHERE value REGEX '^[0-9]'"
+QUERY = "SHOW GLOBAL STATUS"
+ORDER = ['net', 
+         'queries', 
+         'handlers', 
+         'table_locks', 
+         'join_issues', 
+         'sort_issues', 
+         'tmp', 
+         'connections', 
+         'binlog_cache', 
+         'threads', 
+         'thread_cache_misses', 
+         'innodb_io', 
+         'innodb_io_ops',
+         'innodb_io_pending_ops',
+         'innodb_log',
+         'innodb_os_log',
+         'innodb_os_log_io',
+         'innodb_cur_row_lock',
+         'innodb_rows',
+         'innodb_buffer_pool_pages',
+         'innodb_buffer_pool_bytes',
+         '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',
+         'binlog_stmt_cache',
+         'connection_errors']
+
+CHARTS = {
+    'net' : (
+        "'' 'mysql Bandwidth' 'kilobits/s' bandwidth mysql.net area",
+        (
+            ("Bytes_received", "in incremental 8 1024"),
+            ("Bytes_sent",     "out incremental -8 1024")
+        )),
+    'queries' : (
+        "'' 'mysql Queries' 'queries/s' queries mysql.queries line",
+        (
+            ("Queries",      "queries incremental 1 1"),
+            ("Questions",    "questions incremental 1 1"),
+            ("Slow_queries", "slow_queries incremental -1 1")
+        )),
+    'handlers' : (
+        "'' 'mysql Handlers' 'handlers/s' handlers mysql.handlers line",
+        (
+            ("Handler_commit", "commit incremental 1 1"),
+            ("Handler_delete", "delete incremental 1 1"),
+            ("Handler_prepare", "prepare incremental 1 1"),
+            ("Handler_read_first", "read_first incremental 1 1"),
+            ("Handler_read_key", "read_key incremental 1 1"),
+            ("Handler_read_next", "read_next incremental 1 1"),
+            ("Handler_read_prev", "read_prev incremental 1 1"),
+            ("Handler_read_rnd", "read_rnd incremental 1 1"),
+            ("Handler_read_rnd_next", "read_rnd_next incremental 1 1"),
+            ("Handler_rollback", "rollback incremental 1 1"),
+            ("Handler_savepoint", "savepoint incremental 1 1"),
+            ("Handler_savepoint_rollback", "savepoint_rollback incremental 1 1"),
+            ("Handler_update", "update incremental 1 1"),
+            ("Handler_write", "write incremental 1 1")
+        )),
+    'table_locks' : (
+        "'' 'mysql Tables Locks' 'locks/s' locks mysql.table_locks line",
+        (
+            ("Table_locks_immediate", "immediate incremental 1 1"),
+            ("Table_locks_waited", "waited incremental -1 1")
+        )),
+    'join_issues' : (
+        "'' 'mysql Select Join Issues' 'joins/s' issues mysql.join_issues line",
+        (
+            ("Select_full_join", "full_join incremental 1 1"),
+            ("Select_full_range_join", "full_range_join incremental 1 1"),
+            ("Select_range", "range incremental 1 1"),
+            ("Select_range_check", "range_check incremental 1 1"),
+            ("Select_scan", "scan incremental 1 1"),
+        )),
+    'sort_issues' : (
+        "'' 'mysql Sort Issues' 'issues/s' issues mysql.sort.issues line",
+        (
+            ("Sort_merge_passes", "merge_passes incremental 1 1"),
+            ("Sort_range", "range incremental 1 1"),
+            ("Sort_scan", "scan incremental 1 1"),
+        )),
+    'tmp' : (
+        "'' 'mysql Tmp Operations' 'counter' temporaries mysql.tmp line",
+        (
+            ("Created_tmp_disk_tables", "disk_tables incremental 1 1"),
+            ("Created_tmp_files", "files incremental 1 1"),
+            ("Created_tmp_tables", "tables incremental 1 1"),
+        )),
+    'connections' : (
+        "'' 'mysql Connections' 'connections/s' connections mysql.connections line",
+        (
+            ("Connections", "all incremental 1 1"),
+            ("Aborted_connects", "aborted incremental 1 1"),
+        )),
+    'binlog_cache' : (
+        "'' 'mysql Binlog Cache' 'transactions/s' binlog mysql.binlog_cache line",
+        (
+            ("Binlog_cache_disk_use", "disk incremental 1 1"),
+            ("Binlog_cache_use", "all incremental 1 1"),
+        )),
+    'threads' : (
+        "'' 'mysql Threads' 'threads' threads mysql.threads line",
+        (
+            ("Threads_connected", "connected absolute 1 1"),
+            ("Threads_created", "created incremental 1 1"),
+            ("Threads_cached", "cached absolute -1 1"),
+            ("Threads_running", "running absolute 1 1"),
+        )),
+    'thread_cache_misses' : (
+        "'' 'mysql Threads Cache Misses' 'misses' threads mysql.thread_cache_misses area",
+        (
+            ("Thread_cache_misses", "misses misses absolute 1 100"),
+        )),
+    'innodb_io' : (
+        "'' 'mysql InnoDB I/O Bandwidth' 'kilobytes/s' innodb mysql.innodb_io area",
+        (
+            ("Innodb_data_read", "read incremental 1 1024"),
+            ("Innodb_data_written", "write incremental -1 1024"),
+        )),
+    'innodb_io_ops' : (
+        "'' 'mysql InnoDB I/O Operations' 'operations/s' innodb mysql.innodb_io_ops line",
+        (
+            ("Innodb_data_reads", "reads incremental 1 1"),
+            ("Innodb_data_writes", "writes incremental -1 1"),
+            ("Innodb_data_fsyncs", "fsyncs incremental 1 1"),
+        )),
+    'innodb_io_pending_ops' : (
+        "'' 'mysql InnoDB Pending I/O Operations' 'operations' innodb mysql.innodb_io_pending_ops line",
+        (
+            ("Innodb_data_pending_reads", "reads absolute 1 1"),
+            ("Innodb_data_pending_writes", "writes absolute -1 1"),
+            ("Innodb_data_pending_fsyncs", "fsyncs absolute 1 1"),
+        )),
+    'innodb_log' : (
+        "'' 'mysql InnoDB Log Operations' 'operations/s' innodb mysql.innodb_log line",
+        (
+            ("Innodb_log_waits", "waits incremental 1 1"),
+            ("Innodb_log_write_requests", "write_requests incremental -1 1"),
+            ("Innodb_log_writes", "incremental -1 1"),
+        )),
+    'innodb_os_log' : (
+        "'' 'mysql InnoDB OS Log Operations' 'operations' innodb mysql.innodb_os_log line",
+        (
+            ("Innodb_os_log_fsyncs", "fsyncs incremental 1 1"),
+            ("Innodb_os_log_pending_fsyncs", "pending_fsyncs absolute 1 1"),
+            ("Innodb_os_log_pending_writes", "pending_writes absolute -1 1"),
+        )),
+    'innodb_os_log_io' : (
+        "'' 'mysql InnoDB OS Log Bandwidth' 'kilobytes/s' innodb mysql.innodb_os_log_io area",
+        (
+            ("Innodb_os_log_written", "write incremental -1 1024"),
+        )),
+    'innodb_cur_row_lock' : (
+        "'' 'mysql InnoDB Current Row Locks' 'operations' innodb mysql.innodb_cur_row_lock area",
+        (
+            ("Innodb_row_lock_current_waits", "current_waits absolute 1 1"),
+        )),
+    'innodb_rows' : (
+        "'' 'mysql InnoDB Row Operations' 'operations/s' innodb mysql.innodb_rows area",
+        (
+            ("Innodb_rows_inserted", "read incremental 1 1"),
+            ("Innodb_rows_read", "deleted incremental -1 1"),
+            ("Innodb_rows_updated", "inserted incremental 1 1"),
+            ("Innodb_rows_deleted", "updated incremental -1 1"),
+        )),
+    'innodb_buffer_pool_pages' : (
+        "'' 'mysql InnoDB Buffer Pool Pages' 'pages' innodb mysql.innodb_buffer_pool_pages line",
+        (
+            ("Innodb_buffer_pool_pages_data", "data absolute 1 1"),
+            ("Innodb_buffer_pool_pages_dirty", "dirty absolute -1 1"),
+            ("Innodb_buffer_pool_pages_free", "free absolute 1 1"),
+            ("Innodb_buffer_pool_pages_flushed", "flushed incremental -1 1"),
+            ("Innodb_buffer_pool_pages_misc", "misc absolute -1 1"),
+            ("Innodb_buffer_pool_pages_total", "total absolute 1 1"),
+        )),
+    'innodb_buffer_pool_bytes' : (
+        "'' 'mysql InnoDB Buffer Pool Bytes' 'MB' innodb mysql.innodb_buffer_pool_bytes area",
+        (
+            ("Innodb_buffer_pool_bytes_data", "data absolute 1"),
+            ("Innodb_buffer_pool_bytes_dirty", "dirty absolute -1"),
+        )),
+    'innodb_buffer_pool_read_ahead' : (
+        "'' 'mysql InnoDB Buffer Pool Read Ahead' 'operations/s' innodb mysql.innodb_buffer_pool_read_ahead area",
+        (
+            ("Innodb_buffer_pool_read_ahead", "all incremental 1 1"),
+            ("Innodb_buffer_pool_read_ahead_evicted", "evicted incremental -1 1"),
+            ("Innodb_buffer_pool_read_ahead_rnd", "random incremental 1 1"),
+        )),
+    'innodb_buffer_pool_reqs' : (
+        "'' 'mysql InnoDB Buffer Pool Requests' 'requests/s' innodb mysql.innodb_buffer_pool_reqs area",
+        (
+            ("Innodb_buffer_pool_read_requests", "reads incremental 1 1"),
+            ("Innodb_buffer_pool_write_requests", "writes incremental -1 1"),
+        )),
+    'innodb_buffer_pool_ops' : (
+        "'' 'mysql InnoDB Buffer Pool Operations' 'operations/s' innodb mysql.innodb_buffer_pool_ops area",
+        (
+            ("Innodb_buffer_pool_reads", "'disk reads' incremental 1 1"),
+            ("Innodb_buffer_pool_wait_free", "'wait free' incremental -1 1"),
+        )),
+    'qcache_ops' : (
+        "'' 'mysql QCache Operations' 'queries/s' qcache mysql.qcache_ops line",
+        (
+            ("Qcache_hits", "hits incremental 1 1"),
+            ("Qcache_lowmem_prunes", "'lowmem prunes' incremental -1 1"),
+            ("Qcache_inserts", "inserts incremental 1 1"),
+            ("Qcache_not_cached", "'not cached' incremental -1 1"),
+        )),
+    'qcache' : (
+        "'' 'mysql QCache Queries in Cache' 'queries' qcache mysql.qcache line",
+        (
+            ("Qcache_queries_in_cache", "queries absolute 1 1"),
+        )),
+    'qcache_freemem' : (
+        "'' 'mysql QCache Free Memory' 'MB' qcache mysql.qcache_freemem area",
+        (
+            ("Qcache_free_memory", "free absolute 1"),
+        )),
+    'qcache_memblocks' : (
+        "'' 'mysql QCache Memory Blocks' 'blocks' qcache mysql.qcache_memblocks line",
+        (
+            ("Qcache_free_blocks", "free absolute 1"),
+            ("Qcache_total_blocks", "total absolute 1 1"),
+        )),
+    'key_blocks' : (
+        "'' 'mysql MyISAM Key Cache Blocks' 'blocks' myisam mysql.key_blocks line",
+        (
+            ("Key_blocks_unused", "unused absolute 1 1"),
+            ("Key_blocks_used", "used absolute -1 1"),
+            ("Key_blocks_not_flushed", "'not flushed' absolute 1 1"),
+        )),
+    'key_requests' : (
+        "'' 'mysql MyISAM Key Cache Requests' 'requests/s' myisam mysql.key_requests area",
+        (
+            ("Key_read_requests", "reads incremental 1 1"),
+            ("Key_write_requests", "writes incremental -1 1"),
+        )),
+    'key_disk_ops' : (
+        "'' 'mysql MyISAM Key Cache Disk Operations' 'operations/s' myisam mysql.key_disk_ops area",
+        (
+            ("Key_reads", "reads incremental 1 1"),
+            ("Key_writes", "writes incremental -1 1"),
+        )),
+    'files' : (
+        "'' 'mysql Open Files' 'files' files mysql.files line",
+        (
+            ("Open_files", "files absolute 1 1"),
+        )),
+    'files_rate' : (
+        "'' 'mysql Opened Files Rate' 'files/s' files mysql.files_rate line",
+        (
+            ("Opened_files", "files incremental 1 1"),
+        )),
+    'binlog_stmt_cache' : (
+        "'' 'mysql Binlog Statement Cache' 'statements/s' binlog mysql.binlog_stmt_cache line",
+        (
+            ("Binlog_stmt_cache_disk_use", "disk incremental 1 1"),
+            ("Binlog_stmt_cache_use", "all incremental 1 1"),
+        )),
+    'connection_errors' : (
+        "'' 'mysql Connection Errors' 'connections/s' connections mysql.connection_errors line",
+        (
+            ("Connection_errors_accept", "accept incremental 1 1"),
+            ("Connection_errors_internal", "internal incremental 1 1"),
+            ("Connection_errors_max_connections", "max incremental 1 1"),
+            ("Connection_errors_peer_address", "peer_addr incremental 1 1"),
+            ("Connection_errors_select", "select incremental 1 1"),
+            ("Connection_errors_tcpwrap", "tcpwrap incremental 1 1")
+        ))
+}
+mysql_def = {}
+valid = []
+connections = {}
+
+def get_data(config):
+    global connections
+    try:
+        cnx = connections[config['name']]
+    except KeyError as e:
+        stderr.write(NAME + ": reconnecting\n")
+        cnx = MySQLdb.connect(user=config['user'],
+                              passwd=config['password'],
+                              read_default_file=config['my.cnf'],
+                              unix_socket=config['socket'],
+                              host=config['host'],
+                              port=config['port'],
+                              connect_timeout=int(update_every))
+        connections[config['name']] = cnx
+
+    try:
+        with cnx.cursor() as cursor:
+            cursor.execute(QUERY)
+            raw_data = cursor.fetchall()
+    except Exception as e:
+        stderr.write(NAME + ": cannot execute query." + str(e) + "\n")
+        cnx.close()
+        del connections[config['name']]
+        return None
+    
+    return dict(raw_data)
+
+
+def check():
+    # TODO what are the default credentials
+    global valid, config
+    if type(config) is str:
+        from json import loads
+        cfg = loads(config.replace("'",'"').replace('\n',' '))
+        config = cfg
+    for i in range(len(config)):
+        if 'name' not in config[i]:
+            config[i]['name'] = "srv_"+str(i)
+        if 'user' not in config[i]:
+            config[i]['user'] = 'root'
+        if 'password' not in config[i]:
+            config[i]['password'] = ''
+        if 'my.cnf' in config[i]:
+            config[i]['socket'] = ''
+            config[i]['host'] = ''
+            config[i]['port'] = 0
+        elif 'socket' in config[i]:
+            config[i]['my.cnf'] = ''
+            config[i]['host'] = ''
+            config[i]['port'] = 0
+        elif 'host' in config[i]:
+            config[i]['my.cnf'] = ''
+            config[i]['socket'] = ''
+            if 'port' in config[i]:
+                config[i]['port'] = int(config[i]['port'])
+            else:
+                config[i]['port'] = 3306
+
+    for srv in config:
+        try:
+            cnx = MySQLdb.connect(user=srv['user'],
+                                  passwd=srv['password'],
+                                  read_default_file=srv['my.cnf'],
+                                  unix_socket=srv['socket'],
+                                  host=srv['host'],
+                                  port=srv['port'],
+                                  connect_timeout=int(update_every))
+            cnx.close()
+        except Exception as e:
+            stderr.write(NAME + " has problem connecting to server: "+str(e).replace("\n"," ")+"\n")
+            config.remove(srv)
+
+    if len(config) == 0:
+        return False
+    return True
+
+
+def create():
+    global config, mysql_def
+    for name in ORDER:
+        mysql_def[name] = []
+        for line in CHARTS[name][1]:
+            mysql_def[name].append(line[0])
+
+    idx = 0
+    for srv in config:
+        data = get_data(srv)
+        for name in ORDER:
+            header = "CHART mysql_" + \
+                     str(srv['name']) + "." + \
+                     name + " " + \
+                     CHARTS[name][0] + " " + \
+                     str(priority + idx) + " " + \
+                     str(update_every)
+            content = ""
+            # check if server has this datapoint
+            for line in CHARTS[name][1]:
+                if line[0] in data:
+                     content += "DIMENSION " + line[0] + " " + line[1] + "\n"
+            if len(content) > 0:
+                print(header)
+                print(content)
+                idx += 1
+
+    if idx == 0:
+        return False
+    return True
+
+
+def update(interval):
+    global config
+    for srv in config:
+        data = get_data(srv)
+        if data is None:
+            config.remove(srv)
+            # TODO notify user about problems with server
+            continue
+        try:
+            data['Thread cache misses'] = int( int(data['Threads_created']) * 10000 / int(data['Connections']))
+        except Exception:
+            pass
+        for chart, dimensions in mysql_def.items():
+            header = "BEGIN mysql_" + str(srv['name']) + "." + chart + " " + str(interval) + '\n'
+            lines = ""
+            for d in dimensions:
+                try:
+                    lines += "SET " + d + " = " + data[d] + '\n'
+                except KeyError:
+                    pass
+            if len(lines) > 0:
+                print(header + lines + "END")
+            
+    if len(config) == 0:
+        return False 
+    return True