mirror of https://github.com/sysown/proxysql
parent
c623a6b4d9
commit
50b3e3191b
@ -1,9 +0,0 @@
|
||||
{
|
||||
"watches": [
|
||||
{
|
||||
"type": "keyprefix",
|
||||
"prefix": "proxysql/",
|
||||
"handler": "proxysql-consul update"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
proxysql-consul writes table contents read from ProxySQL's admin interface to Consul's key/value store. proxysql-consul uses one key for each table and each key is prefixed with 'proxysql' so that all ProxySQL keys live in their own namespace.
|
||||
|
||||
Example:
|
||||
```
|
||||
key: proxysql/mysql_servers
|
||||
```
|
||||
|
||||
The value written is a JSON with multiple fields:
|
||||
- table - the name of the table.
|
||||
- uuid - unique identifier of the proxysql-consul instance.
|
||||
- rows - content of the table as an array of rows. Each row is an array of string values.
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"table":"mysql_servers",
|
||||
"rows":[
|
||||
[
|
||||
"0",
|
||||
"127.0.0.1",
|
||||
"3306",
|
||||
"ONLINE",
|
||||
"1",
|
||||
"0",
|
||||
"1000",
|
||||
"10"
|
||||
],
|
||||
[
|
||||
"0",
|
||||
"/var/lib/mysql/mysql.sock",
|
||||
"0",
|
||||
"ONLINE",
|
||||
"1",
|
||||
"0",
|
||||
"1000",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"uuid":"aae13abd-2a76-4882-afd3-0b2369e02b07"
|
||||
}
|
||||
```
|
||||
|
||||
The value corresponds to the following mysql_servers dump:
|
||||
```
|
||||
+--------------+---------------------------+------+--------+--------+-------------+-----------------+---------------------+
|
||||
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag |
|
||||
+--------------+---------------------------+------+--------+--------+-------------+-----------------+---------------------+
|
||||
| 0 | 127.0.0.1 | 3306 | ONLINE | 1 | 0 | 1000 | 10 |
|
||||
| 0 | /var/lib/mysql/mysql.sock | 0 | ONLINE | 1 | 0 | 1000 | 0 |
|
||||
+--------------+---------------------------+------+--------+--------+-------------+-----------------+---------------------+
|
||||
```
|
||||
@ -1,217 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Usage:
|
||||
proxysql-consul put <table> [--config-file=<config-file>]
|
||||
proxysql-consul update [--config-file=<config-file>]
|
||||
proxysql-consul (-h | --help)
|
||||
|
||||
Options:
|
||||
-h --help
|
||||
Show this screen.
|
||||
--config-file=<config-file>
|
||||
Path to config file [default: /etc/proxysql-consul.cnf].
|
||||
|
||||
"""
|
||||
|
||||
from docopt import docopt
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import MySQLdb
|
||||
|
||||
|
||||
CFG_PATH = '/etc/proxysql-consul.cnf'
|
||||
CFG_UUID_FILE = 'uuid_file'
|
||||
CFG_UUID = 'uuid'
|
||||
CFG_CONSUL_IFACE = 'consul_iface'
|
||||
CFG_CONSUL_PORT = 'consul_port'
|
||||
CFG_PROXY_IFACE = 'proxysql_admin_iface'
|
||||
CFG_PROXY_PORT = 'proxysql_admin_port'
|
||||
CFG_PROXY_USERNAME = 'proxysql_admin_username'
|
||||
CFG_PROXY_PASSWORD = 'proxysql_admin_password'
|
||||
|
||||
# Proxysql config types to Consul key mapping
|
||||
TABLE_TO_KEY = {
|
||||
'mysql_servers': 'proxysql/mysql_servers',
|
||||
'mysql_query_rules': 'proxysql/mysql_query_rules'
|
||||
}
|
||||
|
||||
SAVE_QUERY_BY_TABLE = {
|
||||
'mysql_servers': 'SAVE MYSQL SERVERS TO MEMORY',
|
||||
'mysql_query_rules': 'SAVE MYSQL QUERY RULES TO MEMORY'
|
||||
}
|
||||
|
||||
LOAD_QUERY_BY_TABLE = {
|
||||
'mysql_servers': 'LOAD MYSQL SERVERS FROM MEMORY',
|
||||
'mysql_query_rules': 'LOAD MYSQL QUERY RULES FROM MEMORY'
|
||||
}
|
||||
|
||||
config = {}
|
||||
|
||||
|
||||
def read_config_from_file():
|
||||
global config
|
||||
with open(CFG_PATH) as config_file:
|
||||
config_data = config_file.read()
|
||||
config = json.loads(config_data)
|
||||
|
||||
# TODO(ipruanche) validate config
|
||||
|
||||
|
||||
def read_uuid_from_file():
|
||||
"""
|
||||
Reads the UUID identifying this script instance from a file store. If the
|
||||
file does not exist creates it and writes a new UUID.
|
||||
|
||||
Puts the UUID in the config dictionary.
|
||||
"""
|
||||
uuid_file_path = config[CFG_UUID_FILE]
|
||||
|
||||
if not os.path.isfile(uuid_file_path):
|
||||
_uuid = str(uuid.uuid4())
|
||||
with open(uuid_file_path, 'w') as uuid_file:
|
||||
uuid_file.write(_uuid)
|
||||
else:
|
||||
with open(uuid_file_path, 'r') as uuid_file:
|
||||
_uuid = uuid_file.read()
|
||||
config[CFG_UUID] = _uuid
|
||||
|
||||
def fetch_proxysql_config(table):
|
||||
admin_connection = MySQLdb.connect(config[CFG_PROXY_IFACE],
|
||||
config[CFG_PROXY_USERNAME],
|
||||
config[CFG_PROXY_PASSWORD],
|
||||
port=config[CFG_PROXY_PORT],
|
||||
db='main')
|
||||
|
||||
# save runtime config to memory so we can read them
|
||||
cursor = admin_connection.cursor()
|
||||
save_query = SAVE_QUERY_BY_TABLE[table]
|
||||
cursor.execute(save_query)
|
||||
cursor.close()
|
||||
|
||||
# read runtime config from memory
|
||||
cursor = admin_connection.cursor()
|
||||
cursor.execute('SELECT * FROM %s' % table)
|
||||
rows = cursor.fetchall()
|
||||
cursor.close()
|
||||
admin_connection.close()
|
||||
return rows
|
||||
|
||||
|
||||
def put_config_to_consul(table):
|
||||
if table not in TABLE_TO_KEY:
|
||||
print 'Unknown config table. Exiting.'
|
||||
exit(1)
|
||||
|
||||
rows = fetch_proxysql_config(table)
|
||||
consul_data = {}
|
||||
consul_data['table'] = table
|
||||
consul_data['rows'] = rows
|
||||
consul_data['uuid'] = config[CFG_UUID]
|
||||
consul_data_json = json.dumps(consul_data)
|
||||
|
||||
key = TABLE_TO_KEY[table]
|
||||
consul_iface = config[CFG_CONSUL_IFACE]
|
||||
consul_port = config[CFG_CONSUL_PORT]
|
||||
|
||||
url = 'http://%s:%s/v1/kv/%s' % (consul_iface, consul_port, key)
|
||||
r = requests.put(url, data=consul_data_json)
|
||||
r.raise_for_status()
|
||||
print 'Configs pushed successfully.'
|
||||
|
||||
|
||||
def build_multivalue_insert(table, rows):
|
||||
# add quotes arround all values to make them strings in the sql query
|
||||
quoted_values = [['"' + x + '"' for x in row] for row in rows]
|
||||
# join each row in a values() group
|
||||
row_join = [','.join(x) for x in quoted_values]
|
||||
query = 'INSERT INTO %s VALUES(%s)' % (table, '),('.join(row_join))
|
||||
return query
|
||||
|
||||
def update_proxysql_runtime_config(table, rows):
|
||||
admin_connection = MySQLdb.connect(config[CFG_PROXY_IFACE],
|
||||
config[CFG_PROXY_USERNAME],
|
||||
config[CFG_PROXY_PASSWORD],
|
||||
port=config[CFG_PROXY_PORT],
|
||||
db='main')
|
||||
|
||||
# clear table
|
||||
cursor = admin_connection.cursor()
|
||||
cursor.execute('DELETE FROM %s' % table)
|
||||
cursor.close()
|
||||
|
||||
# insert values from Consul
|
||||
insert_query = build_multivalue_insert(table, rows)
|
||||
cursor = admin_connection.cursor()
|
||||
cursor.execute(insert_query)
|
||||
cursor.close()
|
||||
|
||||
# commit changes to runtine
|
||||
load_query = LOAD_QUERY_BY_TABLE[table]
|
||||
cursor = admin_connection.cursor()
|
||||
cursor.execute(load_query)
|
||||
cursor.close()
|
||||
|
||||
admin_connection.close()
|
||||
|
||||
|
||||
def update_config():
|
||||
"""
|
||||
Reads all input from stdin that is passed by Consul and extracts the config
|
||||
that was modified.
|
||||
|
||||
Consul is configured to watch all proxysql keys and returns all their values
|
||||
when one key is modified. To determine the value that was actually modified
|
||||
we use the CONSUL_INDEX env var written by Consul. This should match the
|
||||
'ModifyIndex' field of the value that triggered the watch.
|
||||
|
||||
Values are returned as a JSON array. The actual value content is stored in
|
||||
the 'Value' field, base64 encoded.
|
||||
"""
|
||||
if 'CONSUL_INDEX' not in os.environ:
|
||||
print 'Missing consul index on update request'
|
||||
exit(1)
|
||||
consul_index = int(os.environ['CONSUL_INDEX'])
|
||||
consul_data = sys.stdin.read()
|
||||
|
||||
updated_value = None
|
||||
values = json.loads(consul_data)
|
||||
for value in values:
|
||||
if 'ModifyIndex' in value and value['ModifyIndex'] == consul_index:
|
||||
updated_value = value
|
||||
break
|
||||
if not updated_value:
|
||||
print 'Failed to determine updated config from Consul data'
|
||||
exit(1)
|
||||
|
||||
proxysql_config_json = base64.b64decode(updated_value['Value'])
|
||||
proxysql_config = json.loads(proxysql_config_json)
|
||||
table = proxysql_config['table']
|
||||
rows = proxysql_config['rows']
|
||||
_uuid = proxysql_config['uuid']
|
||||
|
||||
if not rows:
|
||||
print 'Empty config set for table: %s.' % table
|
||||
return
|
||||
if _uuid == config[CFG_UUID]:
|
||||
print 'Ignoring self update.'
|
||||
return
|
||||
|
||||
update_proxysql_runtime_config(table, rows)
|
||||
print 'Configs updated successfully.'
|
||||
|
||||
if __name__ == '__main__':
|
||||
arguments = docopt(__doc__, version='proxysql-consul 1.0')
|
||||
read_config_from_file()
|
||||
read_uuid_from_file()
|
||||
|
||||
if arguments['put']:
|
||||
put_config_to_consul(arguments['<table>'])
|
||||
elif arguments['update']:
|
||||
update_config()
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"uuid_file": "/var/lib/proxysql/proxysql-consul.uuid",
|
||||
"consul_iface": "127.0.0.1",
|
||||
"consul_port": "8500",
|
||||
"proxysql_admin_iface": "127.0.0.1",
|
||||
"proxysql_admin_port": 6032,
|
||||
"proxysql_admin_username": "admin",
|
||||
"proxysql_admin_password": "admin"
|
||||
}
|
||||
Loading…
Reference in new issue