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)
 |