Example #1
0
def timingtask(request):
    workflow_id = request.POST.get('workflow_id')
    run_date = request.POST.get('run_date')
    if run_date is None or workflow_id is None:
        context = {'errMsg': '时间不能为空'}
        return render(request, 'error.html', context)
    elif run_date < datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'):
        context = {'errMsg': '时间不能小于当前时间'}
        return render(request, 'error.html', context)
    workflow_detail = SqlWorkflow.objects.get(id=workflow_id)

    if can_timingtask(request.user, workflow_id) is False:
        context = {'errMsg': '你无权操作当前工单!'}
        return render(request, 'error.html', context)

    # 判断是否高危SQL,禁止执行
    if SysConfig().sys_config.get('critical_ddl_regex', '') != '':
        if InceptionDao().critical_ddl(workflow_detail.sql_content):
            context = {'errMsg': '高危语句,禁止执行!'}
            return render(request, 'error.html', context)

    run_date = datetime.datetime.strptime(run_date, "%Y-%m-%d %H:%M")
    url = get_detail_url(request, workflow_id)
    job_id = Const.workflowJobprefix['sqlreview'] + '-' + str(workflow_id)

    # 使用事务保持数据一致性
    try:
        with transaction.atomic():
            # 将流程状态修改为定时执行
            workflow_detail.status = Const.workflowStatus['timingtask']
            workflow_detail.save()
            # 调用添加定时任务
            add_sqlcronjob(job_id, run_date, workflow_id, url)
            # 增加工单日志
            # 获取audit_id
            audit_id = Workflow.audit_info_by_workflow_id(workflow_id=workflow_id,
                                                          workflow_type=WorkflowDict.workflow_type[
                                                              'sqlreview']).audit_id
            Workflow().add_workflow_log(audit_id=audit_id,
                                        operation_type=4,
                                        operation_type_desc='定时执行',
                                        operation_info="定时执行时间:{}".format(run_date),
                                        operator=request.user.username,
                                        operator_display=request.user.display
                                        )
    except Exception as msg:
        logger.error(traceback.format_exc())
        context = {'errMsg': msg}
        return render(request, 'error.html', context)
    return HttpResponseRedirect(reverse('sql:detail', args=(workflow_id,)))
Example #2
0
def global_info(request):
    """存放用户,菜单信息等."""
    user = request.user
    if user:
        # 获取待办数量
        try:
            todo = Workflow().audit_todo(user)
        except Exception:
            todo = 0
    else:
        todo = 0

    return {
        'todo': todo,
    }
Example #3
0
def global_info(request):
    """存放用户,菜单信息等."""
    user = request.user
    if user:
        # 获取待办数量
        try:
            todo = Workflow().auditlist(user, 0, 0, 1)['data']['auditlistCount']
        except Exception:
            todo = 0
    else:
        todo = 0

    return {
        'todo': todo,
    }
