#!/usr/bin/env python2 # -*- mode: python;coding: utf-8 -*- # dooba --- # # Filename: dooba # Description: `dooba' is a easy tools monitoring oceanbase cluster for # oceanbase admins. It's based on python curses library, and is a # powerful tool for watching oceanbase cluster status with # straightfoward vision. # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 3, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; see the file COPYING. If not, see # . # # Code: from collections import deque from datetime import datetime, timedelta, date from errno import * from getopt import getopt, GetoptError from locale import setlocale, LC_ALL from os import environ, read, setsid from pprint import pformat from random import shuffle from subprocess import Popen, PIPE, STDOUT, call from telnetlib import Telnet from threading import Thread from time import sleep, strftime from urllib2 import urlopen, URLError import BaseHTTPServer import atexit import bz2 import curses import curses.textpad import itertools import json import math import os import select import signal import socket import struct import sys import tempfile import textwrap import types import traceback import time import pdb f_data_log = '' def seconds(td): return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / (10**6); def check_datalog(datalog, tenantid=1): global f_data_log d = os.path.split(datalog)[0] l = os.path.split(datalog)[1] now = datetime.now() l = l + "." + str(tenantid) + "_" + now.strftime("%Y%m%d-%H%M%S") if not os.path.isdir(d): return False try: f = open(d+"/"+l, "w") f.write("----") f.close() except: f.close() return False f_data_log = d+"/"+l return True class Global: MAX_LINES = 900 WIDGET_HEIGHT = 7 DEFAULT_USER = '' DEFAULT_PASS = '' class Options(object): host = '127.0.0.1' port = 2881 user = "root" password = "" database = "oceanbase" supermode = False interval = 1 dataid = 0 using_ip_port = False env = 'unknown' machine_interval = 5 degradation = False show_win_file = None daemon = False http = False daemon_action = 'start' http_port = 33244 debug = False datalog = '' def __str__(self): result = {} for k in dir(self): v = getattr(self, k) if k.find('__') == 0: continue result[k] = v return pformat(result) # auxiliary functions class ColumnFactory(object): def __init__(self, svr, ip): DEBUG(self.__init__, "ColumnFactory.__init__(svr='%s',ip='%s') " % (svr, ip), "") self.__svr = svr self.__ip = ip def count(self, name, sname, obname, duration=True, enable=False): DEBUG(self.count, "ColumnFactory.count(name='%s',sname='%s',obname='%s' " % (name, sname, obname), "" ) if type(obname) == str: pass elif type(obname) == list: obname = [name for name in obname] else: raise Exception("unsupport type %s" % type(obname)) return self.count0(name, sname, obname, duration, enable) def count0(self, name, sname, obname,duration=True, enable=False): svr = self.__svr ip = self.__ip def calc_func(stat,ip=ip): def try_get(d, k, b): try: if d.has_key(k): return d['%s' % k] else: return d['%s' % b] except Exception as e: raise e try: if type(obname) == str: return try_get(stat, svr, oceanbase.get_current_tenant())[ip][obname] elif type(obname) == list: return sum([try_get(stat, svr, oceanbase.get_current_tenant())[ip][name] for name in obname]) else: raise Exception("ColumnFactory.count0 unsupport type %s" % type(obname)) except KeyError as e: DEBUG(calc_func, "ColumnFactory.count0( stat='%s', ip='%s') exception :" % (stat, ip) , e) pass return Column(name, calc_func, 7, duration=duration, enable=enable, sname=sname) def time(self, name, sname, obnamet, obnamec=None, duration=False, enable=False): if obnamec is None: obnamec = obnamet return self.time0(name, sname, obnamet, obnamec, duration=duration,enable=enable) def time0(self, name, sname, obnamet, obnamec=None, duration=False, enable=False): DEBUG(self.time0, "ColumnFactory.time0(svr='%s',ip='%s',name='%s',sname='%s',obnamet='%s', obnamec='%s')" % (self.__svr, self.__ip, name, sname, obnamet, obnamec) , "") svr = self.__svr ip = self.__ip def calc_func(stat,ip=ip): def try_get(d, k, b): if d.has_key(k): return d['%s' % k] else: return d['%s' % b] try: if type(obnamec) == str: total_count = try_get(stat, svr, oceanbase.get_current_tenant())[ip][obnamec] elif type(obnamec) == list: total_count = sum([try_get(stat, svr, oceanbase.get_current_tenant())[ip][name] for name in obnamec]) return try_get(stat, svr, oceanbase.get_current_tenant())[ip][obnamet] / 1000 / float(total_count or 1) except Exception as e: DEBUG(calc_func, "ColumnFactory.time0.calc_func(stat='%s',ip='%s') exception:" % (stat, ip), e) pass return Column(name, calc_func, 7, duration=duration, enable=enable, sname=sname) def cache(self, name, sname, obname): svr = self.__svr ip = self.__ip return Column(name, lambda stat,ip=ip: stat[svr][ip][obname+"_cache_hit"] / float(stat[svr][ip][obname+"_cache_hit"] + stat[svr][ip][obname+"_cache_miss"] or 1), 7, enable=False, sname=sname) def mem_str(mem_int, bit=False): mem_int = int(mem_int) if mem_int < 1024: return str(mem_int) + (bit and 'b' or '') mem_int = float(mem_int) mem_int /= 1024 if mem_int < 1024: return "%.1fK" % mem_int + (bit and 'b' or '') mem_int /= 1024 if mem_int < 1024: return "%.1fM" % mem_int + (bit and 'b' or '') mem_int /= 1024 if mem_int < 1024: return "%.2fG" % mem_int + (bit and 'b' or '') mem_int /= 1024 if mem_int < 1024: return "%.2fT" % mem_int + (bit and 'b' or '') return "UNKNOW" def count_str(count_int, kilo=True): return str(count_int) def percent_str(percent_int): return str(round(float(percent_int) * 100,1)) + "%" class Cowsay(object): '''Copyright 2011 Jesse Chan-Norris https://github.com/jcn/cowsay-py/blob/master/cowsay.py''' def __init__(self, str, length=40): self.__result = self.build_bubble(str, length) + self.build_cow() def __str__(self): return self.__result def build_cow(self): return """ \ ^__^ \ (oo)\_______ (__)\ )\/\\ ||----w | || || """ def build_bubble(self, str, length=40): bubble = [] lines = self.normalize_text(str, length) bordersize = len(lines[0]) bubble.append(" " + "_" * bordersize) for index, line in enumerate(lines): border = self.get_border(lines, index) bubble.append("%s %s %s" % (border[0], line, border[1])) bubble.append(" " + "-" * bordersize) return "\n".join(bubble) def normalize_text(self, str, length): lines = textwrap.wrap(str, length) maxlen = len(max(lines, key=len)) return [ line.ljust(maxlen) for line in lines ] def get_border(self, lines, index): if len(lines) < 2: return [ "<", ">" ] elif index == 0: return [ "/", "\\" ] elif index == len(lines) - 1: return [ "\\", "/" ] else: return [ "|", "|" ] class Daemon: """ A generic daemon class. Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile def daemonize(self): """ do the UNIX double-fork magic, see Stevens' "Advanced Programming in the UNIX Environment" for details (ISBN 0201563177) http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 """ try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError, e: sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent sys.exit(0) except OSError, e: sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = file(self.stdin, 'r') so = file(self.stdout, 'a+') se = file(self.stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile atexit.register(self.delpid) pid = str(os.getpid()) file(self.pidfile,'w+').write("%s\n" % pid) def delpid(self): os.remove(self.pidfile) def start(self): """ Start the daemon """ # Check for a pidfile to see if the daemon already runs try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid: message = "pidfile %s already exist. Daemon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run() def stop(self): """ Stop the daemon """ # Get the pid from the pidfile try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if not pid: message = "pidfile %s does not exist. Daemon not running?\n" sys.stderr.write(message % self.pidfile) return # not an error in a restart # Try killing the daemon process try: while 1: os.kill(pid, signal.SIGTERM) sleep(0.1) except OSError, err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print str(err) sys.exit(1) def restart(self): """ Restart the daemon """ self.stop() self.start() def run(self): """ You should override this method when you subclass Daemon. It will be called after the process has been daemonized by start() or restart(). """ class Blowfish: """Blowfish encryption Scheme This class implements the encryption and decryption functionality of the Blowfish cipher. Public functions: def __init__ (self, key) Creates an instance of blowfish using 'key' as the encryption key. Key is a string of length ranging from 8 to 56 bytes (64 to 448 bits). Once the instance of the object is created, the key is no longer necessary. def encrypt (self, data): Encrypt an 8 byte (64-bit) block of text where 'data' is an 8 byte string. Returns an 8-byte encrypted string. def decrypt (self, data): Decrypt an 8 byte (64-bit) encrypted block of text, where 'data' is the 8 byte encrypted string. Returns an 8-byte string of plaintext. def cipher (self, xl, xr, direction): Encrypts a 64-bit block of data where xl is the upper 32-bits and xr is the lower 32-bits. 'direction' is the direction to apply the cipher, either ENCRYPT or DECRYPT constants. returns a tuple of either encrypted or decrypted data of the left half and right half of the 64-bit block. def initCTR(self): Initializes CTR engine for encryption or decryption. def encryptCTR(self, data): Encrypts an arbitrary string and returns the encrypted string. The method can be called successively for multiple string blocks. def decryptCTR(self, data): Decrypts a string encrypted with encryptCTR() and returns the decrypted string. Private members: def __round_func (self, xl) Performs an obscuring function on the 32-bit block of data 'xl', which is the left half of the 64-bit block of data. Returns the 32-bit result as a long integer. """ # Cipher directions ENCRYPT = 0 DECRYPT = 1 # For the __round_func modulus = long (2) ** 32 def __init__ (self, key): if not key or len (key) < 8 or len (key) > 56: raise RuntimeError, "Attempted to initialize Blowfish cipher with key of invalid length: %s" % len (key) self.p_boxes = [ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B ] self.s_boxes = [ [ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A ], [ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 ], [ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 ], [ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 ] ] # Cycle through the p-boxes and round-robin XOR the # key with the p-boxes key_len = len (key) index = 0 for i in range (len (self.p_boxes)): val = (ord (key[index % key_len]) << 24) + \ (ord (key[(index + 1) % key_len]) << 16) + \ (ord (key[(index + 2) % key_len]) << 8) + \ ord (key[(index + 3) % key_len]) self.p_boxes[i] = self.p_boxes[i] ^ val index = index + 4 # For the chaining process l, r = 0, 0 # Begin chain replacing the p-boxes for i in range (0, len (self.p_boxes), 2): l, r = self.cipher (l, r, self.ENCRYPT) self.p_boxes[i] = l self.p_boxes[i + 1] = r # Chain replace the s-boxes for i in range (len (self.s_boxes)): for j in range (0, len (self.s_boxes[i]), 2): l, r = self.cipher (l, r, self.ENCRYPT) self.s_boxes[i][j] = l self.s_boxes[i][j + 1] = r self.initCTR() def cipher (self, xl, xr, direction): """Encryption primitive""" if direction == self.ENCRYPT: for i in range (16): xl = xl ^ self.p_boxes[i] xr = self.__round_func (xl) ^ xr xl, xr = xr, xl xl, xr = xr, xl xr = xr ^ self.p_boxes[16] xl = xl ^ self.p_boxes[17] else: for i in range (17, 1, -1): xl = xl ^ self.p_boxes[i] xr = self.__round_func (xl) ^ xr xl, xr = xr, xl xl, xr = xr, xl xr = xr ^ self.p_boxes[1] xl = xl ^ self.p_boxes[0] return xl, xr def __round_func (self, xl): a = (xl & 0xFF000000) >> 24 b = (xl & 0x00FF0000) >> 16 c = (xl & 0x0000FF00) >> 8 d = xl & 0x000000FF # Perform all ops as longs then and out the last 32-bits to # obtain the integer f = (long (self.s_boxes[0][a]) + long (self.s_boxes[1][b])) % self.modulus f = f ^ long (self.s_boxes[2][c]) f = f + long (self.s_boxes[3][d]) f = (f % self.modulus) & 0xFFFFFFFF return f def encrypt (self, data): if not len (data) == 8: raise RuntimeError, "Attempted to encrypt data of invalid block length: %s" % len(data) # Use big endianess since that's what everyone else uses xl = ord (data[3]) | (ord (data[2]) << 8) | (ord (data[1]) << 16) | (ord (data[0]) << 24) xr = ord (data[7]) | (ord (data[6]) << 8) | (ord (data[5]) << 16) | (ord (data[4]) << 24) cl, cr = self.cipher (xl, xr, self.ENCRYPT) chars = ''.join ([ chr ((cl >> 24) & 0xFF), chr ((cl >> 16) & 0xFF), chr ((cl >> 8) & 0xFF), chr (cl & 0xFF), chr ((cr >> 24) & 0xFF), chr ((cr >> 16) & 0xFF), chr ((cr >> 8) & 0xFF), chr (cr & 0xFF) ]) return chars def decrypt (self, data): if not len (data) == 8: raise RuntimeError, "Attempted to encrypt data of invalid block length: %s" % len(data) # Use big endianess since that's what everyone else uses cl = ord (data[3]) | (ord (data[2]) << 8) | (ord (data[1]) << 16) | (ord (data[0]) << 24) cr = ord (data[7]) | (ord (data[6]) << 8) | (ord (data[5]) << 16) | (ord (data[4]) << 24) xl, xr = self.cipher (cl, cr, self.DECRYPT) chars = ''.join ([ chr ((xl >> 24) & 0xFF), chr ((xl >> 16) & 0xFF), chr ((xl >> 8) & 0xFF), chr (xl & 0xFF), chr ((xr >> 24) & 0xFF), chr ((xr >> 16) & 0xFF), chr ((xr >> 8) & 0xFF), chr (xr & 0xFF) ]) return chars # ==== CBC Mode ==== def initCBC(self, iv=0): """Initializes CBC mode of the cypher""" assert struct.calcsize("Q") == self.block_size() self.cbc_iv = struct.pack("Q", iv) def encryptCBC(self, data): """ Encrypts a buffer of data using CBC mode. Multiple successive buffers (belonging to the same logical stream of buffers) can be encrypted with this method one after the other without any intermediate work. Each buffer must be a multiple of 8-octets (64-bits) in length. """ if type(data) != types.StringType: raise RuntimeError, "Can only work on 8-bit strings" if (len(data) % 8) != 0: raise RuntimeError, "Can only work with data in 64-bit multiples in CBC mode" xor = lambda t: ord(t[0]) ^ ord(t[1]) result = '' block_size = self.block_size() for i in range(0, len(data), block_size): p_block = data[i:i+block_size] pair = zip(p_block, self.cbc_iv) j_block = ''.join(map(chr, map(xor, pair))) c_block = self.encrypt(j_block) result += c_block self.cbc_iv = c_block return result def decryptCBC(self, data): if type(data) != types.StringType: raise RuntimeError, "Can only work on 8-bit strings" if (len(data) % 8) != 0: raise RuntimeError, "Can only work with data in 64-bit multiples in CBC mode" xor = lambda t: ord(t[0]) ^ ord(t[1]) result = '' block_size = self.block_size() for i in range(0, len(data), block_size): c_block = data[i:i+block_size] j_block = self.decrypt(c_block) pair = zip(j_block, self.cbc_iv) p_block = ''.join(map(chr, map(xor, pair))) result += p_block self.cbc_iv = c_block return result # ==== CTR Mode ==== def initCTR(self, iv=0): """Initializes CTR mode of the cypher""" assert struct.calcsize("Q") == self.block_size() self.ctr_iv = iv self._calcCTRBUF() def _calcCTRBUF(self): """Calculates one block of CTR keystream""" self.ctr_cks = self.encrypt(struct.pack("Q", self.ctr_iv)) # keystream block self.ctr_iv += 1 self.ctr_pos = 0 def _nextCTRByte(self): """Returns one byte of CTR keystream""" b = ord(self.ctr_cks[self.ctr_pos]) self.ctr_pos += 1 if self.ctr_pos >= len(self.ctr_cks): self._calcCTRBUF() return b def encryptCTR(self, data): """ Encrypts a buffer of data with CTR mode. Multiple successive buffers (belonging to the same logical stream of buffers) can be encrypted with this method one after the other without any intermediate work. """ if type(data) != types.StringType: raise RuntimeException, "Can only work on 8-bit strings" result = [] for ch in data: result.append(chr(ord(ch) ^ self._nextCTRByte())) return "".join(result) def decryptCTR(self, data): return self.encryptCTR(data) def block_size(self): return 8 def key_length(self): return 56 def key_bits(self): return 56 * 8 @staticmethod def testVectors(): import binascii # for more vectors see http://www.schneier.com/code/vectors.txt vectors = ( ('0000000000000000', '0000000000000000', '4EF997456198DD78'), ('FFFFFFFFFFFFFFFF', 'FFFFFFFFFFFFFFFF', '51866FD5B85ECB8A'), ('3000000000000000', '1000000000000001', '7D856F9A613063F2'), ('1111111111111111', '1111111111111111', '2466DD878B963C9D'), ('49E95D6D4CA229BF', '02FE55778117F12A', 'CF9C5D7A4986ADB5'), ('E0FEE0FEF1FEF1FE', '0123456789ABCDEF', 'C39E072D9FAC631D'), ('07A7137045DA2A16', '3BDD119049372802', '2EEDDA93FFD39C79'), ) ok = True for v in vectors: c = Blowfish(binascii.a2b_hex(v[0])) e = binascii.b2a_hex(c.encrypt(binascii.a2b_hex(v[1]))).upper() if e != v[2]: print "VECTOR TEST FAIL: expecting %s, got %s" % (repr(v), e) ok = False return ok class oceanbase(object): instant_key_list = [ # mergeserver 'ms_memory_limit', 'ms_memory_total', 'ms_memory_parser', 'ms_memory_transformer', 'ms_memory_ps_plan', 'ms_memory_rpc_request', 'ms_memory_sql_array', 'ms_memory_expression', 'ms_memory_row_store', 'ms_memory_session', 'ps_count', # chunkserver 'serving_version', 'old_ver_tablets_num', 'old_ver_merged_tablets_num', 'new_ver_tablets_num', 'new_ver_tablets_num', 'memory_used_default', 'memory_used_network', 'memory_used_thread_buffer', 'memory_used_tablet', 'memory_used_bi_cache', 'memory_used_block_cache', 'memory_used_bi_cache_unserving', 'memory_used_block_cache_unserving', 'memory_used_join_cache', 'memory_used_sstable_row_cache', 'memory_used_merge_buffer', 'memory_used_merge_split_buffer', # updateserver 'memory_total', 'memory_limit', 'memtable_total', 'memtable_used', 'total_rows', 'active_memtable_limit', 'active_memtable_total', 'active_memtable_used', 'active_total_rows', 'frozen_memtable_limit', 'frozen_memtable_total', 'frozen_memtable_used', 'frozen_total_rows', 'low_prio_queued_count', 'normal_prio_queued_count', 'high_prio_queued_count', 'hotspot_queued_count', # machine stat 'load1', 'load5', 'load15', 'MemTotal', 'MemFree', # mock 'timestamp', 'tenant','memstore limit','total memstore used','active sessions','cpu usage' ] delta_key_list = [ # sql 'active memstore used', 'active sessions', 'block cache hit', 'block cache miss', 'block index cache hit', 'block index cache miss', 'bloom filter cache hit', 'bloom filter cache miss', 'bloom filter filts', 'bloom filter passes', 'io read bytes', 'io read count', 'io write bytes', 'io write count', 'location cache hit', 'location cache miss', 'row cache hit', 'row cache miss', 'rpc deliver fail', 'rpc net delay', 'rpc net frame delay', 'rpc packet in', 'sql delete count', 'sql delete time', 'sql distributed count', 'sql insert count', 'sql insert time', 'sql local count', 'sql remote count', 'sql replace count', 'sql replace time', 'sql select count', 'sql select time', 'sql update count', 'sql update time', 'trans commit count', 'trans commit time', 'trans rollback count', 'time_delta' ] sum_key_list = [ 'tenant', 'total memstore used', 'memstore limit', 'major freeze trigger', 'timestamp' ] app_info = {'username':None, 'password':None} def __init__(self, dataid=None): self.__q = {} self.__stop = True self.__machine_stat = {} self.__host = Options.host self.__port = Options.port self.update_dataid(dataid) self.__cur_cluster_svrs = None self.__cur_tenant_id = 1 self.tenant = [] def update_dataid(self, dataid): if dataid: self.app_info = ObConfigHelper().get_app_info(dataid) def dosql(self, sql, host=None, port=None, database=None): cmd = "mysql -V" p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) err = p.wait() if err: raise Exception('please install mysql', cmd) if host is None: host = self.__host if port is None: port = self.__port if host is None: host = Options.host if port is None: port = Options.port if database is None: database = Options.database username = Options.user or self.app_info['username'] or Global.DEFAULT_USER password = Options.password or self.app_info['password'] or Global.DEFAULT_PASS if password == "": mysql = "mysql --connect_timeout=10 -c -s -N -A -h%s -P%d -u%s %s" % (host, port, username, database) else: mysql = "mysql --connect_timeout=10 -c -s -N -A -h%s -P%d -u%s -p'%s' %s" % (host, port, username, password, database) cmd = "%s -e \"%s\"" % (mysql, sql) DEBUG(self.dosql, "oceanbase.dosql : ", cmd) p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) output = p.communicate()[0] err = p.wait() if err: raise Exception('popen Fail', cmd) return output def show_sql_result(self, sql, host=None, port=None): if host is None: host = self.__host if port is None: port = self.__port if host is None: host = Options.host if port is None: port = Options.port if database is None: database = Options.database username = Options.user or self.app_info['username'] or Global.DEFAULT_USER password = Options.password or self.app_info['password'] or Global.DEFAULT_PASS if password == "": mysql = "mysql --connect_timeout=10 -c -s -N -A -h%s -P%d -u%s %s" % (host, port, username, database) else: mysql = "mysql --connect_timeout=10 -c -s -N -A -h%s -P%d -u%s -p'%s' %s" % (host, port, username, password, database) cmd = "%s -e \"%s\" | less" % (mysql, sql) curses.endwin() call("clear") DEBUG(self.dosql, "oceanbase.show_sql_result : ", cmd) os.system(cmd) curses.doupdate() def mysql(self, host=None, port=None): if host is None: host = self.__host if port is None: port = self.__port if host is None: host = Options.host if port is None: port = Options.port if database is None: database = Options.database username = Options.user or self.app_info['username'] or Global.DEFAULT_USER password = Options.password or self.app_info['password'] or Global.DEFAULT_PASS cmd = "mysql --connect_timeout=10 -c -A -h%s -P%d -u%s -p'%s' %s" % (host, port, username, password, database) environ['MYSQL_PS1'] = "(\u@\h) [%s]> " % self.app call('clear') try: call(cmd, shell=True) except KeyboardInterrupt: pass call('clear') def ssh(self, host): cmd = "ssh -o StrictHostKeyChecking=no %s" % host call('clear') try: call(cmd, shell=True) except KeyboardInterrupt: pass call('clear') def test_alive(self, fatal=True, do_false=None, do_true=None, host=None, port=None): if host is None: host = Options.host if port is None: port = Options.port try: oceanbase.dosql('select 1', host=host, port=port) if do_true is not None: do_true("Check oceanbase alive successfully! [%s:%d]" % (host, port)) except Exception: if do_false is not None: do_false("Can't connect oceanbase, plz check options!\n" "Options: [IP:%s] [PORT:%d] [USER:%s] [PASS:%s]" % (host, port, Options.user, Options.password)) if fatal: exit(1) else: return False return True def __check_schema(self): stat_ids = "stat_id in (10000,10004,10005,10006,30007,30008,30009,"\ "40000,40001,40002,40003,40004,40005,40006,40007,40008,40009,40010,40011,40012,40013,"\ "50000,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,"\ "60000,60002,60003,60005,130000,130001,130002,130004,"\ "190001,190005,190405,190401,190205,190201,190105,190101,190305,190301,190505,190501,190903,190003,190403,190203,"\ "190103,190303,190503,190901,190004,190404,190204,190104,190304,190504,190902"\ ") " sql = """ select current_time(), stat.con_id, stat.svr_ip, stat.svr_port, stat.name, stat.value from gv\$sysstat stat where stat.class IN (1,4,8,16,32,4096) and con_id = %s and (%s) """ % ( str(self.get_current_tenant()) , stat_ids) DEBUG(self.__check_schema, "oceanbase.check schema sql : ", sql) return sql def __get_cpu_usage(self): #sql = """SELECT t1.tenant_id,t2.zone, t1.svr_ip,t1.svr_port , round(cpu_quota_used/t4.max_cpu, 3) cpu_usage FROM __all_tenant_resource_usage t1 JOIN __all_server t2 ON (t1.svr_ip=t2.svr_ip and t1.svr_port=t2.svr_port) JOIN __all_resource_pool t3 ON (t1.tenant_id=t3.tenant_id) JOIN __all_unit_config t4 ON (t3.unit_config_id=t4.unit_config_id) WHERE report_time > date_sub(now(), INTERVAL 30 SECOND) AND t1.tenant_id IN (%s) ORDER BY t1.tenant_id, t2.zone, t1.svr_ip, t1.svr_port; """ % (str(self.get_current_tenant())) sql = """SELECT t1.con_id tenant_id,t2.zone, t1.svr_ip,t1.svr_port , round(t1.value/(100*t4.max_cpu), 3) cpu_usage FROM gv\$sysstat t1 JOIN __all_server t2 ON (t1.svr_ip=t2.svr_ip and t1.svr_port=t2.svr_port) JOIN __all_resource_pool t3 ON (t1.con_id=t3.tenant_id) JOIN __all_unit_config t4 ON (t3.unit_config_id=t4.unit_config_id) WHERE t1.con_id IN (%s) and t1.stat_id=140006 ORDER BY t1.con_id, t2.zone, t1.svr_ip, t1.svr_port; """ % (str(self.get_current_tenant())) res = self.dosql(sql) DEBUG(self.__get_cpu_usage, "oceanbase.get cpu usage res : ", res) r = dict() for one in res.split("\n")[:-1]: a = one.split("\t") tnt = a[0] #ip = a[2] #port = a[3] ip = '%s:%s' % (a[2],a[3]) name = "cpu usage" value = float(a[4]) if tnt not in r: r[tnt] = dict() if ip not in r[tnt]: r[tnt][ip] = dict() r[tnt][ip][name] = value DEBUG(self.__get_cpu_usage, "oceanbase.get cpu usage r: ", r) return r; def __get_all_stat(self): sql = self.__check_schema() res = self.dosql(sql) r = self.__get_cpu_usage() time = str(datetime.now()) now = datetime.strptime(time, '%Y-%m-%d %H:%M:%S.%f') if not self.__using_server_time(): time = str(datetime.now()) for one in res.split("\n")[:-1]: a = one.split("\t") if self.__using_server_time(): time = a[0] now = datetime.strptime(time, '%Y-%m-%d %H:%M:%S.%f') tnt = a[1] #ip = a[2] #port = a[3] ip = '%s:%s' % (a[2],a[3]) name = a[4] value = int(a[5]) if tnt not in r: r[tnt] = dict() if ip not in r[tnt]: r[tnt][ip] = dict() r[tnt][ip][name] = value r['time_delta'] = timedelta() r['timestamp'] = now r['tenant'] = int(a[1]) DEBUG(self.__get_all_stat, "oceanbase.__get_all_stat returned: ", r) DEBUG(self.__get_all_stat, "oceanbase.__get_all_stat finished with r['tenant']=%d" % r['tenant'], "") return r def __get_stat(self, stat): r = '' for k in stat.keys(): if type(stat[k]) == dict: r = r + self.__get_stat(stat[k]) + "," else: r = r + "'" + k + "':" + str(stat[k]) + "," return r.rstrip(',') def __print_stat(self, stat): now = datetime.now() s = now.strftime("%Y%m%d%H%M%S") if f_data_log != '': s = s + ',' + self.__get_stat(stat) f = open(f_data_log, "a") f.write(s+"\n") f.close() def sub_stat(self, now, prev): r = self.__sub_stat(now, prev) return r def __sub_stat(self, now, prev): r = {} for k in now.keys(): if k in self.instant_key_list: r[k] = now[k] elif k == 'time_delta': r[k] = now['timestamp'] - prev['timestamp'] else: if type(now[k]) == dict: r.update({k: self.__sub_stat(now[k], prev[k])}) elif type(now[k]) == int: r[k] = now[k] - prev[k] elif type(now[k]) == float: r[k] = round(now[k] - prev[k],2) else: DEBUG(self.__sub_stat, "unknown type of now['%s'] : " % k, type(now[k])) pass self.__print_stat(r) return r def __init_stat(self, stat): r = stat.copy() for k in r.keys(): if k == 'tenant': r[k] = stat[k] elif k == 'time_delta': r[k] = timedelta() else: if type(r[k]) == dict: r[k] = self.__init_stat(r[k]) elif type(r[k]) == type(datetime.min): r[k] = datetime.strptime('2016-11-11 00:00:00', "%Y-%m-%d %H:%M:%S") else: if k in ( 'tenant'): stat_sum[k] = 1 elif type(r[k]) == float: r[k] = 0.0 else: r[k] = 0 return r; def __op_stat(self, stat_sum, stat_new, op_type): r = stat_sum.copy() for k in stat_sum.keys(): #if k == 'time_delta': #DEBUG(self.__op_stat, "oceanbase.__op_stat 'time_delta' : ", stat_sum[k]) if type(stat_sum[k]) == dict: r[k] = self.__op_stat(stat_sum[k], stat_new[k], op_type) elif k == 'timestamp': r[k] = datetime.strptime('2016-11-11 00:00:00', "%Y-%m-%d %H:%M:%S") elif k in self.delta_key_list: if op_type == 'add': r[k] = stat_sum[k] + stat_new[k] elif op_type == 'sub': r[k] = stat_sum[k] - stat_new[k] else: r[k] = stat_new[k] #if k == 'time_delta': #DEBUG(self.__op_stat, "oceanbase.__op_stat 'time_delta' : ", stat_sum[k]) return r; def __update_oceanbase_stat_runner(self): DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner : ", "") q = self.__q prev = self.__get_all_stat() while not self.__stop: sleep(Options.interval) if self.__stop: break try: cur = self.__get_all_stat() except Exception: continue if str(oceanbase.get_current_tenant()) not in cur: # tid has changed before result returned, ignore it. prev = self.__get_all_stat() continue try: stat_new = self.__sub_stat(cur, prev) DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner stat_new :", stat_new) if len(q) == 0: stat_sum = self.__init_stat(prev) stat_sum['timestamp'] = stat_new['timestamp'] #stat_avg = stat_sum.copy() #q.appendleft(stat_avg) q.appendleft(stat_sum) stat_sum = q.popleft() #q.popleft() DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner stat_sum :", stat_sum) q.appendleft(stat_new) stat_sum = self.__op_stat(stat_sum, stat_new, 'add') DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner stat_sum :", stat_sum) #DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner stat_avg :", stat_avg) #stat_avg = self.__avg_stat(stat_sum, len(q) ) #DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner stat_avg :", stat_avg) if (len(q) > Global.MAX_LINES): r = q.pop() stat_sum = self.__op_stat(stat_sum, r, 'sub') #stat_avg = self.__avg_stat(stat_sum, len(q)) #q.appendleft(stat_avg) q.appendleft(stat_sum) except Exception, e: DEBUG(self.__update_oceanbase_stat_runner, "oceanbase.__update_oceanbase_stat_runner tenant changed from %d to %d , exception" % (prev['tenant'], cur['tenant']), e) q.clear() prev = cur pass prev = cur def __do_ssh(self, host, cmd): BUFFER_SIZE = 1 << 16 ssh = Popen(['ssh', '-o', 'PreferredAuthentications=publickey', '-o', 'StrictHostKeyChecking=no', host, cmd], stdout=PIPE, stderr=PIPE, preexec_fn=setsid, shell=False) output = ssh.communicate()[0] err = ssh.wait() if err: raise Exception('popen Fail', "%s: %s" % (host, cmd)) return output def __get_machine_stat(self, ip): cmd = ("cat <(date +%s%N) <(cat /proc/stat | head -1 | cut -d' ' -f 3-9) " "/proc/loadavg <(cat /proc/meminfo) <(echo END_MEM) " "<(cat /proc/net/dev | sed -n 3~1p) <(echo END_NET) " "<(cat /proc/diskstats | grep ' 8 ') <(echo END_DISK)") try: output = self.__do_ssh(ip, cmd) except Exception: return {} lines = output.split('\n') res = {} res['time_delta'] = float(lines[0]) / 1000 # ms lines = lines[1:] cpuinfo = lines[0] columns = cpuinfo.split(" ") res['user'] = int(columns[0]) res['nice'] = int(columns[1]) res['sys'] = int(columns[2]) res['idle'] = int(columns[3]) res['iowait'] = int(columns[4]) res['irq'] = int(columns[5]) res['softirq'] = int(columns[6]) res['total'] = 0 for c in columns: res['total'] += int(c) lines = lines[1:] loadavg = lines[0] columns = loadavg.split(" ") res['load1'] = columns[0] res['load5'] = columns[1] res['load15'] = columns[2] lines = lines[1:] # mem for line in lines: if 'END_MEM' == line: break kv = line.split() k = kv[0][:-1] v = kv[1] res[k] = int(v) * 1024 idx = lines.index('END_MEM') lines = lines[idx + 1:] # net res['net'] = {} for line in lines: if 'END_NET' == line: break colon = line.find(':') assert colon > 0, line name = line[:colon].strip() res['net'][name] = {} fields = line[colon+1:].strip().split() res['net'][name]['bytes_recv'] = int(fields[0]) res['net'][name]['packets_recv'] = int(fields[1]) res['net'][name]['errin'] = int(fields[2]) res['net'][name]['dropin'] = int(fields[3]) res['net'][name]['bytes_sent'] = int(fields[8]) res['net'][name]['packets_sent'] = int(fields[9]) res['net'][name]['errout'] = int(fields[10]) res['net'][name]['dropout'] = int(fields[11]) idx = lines.index('END_NET') lines = lines[idx + 1:] res['disk'] = {} SECTOR_SIZE = 512 for line in lines: # http://www.mjmwired.net/kernel/Documentation/iostats.txt if 'END_DISK' == line: break _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = line.split()[:11] res['disk'][name] = {} res['disk'][name]['rbytes'] = int(rbytes) * SECTOR_SIZE res['disk'][name]['wbytes'] = int(wbytes) * SECTOR_SIZE res['disk'][name]['reads'] = int(reads) res['disk'][name]['writes'] = int(writes) res['disk'][name]['rtime'] = int(rtime) res['disk'][name]['wtime'] = int(wtime) return res idx = lines.index('END_DISK') lines = lines[idx + 1:] def __update_server_stat_runner(self): prev = {} for ip in self.ip_list: prev[ip] = self.__get_machine_stat(ip) while not self.__stop: sleep(Options.machine_interval) shuffle(self.ip_list) for ip in self.ip_list: if self.__stop: break cur = self.__get_machine_stat(ip) try: result = self.__sub_stat(cur, prev[ip]) self.__machine_stat.update({ip: result}) except KeyError: pass prev[ip] = cur def __update_version(self): self.version = "Unknown" res = self.dosql("show variables like 'version_comment'") for one in res.split("\n")[:-1]: a = one.split("\t") self.version = a[1] self.app = oceanbase.dosql("select value from __all_sys_config_stat" " where name='appname' limit 1;")[:-1] def __using_server_time(self): return False def start(self): self.__stop = False self.__q = deque([]) self.__th = Thread(target=self.__update_oceanbase_stat_runner, args=()) self.__th.daemon = True self.__th.start() if Options.env == 'online': self.__update_ip_list() self.__machine_stat = {} self.__svr_th = Thread(target=self.__update_server_stat_runner, args=()) self.__svr_th.daemon = True self.__svr_th.start() def dump_queue(self): print self.__q def now(self): return self.__q[1] def stat_count(self): return len(self.__q) def latest(self, num=1): return list(self.__q)[0:num] def machine_stat(self): return self.__machine_stat def update_tenant_info(self): DEBUG(self.update_tenant_info, "oceanbase.update_tenant_info" , "") class Tenant: def __init__(self, tid, name, zone_list, selected): self.tenant_id = tid self.tenant_name = name self.zone_list = zone_list self.selected = selected self.svr_list = {"observer":[]} res = self.dosql("select tenant_id, tenant_name, zone_list from __all_tenant") self.tenant = [] flg = True for line in res.split("\n")[0:-1]: tnt = line.split("\t") self.tenant.append(Tenant(tnt[0], tnt[1], tnt[2], flg)) flg = False svrs = self.dosql("select server.svr_ip, server.svr_port, server.id, server.zone, server.inner_port, server.with_rootserver, server.status, pool.tenant_id, unit.migrate_from_svr_ip, unit.migrate_from_svr_port, server.zone from __all_server server join __all_unit unit on (server.svr_ip=unit.svr_ip and server.svr_port=unit.svr_port) join __all_resource_pool pool on (unit.resource_pool_id=pool.resource_pool_id) ") for line in svrs.rstrip("\n").split("\n"): svr = line.split("\t") for tnt in self.tenant: if tnt.tenant_id == svr[7]: tnt.svr_list["observer"].append({"zone":svr[10], "ip":svr[0], "port":svr[1], "role":svr[5]}) if svr[8] != '': tnt.svr_list["observer"].append({"zone":svr[10], "ip":svr[8], "port":svr[9], "role":svr[5]}) #self.__update_version() #self.__update_ip_list() self.__update_cur_cluster_info() self.__update_sample() # def update_cluster_info(self): # class Cluster: # def __init__(self, id, vip, port, role): # self.id = id # self.vip = vip # try: # self.port = int(port) # except ValueError: # self.port = 0 # self.role = role # self.selected = False # self.svr_list = {'chunkserver':[], 'mergeserver':[], 'rootserver':[], 'updateserver':[]} # res = self.dosql('SELECT cluster_id,cluster_vip,cluster_port,cluster_role FROM __all_cluster') # self.cluster = [] # for line in res.split("\n")[:-1]: # clu = line.split("\t") # self.cluster.append(Cluster(int(clu[0]), clu[1], clu[2], int(clu[3]))) # svrs = oceanbase.dosql("select svr_type,cluster_id,svr_ip,svr_port,svr_role from __all_server") # for line in svrs.rstrip("\n").split("\n"): # svr = line.split("\t") # for clu in self.cluster: # if clu.id == int(svr[1]): # clu.svr_list[svr[0]].append({'ip': svr[2], 'port': int(svr[3]), 'role': int(svr[4])}) # if Options.dataid: # self.update_lms() # self.__update_version() # self.__update_ip_list() # self.__update_cur_cluster_info() # self.__update_sample() def check_lms(self, say): if Options.dataid is not None: lms_list = self.app_info['lms_list'] if not lms_list or len(lms_list) <= 0: say('Get lms list fail, plz check' + ' [ dataid = %s, lms_list = %s ]' % (Options.dataid, lms_list)) else: for lms in lms_list: say('checking lms [%s:%s]' % lms) if oceanbase.test_alive(host=lms[0], port=lms[1], fatal=False, do_false=say, do_true=say): self.__host = lms[0] self.__port = lms[1] #oceanbase.update_cluster_info() oceanbase.update_tenant_info() return True return False def __update_sample(self): self.sample = self.__get_all_stat() DEBUG(self.__update_sample, "stat:" , self.sample) def __update_ip_list(self): ip_list = [] ip_map = {} for clu in self.tenant: svr_list = clu.svr_list for name in ("observer"): ip_list += [svr["ip"] for svr in svr_list[name]] for ip in [svr["ip"] for svr in svr_list[name]]: if ip in ip_map: ip_map[ip].append(name) else: ip_map[ip] = [name] self.ip_list = list(set(ip_list)) self.ip_map = ip_map # def __update_ip_list(self): # ip_list = [] # ip_map = {} # for clu in self.cluster: # svr_list = clu.svr_list # for name in ('chunkserver', 'mergeserver', 'updateserver', 'rootserver'): # ip_list += [svr['ip'] for svr in svr_list[name]] # for ip in [svr['ip'] for svr in svr_list[name]]: # if ip in ip_map: # ip_map[ip].append(name) # else: # ip_map[ip] = [name] # self.ip_list = list(set(ip_list)) # self.ip_map = ip_map def __update_cur_cluster_info(self): for tnt in self.tenant: if int(self.__cur_tenant_id)== int(tnt.tenant_id) : self.__cur_cluster_svrs = tnt.svr_list DEBUG(self.__update_cur_cluster_info, "oceanbase.__update_cur_cluster_info return tenant_id : %s, cluster_svrs : " % tnt.tenant_id , self.__cur_cluster_svrs) return self.__cur_cluster_svrs def update_lms(self): svrs = oceanbase.dosql("select svr_ip, svr_port from __all_server where with_rootserver = 1 limit 1") for line in svrs.rstrip("\n").split("\n"): svr = line.split("\t") ip = svr[0] try: port = int(svr[1]) self.__host = svr[0] self.__port = svr[1] except ValueError: return def set_current_tenant(self, tid): DEBUG(self.set_current_tenant, "oceanbase.set_current_tenant(tid='%s')" % (tid), "") self.__cur_tenant_id = tid def get_current_tenant(self): return self.__cur_tenant_id def get_tenant_svr(self, tid=None): for tnt in self.tenant: if True == tnt.selected: return tnt.svr_list def find_svr_list(self): if not self.__cur_cluster_svrs: self.__update_cur_cluster_info() return self.__cur_cluster_svrs def stop(self): self.__stop = True def switch_tenant(self, tid): for tnt in self.tenant: if tid == tnt.tenant_id: tnt.selected = True self.__q.clear() else: tnt.selected = False DEBUG(self.switch_tenant, "selected false!", tnt) DEBUG(self.switch_tenant, "oceanbase.switch_tenant : ", [" ".join([tnt.tenant_id, str(tnt.selected)]) for tnt in self.tenant]) self.__update_cur_cluster_info() self.__update_sample() class Page(object): def __init__(self, parent, layout, y, x, height, width): self.__parent = parent self.__layout = layout self.__widgets = [] self.__win = parent.derwin(height, width, y, x) self.__y = y self.__x = x self.__height = height self.__width = width self.border() self.__win.nodelay(1) self.__win.timeout(0) self.__win.keypad(1) self.move(y, x) self.resize(height, width) self.__cur_select = 0 self.__shown_widget_num = 0 self.cur_tenant_id = 1 self.__cur_tenant_id = 1 def get_current_tenant(self): return self.__cur_tenant_id def set_current_tenant(self, tid): DEBUG(self.set_current_tenant, "Page.set_current_tenant(tid='%s' )" % tid, "") self.__cur_tenant_id = tid def add_widget(self, widget): DEBUG(self.add_widget, "Page.add_widget(widget='%s') " % widget, "") if 0 == len(self.__widgets): widget.select(True) self.__widgets.append(widget) self.__rearrange() def update_widgets(self): pass def __rearrange(self): undeleted_widgets = filter(lambda w: False == w.delete(), self.__widgets) self.__layout.rearrange(0, 0, self.__height, self.__width, undeleted_widgets) def rearrange(self): self.__rearrange() def update(self): self.update_widgets() def clear_widgets(self): self.__widgets = [] def resize(self, height, width): self.__height = height self.__width = width self.__win.resize(height, width) self.__rearrange() def move(self, y, x): self.__x = x self.__y = y self.__win.mvderwin(y, x) self.__rearrange() def __reset_widgets(self): if len(self.__widgets) <= 0: return shown_widgets = self.shown_widgets() map(lambda w: w.select(False), self.__widgets) map(lambda w: w.delete(False), self.__widgets) self.__cur_select = 0 self.__widgets[self.__cur_select].select(True) self.__rearrange() def __delete_current_widget(self): shown_widgets = self.shown_widgets() if len(shown_widgets) <= 0: return shown_widgets[self.__cur_select].select(False) shown_widgets[self.__cur_select].delete(True) self.__cur_select -= 1 self.select_next() self.__rearrange() def border(self): pass def process_key(self, ch): if ch == ord('d'): self.__delete_current_widget() elif ch == ord('R'): self.__reset_widgets() elif ch == ord('m'): curses.endwin() oceanbase.mysql() curses.doupdate() elif ch == ord('j'): curses.endwin() w = self.selected_widget() oceanbase.ssh(w.host()) curses.doupdate() def redraw(self): self.erase() self.__layout.redraw(self.shown_widgets()) def getch(self): return self.__win.getch() def erase(self): self.__win.erase() def win(self): return self.__win def title(self): return 'Untitled' def select_next(self): shown_widgets = self.shown_widgets() if len(shown_widgets) <= 0: return shown_widgets[self.__cur_select].select(False) self.__cur_select = (self.__cur_select + 1) % len(shown_widgets) shown_widgets[self.__cur_select].select(True) def parent(self): return self.__parent def shown_widgets(self): '''Actually shown widgets that is all widgets except for 1. deleted widgets and, 2. couldn\'t display widgets as no space for them. ''' return filter(lambda w: w.show() and False == w.delete(), self.__widgets) def valid_widgets(self): '''All widgets excpet for the deleted widgets.''' return filter(lambda w: False == w.delete(), self.__widgets) def all_widgets(self): return self.__widgets def selected_widget(self): return self.shown_widgets()[self.__cur_select] def select_columns(self): if len(self.all_widgets()) > 0: all_widgets = [hc_widget.column_widget() for hc_widget in self.all_widgets()] columns = all_widgets[0].valid_columns() columns_ret = ColumnCheckBox('Select Columns', columns, self.__parent).run() for widget in all_widgets: for idx in range(0, len(columns)): widget.valid_columns()[idx].enable(columns[idx].enable(enable=True)) widget.update() [hc_widget.resize() for hc_widget in self.all_widgets()] self.rearrange() class Layout(object): def __init__(self): pass def redraw(self, widgets): for widget in widgets: try: if widget.show(): widget.redraw() except Exception: pass def __calc_widget_height(self, height, width, widgets): max_min_height = max([ widget.min_height() for widget in widgets ]) for widget in widgets: widget.min_height(max_min_height) wwidths = [ widget.min_width() for widget in widgets ] cur_width = 0 nline = 1 for wwidth in wwidths: if cur_width + wwidth <= width: cur_width += wwidth else: cur_width = wwidth nline += 1 widget_height = height / nline return max(widget_height, max_min_height) def rearrange(self, y, x, height, width, widgets): if height <= 0 or width <= 0 or len(widgets) <= 0: return 0 widget_height = self.__calc_widget_height(height, width, widgets) for widget in widgets: widget.show(False) cur_y = 0 cur_x = 0 for index,widget in enumerate(widgets): if cur_x + widget.min_width() > width: cur_y += widget_height cur_x = 0 try: widget.move(0, 0) widget.resize(widget_height, widget.min_width()) widget.move(cur_y + y, cur_x + x) widget.show(True) except curses.error: return index cur_x += widget.min_width() return len(widgets) class Widget(object): def __init__(self, min_height, min_width, parent, use_win=False): DEBUG(self.__init__, "Widget.__init__(%d, %d, %s)" % (min_height, min_width, parent), "") if use_win: self.__win = parent else: self.__win = parent.derwin(min_height, min_width, 0, 0) self.__min_height = min_height self.__min_width = min_width self.__height, self.__width = self.__win.getmaxyx() self.__y, self.__x = self.__win.getmaxyx() self.__select = False self.__show = False self.__deleted = False def resize(self, height, width): self.__height = height self.__width = width self.__win.resize(height, width) def move(self, y, x): self.__win.mvderwin(y, x) self.__y = y self.__x = x def mvwin(self, y, x): self.__win.mvwin(y, x) self.__y = y self.__x = x def min_height(self, height=None): if height: self.__min_height = height return self.__min_height def min_width(self, width=None): if width: self.__min_width = width return self.__min_width def geometry(self): return self.__y, self.__x, self.__height, self.__width def height(self): return self.__height def width(self): return self.__width def redraw(self): pass def refresh(self): self.__win.refresh() def erase(self): self.__win.erase() def win(self): return self.__win def select(self, select = None): if select is not None: self.__select = select return self.__select def win(self): return self.__win def show(self, show=None): if show is not None: self.__show = show return self.__show def update(self): pass def delete(self, delete=None): if delete is not None: self.__deleted = delete return self.__deleted class Column(object): def __init__(self, name, filter, width, duration=False, enable=True, sname=None): DEBUG(self.__init__, "Column.__init__(name='%s', filter='%s', width=%d) " % (name, filter, width), "") self.__name = name self.__filter = filter self.__width = width self.__duration = duration self.__enable = enable self.__sname = sname or name self.__valid = True def __str__(self): return self.name() + " (" + self.sname() + ")" def __repr__(self): return self.name() def __eq__(self, obj): return isinstance(obj, Column) and self.name() == obj.name() def name(self): return self.__name def sname(self): return self.__sname def header(self): return self.__sname.center(self.__width).upper() def value(self, stat): def seconds(td): return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / (10**6); d = self.__filter(stat) DEBUG(self.__init__, "Column.__init__ , %s : " % self.__name , d ) if type(d) == type(0.0): div = (seconds(stat['time_delta']) or 1.0) if self.__duration else 1.0 DEBUG(self.__init__, "Column.__init__ , div : " , div ) v = d / div if (v > 100): v = int(v) return mem_str(v).center(self.__width)[:self.__width] elif self.__sname == "PCT.": return percent_str(v).center(self.__width)[:self.__width] return ("%.2f" % v).center(self.__width)[:self.__width] elif type(d) == type(0): div = (seconds(stat['time_delta']) or 1.0) if self.__duration else 1.0 DEBUG(self.__init__, "Column.__init__ , div : " , div ) v = int(d / div) if (self.__sname == 'ni' or self.__sname == 'no'): return mem_str(v).center(self.__width)[:self.__width] return mem_str(v).center(self.__width)[:self.__width] elif d == "00:00:00": v = str(stat['time_delta']) return v[0:v.find('.')].center(self.__width)[:self.__width] elif type(d) == type(''): return d.center(self.__width)[:self.__width] def enable(self, enable=None): if enable is not None: self.__enable = enable return self.__enable def valid(self, valid=None): if valid is not None: self.__valid = valid return self.__valid class MachineStatWidget(Widget): def __init__(self, name, parent, border=True): self.__name = name self.__border = border width = 48 Widget.__init__(self, 25, width, parent) def host(self): return self.__name.split(':')[0] def type_str(self): return self.__name.split(':')[1] def redraw(self): if self.__border: if self.select(): self.win().attron(curses.color_pair(1) | curses.A_BOLD) self.win().box() self.win().attroff(curses.color_pair(1) | curses.A_BOLD) else: self.win().box() self.win().addstr(0, 2, " " + self.__name + " ", curses.color_pair(3)) try: stat = oceanbase.machine_stat()[self.host()] except KeyError: return if self.__border: # cpu info self.win().addstr(1, 2, "%-7s%4s%%" % ("CPU", "100"), curses.color_pair(7) | curses.A_BOLD) # self.win().attron(curses.color_pair(8)) self.win().addstr(1, 17, "%-7s%4.1f%%" % ("nice:", float(stat['nice']) / stat['total'] * 100)) self.win().addstr(2, 2, "%-7s%4.1f%%" % ("user:", float(stat['user']) / stat['total'] * 100)) self.win().addstr(2, 17, "%-7s%4.1f%%" % ("iowait:", float(stat['iowait']) / stat['total'] * 100)) self.win().addstr(3, 2, "%-7s%4.1f%%" % ("sys:", float(stat['sys']) / stat['total'] * 100)) self.win().addstr(3, 17, "%-7s%4.1f%%" % ("irq:", float(stat['irq']) / stat['total'] * 100)) self.win().addstr(4, 2, "%-7s%4.1f%%" % ("idle:", float(stat['idle']) / stat['total'] * 100)) self.win().addstr(4, 17, "%-7s%4.1f%%" % ("sirq:", float(stat['softirq']) / stat['total'] * 100)) # self.win().attroff(curses.color_pair(8)) # load self.win().addstr(1, 32, "%-7s%6s" % ("Load", "x-core"), curses.color_pair(7) | curses.A_BOLD) # self.win().attron(curses.color_pair(8)) self.win().addstr(2, 32, "%-7s%6s" % ("1 min:", stat['load1'])) self.win().addstr(3, 32, "%-7s%6s" % ("5 min:", stat['load5'])) self.win().addstr(4, 32, "%-7s%6s" % ("15 min:", stat['load15'])) # self.win().attroff(curses.color_pair(8)) # mem info self.win().addstr(6, 2, "%-6s%7s" % ("Mem", mem_str(stat['MemTotal'])), curses.color_pair(7) | curses.A_BOLD) self.win().addstr(7, 2, "%-6s%7s" % ("used:", mem_str(stat['MemTotal'] - stat['MemFree']))) self.win().addstr(8, 2, "%-6s%7s" % ("free:", mem_str(stat['MemFree']))) # net info self.win().addstr(6, 17, "%-9s%9s%9s" % ("Network", 'Rx/s', 'Tx/s'), curses.color_pair(7) | curses.A_BOLD) for idx,item in enumerate(stat['net'].items()): if idx > 5: break self.win().addstr(7 + idx, 17, "%-9s%9s%9s" % (item[0], mem_str(item[1]['bytes_recv'] * pow(10, 6) / float(stat['time_delta']), bit=True), mem_str(item[1]['bytes_sent'] * pow(10, 6) / float(stat['time_delta']), bit=True))) # disk io info self.win().addstr(14, 2, "%-5s%6s%9s" % ("Disk I/O", 'In/s', 'Out/s'), curses.color_pair(7) | curses.A_BOLD) idx = 15 for item in stat['disk'].items(): if idx >= self.height() - 1: break if item[1]['rbytes'] <= 0 and item[1]['wbytes'] <= 0: continue self.win().addstr(idx, 2, "%-5s%9s%9s" % (item[0], mem_str(item[1]['rbytes'] * pow(10, 6) / float(stat['time_delta'])), mem_str(item[1]['wbytes'] * pow(10, 6) / float(stat['time_delta'])))) idx += 1 class ColumnWidget(Widget): def __init__(self, name, columns, parent, border=True, colorindex=0): DEBUG(self.__init__, "ColumnWidget.__init__(name='%s', columns='%s', parent='%s')" % (name, columns, parent), "") self.__check_column_valid(columns) self.__name = name self.__columns = columns self.__border = border self.__colorindex = colorindex enabled_columns = filter(lambda c: c.enable(), self.valid_columns()) width = len(" ".join([c.header() for c in enabled_columns])) + 2 # 2 padding width = max(width, 18) if border: width += 2 Widget.__init__(self, Global.WIDGET_HEIGHT, width, parent) def __check_column_valid(self, columns): for c in columns: try: c.value(oceanbase.sample) except KeyError: c.valid(False) def valid_columns(self): return filter(lambda c: c.valid(), self.__columns) def update(self): enabled_columns = filter(lambda c: c.enable(), self.valid_columns()) width = len(" ".join([c.header() for c in enabled_columns])) + 2 # 2 border + 2 padding if self.__border: width += 2 self.min_width(width) def redraw(self): def stat_count(): return oceanbase.stat_count() def latest(num): return oceanbase.latest(num) enabled_columns = filter(lambda c: c.enable(), self.valid_columns()) DEBUG(self.redraw, "ColumnWidget.redraw(stat_count=%d, enabled_columns='%s') " % (oceanbase.stat_count(), enabled_columns), "") if self.__border: if self.select(): self.win().attron(curses.color_pair(1) | curses.A_BOLD) self.win().box() self.win().attroff(curses.color_pair(1) | curses.A_BOLD) else: self.win().box() if self.__name: self.win().addstr(0, 2, " " + self.__name + " ", curses.color_pair(3)) print_lines = min(stat_count(), self.height() - 3) self.win().addstr(1, 1, " " + " ".join([ c.header() for c in enabled_columns ]) + " ", curses.color_pair(2)) border_offset = 1 else: print_lines = min(stat_count(), self.height() - 1) self.win().addstr(0, 0, " " + " ".join([ c.header() for c in enabled_columns ]) + " ", curses.color_pair(2)) border_offset = 0 latest = latest(print_lines) DEBUG(self.redraw, "ColumnWidget.redraw latest stat[%s/%s]:" % (print_lines, stat_count()) , latest) li = [] for i in range(0, len(latest)): item = [] for c in enabled_columns: v = c.value(latest[i]) DEBUG(self.redraw, "ColumnWidget.redraw : item value: %s:%s" % (c, v), "") item.append(str(v)) li.append(" " + " ".join(item)) for ii in range(0, len(li)): if ii==0: self.win().addstr(ii + 1 + border_offset, border_offset, li[ii], curses.color_pair(10)) else: self.win().addstr(ii + 1 + border_offset, border_offset, li[ii], curses.color_pair(self.__colorindex)) #DEBUG(self.redraw, "ColumnWidget.redraw latest output :", li[3]) class HeaderColumnWidget(Widget): def __init__(self, name, columns, parent, padding=0, get_header=None, colorindex=0): DEBUG(self.__init__, "HeaderColumnWidget.__init__(name='%s', columns='%s', parent='%s')" % (name, columns, parent), "") self.__name = name self.__padding = padding self.__get_header = get_header Widget.__init__(self, Global.WIDGET_HEIGHT, 0, parent) self.__column_widget = ColumnWidget(None, columns, self.win(), False, colorindex) self.min_width(self.__column_widget.min_width() + 2) self.resize(self.min_height(), self.min_width()) def redraw(self): DEBUG(self.redraw, "HeaderColumnWidget.redraw() ", "") if self.select(): self.win().attron(curses.color_pair(1)) self.win().box() self.win().attroff(curses.color_pair(1)) else: self.win().box() nline = 1 if self.__get_header and oceanbase.stat_count() > 0: header = self.__get_header(oceanbase.now()) string = "" hitems = header.items() hitems.sort() DEBUG(self.redraw, "HeaderColumnWidget.redraw() header.items() ", hitems) for item in hitems : item_str = ": ".join([item[0], str(item[1])]) if (len(string) + len(item_str) + 2 > self.min_width() - 2): string = string[2:] self.win().addstr(nline, 1, string.center(self.min_width() - 2)) string = "" nline += 1 string += " " + item_str if string: string = string[2:] self.win().addstr(nline, 1, string.center(self.min_width() - 2)) self.__set_padding(nline) else: # fix ob 0.4.1 wired bug self.__set_padding(0) self.__column_widget.redraw() if self.__name: self.win().addstr(0, 2, " " + self.__name + " ", curses.color_pair(3)) time = strftime(" %Y-%m-%d %T ") self.win().addstr(0, self.width() - len(time) - 2, time, curses.color_pair(3)) def resize(self, height=None, width=None): DEBUG(self.resize, "height, width", "|".join([str(height), str(width)])) if height is not None and width is not None: Widget.resize(self, height, width) maxy, maxx = self.win().getmaxyx() self.__column_widget.resize(maxy - self.__padding - 2, maxx - 2) if height is None: height = self.__column_widget.min_height() + self.__padding + 2 self.resize(height, width) if width is None: width = self.__column_widget.min_width() + 2 self.min_width(width) self.resize(height, width) def move(self, y, x): self.win().mvderwin(y, x) self.__column_widget.win().mvderwin(self.__padding + 1, 1) def __set_padding(self, padding): self.min_height(padding + 2 + 2) maxy, maxx = self.win().getmaxyx() self.__padding = padding self.__column_widget.move(0, 0) self.__column_widget.resize(maxy - self.__padding - 2, maxx - 2) self.__column_widget.move(self.__padding + 1, 1) def update(self): self.__column_widget.update() def host(self): return self.__name.split(":")[0] def sql_port(self): return self.__name.split(":")[1] def column_widget(self): return self.__column_widget class StatusWidget(Widget): def __init__(self, parent): DEBUG(self.__init__, "StatusWidget.__init__(parent='%s') " % parent, "") self.__parent = parent maxy, maxx = parent.getmaxyx() Widget.__init__(self, 2, maxx, parent) self.resize(2, maxx) self.move(maxy - 2, 0) self.win().bkgd(curses.color_pair(1)) def redraw(self): now = strftime("%Y-%m-%d %T") maxy, maxx = self.win().getmaxyx() # tps = self.__get_tps() # qps = self.__get_qps() svr_list = oceanbase.find_svr_list()["observer"] statstr = "HOST: %s:%d " % (Options.host, Options.port) statstr += "RUMC: %d" % (len(svr_list)) try: self.win().addstr(1, maxx - len(now) - 2, now) self.win().addstr(1, 2, statstr) self.win().hline(0, 0, curses.ACS_HLINE, 1024) except curses.error: pass def __get_tps(self): if oceanbase.stat_count() > 1: last = oceanbase.now() insert = sum([item["SQL_INSERT_COUNT"] for item in last[str(self.cur_tenant_id)].values()]) replace = sum([item["SQL_REPLACE_COUNT"] for item in last[str(self.cur_tenant_id)].values()]) delete = sum([item["SQL_DELETE_COUNT"] for item in last[str(self.cur_tenant_id)].values()]) update = sum([item["SQL_UPDATE_COUNT"] for item in last[str(self.cur_tenant_id)].values()]) tps = float(insert + replace + delete + update) / seconds(last['time_delta']) return int(tps) else: return 0 def __get_qps(self): if oceanbase.stat_count() > 1: last = oceanbase.now() total = float(sum([item["SQL_SELECT_COUNT"] for item in last[oceanbase.__cur_tenant_id].values()])) qps = total / seconds(last['time_delta']) return int(qps) else: return 0 class HeaderWidget(Widget): def __init__(self, parent): DEBUG(self.__init__, "HeaderWidget.__init__(parent='%s') " % parent, "") maxy, maxx = parent.getmaxyx() Widget.__init__(self, 2, maxx, parent) self.__page = None self.win().bkgd(curses.color_pair(1)) def redraw(self): DEBUG(self.redraw, "HeaderWidget.redraw ", "") self.erase() if self.__page is None: try: self.win().addstr(0, 2, '1:Help 2:Gallery 3:SQL(ObServer) 4:History(30 minutes) 5:Bianque 6:Machine Stat 7-0:Blank', curses.A_BOLD) self.win().hline(1, 0, curses.ACS_HLINE, 1024, curses.A_BOLD) except curses.error: pass else: maxy, maxx = self.win().getmaxyx() shown_num = len(self.__page.shown_widgets()) valid_num = len(self.__page.valid_widgets()) all_num = len(self.__page.all_widgets()) shown_str = 'Shown: %d / Valid: %d / Total: %d' % (shown_num, valid_num, all_num) try: self.win().addstr(0, 2, self.__page.title(), curses.A_BOLD) self.win().addstr(0, maxx - len(shown_str) - 4, shown_str, curses.A_BOLD) self.win().hline(1, 0, curses.ACS_HLINE, 1024, curses.A_BOLD) except curses.error: pass def switch_page(self, page): DEBUG(self.switch_page, "HeaderWidget.switch_page(page='%s' " % (page) , "") self.__page = page class MessageBox(object): def __init__(self, parent, msg): self.msg = msg self.parent = parent maxy, maxx = parent.getmaxyx() width = min(maxx - 20, len(msg)) + 10 height = 5 y = (maxy - height) / 2 x = (maxx - width) / 2 self.win = parent.derwin(height, width, y, x) self.win.erase() self.win.attron(curses.color_pair(1)) self.win.box() self.win.attroff(curses.color_pair(1)) self.win.addstr(2, 4, self.msg, curses.color_pair(0)) def run(self, anykey=False): res = True while (1): c = self.win.getch() if anykey: break elif c == ord('y'): res = True break elif c == ord('n'): res = False break elif c == ord('q'): res = False break self.win.erase() self.win.refresh() return res class PopPad(Widget): def __init__(self, name, items, parent, **args): self.__offset_x = 0 self.__offset_y = 0 self.__name = name maxy, maxx = parent.getmaxyx() width = maxx - 100 height = 10 Widget.__init__(self, height, width, parent) self.resize(height, width) y = (maxy - height - self.__offset_y) / 2 x = (maxx - width - self.__offset_x) / 2 self.mvwin(self.__offset_y + y, self.__offset_x + x) self.move(self.__offset_y + y, self.__offset_x + x) def __do_key(self): ch = self.win().getch() stopFlg = False if ch in (ord("q"), ord("Q")): stopFlg = True def __redraw(self): self.win().erase() self.win().attron(curses.color_pair(1)) self.win().box() self.win().attroff(curses.color_pair(1)) self.win().addstr(0, 2, ' %s ' % self.__name, curses.color_pair(3)) def run(self): self.__redraw() while(True): if self.__do_key(): self.win().erase() self.win().refresh() return self.__redraw() self.win().refresh() class SelectionBox(Widget): __padding_left = 5 __padding_top = 0 __offset_y = 0 __offset_x = 0 __chr_list = 'abcdefghimorstuvwxyzABCDEFGHIMORSTUVWXYZ0123456789' __hot_key = False def __init__(self, name, items, parent, **args): self.__index = 0 self.__name = name self.items = items self.parent = parent self.__start_idx = 0 self.__stop_idx = 0 s = list(self.__chr_list) shuffle(s) self.__chr_list = ''.join(s) self.__offset_y = args.get('offset_y', self.__offset_y) self.__offset_x = args.get('offset_x', self.__offset_x) self.__hot_key = args.get('hot_key', self.__hot_key) maxy, maxx = parent.getmaxyx() width = max(len(name) + 6, max([len(it) + 5 for it in items])) height = min(len(items) + 2, maxy - self.__offset_y) Widget.__init__(self, height, width, parent) self.resize(height, width) y = (maxy - height - self.__offset_y) / 2 x = (maxx - width - self.__offset_x) / 2 self.mvwin(self.__offset_y + y, self.__offset_x + x) self.move(self.__offset_y + y, self.__offset_x + x) self.win().nodelay(1) self.win().timeout(0) self.win().keypad(1) self.__stop_idx = self.__start_idx + self.height() - 2 def __do_key(self): ch = self.win().getch() stop = False if self.__fm is not None and ch != -1: self.__fm() self.__fm = None elif ch in [ord('s'), ord('S')]: self.__index = -2 stop = True if ch in [ ord('j'), ord('\t'), ord('n'), ord('J'), ord('N'), curses.KEY_DOWN ]: if self.__index == len(self.items) - 1: self.__start_idx = 0 self.__stop_idx = self.__start_idx + self.height() - 2 self.__index = 0 elif self.__index == self.__stop_idx - 1: self.__start_idx = self.__start_idx + 1 self.__stop_idx = self.__stop_idx + 1 self.__index = self.__index + 1 else: self.__index = self.__index + 1 elif ch in [ ord('k'), ord('K'), ord('p'), ord('P'), curses.KEY_UP ]: if self.__index == 0: self.__start_idx = max(0, len(self.items) - self.height() + 2) self.__stop_idx = self.__start_idx + self.height() - 2 self.__index = len(self.items) - 1 elif self.__index == self.__start_idx: self.__start_idx = self.__start_idx - 1 self.__stop_idx = self.__stop_idx - 1 self.__index = self.__index - 1 else: self.__index = self.__index - 1 elif ch in [ ord('\n'), ord(' ') ]: stop = True elif ch in [ ord('q'), ord('Q') ]: self.__index = -1 stop = True elif not self.__hot_key: pass elif ch in [ ord(c) for c in self.__chr_list ]: self.__index = [ ord(c) for c in self.__chr_list ].index(ch) + self.__start_idx stop = True return stop def __redraw(self): self.win().erase() self.win().attron(curses.color_pair(1)) self.win().box() self.win().attroff(curses.color_pair(1)) self.win().addstr(0, 2, ' %s ' % self.__name, curses.color_pair(3)) for idx, item in enumerate(self.items): if idx < self.__start_idx or idx >= self.__stop_idx: continue if not self.__hot_key: line = item.center(self.width() - 2) elif idx - self.__start_idx < len(self.__chr_list): pref = ' %s)' % self.__chr_list[idx - self.__start_idx] line = pref + item.center(self.width() - 5) else: pref = ' ' line = pref + item.center(self.width() - 5) flag = 0 if idx == self.__index: flag = curses.A_BOLD | curses.color_pair(5) self.win().addstr(idx - self.__start_idx + self.__padding_top + 1, 1, item, flag) def run(self, first_movement=None): self.__fm = first_movement res = True while (1): if self.__do_key(): self.win().erase() self.win().refresh() return self.__index self.__redraw() self.win().refresh() class ColumnCheckBox(Widget): __padding_left = 5 __padding_top = 0 __margin_top = 4 __margin_bottom = 4 __offset_y = 0 __offset_x = 0 def __init__(self, name, items, parent, **args): self.__index = 0 self.__name = name self.items = items self.parent = parent self.__start_idx = 0 self.__stop_idx = 0 self.__margin_top = args.get('marging_top', self.__margin_top) self.__margin_bottom = args.get('marging_bottom', self.__margin_bottom) self.__offset_y = args.get('offset_y', self.__offset_y) self.__offset_x = args.get('offset_x', self.__offset_x) maxy, maxx = parent.getmaxyx() width = max(len(name) + 6, max([len(str(it)) + 9 for it in items])) height = min(len(items) + 2, maxy - self.__offset_y - self.__margin_top - self.__margin_bottom) Widget.__init__(self, height, width, parent) self.resize(height, width) y = (maxy - height - self.__offset_y) / 2 x = (maxx - width - self.__offset_x) / 2 self.mvwin(self.__offset_y + y, self.__offset_x + x) self.move(self.__offset_y + y, self.__offset_x + x) self.win().nodelay(1) self.win().timeout(0) self.win().keypad(1) self.__stop_idx = self.__start_idx + self.height() - 2 def __do_key(self): ch = self.win().getch() stop = False if self.__fm is not None and ch != -1: self.__fm() self.__fm = None if ch in [ ord('j'), ord('\t'), ord('n'), ord('J'), ord('N'), curses.KEY_DOWN ]: if self.__index == len(self.items) - 1: self.__start_idx = 0 self.__stop_idx = self.__start_idx + self.height() - 2 self.__index = 0 elif self.__index == self.__stop_idx - 1: self.__start_idx = self.__start_idx + 1 self.__stop_idx = self.__stop_idx + 1 self.__index = self.__index + 1 else: self.__index = self.__index + 1 elif ch in [ ord('k'), ord('K'), ord('p'), ord('P'), curses.KEY_UP ]: if self.__index == 0: self.__start_idx = max(0, len(self.items) - self.height() + 2) self.__stop_idx = self.__start_idx + self.height() - 2 self.__index = len(self.items) - 1 elif self.__index == self.__start_idx: self.__start_idx = self.__start_idx - 1 self.__stop_idx = self.__stop_idx - 1 self.__index = self.__index - 1 else: self.__index = self.__index - 1 elif ch in [ ord(' ') ]: self.items[self.__index].enable(not self.items[self.__index].enable()) elif ch in [ ord('\n') ]: stop = True elif ch in [ ord('q'), ord('Q') ]: self.__index = -1 stop = True return stop def __redraw(self): self.win().erase() self.win().attron(curses.color_pair(1)) self.win().box() self.win().attroff(curses.color_pair(1)) self.win().addstr(0, 2, ' %s ' % self.__name, curses.color_pair(3)) for idx, item in enumerate(self.items): if idx < self.__start_idx or idx >= self.__stop_idx: continue pref = ' [%s]' % (item.enable() and 'X' or ' ') line = pref + str(item).center(self.width() - 6) flag = 0 if idx == self.__index: flag = curses.A_BOLD | curses.color_pair(5) self.win().addstr(idx - self.__start_idx + self.__padding_top + 1, 1, line, flag) def run(self, first_movement=None): self.__fm = first_movement res = True while (1): if self.__do_key(): self.win().erase() self.win().refresh() return self.__index self.__redraw() self.win().refresh() if self.__index == -1: return [] else: return self.items class InputBox(Widget): def __init__(self, parent, prompt="Password", password=False, width=30): self.__index = 0 self.__name = "name" self.__offset_x = 0 self.__offset_y = 0 self.__password = password height = 3 win = curses.newwin(0, 0, 0, 0) Widget.__init__(self, height, width, win, True) self.resize(height, width) maxy, maxx = parent.getmaxyx() y = (maxy - height - self.__offset_y) / 2 x = (maxx - width - self.__offset_x) / 2 self.mvwin(y, x) win.box() win.refresh() self.__prompt = prompt + ": " win.addstr(1, 1, self.__prompt) win.move(1, 1 + len(self.__prompt)) self.__textbox = curses.textpad.Textbox(win) self.__result = "" def validator(self, ch): y, x = self.win().getyx() if ch == 127: new_x = max(x - 1, len(self.__prompt) + 1) self.win().delch(y, new_x) self.win().insch(' ') self.win().move(y, new_x) self.__result = self.__result[:-1] return 0 elif ch == 7 or ch == 10: # submit return 7 elif x == self.width() - 2: return 0 elif ch < 256: self.__result += chr(ch) if self.__password: return ord('*') else: return ch def run(self): curses.noecho() curses.curs_set(1) self.__textbox.edit(self.validator) curses.curs_set(0) return self.__result class GalleryPage(Page): def __add_widgets(self): DEBUG(self.__add_widgets, "GalleryPage.__add_widgets() tenant_id : ", self.cur_tenant_id) time_widget = ColumnWidget("TIME-TENANT", [ Column("time", lambda stat:stat["timestamp"].strftime("%H:%M:%S"), 8, False), Column("tenant", lambda stat:str(stat["tenant"]), 6, False ), ], self.win(), True, 3) sql_count_widget = ColumnWidget("SQL COUNT", [ Column("sel.", lambda stat:sum([item["sql select count"] for item in stat[str(self.cur_tenant_id)].values()]), 6, True), Column("ins.", lambda stat:sum([ item["sql insert count"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("upd.", lambda stat:sum([ item["sql update count"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("del.", lambda stat:sum([ item["sql delete count"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("rep.", lambda stat:sum([ item["sql replace count"] for item in stat[str(self.cur_tenant_id)].values() ]), 5, True), Column("cmt.", lambda stat:sum([ item["trans commit count"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("rol.", lambda stat:sum([ item["trans rollback count"] for item in stat[str(self.cur_tenant_id)].values() ]), 4, True), ], self.win(), True, 6) sql_rt_widget = ColumnWidget("SQL RT", [ Column("sel.", lambda stat: (sum([ item["sql select time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["sql select count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 6, False), Column("ins.", lambda stat: (sum([ item["sql insert time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["sql insert count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 6, False), Column("upd.", lambda stat: (sum([ item["sql update time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["sql update count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 6, False), Column("del.", lambda stat: (sum([ item["sql delete time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["sql delete count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 6, False), Column("rep.", lambda stat: (sum([ item["sql replace time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["sql replace count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 5, False), Column("cmt.", lambda stat: (sum([ item["trans commit time"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["trans commit count"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000), 6, False), ], self.win(), True, 8) net_rt_widget = ColumnWidget("RPC", [ Column("fail", lambda stat:sum([item["rpc deliver fail"] for item in stat[str(self.cur_tenant_id)].values()]), 4, True), Column("net", lambda stat:sum([ item["rpc net delay"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["rpc packet in"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000, 4, False), Column("frame", lambda stat:sum([ item["rpc net frame delay"] for item in stat[str(self.cur_tenant_id)].values() ]) / float(sum([ item["rpc packet in"] for item in stat[str(self.cur_tenant_id)].values() ]) or 1) / 1000, 4, False), ], self.win(), True, 0) memory_widget = ColumnWidget("MEMORY(T)", [ Column("⊿active", lambda stat: sum([item["active memstore used"] for item in stat[str(self.cur_tenant_id)].values()]), 7, True), Column("TOTAL", lambda stat: sum([item["total memstore used"] for item in stat[str(self.cur_tenant_id)].values()]), 7, False), Column("PCT.", lambda stat: sum([item["total memstore used"] for item in stat[str(self.cur_tenant_id)].values()]) / float(sum([item["memstore limit"] for item in stat[str(self.cur_tenant_id)].values()]) or 1), 6, False) ], self.win(), True, 8) iops_widget = ColumnWidget("IOPS", [ Column("SES.", lambda stat: sum([item["active sessions"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("ior", lambda stat: sum([item["io read count"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), Column("ior-sz", lambda stat: sum([item["io read bytes"] for item in stat[str(self.cur_tenant_id)].values() ]), 7, True), Column("iow", lambda stat: sum([item["io write count"] for item in stat[str(self.cur_tenant_id)].values() ]), 5, True), Column("iow-sz", lambda stat: sum([item["io write bytes"] for item in stat[str(self.cur_tenant_id)].values() ]), 6, True), ], self.win(), True, 6) self.add_widget(time_widget) self.add_widget(sql_count_widget) self.add_widget(sql_rt_widget) self.add_widget(net_rt_widget) self.add_widget(memory_widget) self.add_widget(iops_widget) def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) try: self.__add_widgets() except curses.error: pass def title(self): return "Gallery" def process_key(self, ch): if ch == ord('j'): pass else: Page.process_key(self, ch) class BianquePage(Page): def update_widgets(self): pass def title(self): return "Bianque" def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) try: self.update_widgets() except curses.error: pass class HistoryPage(Page): def update_widgets(self): pass def title(self): return "History" def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) try: self.update_widgets() except curses.error: pass #DEV = file("/Users/mq/Downloads/dooba.log", "w") DEV = file("/dev/null","w") def DEBUG(*args): global DEV if Options.debug : print >> DEV, "line %s :%s in [%s] %s %s" % (sys._getframe().f_lineno, time.asctime(), args[0].func_name, args[1], args[2]) class TableApiPage(Page): def __add_widgets(self): tid = oceanbase.get_current_tenant() DEBUG(self.__add_widgets, "cur_tenant_id", tid) time_widget = ColumnWidget("TIME-TENANT", [ Column("timestamp", lambda stat: stat["timestamp"].strftime("%H:%m:%S"), 10, True), Column("tenant", lambda stat: tid, 10, True ), ], self.win()) tableapi_widget = ColumnWidget("TABLE API ROWS", [ Column("get", lambda stat:(sum([ item["multi retrieve rows"] for item in stat[str(tid)].values() ]) + sum([ item["single retrieve execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("put", lambda stat:(sum([ item["multi insert_or_update rows"] for item in stat[str(tid)].values()]) + sum([ item["single insert_or_update execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("del", lambda stat:(sum([ item["multi delete rows"] for item in stat[str(tid)].values() ]) + sum([ item["single delete execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("ins", lambda stat:(sum([ item["multi insert rows"] for item in stat[str(tid)].values() ]) + sum([ item["single insert execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("upd", lambda stat:(sum([ item["multi update rows"] for item in stat[str(tid)].values() ]) + sum([ item["single update execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("repl", lambda stat:(sum([ item["multi replace rows"] for item in stat[str(tid)].values() ]) + sum([ item["single replace execute count"] for item in stat[str(tid)].values() ])), 6, True), Column("query", lambda stat:(sum([ item["query row count"] for item in stat[str(tid)].values() ])), 6, True), ], self.win()) tableapi_widget2 = ColumnWidget("TABLE API OPS", [ Column("get", lambda stat:(sum([ item["multi retrieve execute count"] for item in stat[str(tid)].values()])),6, True), Column("put", lambda stat:(sum([ item["multi insert_or_update execute count"] for item in stat[str(tid)].values()])),6, True), Column("del", lambda stat:(sum([ item["multi delete execute count"] for item in stat[str(tid)].values()])),6, True), Column("ins", lambda stat:(sum([ item["multi insert execute count"] for item in stat[str(tid)].values()])),6, True), Column("upd", lambda stat:(sum([ item["multi update execute count"] for item in stat[str(tid)].values()])),6, True), Column("repl", lambda stat:(sum([ item["multi replace execute count"] for item in stat[str(tid)].values()])),6, True), Column("query", lambda stat:(sum([ item["query count"] for item in stat[str(tid)].values()])),6, True), ], self.win()) tableapi_widget3 = ColumnWidget("Table API RT", [ Column("get", lambda stat:(sum([ item["multi retrieve execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi retrieve execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("put", lambda stat:(sum([ item["multi insert_or_update execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi insert_or_update execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("del", lambda stat:(sum([ item["multi delete execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi delete execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("ins", lambda stat:(sum([ item["multi insert execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi insert execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("upd", lambda stat:(sum([ item["multi update execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi update execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("repl", lambda stat:(sum([ item["multi replace execute time"] for item in stat[str(tid)].values() ]) / float(sum([ item["multi replace execute count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), Column("query", lambda stat:(sum([ item["query time"] for item in stat[str(tid)].values() ]) / float(sum([ item["query count"] for item in stat[str(tid)].values() ]) or 1) / 1000), 6), ], self.win()) self.add_widget(time_widget) self.add_widget(tableapi_widget2) self.add_widget(tableapi_widget) self.add_widget(tableapi_widget3) def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) try: self.__add_widgets() except curses.error: pass def title(self): return "Table API" def process_key(self, ch): if ch == ord('j'): pass else: Page.process_key(self, ch) class SQLPage(Page): def update_widgets(self): DEBUG(self.update_widgets, "SQLPage.update_widgets() ", "") def observer_info(stat, ip): def try_add(name, key, wrapper): try: if key in oceanbase.delta_key_list: result[name] = wrapper(int(stat[str(oceanbase.get_current_tenant())][ip][key] / seconds(stat['time_delta']))) else: result[name] = wrapper(stat[str(oceanbase.get_current_tenant())][ip][key]) DEBUG(try_add,"SQLPage.try_add(name='%s', key='%s', wrapper='%s', value='%s'" % (name, key, wrapper, result[name]), "") except Exception as e: DEBUG(try_add, "SQLPage.try_add(name='%s', key='%s', wrapper='%s' exception :" % (name, key, wrapper) , e) def try_add2(name, key1, key2, wrapper): try: result[name] = wrapper(float(stat[str(oceanbase.get_current_tenant())][ip][key1] )/ float((stat[str(oceanbase.get_current_tenant())][ip][key1] + stat[str(oceanbase.get_current_tenant())][ip][key2]) or 1)) DEBUG(try_add2, "wrapper %s" % (name) , (stat[str(oceanbase.get_current_tenant())][ip][key1] + stat[str(oceanbase.get_current_tenant())][ip][key2])) DEBUG(try_add2, "wrapper %s" % (name) , stat[str(oceanbase.get_current_tenant())][ip][key1]) DEBUG(try_add2, "%s result " % (name) , result[name]) except Exception as e: DEBUG(try_add2, "SQLPage.try_add2(name='%s', key1='%s', key2='%s' ) exception" % (name ,key1 ,key2), e) pass result = dict() try_add("Active Sess", "active sessions", count_str) try_add("IO-R Cnt", "io read count", mem_str) try_add("IO-R Size", "io read bytes", mem_str) try_add("IO-W Cnt", "io write count", mem_str) try_add("IO-W Size", "io write bytes", mem_str) try_add("CPU", "cpu usage", percent_str) try_add2("Cache-Row Hit", "row cache hit", "row cache miss", percent_str) try_add2("Cache-Loc Hit", "location cache hit", "location cache miss", percent_str) try_add2("Cache-Blk Hit", "block cache hit", "block cache miss", percent_str) try_add2("Cache-BI Hit", "block index cache hit", "block index cache miss", percent_str) #try_add2("B-F-cache hit", "bloom filter cache hit", "bloom filter cache miss", percent_str) return result svr = "observer" self.clear_widgets() DEBUG(self.update_widgets, "SQLPage.update_widgets svr_list :", oceanbase.get_tenant_svr()) i = 0 ci = 0 for ms in oceanbase.get_tenant_svr()[svr]: i = i+1 if i % 2 == 0 : ci = 6 else: ci = 8 zone = ms['zone'] #ip = ms['ip'] #port = ms['port'] ip = '%s:%s' % (ms['ip'], ms['port']) DEBUG(observer_info, "oceanbase.get_current_tenant()", oceanbase.get_current_tenant()) f = ColumnFactory(svr, ip) DEBUG(self.update_widgets, "column factory:", f) #widget = HeaderColumnWidget( '%s:%s:%d' % (zone,ip,int(port)), [ widget = HeaderColumnWidget( '%s:%s' % (zone,ip), [ #default(SQL) f.count("Sql Select Count", "ssc", "sql select count", duration=True, enable=True), f.time("Sql Select Time", "ssrt", "sql select time","sql select count" , duration=False,enable=True), f.count("Sql Insert Count", "sic", "sql insert count", duration=True, enable=True), f.time("Sql Insert Time", "sirt", "sql insert time","sql insert count", duration=False, enable=True), f.count("Sql Update Count", "suc", "sql update count", duration=True, enable=True), f.time("Sql Update Time", "surt", "sql update time", "sql update count", duration=False, enable=True), f.count("Sql Delete Count", "sdc", "sql delete count", duration=True, enable=True), f.time("Sql Delete Time", "sdrt", "sql delete time", "sql delete count", duration=False, enable=True), f.count("Sql Replace Count", "src", "sql replace count", duration=True, enable=True), f.time("Sql Replace Time", "srrt", "sql replace time","sql replace count", duration=False, enable=True), f.count("Trans Commit Count", "tcc", "trans commit count", duration=True, enable=True), f.time("Trans Commit Time", "tcrt", "trans commit time","trans commit count", duration=False, enable=True), f.count("Sql Local Count", "slc", "sql local count", duration=True, enable=True), f.count("Sql Remote Count", "src", "sql remote count", duration=True, enable=True), #f.count("Sql Distributed Count", "sdc", "sql distributed count", duration=True,enable=True), #f.count("Inner SQL Connection Execute Count", "iscec", "inner sql connection execute count"), #f.time("Inner SQL Connection Execute Time", "iscet", "inner sql connection execute time"), #inner packet #f.count("RPC Packet In", "rpci", "rpc packet in"), #f.count("RPC Packet In Bytes", "rpci(B)", "rpc packet in bytes"), #f.count("RPC Packet Out", "rpco", "rpc packet out"), #f.count("RPC Packet Out Bytes", "rpco(B)", "rpc packet out bytes"), #f.count("RPC Deliver Fail", "rpc fail", "rpc deliver fail"), #f.time("RPC Net Delay", "rpc delay", "rpc net delay"), #f.count("RPC Net Frame Delay", "rpc frame delay", "rpc net frame delay"), #outer packet #f.count("MySQL Packet In", "mpci", "mysql packet in"), #f.count("MySQL Packet In Bytes", "mpci(B)", "mysql packet in bytes"), #f.count("MySQL Packet Out", "mpco", "mysql packet out"), #f.count("MySQL Packet Out Bytes", "mpco(B)", "mysql packet out bytes"), #f.count("MySQL Deliver Fail", "mdf", "mysql deliver fail"), #request queue #f.count("Request Enqueue Count", "enqueue", "request enqueue count"), #f.count("Request Dequeue Count", "dequeue", "request dequeue count"), #f.time("Request Queue Time", "QT", "request queue time"), #transmition #f.time("Trans Commit Log Time", "tclt", "trans commit log time"), #f.count("Trans Commit Log Sync Count", "tclsc(sync)", "trans commit log sync count"), #f.count("Trans Commit LOg Submit Count", "tclsc(submit)", "trans commit log submit count"), #f.count("Trans System Trans Count", "tstc", "trans system trans count"), #f.count("Trans User Trans Count", "tutc", "trans user trans count"), #f.count("Trans Start Count", "tsc", "trans start count"), #f.count("Trans Total Used Time", "ttut", "trans total used time"), #f.count("Trans Rollback Count", "trc", "trans rollback count"), #f.time("Trans Rollback Time", "trt", "trans rollback time"), #f.count("Trans Timeout Count", "ttc", "trans timeout count"), #f.count("Trans Single Partition Count", "tspc", "trans single partition count"), #cache #FIXME #f.count("Row Cache Hit", "rch", "row cache hit"), #f.count("Row Cache Miss", "rcm", "row cache miss"), #f.count("Block Index Cache Hit", "bich", "block index cache hit"), #f.count("Block Index Cache Miss", "bicm", "block index cache miss"), #f.count("Bloom Filter Cache Hit", "bfch", "bloom filter cache hit"), #f.count("Bloom Filter Cache Miss", "bfcm", "bloom filter cache miss"), #f.count("Bloom Filter Filts", "bff", "bloom filter filts"), #f.count("Bloom Filter Passes", "bfp", "bloom filter passes"), #f.count("Block Cache Hit", "bch", "block cache hit"), #f.count("Block Cache Miss", "bcm", "block cache miss"), #f.count("Location Cache Hit", "lch", "location cache hit"), #f.count("Location Cache Miss", "lcm", "location cache miss"), #resources #f.count("Active Sessions", "as", "active sessions"), #f.count("IO Read Count", "iorc", "io read count"), #f.count("IO Read Delay", "iord", "io read delay"), #f.count("IO Read Bytes", "ior(B)", "io read bytes"), #f.count("IO Write Count", "iowc", "io write count"), #f.count("IO Write Delay", "iowd", "io write delay"), #f.count("IO Write Bytes", "iow(B)", "io write bytes"), #f.count("Memstore Scan Count", "msc", "memstore scan count"), #f.count("Memstore Scan Succ Count", "mssc", "memstore scan succ count"), #f.count("Memstore Scan Fail Count", "msfc", "memstore scan fail count"), #f.count("Memstore Get Count", "mgc", "memstore get count"), #f.count("Memstore Get Succ Count", "mgsc", "memstore get succ count"), #f.count("Memstore Get Fail Count", "mgfc", "memstore get fail count"), #f.count("Memstore Apply Count", "mac", "memstore apply count"), #f.count("Memstore Apply Succ Count", "masc", "memstore apply succ count"), #f.count("Memstore Apply Fail Count", "mafc", "memstore apply fail count"), #f.count("Memstore Row Count", "mrc", "memstore row count"), #f.time("Memstore Get Time", "mgt", "memstore get time"), #f.time("Memstore Scan Time", "mst", "memstore scan time"), #f.time("Memstore Apply Time", "mat", "memstore apply time"), #f.count("Memstore Read Lock Succ Count", "mrlsc", "memstore read lock succ count"), #f.count("Memstore Read Lock Fail Count", "mrlfc", "memstore read lock fail count"), #f.count("Memstore Write Lock Succ Count", "mwlsc", "memstore write lock succ count"), #f.count("Memstore Write Lock Fail Count", "mwlfc", "memstore write lock fail count"), #f.time("Memstore Wait Write Lock Time", "mwwlt", "memstore wait write lock time"), #f.time("Memstore Wait Read Lock Time", "mwrlt", "memstore wait read lock time"), #f.count("IO Read Micro Index Count", "iormic", "io read micro index count"), #f.count("IO Read Micro Index Bytes", "iormib", "io read micro index bytes"), #f.count("IO Prefetch Micro Block Count", "iopmbc", "io prefetch micro block count"), #f.count("IO Prefetch Micro Block Bytes", "iopmbb", "io prefetch micro block bytes"), #f.count("IO Read Uncompress Micro Block Count", "iorumbc", "io read uncompress micro block count"), #f.count("IO Read Uncompress Micro Block Bytes", "iorumbb", "io read uncompress micro block bytes"), #f.count("Active Memstore Used", "amu", "active memstore used"), #f.count("Total Memstore Used", "tmu", "total memstore used"), #f.count("Major Freeze Trigger", "mft", "major freeze trigger"), #f.count("Memstore Limit", "ml", "memstore limit"), #f.count("Min Memory Size", "mms(min)", "min memory size"), #f.count("Max Memory Size", "mms(max)", "max memory size"), #f.count("Memory Usage", "mu", "memory usage"), #f.count("Min CPUS", "mc(min)", "min cpus"), #f.count("Max CPUS", "mc(max)", "max cpus"), #meta #f.count("Refresh Schema Count", "rsc", "refresh schema count"), #f.time("Refresh Schema Time", "rst", "refresh schema time"), #f.count("Partition Table Operator Get Count", "ptogc", "partition table operator get count"), #f.time("Partition Table Operator Get Time", "ptogt", "partition table operator get time"), #clog #f.count("Submitted To Sliding Window Log Count", "sswlc", "submitted to sliding window log count"), #f.count("Submitted To Sliding Window Log Size", "sswls", "submitted to sliding window log size"), #f.count("Index Log Flushed Count", "ilfc", "index log flushed count"), #f.count("Index Log Flushed Clog Size", "ilfcs", "index log flushed clog size"), #f.count("Clog Flushed Count", "cfc", "clog flushed count"), #f.count("Clog Flushed Size", "cfs", "clog flushed size"), #f.count("Clog Read Count", "crc(read)", "clog read count"), #f.count("Clog Read Size", "crs", "clog read size"), #f.count("Clog Disk Read Size", "cdrs", "clog disk read size"), #f.count("Clog Disk Read Count", "cdrc", "clog disk read count"), #f.time("Clog Disk Read Time", "cdrt", "clog disk read time"), #f.count("Clog Fetch Log Size", "cfls", "clog fetch log size"), #f.count("Clog Fetch Log Count", "cflc", "clog fetch log size"), #f.count("Clog Fetch Log By Location Size", "cflbls", "clog fetch log by location size"), #f.count("Clog Fetch Log By Location Count", "cflblc", "clog fetch log by location count"), #f.count("Clog Read Request Succ Size", "crrss", "clog read request succ size"), #f.count("Clog Read Request Succ Count", "crrsc", "clog read request succ count"), #f.count("Clog Read Request Fail Count", "crrfc", "clog read request fail count"), #f.time("Clog Confirm Delay Time", "ccdt", "clog confirm delay time"), #f.count("Clog Flush Task Generate Count", "cftgc", "clog flush task generate count"), #f.count("Clog Flush Task Release Count", "cftrc", "clog flush task release count"), #f.count("Clog RPC Delay Time", "crdt", "clog rpc delay time"), #f.count("Clog RPC Count", "crc(rpc)", "clog rpc count"), #f.count("Clog Non KV Cache Hit Count", "cnkchc", "clog non kv cache hit count"), #f.time("Clog RPC Request Handle Time", "crrht", "clog rpc request handle time"), #f.count("Clog RPC Request Count", "crrc", "clog rpc request count"), #f.count("Clog Cache Hit Count", "cchc", "clog cache hit count") ], self.win(), get_header=lambda stat,ip=ip: observer_info(stat,ip) , colorindex=ci) self.add_widget(widget) def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) try: self.update_widgets() except curses.error: pass def select_columns(self): if len(self.all_widgets()) > 0: all_widgets = [hc_widget.column_widget() for hc_widget in self.all_widgets()] columns = all_widgets[0].valid_columns() i = ColumnCheckBox("Select Columns", columns, self.parent()).run() for w in all_widgets: w.valid_columns()[i].enable(enable=(not w.valid_columns()[i].enable())) w.update() # for widget in all_widgets: # for idx in range(0, len(columns)): # widget.valid_columns()[idx].enable(columns[idx].enable(enable=True)) # widget.update() # [hc_widget.resize() for hc_widget in self.all_widgets()] # self.rearrange() def process_key(self, ch): w = self.selected_widget() if ch == ord('m'): curses.endwin() oceanbase.mysql(host=w.host(), port=int(w.sql_port())) curses.doupdate() elif ch == ord('o'): pass elif ch == ord('O'): like_str = InputBox(self.win(), prompt="LIKE STR").run() oceanbase.show_sql_result("select name,value from __all_sys_config_stat where svr_ip='%s' and svr_type='mergeserver' and name like '%s' order by name" % (w.host(), like_str)) elif Options.env == 'online' and ch == ord('l'): cmd = "ssh -t %s 'less oceanbase/log/mergeserver.log'" % w.host() curses.endwin() os.system(cmd) curses.doupdate() elif ch == ord("i"): self.select_columns() else: Page.process_key(self, ch) def title(self): return "SQLPage" class EventPage(Page): def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) def title(self): return "EventPage" def update_widgets(self): Page.update_widgets(self) class MachineStatPage(Page): def update_widgets(self): pass def title(self): return "Machine Stat" def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) class HelpPage(Page): def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) self.win().bkgd(curses.color_pair(4)) def redraw(self): nline = [0] # hack mutating outer variables for python2 def addline(x, line, attr=0): self.win().addstr(nline[0], x, line, attr) nline[0] += 1 def addkeys(keys): for key_item in keys: if 0 == len(key_item): string = "" else: string = " %-14s: %s" % key_item addline(4, string) nline[0] += 1 def addgroup(group_name, keys): addline(4, group_name, curses.color_pair(4) | curses.A_BOLD) addline(2, "----------------------------------------------", curses.color_pair(4) | curses.A_BOLD) addkeys(keys) try: Page.redraw(self) ob_keys = [('c', 'Switch between tenants'), ('w', 'write a screenshot file to current window')] addgroup("Global Keys - oceanbase", ob_keys) widget_keys = [('Tab','Select next widget'), ('m', 'Connect to oceanbase lms for this cluster using mysql'), ('j', 'ssh to selected host')] addgroup("Global Keys - Widget", widget_keys) pages_keys = [('1 F1', 'Help page'), ('2 F2', 'Gallery page'), ('3 F3', 'Observer page'), ('4 F4', 'History page'), ('d', 'Delete selected widget'), ('R', 'Restore deleted widgets'), ('=', 'Filter Columns for current page (ms,cs,ups page only)')] addgroup("Global Keys - Page", pages_keys) test_keys = [('p', 'Messsage box tooltips')] addgroup("Global Keys - Test", test_keys) select_keys = [('DOWN TAB J P', 'Next item'), ('UP K N', 'Previous item'), ('SPC ENTER', 'Select current item'), ('Q q', 'Quit selection box')] addgroup("Global Keys - Selection Box", select_keys) system_keys = [('q', 'quit dooba')] addgroup("Global Keys - System", system_keys) support = [ ('Author', ''), ('Mail', ''), (), ('github page', 'https://github.com/oceanbase/oceanbase'), ('bug report', 'https://github.com/oceanbase/oceanbase/issues'), ('feature req', 'https://github.com/oceanbase/oceanbase/issues') ] addgroup("Support", support) except curses.error: pass def title(self): return 'Help' class BlankPage(Page): def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) def title(self): return 'Blank Page' class FalloutPage(Page): def __init__(self, y, x, h, w, parent): Page.__init__(self, parent, Layout(), y, x, h, w) self.c = 0 def redraw(self): if self.c < 9: _____="""FFFFFFFFFFFFFFFFFFFFFF lllllll lllllll tttt\nF::::::::::::::::::::F l:::::l l:::::l ttt:::t\nF::::::::::::::::::::F l:::::l l:::::l t:::::t\nFF::::::FFFFFFFFF::::F l:::::l l:::::l t:::::t\n F:::::F FFFFFFaaaaaaaaaaaaa l::::l l::::l ooooooooooo uuuuuu uuuuuuttttttt:::::ttttttt\n F:::::F a::::::::::::a l::::l l::::l oo:::::::::::oo u::::u u::::ut:::::::::::::::::t\n F::::::FFFFFFFFFF aaaaaaaaa:::::a l::::l l::::l o:::::::::::::::ou::::u u::::ut:::::::::::::::::t\n F:::::::::::::::F a::::a l::::l l::::l o:::::ooooo:::::ou::::u u::::utttttt:::::::tttttt\n F:::::::::::::::F aaaaaaa:::::a l::::l l::::l o::::o o::::ou::::u u::::u t:::::t\n F::::::FFFFFFFFFF aa::::::::::::a l::::l l::::l o::::o o::::ou::::u u::::u t:::::t\n F:::::F a::::aaaa::::::a l::::l l::::l o::::o o::::ou::::u u::::u t:::::t\n F:::::F a::::aaaa::::::a l::::l l::::l o::::o o::::ou::::u u::::u t:::::t\n F:::::F a::::a a:::::a l::::l l::::l o::::o o::::ou:::::uuuu:::::u t:::::t tttttt\nFF:::::::FF a::::a a:::::a l::::::ll::::::lo:::::ooooo:::::ou:::::::::::::::uu t::::::tttt:::::t\nF::::::::FF a:::::aaaa::::::a l::::::ll::::::lo:::::::::::::::o u:::::::::::::::u tt::::::::::::::t\nF::::::::FF a::::::::::aa:::al::::::ll::::::l oo:::::::::::oo uu::::::::uu:::u tt:::::::::::tt\nF::::::::FF a::::::::::aa:::al::::::ll::::::l oo:::::::::::oo uu::::::::uu:::u tt:::::::::::tt\nFFFFFFFFFFF aaaaaaaaaa aaaallllllllllllllll ooooooooooo uuuuuuuu uuuu ttttttttttt\n\n\n 00000000000000000\n 00000000000000000000000000000000000000\n 0000000000000000000000000000000000000000000000000000000000000\n 0000000000000000000000000000000000000000000000000000000000000000000000000000000\n 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n 0000000000000000000000000000000000000000000000000000000000000000000000000000000\n 0000000000000000000000000000000000000000\n 000000000000000000000000000000000\n 0000000000000000000\n 0000000\n 000\n 000\n 000\n 00000\n 000000 000000000 00000\n 000000000000 000000000000000 00000000000\n 000000000000000000000000000000000000000000000000000000000000000000000000000000000\n00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n__ ____ _ _ __ __ ____ _ _ __ _ __ _____ _____ _ __ ___| |__ __ _ _ __ __ _ ___ ___\n\ \ /\ / / _` | '__| \ \ /\ / / _` | '__| | '_ \ / _ \ \ / / _ \ '__| / __| '_ \ / _` | '_ \ / _` |/ _ \/ __|\n \ V V / (_| | | _ \ V V / (_| | | | | | | __/\ V / __/ | | (__| | | | (_| | | | | (_| | __/\__ \\\n \_/\_/ \__,_|_|( ) \_/\_/ \__,_|_| |_| |_|\___| \_/ \___|_| \___|_| |_|\__,_|_| |_|\__, |\___||___/\n |/ |___/\n """ self.win().addstr(2, 0, _____, curses.color_pair(0)) else: return def title(self): return "Fallout" class Dooba(object): def build_oceanbase(self): return ''' ___ ____ / _ \ ___ ___ __ _ _ __ | __ ) __ _ ___ ___ | | | |/ __/ _ \/ _` | \'_ \| _ \ / _` / __|/ _ \\ | |_| | (_| __/ (_| | | | | |_) | (_| \__ \ __/ \___/ \___\___|\__,_|_| |_|____/ \__,_|___/\___|''' def build_dooba(self): return ''' _ _ __| | ___ ___ | |__ __ _ / _` |/ _ \ / _ \| \'_ \ / _` | | (_| | (_) | (_) | |_) | (_| | \__,_|\___/ \___/|_.__/ \__,_|''' def __init_curses(self, win): self.stdscr = win self.stdscr.keypad(1) self.stdscr.nodelay(1) self.stdscr.timeout(0) self.maxy, self.maxx = self.stdscr.getmaxyx() curses.curs_set(0) curses.noecho() self.__term = curses.termname() self.__init_colors() def __init_colors(self): if curses.has_colors(): curses.use_default_colors() curses.init_pair(1, curses.COLOR_RED, -1) # header widget and status widget curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_GREEN) # column header curses.init_pair(3, curses.COLOR_MAGENTA, -1) # widget title curses.init_pair(4, curses.COLOR_YELLOW, -1) # help page color curses.init_pair(5, curses.COLOR_RED, curses.COLOR_CYAN) curses.init_pair(6, curses.COLOR_GREEN, -1) # column header curses.init_pair(7, curses.COLOR_WHITE, -1) # machine stat header curses.init_pair(8, curses.COLOR_BLUE, -1) curses.init_pair(9, curses.COLOR_MAGENTA, -1) curses.init_pair(10, curses.COLOR_WHITE, curses.COLOR_CYAN) def __resize_term(self): self.maxy, self.maxx = self.stdscr.getmaxyx() try: self.help_w.move(0, 0) self.help_w.mvwin(0, 0) self.help_w.resize(2, self.maxx) self.stat_w.move(self.maxy - 2, 0) self.stat_w.resize(2, self.maxx) for page in self.__all_page: page.resize(self.maxy - 4, self.maxx) except curses.error: pass self.stdscr.erase() def __do_key(self): ch = self.__all_page[self.__page_index].getch() if ch == ord('q'): curses.endwin() return True elif ch >= ord('0') and ch <= ord('9'): self.__page_index = ch - ord('0') self.help_w.switch_page(self.__all_page[self.__page_index]) elif ch >= curses.KEY_F1 and ch <= curses.KEY_F9: self.__page_index = ch - curses.KEY_F1 self.help_w.switch_page(self.__all_page[self.__page_index]) elif ch == ord('\t'): self.__all_page[self.__page_index].select_next() elif ch == ord('w'): pagename = self.__all_page[self.__page_index].title() appname = oceanbase.app filename = "%s_%s.dooba.win.bz2" % (appname, pagename) tmpf = tempfile.TemporaryFile() self.stdscr.putwin(tmpf); tmpf.seek(0) f = bz2.BZ2File(filename, "w") f.write(tmpf.read()) f.close() tmpf.close() MessageBox(self.stdscr, "[ INFO ] save screen to %s done!" % filename).run(anykey=True) #what is this ? elif ch == ord('p'): MessageBox(self.stdscr, "[TEST] illegal").run() elif ch == ord('z'): items = [('check1 12344', False), ('check2 1', True), ('check3 124328432431', False)] CheckBox('Test check box', items, self.stdscr).run() # elif ch == ord('i'): # result = InputBox(self.stdscr).run() # MessageBox(self.stdscr, "[TEST] %s" % result).run(anykey=True) elif ch == ord('C'): pass elif ch == ord('c'): self.__select_cluster() elif ch == ord("t"): PopPad("Abbreviation", [], self.stdscr).run() self.__all_page[self.__page_index].process_key(ch) def __run(self): while (1): self.stdscr.erase() self.help_w.redraw() DEBUG(self.__run, "page __all_page[%s] redraw" % self.__page_index, self.__all_page[self.__page_index]) self.__all_page[self.__page_index].redraw() self.stat_w.redraw() if (curses.is_term_resized(self.maxy, self.maxx)): self.__resize_term() if self.__do_key(): break self.stdscr.refresh() sleep(0.05) def __select_cluster(self): DEBUG(self.__select_cluster,"oceanbase.tenant: %s" % (len(oceanbase.tenant)),"") if len(oceanbase.tenant) <= 1: idx = 0 else: DEBUG(self.__select_cluster, "tnt.selected", [tnt.selected for tnt in oceanbase.tenant]) idx = SelectionBox("Select Tenant", [ "[%s] %s " % ("*" if tnt.selected else " ", tnt.tenant_name) for tnt in oceanbase.tenant], self.stdscr).run() if True == oceanbase.tenant[idx].selected: pass else: tid = oceanbase.tenant[idx].tenant_id oceanbase.set_current_tenant(tid) if f_data_log != '': if not check_datalog(Options.datalog, tid): print "fail to write datalog [%s]" % (Options.datalog) oceanbase.switch_tenant(tid) DEBUG(self.__select_cluster, "global oceanbase", oceanbase) DEBUG(self.__select_cluster, "oceanbase.tenant", oceanbase.tenant[idx]) DEBUG(self.__select_cluster, "oceanbase.tenant[idx]", oceanbase.tenant[idx].selected) DEBUG(self.__select_cluster, "oceanbase.get_current_tenant()", oceanbase.get_current_tenant()) for page in self.__all_page: page.cur_tenant_id = tid page.update() DEBUG(self.__select_cluster, "page.cur_tenant_id", [" ".join([str(page.title()), page.cur_tenant_id]) for page in self.__all_page]) def __show_logo(self): self.stdscr.hline(7, 0, curses.ACS_HLINE, 1024, curses.A_BOLD | curses.color_pair(1)) self.stdscr.refresh() oceanbase_width = max([len(line) for line in self.build_oceanbase().split('\n')]) oceanbase_height = len(self.build_oceanbase().split('\n')) ob_win = curses.newwin(oceanbase_height + 1, oceanbase_width + 1, 0, 10) ob_win.addstr(self.build_oceanbase(), curses.A_BOLD | curses.color_pair(1)) ob_win.refresh() dooba_width = max([len(line) for line in self.build_dooba().split('\n')]) dooba_height = len(self.build_dooba().split('\n')) dooba_win = curses.newwin(dooba_height + 1, dooba_width + 1, 0, self.maxx - dooba_width - 10) dooba_win.addstr(self.build_dooba(), curses.A_BOLD | curses.color_pair(6)) dooba_win.refresh() def __cowsay(self, saying): cowsay = str(Cowsay(saying)) cowsay_width = max([len(line) for line in cowsay.split('\n')]) cowsay_height = len(cowsay.split('\n')) try: cowsay_win = curses.newwin(cowsay_height + 1, cowsay_width + 1, self.maxy - cowsay_height - 2, self.maxx - cowsay_width - 10) cowsay_win.addstr(cowsay, curses.color_pair(3)) cowsay_win.refresh() self.__cowsay_win = cowsay_win except curses.error: pass def __update_lms(self): return oceanbase.check_lms(self.__cowsay) def __run_show_win_file(self): self.stdscr.erase() bzfh = bz2.BZ2File(Options.show_win_file) fh = tempfile.TemporaryFile() fh.write(bzfh.read()) fh.seek(0) bzfh.close() self.stdscr = curses.getwin(fh) self.stdscr.nodelay(0) self.stdscr.notimeout(1) fh.close() ch = self.stdscr.getch() curses.endwin() exit(0) def __init__(self, win): DEBUG(self.__init__, "Dooba.__init__(win='%s') " % win, "") self.__all_page = [] self.__page_index = 2 self.__init_curses(win) minx = 80 if self.maxx < minx or self.maxy < 20: MessageBox(self.stdscr, "[ ERROR ] %dx%d is tooooo smalllll! %dx20 is at least..." % (self.maxx, self.maxy, minx)).run(anykey=True) return self.__select_cluster() oceanbase.set_current_tenant('1') oceanbase.update_tenant_info() oceanbase.start() self.__show_logo() self.stat_w = StatusWidget(self.stdscr) self.help_w = HeaderWidget(self.stdscr) self.__all_page.append(FalloutPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 0 self.__all_page.append(HelpPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 1 self.__all_page.append(GalleryPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 2 self.__all_page.append(SQLPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 3 self.__all_page.append(TableApiPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 4 self.__all_page.append(HistoryPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 5 self.__all_page.append(BianquePage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 6 self.__all_page.append(MachineStatPage(2, 0, self.maxy-4, self.maxx, self.stdscr)) # 7 self.__all_page.append(BlankPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 8 self.__all_page.append(BlankPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 9 self.__run() class DoobaMain(object): '''NAME dooba - A curses powerful tool for oceanbase admin, more than a monitor SYNOPSIS dooba [OPTIONS] Options · --host=HOST, -h HOST Connect to oceanbase on the given host. · --port=PORT, -P PORT The TCP/IP port to use for connecting to oceanbase server. · --user=USER, -u USER The user to use for connecting to oceanbase server. · --password=PASSWORD, -p PASSWORD The password to use for connecting to oceanbase server. ''' def __usage(self): print('Usage: dooba [-h|--host=HOST] [-P|--port=PORT]') print('Usage: dooba [--dataid=DATAID]') print('Usage: dooba --show dooba_win_file') def __set_env(self): setlocale(LC_ALL, "en_US.UTF-8") environ['TERM'] = 'xterm' def __parse_options(self): try: opts, args = getopt(sys.argv[1:], '?dh:i:I:p:P:su:D', ['debug', 'help', 'host=', 'interval=', 'port=', 'password=', 'supermode', 'user=', 'dataid=', 'online', 'offline', 'machine-interval=', 'degradation', 'show=', 'daemon', 'http', 'start', 'stop', 'restart', 'http-port=', 'database','datalog=']) except GetoptError as err: print str(err) # will print something like "option -a not recognized" self.__usage() exit(2) for o, v in opts: if o in ('-?', '--help'): print self.__doc__ exit(1) if o in ('-d', '--debug'): Options.debug = True elif o in ('-h', '--host'): Options.host = v Options.using_ip_port = True elif o in ('-P', '--port'): Options.port = int(v) Options.using_ip_port = True elif o in ('-u', '--user'): Options.user = v elif o in ('-p', '--password'): Options.password = v elif o in ('-s', '--supermode'): Options.supermode = True elif o in ('-i', '--interval'): Options.interval = float(v) elif o in ('-I', '--machine-interval'): Options.machine_interval = float(v) elif o in ('--dataid'): Options.dataid = v elif o in ('--online'): Options.env = 'online' elif o in ('--offline'): Options.env = 'offline' elif o in ('-D', '--degradation'): Options.degradation = True elif o in ('--show'): Options.show_win_file = v elif o in ('--http'): Options.http = True elif o in ('--start'): Options.daemon_action = 'start' elif o in ('--stop'): Options.daemon_action = 'stop' elif o in ('--restart'): Options.daemon_action = 'restart' elif o in ('--daemon'): Options.daemon = True elif o in ('--http-port'): Options.http_port = int(v) elif o in ('--database'): Options.database = v elif o in ('--datalog'): if not check_datalog(v) : assert False, 'invalid datalog filename [%s]' % (v) Options.datalog = v else: assert False, 'unhandled option [%s]' % ( o ) return args def __ignore_signal(self): def signal_handler(signal, frame): pass signal.signal(signal.SIGINT, signal_handler) def __myprint(self, info): print info print self.__usage() def __init__(self): global oceanbase self.__set_env() if "print-config" in self.__parse_options(): print "".join(ObConfigHelper().get_config(Options.dataid)) ObConfigHelper().get_dataid_list() exit(0) if not Options.show_win_file: oceanbase = oceanbase(Options.dataid) if Options.using_ip_port: oceanbase.test_alive(do_false=self.__myprint) if Options.http: pid_file = '/tmp/dooba.%d.pid' % Options.http_port if Options.daemon: if Options.daemon_action == 'start': HTTPServerDaemon(pid_file).start() elif Options.daemon_action == 'stop': HTTPServerDaemon(pid_file).stop() elif Options.daemon_action == 'restart': HTTPServerDaemon(pid_file).restart() else: try: HTTPServerDaemon(pid_file).run() except KeyboardInterrupt: pass else: try: curses.wrapper(Dooba) except KeyboardInterrupt: pass class HTTPServerDaemon(Daemon): def run(self): #oceanbase.update_cluster_info() oceanbase.update_tenant_info() oceanbase.start() server = BaseHTTPServer.HTTPServer(('', Options.http_port), WebRequestHandler) server.serve_forever() class JSONDateTimeEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, (date, datetime)): return obj.isoformat() elif isinstance(obj, (timedelta)): return 'NULL' else: return json.JSONEncoder.default(self, obj) class WebRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): sample = oceanbase.now() self.wfile.write('HTTP/1.1 200 OK\nContent-Type: text/html\n\n') self.wfile.write(json.dumps(sample, cls=JSONDateTimeEncoder)) if __name__ == "__main__": DoobaMain() # # dooba ends here