210 lines
5.6 KiB
Python
210 lines
5.6 KiB
Python
import threading
|
|
import random
|
|
import logging
|
|
import os
|
|
import sys
|
|
from typing import Optional
|
|
|
|
import taos
|
|
|
|
|
|
class CrashGenError(taos.error.ProgrammingError):
|
|
INVALID_EMPTY_RESULT = 0x991
|
|
INVALID_MULTIPLE_RESULT = 0x992
|
|
DB_CONNECTION_NOT_OPEN = 0x993
|
|
# def __init__(self, msg=None, errno=None):
|
|
# self.msg = msg
|
|
# self.errno = errno
|
|
|
|
# def __str__(self):
|
|
# return self.msg
|
|
pass
|
|
|
|
|
|
class LoggingFilter(logging.Filter):
|
|
def filter(self, record: logging.LogRecord):
|
|
if (record.levelno >= logging.INFO):
|
|
return True # info or above always log
|
|
|
|
# Commenting out below to adjust...
|
|
|
|
# if msg.startswith("[TRD]"):
|
|
# return False
|
|
return True
|
|
|
|
|
|
class MyLoggingAdapter(logging.LoggerAdapter):
|
|
def process(self, msg, kwargs):
|
|
shortTid = threading.get_ident() % 10000
|
|
return "[{:04d}] {}".format(shortTid, msg), kwargs
|
|
# return '[%s] %s' % (self.extra['connid'], msg), kwargs
|
|
|
|
|
|
class Logging:
|
|
logger = None # type: Optional[MyLoggingAdapter]
|
|
|
|
@classmethod
|
|
def getLogger(cls):
|
|
return cls.logger
|
|
|
|
@classmethod
|
|
def clsInit(cls, debugMode: bool):
|
|
if cls.logger:
|
|
return
|
|
|
|
# Logging Stuff
|
|
# global misc.logger
|
|
_logger = logging.getLogger('CrashGen') # real logger
|
|
_logger.addFilter(LoggingFilter())
|
|
ch = logging.StreamHandler(sys.stdout) # Ref: https://stackoverflow.com/questions/14058453/making-python-loggers-output-all-messages-to-stdout-in-addition-to-log-file
|
|
_logger.addHandler(ch)
|
|
|
|
# Logging adapter, to be used as a logger
|
|
# print("setting logger variable")
|
|
# global logger
|
|
cls.logger = MyLoggingAdapter(_logger, {})
|
|
cls.logger.setLevel(logging.DEBUG if debugMode else logging.INFO) # default seems to be INFO
|
|
|
|
@classmethod
|
|
def info(cls, msg):
|
|
cls.logger.info(msg)
|
|
|
|
@classmethod
|
|
def debug(cls, msg):
|
|
cls.logger.debug(msg)
|
|
|
|
@classmethod
|
|
def warning(cls, msg):
|
|
cls.logger.warning(msg)
|
|
|
|
@classmethod
|
|
def error(cls, msg):
|
|
cls.logger.error(msg)
|
|
|
|
class Status:
|
|
STATUS_EMPTY = 99
|
|
STATUS_STARTING = 1
|
|
STATUS_RUNNING = 2
|
|
STATUS_STOPPING = 3
|
|
STATUS_STOPPED = 4
|
|
|
|
def __init__(self, status):
|
|
self.set(status)
|
|
|
|
def __repr__(self):
|
|
return "[Status: v={}]".format(self._status)
|
|
|
|
def set(self, status: int):
|
|
self._status = status
|
|
|
|
def get(self):
|
|
return self._status
|
|
|
|
def isEmpty(self):
|
|
''' Empty/Undefined '''
|
|
return self._status == Status.STATUS_EMPTY
|
|
|
|
def isStarting(self):
|
|
return self._status == Status.STATUS_STARTING
|
|
|
|
def isRunning(self):
|
|
# return self._thread and self._thread.is_alive()
|
|
return self._status == Status.STATUS_RUNNING
|
|
|
|
def isStopping(self):
|
|
return self._status == Status.STATUS_STOPPING
|
|
|
|
def isStopped(self):
|
|
return self._status == Status.STATUS_STOPPED
|
|
|
|
def isStable(self):
|
|
return self.isRunning() or self.isStopped()
|
|
|
|
def isActive(self):
|
|
return self.isStarting() or self.isRunning() or self.isStopping()
|
|
|
|
# Deterministic random number generator
|
|
class Dice():
|
|
seeded = False # static, uninitialized
|
|
|
|
@classmethod
|
|
def seed(cls, s): # static
|
|
if (cls.seeded):
|
|
raise RuntimeError(
|
|
"Cannot seed the random generator more than once")
|
|
cls.verifyRNG()
|
|
random.seed(s)
|
|
cls.seeded = True # TODO: protect against multi-threading
|
|
|
|
@classmethod
|
|
def verifyRNG(cls): # Verify that the RNG is determinstic
|
|
random.seed(0)
|
|
x1 = random.randrange(0, 1000)
|
|
x2 = random.randrange(0, 1000)
|
|
x3 = random.randrange(0, 1000)
|
|
if (x1 != 864 or x2 != 394 or x3 != 776):
|
|
raise RuntimeError("System RNG is not deterministic")
|
|
|
|
@classmethod
|
|
def throw(cls, stop): # get 0 to stop-1
|
|
return cls.throwRange(0, stop)
|
|
|
|
@classmethod
|
|
def throwRange(cls, start, stop): # up to stop-1
|
|
if (not cls.seeded):
|
|
raise RuntimeError("Cannot throw dice before seeding it")
|
|
return random.randrange(start, stop)
|
|
|
|
@classmethod
|
|
def choice(cls, cList):
|
|
return random.choice(cList)
|
|
|
|
class Helper:
|
|
@classmethod
|
|
def convertErrno(cls, errno):
|
|
return errno if (errno > 0) else 0x80000000 + errno
|
|
|
|
@classmethod
|
|
def getFriendlyPath(cls, path): # returns .../xxx/yyy
|
|
ht1 = os.path.split(path)
|
|
ht2 = os.path.split(ht1[0])
|
|
return ".../" + ht2[1] + '/' + ht1[1]
|
|
|
|
|
|
class Progress:
|
|
STEP_BOUNDARY = 0
|
|
BEGIN_THREAD_STEP = 1
|
|
END_THREAD_STEP = 2
|
|
SERVICE_HEART_BEAT= 3
|
|
SERVICE_RECONNECT_START = 4
|
|
SERVICE_RECONNECT_SUCCESS = 5
|
|
SERVICE_RECONNECT_FAILURE = 6
|
|
SERVICE_START_NAP = 7
|
|
CREATE_TABLE_ATTEMPT = 8
|
|
QUERY_GROUP_BY = 9
|
|
CONCURRENT_INSERTION = 10
|
|
ACCEPTABLE_ERROR = 11
|
|
|
|
tokens = {
|
|
STEP_BOUNDARY: '.',
|
|
BEGIN_THREAD_STEP: ' [',
|
|
END_THREAD_STEP: ']',
|
|
SERVICE_HEART_BEAT: '.Y.',
|
|
SERVICE_RECONNECT_START: '<r.',
|
|
SERVICE_RECONNECT_SUCCESS: '.r>',
|
|
SERVICE_RECONNECT_FAILURE: '.xr>',
|
|
SERVICE_START_NAP: '_zz',
|
|
CREATE_TABLE_ATTEMPT: 'c',
|
|
QUERY_GROUP_BY: 'g',
|
|
CONCURRENT_INSERTION: 'x',
|
|
ACCEPTABLE_ERROR: '_',
|
|
}
|
|
|
|
@classmethod
|
|
def emit(cls, token):
|
|
print(cls.tokens[token], end="", flush=True)
|
|
|
|
@classmethod
|
|
def emitStr(cls, str):
|
|
print('({})'.format(str), end="", flush=True)
|