Example #4
0
def passed(request):
    workflow_id = request.POST['workflow_id']
    if workflow_id == '' or workflow_id is None:
        context = {'errMsg': 'workflow_id参数为空.'}
        return render(request, 'error.html', context)
    workflow_id = int(workflow_id)
    workflow_detail = SqlWorkflow.objects.get(id=workflow_id)
    audit_remark = request.POST.get('audit_remark', '')

    user = request.user
    if Workflow.can_review(request.user, workflow_id, 2) is False:
        context = {'errMsg': '你无权操作当前工单!'}
        return render(request, 'error.html', context)

    # 使用事务保持数据一致性
    try:
        with transaction.atomic():
            # 调用工作流接口审核
            # 获取audit_id
            audit_id = Workflow.audit_info_by_workflow_id(
                workflow_id=workflow_id,
                workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id
            audit_result = Workflow().auditworkflow(
                request, audit_id,
                WorkflowDict.workflow_status['audit_success'], user.username,
                audit_remark)

            # 按照审核结果更新业务表审核状态
            if audit_result['data'][
                    'workflow_status'] == WorkflowDict.workflow_status[
                        'audit_success']:
                # 将流程状态修改为审核通过,并更新reviewok_time字段
                workflow_detail.status = Const.workflowStatus['pass']
                workflow_detail.reviewok_time = timezone.now()
                workflow_detail.audit_remark = audit_remark
                workflow_detail.save()
    except Exception as msg:
        logger.error(traceback.format_exc())
        context = {'errMsg': msg}
        return render(request, 'error.html', context)

    return HttpResponseRedirect(reverse('sql:detail', args=(workflow_id, )))
Example #5
0
from common.config import SysConfig
from common.utils.const import Const, WorkflowDict
from common.utils.extend_json_encoder import ExtendJSONEncoder
from sql.models import SqlGroup, Users
from sql.utils.execute_sql import execute_call_back, execute_skipinc_call_back
from sql.utils.group import user_groups, user_instances
from sql.utils.inception import InceptionDao
from sql.utils.jobs import add_sqlcronjob, del_sqlcronjob
from sql.utils.sql_review import can_timingtask, get_detail_url, can_cancel, can_execute
from sql.utils.workflow import Workflow
from .models import SqlWorkflow

logger = logging.getLogger('default')
sqlSHA1_cache = {
}  # 存储SQL文本与SHA1值的对应关系,尽量减少与数据库的交互次数,提高效率。格式: {工单ID1:{SQL内容1:sqlSHA1值1, SQL内容2:sqlSHA1值2},}
workflowOb = Workflow()


# 获取审核列表
@permission_required('sql.menu_sqlworkflow', raise_exception=True)
def sqlworkflow_list(request):
    limit = int(request.POST.get('limit'))
    offset = int(request.POST.get('offset'))
    limit = offset + limit
    search = request.POST.get('search', '')

    # 获取筛选参数
    navStatus = request.POST.get('navStatus')

    # 管理员可以看到全部工单,其他人能看到自己提交和审核的工单
    user = request.user
Example #6
0
def cancel(request):
    workflow_id = request.POST['workflow_id']
    if workflow_id == '' or workflow_id is None:
        context = {'errMsg': 'workflow_id参数为空.'}
        return render(request, 'error.html', context)

    workflow_id = int(workflow_id)
    workflow_detail = SqlWorkflow.objects.get(id=workflow_id)
    audit_remark = request.POST.get('cancel_remark')
    if audit_remark is None:
        context = {'errMsg': '终止原因不能为空'}
        return render(request, 'error.html', context)

    user = request.user
    if can_cancel(request.user, workflow_id) is False:
        context = {'errMsg': '你无权操作当前工单!'}
        return render(request, 'error.html', context)

    # 使用事务保持数据一致性
    try:
        with transaction.atomic():
            # 调用工作流接口取消或者驳回
            # 获取audit_id
            audit_id = Workflow.audit_info_by_workflow_id(workflow_id=workflow_id,
                                                          workflow_type=WorkflowDict.workflow_type[
                                                              'sqlreview']).audit_id
            # 仅待审核的需要调用工作流,审核通过的不需要
            if workflow_detail.status != Const.workflowStatus['manreviewing']:
                # 增加工单日志
                if user.username == workflow_detail.engineer:
                    Workflow().add_workflow_log(audit_id=audit_id,
                                                operation_type=3,
                                                operation_type_desc='取消执行',
                                                operation_info="取消原因:{}".format(audit_remark),
                                                operator=request.user.username,
                                                operator_display=request.user.display
                                                )
                else:
                    Workflow().add_workflow_log(audit_id=audit_id,
                                                operation_type=2,
                                                operation_type_desc='审批不通过',
                                                operation_info="审批备注:{}".format(audit_remark),
                                                operator=request.user.username,
                                                operator_display=request.user.display
                                                )
            else:
                if user.username == workflow_detail.engineer:
                    Workflow().auditworkflow(request, audit_id,
                                             WorkflowDict.workflow_status['audit_abort'],
                                             user.username, audit_remark)
                # 非提交人需要校验审核权限
                elif user.has_perm('sql.sql_review'):
                    Workflow().auditworkflow(request, audit_id,
                                             WorkflowDict.workflow_status['audit_reject'],
                                             user.username, audit_remark)
                else:
                    raise PermissionDenied

            # 删除定时执行job
            if workflow_detail.status == Const.workflowStatus['timingtask']:
                job_id = Const.workflowJobprefix['sqlreview'] + '-' + str(workflow_id)
                del_sqlcronjob(job_id)
            # 将流程状态修改为人工终止流程
            workflow_detail.status = Const.workflowStatus['abort']
            workflow_detail.audit_remark = audit_remark
            workflow_detail.save()
    except Exception as msg:
        logger.error(traceback.format_exc())
        context = {'errMsg': msg}
        return render(request, 'error.html', context)
    return HttpResponseRedirect(reverse('sql:detail', args=(workflow_id,)))
Example #7
0
def execute(request):
    workflow_id = request.POST['workflow_id']
    if workflow_id == '' or workflow_id is None:
        context = {'errMsg': 'workflow_id参数为空.'}
        return render(request, 'error.html', context)

    workflow_id = int(workflow_id)
    workflow_detail = SqlWorkflow.objects.get(id=workflow_id)
    instance_name = workflow_detail.instance_name
    db_name = workflow_detail.db_name
    url = get_detail_url(request, workflow_id)

    if can_execute(request.user, workflow_id) is False:
        context = {'errMsg': '你无权操作当前工单!'}
        return render(request, 'error.html', context)

    # 判断是否高危SQL,禁止执行
    if SysConfig().sys_config.get('critical_ddl_regex', '') != '':
        if InceptionDao().critical_ddl(workflow_detail.sql_content):
            context = {'errMsg': '高危语句,禁止执行!'}
            return render(request, 'error.html', context)

    # 将流程状态修改为执行中,并更新reviewok_time字段
    workflow_detail.status = Const.workflowStatus['executing']
    workflow_detail.reviewok_time = timezone.now()
    workflow_detail.save()

    # 判断是通过inception执行还是直接执行,is_manual=0则通过inception执行,is_manual=1代表inception审核不通过,需要直接执行
    if workflow_detail.is_manual == 0:
        # 执行之前重新split并check一遍,更新SHA1缓存;因为如果在执行中,其他进程去做这一步操作的话,会导致inception core dump挂掉
        try:
            split_review_result = InceptionDao(instance_name=instance_name).sqlauto_review(workflow_detail.sql_content,
                                                                                           db_name,
                                                                                           is_split='yes')
        except Exception as msg:
            logger.error(traceback.format_exc())
            context = {'errMsg': msg}
            return render(request, 'error.html', context)
        workflow_detail.review_content = json.dumps(split_review_result)
        try:
            workflow_detail.save()
        except Exception:
            # 关闭后重新获取连接,防止超时
            connection.close()
            workflow_detail.save()

        # 采取异步回调的方式执行语句,防止出现持续执行中的异常
        t = Thread(target=execute_call_back, args=(workflow_id, instance_name, url))
        t.start()
    else:
        # 采取异步回调的方式执行语句,防止出现持续执行中的异常
        t = Thread(target=execute_skipinc_call_back,
                   args=(workflow_id, instance_name, db_name, workflow_detail.sql_content, url))
        t.start()
    # 删除定时执行job
    if workflow_detail.status == Const.workflowStatus['timingtask']:
        job_id = Const.workflowJobprefix['sqlreview'] + '-' + str(workflow_id)
        del_sqlcronjob(job_id)
    # 增加工单日志
    # 获取audit_id
    audit_id = Workflow.audit_info_by_workflow_id(workflow_id=workflow_id,
                                                  workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id
    Workflow().add_workflow_log(audit_id=audit_id,
                                operation_type=5,
                                operation_type_desc='执行工单',
                                operation_info="人工操作执行",
                                operator=request.user.username,
                                operator_display=request.user.display
                                )
    return HttpResponseRedirect(reverse('sql:detail', args=(workflow_id,)))
Example #8
0
def autoreview(request):
    workflow_id = request.POST.get('workflow_id')
    sql_content = request.POST['sql_content']
    workflow_title = request.POST['workflow_name']
    group_name = request.POST['group_name']
    group_id = ResourceGroup.objects.get(group_name=group_name).group_id
    instance_name = request.POST['instance_name']
    db_name = request.POST.get('db_name')
    is_backup = request.POST['is_backup']
    notify_users = request.POST.getlist('notify_users')

    # 服务器端参数验证
    if sql_content is None or workflow_title is None or instance_name is None or db_name is None or is_backup is None:
        context = {'errMsg': '页面提交参数可能为空'}
        return render(request, 'error.html', context)

    # 验证组权限(用户是否在该组、该组是否有指定实例)
    try:
        user_instances(request.user, 'master').get(instance_name=instance_name)
    except Exception:
        context = {'errMsg': '你所在组未关联该实例!'}
        return render(request, 'error.html', context)

    # # 删除注释语句
    # sql_content = ''.join(
    #     map(lambda x: re.compile(r'(^--.*|^/\*.*\*/;\s*$)').sub('', x, count=1),
    #         sql_content.splitlines(1))).strip()
    # # 去除空行
    # sql_content = re.sub('[\r\n\f]{2,}', '\n', sql_content)

    sql_content = sql_content.strip()

    # 交给inception进行自动审核
    try:
        inception_result = InceptionDao(instance_name=instance_name).sqlauto_review(sql_content, db_name)
    except Exception as msg:
        context = {'errMsg': msg}
        return render(request, 'error.html', context)

    if inception_result is None or len(inception_result) == 0:
        context = {'errMsg': 'inception返回的结果集为空!可能是SQL语句有语法错误'}
        return render(request, 'error.html', context)
    # 要把result转成JSON存进数据库里,方便SQL单子详细信息展示
    json_result = json.dumps(inception_result)

    # 遍历result,看是否有任何自动审核不通过的地方,并且按配置确定是标记审核不通过还是放行,放行的可以在工单内跳过inception直接执行
    sys_config = SysConfig().sys_config
    is_manual = 0
    workflow_status = Const.workflowStatus['manreviewing']
    for row in inception_result:
        # 1表示警告,不影响执行
        if row[2] == 1 and sys_config.get('auto_review_wrong', '') == '1':
            workflow_status = Const.workflowStatus['autoreviewwrong']
            break
        # 2表示严重错误,或者inception不支持的语法,标记手工执行,可以跳过inception直接执行
        elif row[2] == 2:
            is_manual = 1
            if sys_config.get('auto_review_wrong', '') in ('', '1', '2'):
                workflow_status = Const.workflowStatus['autoreviewwrong']
            break
        elif re.match(r"\w*comments\w*", row[4]):
            is_manual = 1
            if sys_config.get('auto_review_wrong', '') in ('', '1', '2'):
                workflow_status = Const.workflowStatus['autoreviewwrong']
            break

    # 判断SQL是否包含DDL语句,SQL语法 1、DDL,2、DML
    sql_syntax = 2
    for stmt in sqlparse.split(sql_content):
        statement = sqlparse.parse(stmt)[0]
        syntax_type = statement.token_first().ttype.__str__()
        if syntax_type == 'Token.Keyword.DDL':
            sql_syntax = 1
            break

    # 调用工作流生成工单
    # 使用事务保持数据一致性
    try:
        with transaction.atomic():
            # 存进数据库里
            engineer = request.user.username
            if not workflow_id:
                sql_workflow = SqlWorkflow()
                sql_workflow.create_time = timezone.now()
            else:
                sql_workflow = SqlWorkflow.objects.get(id=int(workflow_id))
            sql_workflow.workflow_name = workflow_title
            sql_workflow.group_id = group_id
            sql_workflow.group_name = group_name
            sql_workflow.engineer = engineer
            sql_workflow.engineer_display = request.user.display
            sql_workflow.audit_auth_groups = Workflow.audit_settings(group_id, WorkflowDict.workflow_type['sqlreview'])
            sql_workflow.status = workflow_status
            sql_workflow.is_backup = is_backup
            sql_workflow.review_content = json_result
            sql_workflow.instance_name = instance_name
            sql_workflow.db_name = db_name
            sql_workflow.sql_content = sql_content
            sql_workflow.execute_result = ''
            sql_workflow.is_manual = is_manual
            sql_workflow.audit_remark = ''
            sql_workflow.sql_syntax = sql_syntax
            sql_workflow.save()
            workflow_id = sql_workflow.id
            # 自动审核通过了,才调用工作流
            if workflow_status == Const.workflowStatus['manreviewing']:
                # 调用工作流插入审核信息, 查询权限申请workflow_type=2
                # 抄送通知人
                list_cc_addr = [email['email'] for email in
                                Users.objects.filter(username__in=notify_users).values('email')]
                Workflow().addworkflowaudit(request, WorkflowDict.workflow_type['sqlreview'], workflow_id,
                                            list_cc_addr=list_cc_addr)
    except Exception as msg:
        logger.error(traceback.format_exc())
        context = {'errMsg': msg}
        return render(request, 'error.html', context)

    return HttpResponseRedirect(reverse('sql:detail', args=(workflow_id,)))