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