From 7e7b2f9c9054ac03d6a4ba2c9dfbde598583859a Mon Sep 17 00:00:00 2001 From: LINxiansheng Date: Fri, 10 Sep 2021 15:29:45 +0800 Subject: [PATCH] Add dooba.py --- tools/scripts/dooba.py | 3857 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3857 insertions(+) create mode 100644 tools/scripts/dooba.py diff --git a/tools/scripts/dooba.py b/tools/scripts/dooba.py new file mode 100644 index 000000000..36ea1d9f1 --- /dev/null +++ b/tools/scripts/dooba.py @@ -0,0 +1,3857 @@ +#!/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. +# Author: 符风 +# Created: 2013-07-23 21:05:08 (+0800) +# Version: 2.1.2 +# Last-Updated: 2015-10-27T13:49:36+0800 +# By: Shi Yudi +# Update #: 6933 +# + +# Change Log: +# 2014-05-14 Shi Yudi +# Last-Updated: 2014-05-14T16:15:26+0800 #6913 (Shi Yudi) +# 1. skip to select cluster if only one cluster +# +# 2014-04-24 Shi Yudi +# Last-Updated: 2014-04-24T18:57:09+0800 #6515 (Shi Yudi) +# 1. support ob 0.5 updateserver columns +# +# 2014-04-22 Shi Yudi +# Last-Updated: 2014-04-22T19:57:35+0800 #6288 (Shi Yudi) +# 1. add column filter for ups,ms,cs page +# +# 2014-04-01 Shi Yudi +# Last-Updated: 2014-04-01T13:41:45+0800 #5844 (Shi Yudi) +# 1. fix oceanbase 0.5 gather stats mixed by all cluster +# +# 2014-02-27 Yudi Shi +# Last-Updated: Thu Feb 27 20:01:13 2014 (+0800) #5777 (Yudi Shi) +# 1. add fail sql exec count in gallery page +# +# 2013-12-31 Shi Yudi +# Last-Updated: Tue Dec 31 13:10:39 2013 (+0800) #5770 (Shi Yudi) +# 1. add error msg if geometry is not enough +# 2. fix some msg output +# +# 2013-12-11 Shi Yudi +# Last-Updated: Wed Dec 11 14:51:43 2013 (+0800) #5745 (Shi Yudi) +# 1. fix password decrypt bug (fill 8 width encoding str) +# +# 2013-10-25 Shi Yudi +# Last-Updated: Tue Dec 3 16:01:23 2013 (+0800) #5739 (Shi Yudi) +# 1. add time to widget frame +# +# 2013-10-25 Shi Yudi +# Last-Updated: 2013-10-25 20:28:17 (+0800) #5647 (Shi Yudi) +# 1. add http api support +# 2. add obssh to login server +# 3. using obconfig password +# +# 2013-09-04 Shi Yudi +# Last-Updated: 2013-09-04 10:52:02 (+0800) #4786 (Shi Yudi) +# 1. add machine stat +# 2. fix many bugs +# 3. add ssh, mysql login +# 4. add delete and restore widgets +# +# 2013-08-20 Shi Yudi +# Last-Updated: 2013-08-20 15:29:45 (+0800) #2012 (Shi Yudi) +# 1. support instant statistics monitor for ups/cs/ms +# 2. keyboard response for switch between widget +# 3. rewrite help page +# +# 2013-08-16 Shi Yudi +# Last-Updated: 2013-08-16 16:07:32 (+0800) #1052 (Shi Yudi) +# 1. add header column widget descripe each server +# 2. add instant stat list (only for ms now) +# 3. add mergeserver header info +# +# 2013-08-14 Shi Yudi +# Last-Updated: 2013-08-14 16:11:40 (+0800) #576 (Shi Yudi) +# 1. add MessageBox for dooba, key 'p' for test +# 2. add selective mode, TAB for swtich between widgets +# 3. add supermode option, just a husk right now +# +# 2013-08-07 Shi Yudi +# Last-Updated: 2013-08-07 10:38:33 (+0800) #479 (Shi Yudi) +# 1. fix term evironment setting bug +# 2. fix getting appname bug +# 3. refact main method +# +# 2013-08-06 Shi Yudi +# Last-Updated: 2013-08-06 20:47:35 (+0800) #443 (Shi Yudi) +# 1. oceanbase alive checker before running +# 2. colorfull widgets +# 3. fix some promptions +# 4. change header widget and status widget style with horizontal line +# 5. remove page border +# +# 2013-08-06 Shi Yudi +# Last-Updated: 2013-08-06 16:46:39 (+0800) #347 (Shi Yudi) +# 1. add chunkserver, mergeserver, updateserver info widgets +# 2. fix non-lexical closures problem +# 3. dynamic helper widget promption +# 4. change some pages' index +# +# 2013-07-31 Shi Yudi +# Last-Updated: 2013-07-31 19:11:19 (+0800) #14 (Shi Yudi) +# 1. redesign helper bar and status bar +# 2. add more pages for dooba +# 3. beauty python coding style +# +# 2013-07-23 Shi Yudi +# 1. header with app name, and other mocks. +# 2. sql rt, sql count, cs rt, ups rt are added to screen. +# +# + +#2015-03-26 Qu jinnan +# 1. add group list +# 2. add searching bar + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth +# Floor, Boston, MA 02110-1301, USA. +# +# + +# 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 = 2828 + user = None + 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): + 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): + + sql = """ SELECT unit.svr_ip, unit.svr_port FROM __all_resource_pool pool JOIN __all_unit unit ON (pool.resource_pool_id = unit.resource_pool_id ) WHERE pool.tenant_id = %s ; """ % (str(self.get_current_tenant())) + res = self.dosql(sql) + + s_join = "" + + for one in res.split("\n")[:-1]: + a = one.split("\t") + if s_join == "": + s_join = """ %s (svr_ip = '%s' and svr_port = %s ) """ % ( s_join, a[0], a[1]) + else: + s_join = """ %s or (svr_ip = '%s' and svr_port = %s ) """ % ( s_join, a[0], a[1]) + + 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) " + + sql2 = """ 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) and con_id = %s and (%s) """ % ( str(self.get_current_tenant()) , stat_ids) + + DEBUG(self.__check_schema, "oceanbase.check schema sql : ", sql2) + + return sql2 + + 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 + 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, 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() + +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 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', 'Yudi Shi (fufeng.syd)'), + ('Mail', 'fufeng.syd@alipay.com'), + (), + ('project page', ''), + ('bug report', ''), + ('feature req', '') + ] + 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]What's up? Nigger! (- -)#").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(HistoryPage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 4 + self.__all_page.append(BianquePage(2, 0, self.maxy - 4, self.maxx, self.stdscr)) # 5 + self.__all_page.append(MachineStatPage(2, 0, self.maxy-4, self.maxx, self.stdscr)) # 6 + self.__all_page.append(BlankPage(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