def __init__(self, env, dbname, auth_dict=None): """ 指定所处环境及数据库名称 :param env: 所处环境 :param dbname: 数据库名称 :param auth_dict: 授权字典,样例格式为{"auth_db":"admin", "auth_username":"******", "auth_password":"******"} """ mongodbParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( "mongodb", 1).get(env) self.mongodb = MongoClient(host=mongodbParam['host'], port=mongodbParam['port']) if not mongodbParam.__contains__("auth") and auth_dict is None: logger.warning("请在应用配置文件中指定MongoDB授权信息,或者通过auth_dict字典进行指定!") sys.exit(1) if auth_dict is not None: if isinstance(auth_dict, dict): auth_db = auth_dict.get("auth_db") auth_username = auth_dict.get("auth_username") auth_password = str(auth_dict.get("auth_password")) auth = eval("self.mongodb.{}".format(auth_db)) auth.authenticate(auth_username, auth_password) self.db_client = eval("self.mongodb.{}".format(dbname)) else: logger.warning("auth_dict授权信息必须为字典形式,请检查!") sys.exit(1) if auth_dict is None and mongodbParam.__contains__("auth"): mongodbAuth = mongodbParam.get("auth") auth_db = mongodbAuth.get("db") auth_username = mongodbAuth.get("username") auth_password = str(mongodbAuth.get("password")) auth = eval("self.mongodb.{}".format(auth_db)) auth.authenticate(auth_username, auth_password) self.db_client = eval("self.mongodb.{}".format(dbname))
def __init__(self): """ 根据环境配置初始化ELK连接信息 """ env_params = YamlConfig(config=APPLICATION_CONFIG_FILE).get("elk") username = str(env_params.get("username")) password = str(env_params.get("password")) ElkSupport.__init__(self, username=username, password=password)
def __init__(self): """ 从应用配置文件获取钉钉请求token及签名secret """ self.token = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["token"] self.secret = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["secret"] self.timestamp = str(round(time.time() * 1000))
def __init__(self): """ 根据环境配置初始化Jenkins连接信息 """ env_params = YamlConfig(config=APPLICATION_CONFIG_FILE).get("jenkins") url = str(env_params.get("url")) username = str(env_params.get("username")) password = str(env_params.get("password")) JenkinsTools.__init__(self, url=url, username=username, password=password)
def create_session(self): """ 创建数据库连接会话 :return session:会话实例 :return: """ try: if self.dbtype == r'mysql': mysqlParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( "mysql", 1).get(self.env) self.session = pymysql.connect(host=mysqlParam['host'], user=mysqlParam['username'], password=str( mysqlParam['password']), port=mysqlParam['port'], database=str(self.dbname)) return self.session elif self.dbtype == r'redis': redisParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( 'redis', 1)[self.env] pool = redis.ConnectionPool( host=redisParam['host'], port=redisParam['port'], db=self.dbname, decode_responses=True, ) self.session = redis.StrictRedis(connection_pool=pool, charset='utf-8') return self.session elif self.dbtype == r'mongodb': mongoParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( 'mongodb', 1)[self.env] self.session = MongoClient(host=mongoParam['host'], port=mongoParam['port']) return self.session else: raise DatabaseTypeError except InternalError: logger.exception('[Exception]:当前指定的MySQL数据库"{}"并不存在.'.format( self.dbname)) except DatabaseTypeError: logger.exception('[Exception]:数据库类型{}不支持创建连接会话.'.format( self.dbtype.upper())) except ConnectionError: logger.exception('[Exception]:Redis会话连接错误, 请检查连接会话参数和当前网络状况.') except ResponseError: logger.exception( '[Exception]:Redis数据库索引值"{}"非法, 请输入合法的数据库索引(int类型标识).'.format( self.dbname)) except Exception: logger.exception('[Exception]:创建{}数据库会话实例过程中发生异常,请检查!'.format( self.dbtype.upper()))
def __init__(self, project="bus", env="stg"): """ 初始化环境信息。 :param project: 所属项目名称 :param env: 当前环境名称 """ env_params = YamlConfig(config=ENVIRONMENT_CONFIG_FILE).get(project).get(env) web_params = env_params.get("doctor_admin") self.login_url = web_params.get("login_url") self.username = web_params.get("username") self.password = web_params.get("password") self.validcode = web_params.get("validcode") self.env = env self.token = None
def __init__(self, env, domain=True): """ 根据环境配置初始化Swagger接口文档。 :param env: 项目环境 :param domain: 域名替换开关(默认开启) """ env_params = YamlConfig(config=APPLICATION_CONFIG_FILE).get( "swagger", 2).get(env) swagger_doc_urls = env_params.get("swagger_docs") if domain is True: domain_urls = env_params.get("domain_urls") SwaggerTools.__init__(self, swagger_doc_urls=swagger_doc_urls, swagger_domain_urls=domain_urls) else: SwaggerTools.__init__(self, swagger_doc_urls=swagger_doc_urls)
def __init__(self, env, dbname): """ 指定所处环境及数据库名称 :param env: 所处环境 :param dbname: 数据库名称 """ mysqlParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( "mysql", 1).get(env) self.mysql_engine = create_engine( "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format( mysqlParam['username'], str(mysqlParam['password']), mysqlParam['host'], mysqlParam['port'], dbname))
def __init__(self, ding_notify_file, preview_mode=False): """ 从应用配置文件获取钉钉请求token及签名secret :param ding_notify_file: 钉钉消息模板文件 :param preview_mode: 测试报告截图预览 """ try: DingTools.__init__(self) if not os.path.exists(ding_notify_file): raise FileNotFoundError with open(file=ding_notify_file, mode=r'r', encoding='utf-8') as ding_file: if preview_mode is False: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace("![Allure](report_url)", "> 🐾🐾🐾 ~ {}".format(get_ip()))) else: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace( "report_url", face_bed(pic=capture_image( width=1440, height=797, url="http://localhost:5000/allure", sleep=10, pic=os.path.join(RESOURCE_PATH, "Allure", "Allure.png")), alias="Allure.png"))) self.token = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["token"] self.secret = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["secret"] self.timestamp = str(round(time.time() * 1000)) except FileNotFoundError: logger.warning( '[WARNING]:钉钉消息通知模板文件"{}"当前并不存在,请检查!'.format(ding_notify_file)) sys.exit(1)
class EnvironmentDingTools(DingTools): """ 钉钉机器人消息提醒工具类(依赖环境配置文件) """ def __init__(self, ding_notify_file, preview_mode=False): """ 从应用配置文件获取钉钉请求token及签名secret :param ding_notify_file: 钉钉消息模板文件 :param preview_mode: 测试报告截图预览 """ try: DingTools.__init__(self) if not os.path.exists(ding_notify_file): raise FileNotFoundError with open(file=ding_notify_file, mode=r'r', encoding='utf-8') as ding_file: if preview_mode is False: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace("![Allure](report_url)", "> 🐾🐾🐾 ~ {}".format(get_ip()))) else: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace( "report_url", face_bed(pic=capture_image( width=1440, height=797, url="http://localhost:5000/allure", sleep=10, pic=os.path.join(RESOURCE_PATH, "Allure", "Allure.png")), alias="Allure.png"))) self.token = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["token"] self.secret = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["secret"] self.timestamp = str(round(time.time() * 1000)) except FileNotFoundError: logger.warning( '[WARNING]:钉钉消息通知模板文件"{}"当前并不存在,请检查!'.format(ding_notify_file)) sys.exit(1) @property def get_sign(self): """ 生成钉钉机器人签名字串 :return: """ try: secret_enc = self.secret.encode('utf-8') string_to_sign = '{}\n{}'.format(self.timestamp, self.secret) string_to_sign_enc = string_to_sign.encode('utf-8') hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) return sign except Exception: logger.exception("[Exception]:生成钉钉机器人签名字串过程中发生异常,请检查!") def generate_msg(self, msgtype): """ 构造钉钉机器人消息体 :param msgtype: 消息提醒类型(当前仅支持Text/Link/MarkDown/ActionCard/FeedCard) :return: """ try: if msgtype == 'text': return json.dumps(self.ding.get('text')) if msgtype == 'link': return json.dumps(self.ding.get('link')) if msgtype == 'markdown': return json.dumps(self.ding.get('markdown')) if msgtype == 'actionCard': return json.dumps(self.ding.get('actionCard')) if msgtype == 'feedCard': return json.dumps(self.ding.get('feedCard')) except Exception: logger.warning("[WARNING]:构造钉钉机器人消息体过程中发生异常,请检查!") @retry(stop_max_attempt_number=2, wait_random_min=2000, wait_random_max=5000) def send(self, msgtype): """ 发送钉钉机器人消息提醒 :param msgtype: 消息提醒类型 :return: """ try: if msgtype not in [ 'text', 'link', 'markdown', 'actionCard', 'feedCard' ]: logger.warning( '[WARNING]:检测到非法消息类型"{}",当前仅支持text、link、markdown、actionCard、feedCard,请重新指定!' .format(msgtype)) sys.exit(1) url = "https://oapi.dingtalk.com/robot/send?access_token={}×tamp={}&sign={}".format( self.token, self.timestamp, self.get_sign) headers = {'Content-Type': 'application/json'} response = requests.request("POST", url, headers=headers, data=self.generate_msg(msgtype)) result = response.json() if result.get("errcode") == 0 and result.get("errmsg") == 'ok': logger.info("[Done]:钉钉机器人消息提醒成功.") else: logger.warning( "[WARNING]:钉钉机器人消息提醒失败,接口响应为【{}】,开始重试...".format(result)) sys.exit(1) except Exception: logger.exception("[Exception]:发送钉钉机器人消息提醒过程中发生异常,请检查!") sys.exit(1)
class DingTools(object): """ 钉钉机器人消息提醒工具类 """ def __init__(self): """ 从应用配置文件获取钉钉请求token及签名secret """ self.token = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["token"] self.secret = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["secret"] self.timestamp = str(round(time.time() * 1000)) @property def get_sign(self): """ 生成钉钉机器人签名字串 :return: """ try: secret_enc = self.secret.encode('utf-8') string_to_sign = '{}\n{}'.format(self.timestamp, self.secret) string_to_sign_enc = string_to_sign.encode('utf-8') hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) return sign except Exception: logger.exception("[Exception]:生成钉钉机器人签名字串过程中发生异常,请检查!") @retry(stop_max_attempt_number=2, wait_random_min=2000, wait_random_max=5000) def send(self, message): """ 发送钉钉机器人消息提醒 :param message: 消息提醒 :return: """ try: if not isinstance(message, dict): logger.warning("[WARNING]:参数message必须以字典形式入参,请检查!") sys.exit(1) if not message.__contains__('msgtype'): logger.warning("[WARNING]:消息体message必须包含消息类型msgtype,请检查!") sys.exit(1) if message.get('msgtype') not in [ 'text', 'link', 'markdown', 'actionCard', 'feedCard' ]: logger.warning( '[WARNING]:检测到非法消息类型"{}",当前仅支持text、link、markdown、actionCard、feedCard,请重新指定!' .format(message.get('msgtype'))) sys.exit(1) url = "https://oapi.dingtalk.com/robot/send?access_token={}×tamp={}&sign={}".format( self.token, self.timestamp, self.get_sign) headers = {'Content-Type': 'application/json'} response = requests.request("POST", url, headers=headers, data=json.dumps(message)) result = response.json() if result.get("errcode") == 0 and result.get("errmsg") == 'ok': logger.info("[Done]:钉钉机器人消息提醒成功.") else: logger.warning( "[WARNING]:钉钉机器人消息提醒失败,接口响应为【{}】,开始重试...".format(result)) sys.exit(1) except Exception: logger.exception("[Exception]:发送钉钉机器人消息提醒过程中发生异常,请检查!") sys.exit(1)
class Email(object): ''' 邮件发送模块 ''' emailParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get('email') def __init__(self, server=emailParam['server'], port=emailParam['port'], user=emailParam['user'], password=emailParam['password'], title=emailParam['title'], sender=emailParam['sender'], receiver=emailParam['receiver'], ): ''' 初始化Email参数 :param server: 邮件服务器ip或域名 :param port: 邮件服务器端口号 :param user: 登录账户 :param password: 登录密码 :param title: 邮件标题 :param sender: 发送邮箱 :param receiver: 接收邮箱 ''' self.server = server self.port = port self.user = user self.password = password self.title = title self.sender = 'Automation' + '<' + sender + '>' self.receiver = receiver self.message = None self.files = None self.msg = MIMEMultipart('related') self.msg['From'] = Header(self.sender, 'utf-8') self.msg['To'] = Header(self.receiver, 'utf-8') self.msg['Subject'] = Header(self.title, 'utf-8') def createSession(self): ''' 建立SMTP邮件服务连接 ''' try: self.smtpObj = smtplib.SMTP() self.smtpObj.connect(self.server, self.port) # 若开启SSL,则关闭TLS # self.smtpObj.starttls() self.smtpObj.login(self.user, self.password) return self.smtpObj except SMTPConnectError: logger.exception('邮件SMTPConnectError连接错误, 请检查网络以及邮件配置.') except SMTPAuthenticationError: logger.exception('邮件SMTPAuthenticationError认证错误, 请检查邮件登录账套.') except SMTPException: logger.exception('触发SMTPException异常, 当前无适配邮件认证方式, 请确认当前开启的验证方式(SSL或TSL).') def checkAtType(self, attype): ''' 校验附件类型并自动添加 :param attype:附件类型 :return: ''' if isinstance(attype, list): for af in attype: self.attachFile(af) if isinstance(attype, str): self.attachFile(attype) def attachFile(self, file): ''' 构造附件扩展,仅MIMEBase支持中文附件 :param file: 附件文件 ''' # with open(file, 'rb') as f: # att = MIMEText(f.read(), 'plait', 'utf-8') # att["Content-Type"] = 'application/octet-stream' # filename = re.split(r'[\\|/]', file)[-1] # att["Content-Disposition"] = 'attachment; filename={}'.format(filename) # self.msg.attach(att) # logger.info('邮件已添加附件 {}'.format(filename)) att = MIMEBase('application', 'octet-stream') with open(file, 'rb') as f: att.set_payload(f.read()) filename = re.split(r'[\\|/]', file)[-1] att.add_header('Content-Disposition', 'attachment', filename=('gbk', '', filename)) encoders.encode_base64(att) self.msg.attach(att) logger.info('邮件已添加附件 {}'.format(filename)) def attachImage(self, path=os.path.join(RESOURCE_PATH, "Allure")): ''' 构造附图扩展 :param path: 图片路径 ''' with open(os.path.join(path, 'Allure.png'), 'rb') as f: attimg = MIMEImage(f.read()) attimg.add_header('Content-ID', '<image1>') return attimg def attachHtml(self, href="http://10.16.168.70:5000/allure", desc="API功能自动化测试报告"): """ 构造文本扩展 :param href: 链接地址 :param desc: 链接描述 :return: """ if self.image_flag: img_str = '<p><img src="cid:image1"></p>' message = \ ''' <h4>Dear All:</h4><br/><p>{}</p> <p>【<font size="3" color="gray"><b>测试报告</b></font>】:点击<a href="{}">{}</a>进行查看.</p><br/> {} '''.format(self.message, href, desc, img_str) else: message = \ ''' <h4>Dear All:</h4><br/><p>{}</p> <p>【<font size="3" color="gray"><b>测试报告</b></font>】:点击<a href="{}">{}</a>进行查看.</p><br/> '''.format(self.message, href, desc) msgalter = MIMEMultipart('alternative') msgalter.attach(MIMEText(message, _subtype='html', _charset='utf-8')) return msgalter def build_all_msg(self): ''' 添加所有邮件数据 ''' self.msg.attach(self.attachHtml()) self.msg.attach(self.attachImage()) return self.msg def build_html_msg(self): """ 添加文本邮件信息 :return: """ self.msg.attach(self.attachHtml()) return self.msg def build_image_msg(self): """ 添加图片邮件信息 :return: """ self.msg.attach(self.attachImage()) return self.msg def send(self, message='FYI', image_flag=False, *filepath): ''' 触发邮件发送 :param message: 邮件文本信息 :param image_flag: 是否发送图片 :param filepath: 邮件附件元组 :return: ''' self.message = message self.image_flag = image_flag for p in filepath: if os.path.isdir(p): self.files = list() for f in os.listdir(p): self.files.append(os.path.join(p, f)) self.checkAtType(self.files) elif os.path.isfile(p): self.files = p self.checkAtType(self.files) else: self.files = p filename = re.split(r'[\\|/]', str(p))[-1] logger.warning('注意! 邮件附件"{0}"的路径"{1}"可能无效, 附件无法上传.'. format(filename, p)) try: logger.info("开始发送测试结果邮件......") session = self.createSession() session.sendmail(self.sender, self.receiver.split(';'), self.build_all_msg().as_string() if self.image_flag else self.build_html_msg().as_string() ) except (gaierror and error): logger.exception('邮件发送失败! ~ 无法连接到SMTP服务器, 请检查网络以及邮件配置.') else: logger.info('[Done]:{0}邮件发送成功! 收件人:{1}.'.format(self.title, self.receiver)) session.quit() session.close()
def __init__(self, logger_name='GastepoAutoTest', config=APPLICATION_CONFIG_FILE): ''' 通过logging模块配置控制台和间隔文件log :param logger_name: 日志名称 ''' c = YamlConfig(config=config).get('log') self.color = str(c.get("logcolor")).capitalize() self.logger = logging.getLogger(logger_name) logging.root.setLevel(logging.NOTSET) self.log_file_name = c.get('logname') if c and c.get('logname') else 'GastepoAutoTest.log' self.when_auto = c.get('when') if c and c.get('when') else 'D' self.backup_count = c.get('backup') if c and c.get('backup') else 5 self.console_output_level = c.get('console_level') if c and c.get('console_level') else 'DEBUG' self.file_output_level = c.get('file_level') if c and c.get('file_level') else 'INFO' pattern = c.get('pattern') if c and c.get( 'pattern') else '%(name)s - %(levelname)s - %(asctime)s - %(message)s' if self.color == "True": LOG_COLORS_CONFIG = { 'DEBUG': 'white', 'INFO': 'black', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'bold_red', } self.formatter = colorlog.ColoredFormatter('%(log_color)s{}'.format(pattern), log_colors=LOG_COLORS_CONFIG) else: self.formatter = logging.Formatter(pattern)
# -*- coding: utf-8 -*- import os import shutil import time from subprocess import CalledProcessError from Gastepo.Core.Base.BaseData import APPLICATION_CONFIG_FILE from Gastepo.Core.Base.BaseData import RESULT_PATH, REPORT_PATH, RESOURCE_PATH from Gastepo.Core.Util.CommonUtils import run_command from Gastepo.Core.Util.ConfigUtils import YamlConfig from Gastepo.Core.Util.LogUtils import logger ALLURE_RESULT = RESULT_PATH ALLURE_REPORT = REPORT_PATH CHECK_COUNT = YamlConfig(config=APPLICATION_CONFIG_FILE).get("allure", 2).get("check_count") class AllureTools(object): """ Allure测试报告操作类 """ @classmethod def check_result(cls, check_count=CHECK_COUNT): """ 检查Allure测试结果json文件是否生成。 :param check_count: 检测次数。 :return: """ check_result = None for count in range(check_count):