From 0be3d3f2cd471b861b8e1a8f9230a774b271e775 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 10 Jul 2019 19:25:03 +0200 Subject: [PATCH 1/9] add router information when using REST API Signed-off-by: Frederic Descamps --- router/__init__.py | 0 router/connections.py | 52 +++++++++++++++++++++++++++ router/init.py | 81 +++++++++++++++++++++++++++++++++++++++++++ router/status.py | 65 ++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 router/__init__.py create mode 100644 router/connections.py create mode 100644 router/status.py diff --git a/router/__init__.py b/router/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/router/connections.py b/router/connections.py new file mode 100644 index 0000000..dcf63ee --- /dev/null +++ b/router/connections.py @@ -0,0 +1,52 @@ +import json +import requests + +api_ver = "20190715" + +def __format_bytes(size): + # 2**10 = 1024 + power = 2**10 + n = 0 + power_labels = {0 : 'bytes', 1: 'kb', 2: 'mb', 3: 'gb', 4: 'tb'} + while size > power: + size /= power + n += 1 + return "%d %s" % (size, power_labels[n]) + +def __router_call(route, router_ip, router_port, user, password): + url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route + resp = requests.get(url,auth=(user, password)) + if resp.status_code == 200: + return resp + else: + return False + + +def __cluster_routes(router_ip, router_port, user, password): + result = __router_call("/routes", router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + fmt = "| {0:22s} | {1:15s} | {2:12s} | {3:>20s} | {4:>20s} | {5:27s} |" + header = fmt.format("Route", "Source", "Destination", "From Server", "To Server", "Connection Started") + bar = "+" + "-" * 24 + "+" + "-" * 17 + "+" + "-" * 14 + "+" + "-" * 22 + "+" + "-" * 22 + "+" + "-" * 29 + "+" + print bar + print header + print bar + for item in result_json['items']: + route_name = item['name'] + result_item = __router_call("/routes/%s/connections" % route_name, router_ip, router_port, user, password) + result_item_json = json.loads(result_item.content) + if len(result_item_json['items']) > 0: + for entry in result_item_json['items']: + print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], + str(__format_bytes(entry['bytesFromServer'])), + str(__format_bytes(entry['bytesToServer'])), entry['timeStarted']) + else: + print fmt.format(route_name, " "," ", " ", " ", " ") + + print bar + + +def connections(router_ip, router_port, user, password): + __cluster_routes(router_ip, router_port, user, password) + \ No newline at end of file diff --git a/router/init.py b/router/init.py index 5989ced..b41db86 100644 --- a/router/init.py +++ b/router/init.py @@ -1,2 +1,83 @@ # init.py # ------- + +from ext.mysqlsh_plugins_common import register_plugin +from ext.router import status as router_status +from ext.router import connections as router_connections + +register_plugin("status", router_status.status, + { + "brief": "Get MySQL Router info", + "parameters": [ + { + "name": "router_ip", + "brief": "IP of MySQL Router", + "type": "string", + "required": True + }, + { + "name": "router_port", + "brief": "TCP port where MySQL Router REST API is listening.", + "type": "integer", + "required": True + }, + { + "name": "user", + "brief": "MySQL Router user for REST API", + "type": "string", + "required": True + }, + { + "name": "password", + "brief": "MySQL Router password for REST API", + "type": "string", + "required": True + } + ] + }, + "router", + { + "brief": "MySQL Router management and utilities.", + "details": [ + "A collection of MySQL Router management tools" + ] + }) + + +register_plugin("connections", router_connections.connections, + { + "brief": "Get Connections in MySQL Router", + "parameters": [ + { + "name": "router_ip", + "brief": "IP of MySQL Router", + "type": "string", + "required": True + }, + { + "name": "router_port", + "brief": "TCP port where MySQL Router REST API is listening.", + "type": "integer", + "required": True + }, + { + "name": "user", + "brief": "MySQL Router user for REST API", + "type": "string", + "required": True + }, + { + "name": "password", + "brief": "MySQL Router password for REST API", + "type": "string", + "required": True + } + ] + }, + "router", + { + "brief": "MySQL Router management and utilities.", + "details": [ + "A collection of MySQL Router management tools" + ] + }) \ No newline at end of file diff --git a/router/status.py b/router/status.py new file mode 100644 index 0000000..c12bc16 --- /dev/null +++ b/router/status.py @@ -0,0 +1,65 @@ +import json +import requests + +api_ver = "20190715" + +def __router_call(route, router_ip, router_port, user, password): + url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route + resp = requests.get(url,auth=(user, password)) + if resp.status_code == 200: + return resp + else: + return False + +def __cluster_name(router_ip, router_port, user, password): + result = __router_call("/metadata", router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + cluster_name = result_json['items'][0]['name'] + print "+" + "-" * 16 + "-" * len(cluster_name) + "+" + print "| Cluster name: %s |" % cluster_name + print "+" + "-" * 16 + "-" * len(cluster_name) + "+" + return cluster_name + return False + +def __cluster_metadata_status(router_ip, router_port, user, password, cluster_name): + result = __router_call("/metadata/%s/status" % cluster_name, router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + print " Refresh Succeeded: " + str(result_json['refreshSucceeded']) + print " Refresh Failed: " + str(result_json['refreshFailed']) + print " Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ + + ":" + str(result_json['lastRefreshPort']) + +def __cluster_routes(router_ip, router_port, user, password): + result = __router_call("/routes", router_ip, router_port, user, password) + if result: + print " +--------+" + print " | routes |" + print " +--------+" + result_json = json.loads(result.content) + for item in result_json['items']: + route_name = item['name'] + result_item = __router_call("/routes/%s/health" % route_name, router_ip, router_port, user, password) + result_item_json = json.loads(result_item.content) + if result_item_json['isAlive']: + print(" * %s (alive) :" % route_name) + else: + print(" * %s (dead) :" % route_name) + + result_status = __router_call("/routes/%s/status" % route_name, router_ip, router_port, user, password) + result_status_json= json.loads(result_status.content) + print("\tTotal Connections: %d\tActive Connections: %d\tBlocked Hosts: %d" + % (result_status_json['totalConnections'], result_status_json['activeConnections'], + result_status_json['blockedHosts'])) + result_routes = __router_call("/routes/%s/destinations" % route_name, router_ip, router_port, user, password) + result_routes_json = json.loads(result_routes.content) + for destination in result_routes_json['items']: + print ("\t---> %s : %d" % (destination['address'], destination['port'])) + +def status(router_ip, router_port, user, password): + cluster_name = __cluster_name(router_ip, router_port, user, password) + if cluster_name: + __cluster_metadata_status(router_ip, router_port, user, password, cluster_name) + __cluster_routes(router_ip, router_port, user, password) + \ No newline at end of file From 529a5ae463417d23f0cbac4190f29a9c0c02d8b2 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 10 Jul 2019 19:32:26 +0200 Subject: [PATCH 2/9] Capture some errors and avoid stack trace to be printed Signed-off-by: Frederic Descamps --- router/connections.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/router/connections.py b/router/connections.py index dcf63ee..d937fe3 100644 --- a/router/connections.py +++ b/router/connections.py @@ -15,10 +15,15 @@ def __format_bytes(size): def __router_call(route, router_ip, router_port, user, password): url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route - resp = requests.get(url,auth=(user, password)) + try: + resp = requests.get(url,auth=(user, password)) + except: + print("ERROR: Impossible to connect to the MySQL Router REST API") + return False if resp.status_code == 200: return resp else: + print("ERROR: Got error %d when trying to connect to the MySQL Router REST API" % resp.status_code) return False From 70bbc11408ac979ef586f10a169d7e9156320c89 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 10 Jul 2019 19:41:28 +0200 Subject: [PATCH 3/9] Diplay the route name only once Signed-off-by: Frederic Descamps --- router/connections.py | 1 + 1 file changed, 1 insertion(+) diff --git a/router/connections.py b/router/connections.py index d937fe3..caddb81 100644 --- a/router/connections.py +++ b/router/connections.py @@ -46,6 +46,7 @@ def __cluster_routes(router_ip, router_port, user, password): print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], str(__format_bytes(entry['bytesFromServer'])), str(__format_bytes(entry['bytesToServer'])), entry['timeStarted']) + route_name="" else: print fmt.format(route_name, " "," ", " ", " ", " ") From c0da07f4cba1904f3b49d7430747bc75238b6f05 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 10 Jul 2019 21:56:36 +0200 Subject: [PATCH 4/9] add route_name parameter Signed-off-by: Frederic Descamps --- router/connections.py | 29 +++++++++++++++-------------- router/init.py | 6 ++++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/router/connections.py b/router/connections.py index caddb81..54f7453 100644 --- a/router/connections.py +++ b/router/connections.py @@ -27,7 +27,7 @@ def __router_call(route, router_ip, router_port, user, password): return False -def __cluster_routes(router_ip, router_port, user, password): +def __cluster_routes(router_ip, router_port, user, password, route_to_find): result = __router_call("/routes", router_ip, router_port, user, password) if result: result_json = json.loads(result.content) @@ -39,20 +39,21 @@ def __cluster_routes(router_ip, router_port, user, password): print bar for item in result_json['items']: route_name = item['name'] - result_item = __router_call("/routes/%s/connections" % route_name, router_ip, router_port, user, password) - result_item_json = json.loads(result_item.content) - if len(result_item_json['items']) > 0: - for entry in result_item_json['items']: - print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], - str(__format_bytes(entry['bytesFromServer'])), - str(__format_bytes(entry['bytesToServer'])), entry['timeStarted']) - route_name="" - else: - print fmt.format(route_name, " "," ", " ", " ", " ") + if route_to_find in route_name: + result_item = __router_call("/routes/%s/connections" % route_name, router_ip, router_port, user, password) + result_item_json = json.loads(result_item.content) + if len(result_item_json['items']) > 0: + for entry in result_item_json['items']: + print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], + str(__format_bytes(entry['bytesFromServer'])), + str(__format_bytes(entry['bytesToServer'])), entry['timeStarted']) + route_name="" + else: + print fmt.format(route_name, " "," ", " ", " ", " ") - print bar + print bar -def connections(router_ip, router_port, user, password): - __cluster_routes(router_ip, router_port, user, password) +def connections(router_ip, router_port, user, password, route_to_find=""): + __cluster_routes(router_ip, router_port, user, password, route_to_find) \ No newline at end of file diff --git a/router/init.py b/router/init.py index b41db86..db8cd3e 100644 --- a/router/init.py +++ b/router/init.py @@ -71,6 +71,12 @@ "brief": "MySQL Router password for REST API", "type": "string", "required": True + }, + { + "name": "route_name", + "brief": "MySQL Router's route (can be partial, like 'default_ro') for REST API.", + "type": "string", + "required": False } ] }, From b1770ca57f39c21ad9b18bf0697bd1a514f41b9f Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Mon, 15 Jul 2019 22:52:52 +0200 Subject: [PATCH 5/9] New MySQL Router status() and connections() using a router object Signed-off-by: Frederic Descamps --- router/connections.py | 59 ------------------- router/init.py | 107 +++++++++-------------------------- router/myrouter.py | 129 ++++++++++++++++++++++++++++++++++++++++++ router/status.py | 65 --------------------- 4 files changed, 156 insertions(+), 204 deletions(-) delete mode 100644 router/connections.py create mode 100644 router/myrouter.py delete mode 100644 router/status.py diff --git a/router/connections.py b/router/connections.py deleted file mode 100644 index 54f7453..0000000 --- a/router/connections.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import requests - -api_ver = "20190715" - -def __format_bytes(size): - # 2**10 = 1024 - power = 2**10 - n = 0 - power_labels = {0 : 'bytes', 1: 'kb', 2: 'mb', 3: 'gb', 4: 'tb'} - while size > power: - size /= power - n += 1 - return "%d %s" % (size, power_labels[n]) - -def __router_call(route, router_ip, router_port, user, password): - url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route - try: - resp = requests.get(url,auth=(user, password)) - except: - print("ERROR: Impossible to connect to the MySQL Router REST API") - return False - if resp.status_code == 200: - return resp - else: - print("ERROR: Got error %d when trying to connect to the MySQL Router REST API" % resp.status_code) - return False - - -def __cluster_routes(router_ip, router_port, user, password, route_to_find): - result = __router_call("/routes", router_ip, router_port, user, password) - if result: - result_json = json.loads(result.content) - fmt = "| {0:22s} | {1:15s} | {2:12s} | {3:>20s} | {4:>20s} | {5:27s} |" - header = fmt.format("Route", "Source", "Destination", "From Server", "To Server", "Connection Started") - bar = "+" + "-" * 24 + "+" + "-" * 17 + "+" + "-" * 14 + "+" + "-" * 22 + "+" + "-" * 22 + "+" + "-" * 29 + "+" - print bar - print header - print bar - for item in result_json['items']: - route_name = item['name'] - if route_to_find in route_name: - result_item = __router_call("/routes/%s/connections" % route_name, router_ip, router_port, user, password) - result_item_json = json.loads(result_item.content) - if len(result_item_json['items']) > 0: - for entry in result_item_json['items']: - print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], - str(__format_bytes(entry['bytesFromServer'])), - str(__format_bytes(entry['bytesToServer'])), entry['timeStarted']) - route_name="" - else: - print fmt.format(route_name, " "," ", " ", " ", " ") - - print bar - - -def connections(router_ip, router_port, user, password, route_to_find=""): - __cluster_routes(router_ip, router_port, user, password, route_to_find) - \ No newline at end of file diff --git a/router/init.py b/router/init.py index db8cd3e..582d4b8 100644 --- a/router/init.py +++ b/router/init.py @@ -4,86 +4,33 @@ from ext.mysqlsh_plugins_common import register_plugin from ext.router import status as router_status from ext.router import connections as router_connections +from ext.router.myrouter import MyRouter -register_plugin("status", router_status.status, - { - "brief": "Get MySQL Router info", - "parameters": [ - { - "name": "router_ip", - "brief": "IP of MySQL Router", - "type": "string", - "required": True - }, - { - "name": "router_port", - "brief": "TCP port where MySQL Router REST API is listening.", - "type": "integer", - "required": True - }, - { - "name": "user", - "brief": "MySQL Router user for REST API", - "type": "string", - "required": True - }, - { - "name": "password", - "brief": "MySQL Router password for REST API", - "type": "string", - "required": True - } - ] - }, - "router", - { - "brief": "MySQL Router management and utilities.", - "details": [ - "A collection of MySQL Router management tools" - ] - }) +router = shell.create_extension_object() -register_plugin("connections", router_connections.connections, - { - "brief": "Get Connections in MySQL Router", - "parameters": [ - { - "name": "router_ip", - "brief": "IP of MySQL Router", - "type": "string", - "required": True - }, - { - "name": "router_port", - "brief": "TCP port where MySQL Router REST API is listening.", - "type": "integer", - "required": True - }, - { - "name": "user", - "brief": "MySQL Router user for REST API", - "type": "string", - "required": True - }, - { - "name": "password", - "brief": "MySQL Router password for REST API", - "type": "string", - "required": True - }, - { - "name": "route_name", - "brief": "MySQL Router's route (can be partial, like 'default_ro') for REST API.", - "type": "string", - "required": False - } - ] - }, - "router", - { - "brief": "MySQL Router management and utilities.", - "details": [ - "A collection of MySQL Router management tools" - ] - }) \ No newline at end of file +def create(ip, port, user, password): + my_router = MyRouter(ip, port, user, password) + return { + 'connections': lambda: my_router.connections(), + 'status': lambda: my_router.status(), + 'api': my_router.api + } + +shell.add_extension_object_member(router, 'create', lambda ip, port, user, password=False:create(ip, port, user, password), + { + 'brief':'Create the MySQL Router Object', 'details':['It has MyRouter methods.'], + 'parameters':[ + {'name':'ip', 'type':'string', 'required':True, 'brief':'ip'}, + {'name':'port', 'type':'integer', 'required':True, 'brief':'port'}, + {'name':'user', 'type':'string', 'required':True, 'brief':'user'}, + {'name':'password', 'type':'string', 'required':False, 'brief':'password'} + ] + } + ) + +shell.add_extension_object_member(ext, 'router', router, +{ + 'brief':'MySQL Router Object', + 'details':['MySQL Router Object.'] +}) diff --git a/router/myrouter.py b/router/myrouter.py new file mode 100644 index 0000000..ce65074 --- /dev/null +++ b/router/myrouter.py @@ -0,0 +1,129 @@ +import json +import requests +import mysqlsh +shell = mysqlsh.globals.shell + +class MyRouter: + def __init__(self, ip, port, user, password=False): + self.user = user + self.ip = ip + self.port = str(port) + self.user = user + if not password: + self.__password = shell.prompt('Password: ',{'type': 'password'}) + else: + self.__password = password + + def __format_bytes(self, size): + # 2**10 = 1024 + power = 2**10 + n = 0 + power_labels = {0 : 'bytes', 1: 'kb', 2: 'mb', 3: 'gb', 4: 'tb'} + while size > power: + size /= power + n += 1 + return "%d %s" % (size, power_labels[n]) + + def __router_call(self,route): + + url = "http://" + self.ip + ":" + self.port + "/api/" + self.api + route + try: + resp = requests.get(url,auth=(self.user,self.__password)) + except: + print("ERROR: Impossible to connect to the MySQL Router REST API") + return False + if resp.status_code == 200: + return resp + else: + print("ERROR: Got error %d when trying to connect to the MySQL Router REST API" % resp.status_code) + return False + + + def __cluster_routes(self, route_to_find): + result = self.__router_call("/routes") + if result: + result_json = json.loads(result.content) + fmt = "| {0:22s} | {1:18s} | {2:12s} | {3:>20s} | {4:>20s} | {5:27s} |" + header = fmt.format("Route", "Source", "Destination", "From Server", "To Server", "Connection Started") + bar = "+" + "-" * 24 + "+" + "-" * 20 + "+" + "-" * 14 + "+" + "-" * 22 + "+" + "-" * 22 + "+" + "-" * 29 + "+" + print bar + print header + print bar + for item in result_json['items']: + route_name = item['name'] + if route_to_find in route_name: + result_item = self.__router_call("/routes/%s/connections" % route_name) + result_item_json = json.loads(result_item.content) + if len(result_item_json['items']) > 0: + for entry in result_item_json['items']: + print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], + str(self.__format_bytes(entry['bytesFromServer'])), + str(self.__format_bytes(entry['bytesToServer'])), entry['timeStarted']) + route_name="" + else: + print fmt.format(route_name, " "," ", " ", " ", " ") + + print bar + + + def __cluster_metadata_status(self, cluster_name): + result = self.__router_call("/metadata/%s/status" % cluster_name) + if result: + result_json = json.loads(result.content) + print " Refresh Succeeded: " + str(result_json['refreshSucceeded']) + print " Refresh Failed: " + str(result_json['refreshFailed']) + print " Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ + + ":" + str(result_json['lastRefreshPort']) + + def __cluster_all_routes(self): + result = self.__router_call("/routes") + if result: + print " +--------+" + print " | routes |" + print " +--------+" + result_json = json.loads(result.content) + for item in result_json['items']: + route_name = item['name'] + result_item = self.__router_call("/routes/%s/health" % route_name) + result_item_json = json.loads(result_item.content) + if result_item_json['isAlive']: + print(" * %s (alive) :" % route_name) + else: + print(" * %s (dead) :" % route_name) + + result_status = self.__router_call("/routes/%s/status" % route_name) + result_status_json= json.loads(result_status.content) + print("\tTotal Connections: %d\tActive Connections: %d\tBlocked Hosts: %d" + % (result_status_json['totalConnections'], result_status_json['activeConnections'], + result_status_json['blockedHosts'])) + result_routes = self.__router_call("/routes/%s/destinations" % route_name) + result_routes_json = json.loads(result_routes.content) + for destination in result_routes_json['items']: + print ("\t---> %s : %d" % (destination['address'], destination['port'])) + + def __cluster_name(self): + result = self.__router_call("/metadata") + if result: + result_json = json.loads(result.content) + cluster_name = result_json['items'][0]['name'] + print "+" + "-" * 16 + "-" * len(cluster_name) + "+" + print "| Cluster name: %s |" % cluster_name + print "+" + "-" * 16 + "-" * len(cluster_name) + "+" + return cluster_name + return False + + + def status(self): + cluster_name = self.__cluster_name() + if cluster_name: + self.__cluster_metadata_status(cluster_name) + self.__cluster_all_routes() + + + def connections(self, route_to_find=""): + self.__cluster_routes(route_to_find) + + api = "20190715" + + + \ No newline at end of file diff --git a/router/status.py b/router/status.py deleted file mode 100644 index c12bc16..0000000 --- a/router/status.py +++ /dev/null @@ -1,65 +0,0 @@ -import json -import requests - -api_ver = "20190715" - -def __router_call(route, router_ip, router_port, user, password): - url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route - resp = requests.get(url,auth=(user, password)) - if resp.status_code == 200: - return resp - else: - return False - -def __cluster_name(router_ip, router_port, user, password): - result = __router_call("/metadata", router_ip, router_port, user, password) - if result: - result_json = json.loads(result.content) - cluster_name = result_json['items'][0]['name'] - print "+" + "-" * 16 + "-" * len(cluster_name) + "+" - print "| Cluster name: %s |" % cluster_name - print "+" + "-" * 16 + "-" * len(cluster_name) + "+" - return cluster_name - return False - -def __cluster_metadata_status(router_ip, router_port, user, password, cluster_name): - result = __router_call("/metadata/%s/status" % cluster_name, router_ip, router_port, user, password) - if result: - result_json = json.loads(result.content) - print " Refresh Succeeded: " + str(result_json['refreshSucceeded']) - print " Refresh Failed: " + str(result_json['refreshFailed']) - print " Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ - + ":" + str(result_json['lastRefreshPort']) - -def __cluster_routes(router_ip, router_port, user, password): - result = __router_call("/routes", router_ip, router_port, user, password) - if result: - print " +--------+" - print " | routes |" - print " +--------+" - result_json = json.loads(result.content) - for item in result_json['items']: - route_name = item['name'] - result_item = __router_call("/routes/%s/health" % route_name, router_ip, router_port, user, password) - result_item_json = json.loads(result_item.content) - if result_item_json['isAlive']: - print(" * %s (alive) :" % route_name) - else: - print(" * %s (dead) :" % route_name) - - result_status = __router_call("/routes/%s/status" % route_name, router_ip, router_port, user, password) - result_status_json= json.loads(result_status.content) - print("\tTotal Connections: %d\tActive Connections: %d\tBlocked Hosts: %d" - % (result_status_json['totalConnections'], result_status_json['activeConnections'], - result_status_json['blockedHosts'])) - result_routes = __router_call("/routes/%s/destinations" % route_name, router_ip, router_port, user, password) - result_routes_json = json.loads(result_routes.content) - for destination in result_routes_json['items']: - print ("\t---> %s : %d" % (destination['address'], destination['port'])) - -def status(router_ip, router_port, user, password): - cluster_name = __cluster_name(router_ip, router_port, user, password) - if cluster_name: - __cluster_metadata_status(router_ip, router_port, user, password, cluster_name) - __cluster_routes(router_ip, router_port, user, password) - \ No newline at end of file From 68b4580c1baf3a81162b63b132583d22c1a98fea Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Mon, 15 Jul 2019 23:42:39 +0200 Subject: [PATCH 6/9] modify the lambda command for connections to be able to send search string Signed-off-by: Frederic Descamps --- router/init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/init.py b/router/init.py index 582d4b8..94384aa 100644 --- a/router/init.py +++ b/router/init.py @@ -12,7 +12,7 @@ def create(ip, port, user, password): my_router = MyRouter(ip, port, user, password) return { - 'connections': lambda: my_router.connections(), + 'connections': lambda route_to_find="": my_router.connections(route_to_find), 'status': lambda: my_router.status(), 'api': my_router.api } From 3af780a9e845547d11732ab09d41b58dec2f3fa0 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 17 Jul 2019 22:29:34 +0200 Subject: [PATCH 7/9] fix issue #9 --- router/myrouter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/router/myrouter.py b/router/myrouter.py index ce65074..c847070 100644 --- a/router/myrouter.py +++ b/router/myrouter.py @@ -17,12 +17,12 @@ def __init__(self, ip, port, user, password=False): def __format_bytes(self, size): # 2**10 = 1024 power = 2**10 - n = 0 - power_labels = {0 : 'bytes', 1: 'kb', 2: 'mb', 3: 'gb', 4: 'tb'} - while size > power: + for unit in ('bytes', 'kb', 'mb', 'gb'): + if size <= power: + return "%d %s" % (size, unit) size /= power - n += 1 - return "%d %s" % (size, power_labels[n]) + + return "%d tb" % (size,) def __router_call(self,route): From e8de80ab9b0dd406f0c0c9305433dd874804f33c Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Mon, 16 Sep 2019 19:33:37 +0200 Subject: [PATCH 8/9] fix for 8.0.18 Signed-off-by: Frederic Descamps --- router/connections.py | 59 +++++++++++++++++++++++++++++++++++++++ router/myrouter.py | 36 ++++++++++++------------ router/status.py | 65 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 router/connections.py create mode 100644 router/status.py diff --git a/router/connections.py b/router/connections.py new file mode 100644 index 0000000..984b54d --- /dev/null +++ b/router/connections.py @@ -0,0 +1,59 @@ +import json +import requests + +api_ver = "20190715" + +def __format_bytes(size): + # 2**10 = 1024 + power = 2**10 + n = 0 + power_labels = {0 : 'bytes', 1: 'kb', 2: 'mb', 3: 'gb', 4: 'tb'} + while size > power: + size /= power + n += 1 + return "%d %s" % (size, power_labels[n]) + +def __router_call(route, router_ip, router_port, user, password): + url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route + try: + resp = requests.get(url,auth=(user, password)) + except: + print("ERROR: Impossible to connect to the MySQL Router REST API") + return False + if resp.status_code == 200: + return resp + else: + print("ERROR: Got error %d when trying to connect to the MySQL Router REST API" % resp.status_code) + return False + + +def __cluster_routes(router_ip, router_port, user, password, route_to_find): + result = __router_call("/routes", router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + fmt = "| {0:22s} | {1:15s} | {2:12s} | {3:>20s} | {4:>20s} | {5:27s} |" + header = fmt.format("Route", "Source", "Destination", "From Server", "To Server", "Connection Started") + bar = "+" + "-" * 24 + "+" + "-" * 17 + "+" + "-" * 14 + "+" + "-" * 22 + "+" + "-" * 22 + "+" + "-" * 29 + "+" + print (bar) + print (header) + print (bar) + for item in result_json['items']: + route_name = item['name'] + if route_to_find in route_name: + result_item = __router_call("/routes/%s/connections" % route_name, router_ip, router_port, user, password) + result_item_json = json.loads(result_item.content) + if len(result_item_json['items']) > 0: + for entry in result_item_json['items']: + print (fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], + str(__format_bytes(entry['bytesFromServer'])), + str(__format_bytes(entry['bytesToServer'])), entry['timeStarted'])) + route_name="" + else: + print (fmt.format(route_name, " "," ", " ", " ", " ")) + + print (bar) + + +def connections(router_ip, router_port, user, password, route_to_find=""): + __cluster_routes(router_ip, router_port, user, password, route_to_find) + diff --git a/router/myrouter.py b/router/myrouter.py index c847070..0385191 100644 --- a/router/myrouter.py +++ b/router/myrouter.py @@ -46,9 +46,9 @@ def __cluster_routes(self, route_to_find): fmt = "| {0:22s} | {1:18s} | {2:12s} | {3:>20s} | {4:>20s} | {5:27s} |" header = fmt.format("Route", "Source", "Destination", "From Server", "To Server", "Connection Started") bar = "+" + "-" * 24 + "+" + "-" * 20 + "+" + "-" * 14 + "+" + "-" * 22 + "+" + "-" * 22 + "+" + "-" * 29 + "+" - print bar - print header - print bar + print (bar) + print (header) + print (bar) for item in result_json['items']: route_name = item['name'] if route_to_find in route_name: @@ -56,31 +56,31 @@ def __cluster_routes(self, route_to_find): result_item_json = json.loads(result_item.content) if len(result_item_json['items']) > 0: for entry in result_item_json['items']: - print fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], + print (fmt.format(route_name, entry['sourceAddress'], entry['destinationAddress'], str(self.__format_bytes(entry['bytesFromServer'])), - str(self.__format_bytes(entry['bytesToServer'])), entry['timeStarted']) + str(self.__format_bytes(entry['bytesToServer'])), entry['timeStarted'])) route_name="" else: - print fmt.format(route_name, " "," ", " ", " ", " ") + print (fmt.format(route_name, " "," ", " ", " ", " ")) - print bar + print (bar) def __cluster_metadata_status(self, cluster_name): result = self.__router_call("/metadata/%s/status" % cluster_name) if result: result_json = json.loads(result.content) - print " Refresh Succeeded: " + str(result_json['refreshSucceeded']) - print " Refresh Failed: " + str(result_json['refreshFailed']) - print " Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ - + ":" + str(result_json['lastRefreshPort']) + print (" Refresh Succeeded: " + str(result_json['refreshSucceeded'])) + print (" Refresh Failed: " + str(result_json['refreshFailed'])) + print (" Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ + + ":" + str(result_json['lastRefreshPort'])) def __cluster_all_routes(self): result = self.__router_call("/routes") if result: - print " +--------+" - print " | routes |" - print " +--------+" + print (" +--------+") + print (" | routes |") + print (" +--------+") result_json = json.loads(result.content) for item in result_json['items']: route_name = item['name'] @@ -106,9 +106,9 @@ def __cluster_name(self): if result: result_json = json.loads(result.content) cluster_name = result_json['items'][0]['name'] - print "+" + "-" * 16 + "-" * len(cluster_name) + "+" - print "| Cluster name: %s |" % cluster_name - print "+" + "-" * 16 + "-" * len(cluster_name) + "+" + print ("+" + "-" * 16 + "-" * len(cluster_name) + "+") + print ("| Cluster name: %s |" % cluster_name) + print ("+" + "-" * 16 + "-" * len(cluster_name) + "+") return cluster_name return False @@ -126,4 +126,4 @@ def connections(self, route_to_find=""): api = "20190715" - \ No newline at end of file + diff --git a/router/status.py b/router/status.py new file mode 100644 index 0000000..b101133 --- /dev/null +++ b/router/status.py @@ -0,0 +1,65 @@ +import json +import requests + +api_ver = "20190715" + +def __router_call(route, router_ip, router_port, user, password): + url = "http://" + router_ip + ":" + str(router_port) + "/api/" + api_ver + route + resp = requests.get(url,auth=(user, password)) + if resp.status_code == 200: + return resp + else: + return False + +def __cluster_name(router_ip, router_port, user, password): + result = __router_call("/metadata", router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + cluster_name = result_json['items'][0]['name'] + print ("+" + "-" * 16 + "-" * len(cluster_name) + "+") + print ("| Cluster name: %s |" % cluster_name) + print ("+" + "-" * 16 + "-" * len(cluster_name) + "+") + return cluster_name + return False + +def __cluster_metadata_status(router_ip, router_port, user, password, cluster_name): + result = __router_call("/metadata/%s/status" % cluster_name, router_ip, router_port, user, password) + if result: + result_json = json.loads(result.content) + print (" Refresh Succeeded: " + str(result_json['refreshSucceeded'])) + print (" Refresh Failed: " + str(result_json['refreshFailed'])) + print (" Last Refresh Hostname: " + result_json['lastRefreshHostname'] \ + + ":" + str(result_json['lastRefreshPort'])) + +def __cluster_routes(router_ip, router_port, user, password): + result = __router_call("/routes", router_ip, router_port, user, password) + if result: + print (" +--------+") + print (" | routes |") + print (" +--------+") + result_json = json.loads(result.content) + for item in result_json['items']: + route_name = item['name'] + result_item = __router_call("/routes/%s/health" % route_name, router_ip, router_port, user, password) + result_item_json = json.loads(result_item.content) + if result_item_json['isAlive']: + print(" * %s (alive) :" % route_name) + else: + print(" * %s (dead) :" % route_name) + + result_status = __router_call("/routes/%s/status" % route_name, router_ip, router_port, user, password) + result_status_json= json.loads(result_status.content) + print("\tTotal Connections: %d\tActive Connections: %d\tBlocked Hosts: %d" + % (result_status_json['totalConnections'], result_status_json['activeConnections'], + result_status_json['blockedHosts'])) + result_routes = __router_call("/routes/%s/destinations" % route_name, router_ip, router_port, user, password) + result_routes_json = json.loads(result_routes.content) + for destination in result_routes_json['items']: + print ("\t---> %s : %d" % (destination['address'], destination['port'])) + +def status(router_ip, router_port, user, password): + cluster_name = __cluster_name(router_ip, router_port, user, password) + if cluster_name: + __cluster_metadata_status(router_ip, router_port, user, password, cluster_name) + __cluster_routes(router_ip, router_port, user, password) + From 182a376ef0d0587b9a3568d162e96008ee9d690b Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Tue, 17 Sep 2019 06:39:41 +0200 Subject: [PATCH 9/9] fix missing ext object when not loaded previously Signed-off-by: Frederic Descamps --- router/init.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/router/init.py b/router/init.py index 94384aa..0348b45 100644 --- a/router/init.py +++ b/router/init.py @@ -9,6 +9,23 @@ router = shell.create_extension_object() + +# Check if global object 'ext' has already been registered +if 'ext' in globals(): + global_obj = ext +else: + # If not, register a new global object named 'ext' now + global_obj = shell.create_extension_object() + shell.register_global("ext", global_obj, + { + "brief": "MySQL Shell community plugins.", + "details": [ + "The global object ext is the entry points for " + "MySQL Shell extensions." + ] + }) + + def create(ip, port, user, password): my_router = MyRouter(ip, port, user, password) return { @@ -29,7 +46,7 @@ def create(ip, port, user, password): } ) -shell.add_extension_object_member(ext, 'router', router, +shell.add_extension_object_member(global_obj, 'router', router, { 'brief':'MySQL Router Object', 'details':['MySQL Router Object.']