diff --git a/sql/engines/redis.py b/sql/engines/redis.py index a9b454bc64..2d674a7729 100644 --- a/sql/engines/redis.py +++ b/sql/engines/redis.py @@ -11,7 +11,7 @@ import logging import traceback from . import EngineBase -from .models import ResultSet +from .models import ResultSet, ReviewSet, ReviewResult __author__ = 'hhyo' @@ -91,3 +91,55 @@ def filter_sql(self, sql='', limit_num=0): def query_masking(self, db_name=None, sql='', resultset=None): """不做脱敏""" return resultset + + def execute_check(self, db_name=None, sql=''): + """上线单执行前的检查, 返回Review set""" + check_result = ReviewSet(full_sql=sql) + result = ReviewResult(id=1, + errlevel=0, + stagestatus='Audit completed', + errormessage='None', + sql=sql, + affected_rows=0, + execute_time=0, ) + check_result.rows += [result] + return check_result + + def execute_workflow(self, workflow): + """执行上线单,返回Review set""" + sql = workflow.sqlworkflowcontent.sql_content + execute_result = ReviewSet(full_sql=sql) + try: + conn = self.get_connection() + if workflow.db_name: + conn.execute_command(f"select {workflow.db_name}") + conn.execute_command(workflow.sqlworkflowcontent.sql_content) + except Exception as e: + logger.error(f"Redis命令执行报错,语句:{sql}, 错误信息:{traceback.format_exc()}") + execute_result.error = str(e) + execute_result.status = "workflow_exception" + execute_result.rows.append(ReviewResult( + id=1, + errlevel=2, + stagestatus='Execute Failed', + errormessage=f'异常信息:{e}', + sql=sql, + affected_rows=0, + execute_time=0, + )) + else: + execute_result.status = "workflow_finish" + execute_result.rows.append(ReviewResult( + id=1, + errlevel=0, + stagestatus='Execute Successfully', + errormessage='None', + sql=sql, + affected_rows=0, + execute_time=0, + )) + finally: + workflow.sqlworkflowcontent.execute_result = execute_result.json() + workflow.sqlworkflowcontent.save() + workflow.save() + return execute_result diff --git a/sql/engines/tests.py b/sql/engines/tests.py index d18efa59ee..46d36cb3aa 100644 --- a/sql/engines/tests.py +++ b/sql/engines/tests.py @@ -6,7 +6,7 @@ from django.test import TestCase from sql.engines import EngineBase -from sql.engines.models import ResultSet, ReviewSet +from sql.engines.models import ResultSet, ReviewSet, ReviewResult from sql.engines.mssql import MssqlEngine from sql.engines.mysql import MysqlEngine from sql.engines.redis import RedisEngine @@ -45,7 +45,6 @@ def setUpClass(cls): 'id': 1, 'sql': 'some_content' }])) - cls.wf1.save() @classmethod def tearDownClass(cls): @@ -240,6 +239,8 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.ins.delete() + SqlWorkflow.objects.all().delete() + SqlWorkflowContent.objects.all().delete() @patch('redis.Redis') def test_get_connection(self, _conn): @@ -299,6 +300,51 @@ def test_query_masking(self): masking_result = new_engine.query_masking(db_name=0, sql='', resultset=query_result) self.assertEqual(masking_result, query_result) + def test_execute_check(self): + sql = 'set 1 1' + row = ReviewResult(id=1, + errlevel=0, + stagestatus='Audit completed', + errormessage='None', + sql=sql, + affected_rows=0, + execute_time=0, + full_sql=sql) + new_engine = RedisEngine(instance=self.ins) + check_result = new_engine.execute_check(db_name=0, sql=sql) + self.assertIsInstance(check_result, ReviewSet) + self.assertEqual(check_result.rows[0].__dict__, row.__dict__) + + @patch('redis.Redis.execute_command', return_value='text') + def test_execute_workflow_success(self, _execute_command): + sql = 'set 1 1' + row = ReviewResult(id=1, + errlevel=0, + stagestatus='Execute Successfully', + errormessage='None', + sql=sql, + affected_rows=0, + execute_time=0, + full_sql=sql) + wf = SqlWorkflow.objects.create( + workflow_name='some_name', + group_id=1, + group_name='g1', + engineer_display='', + audit_auth_groups='some_group', + create_time=datetime.now() - timedelta(days=1), + status='workflow_finish', + is_backup='是', + instance=self.ins, + db_name='some_db', + syntax_type=1 + ) + SqlWorkflowContent.objects.create(workflow=wf, sql_content=sql) + new_engine = RedisEngine(instance=self.ins) + execute_result = new_engine.execute_workflow(workflow=wf) + self.assertIsInstance(execute_result, ReviewSet) + self.assertEqual(execute_result.rows[0].__dict__, row.__dict__) + class TestPgSQL(TestCase): @classmethod @@ -358,7 +404,7 @@ def test_get_all_columns_by_tb(self, _query): return_value=ResultSet(rows=[('postgres',), ('archery',), ('template1',), ('template0',)])) def test_describe_table(self, _query): new_engine = PgSQLEngine(instance=self.ins) - describe = new_engine.describe_table(db_name='archery', schema_name='archery',tb_name='text') + describe = new_engine.describe_table(db_name='archery', schema_name='archery', tb_name='text') self.assertIsInstance(describe, ResultSet) def test_query_check_disable_sql(self): diff --git a/sql/models.py b/sql/models.py index 585f0cf79e..ecedc10c90 100644 --- a/sql/models.py +++ b/sql/models.py @@ -131,7 +131,7 @@ class SqlWorkflow(models.Model): group_name = models.CharField('组名称', max_length=100) instance = models.ForeignKey(Instance, on_delete=models.CASCADE) db_name = models.CharField('数据库', max_length=64) - syntax_type = models.IntegerField('工单类型 0、未知,1、DDL,2、DML', choices=((0, '未知'), (1, 'DDL'), (2, 'DML')), default=0) + syntax_type = models.IntegerField('工单类型 0、未知,1、DDL,2、DML', choices=((0, '其他'), (1, 'DDL'), (2, 'DML')), default=0) is_backup = models.CharField('是否备份', choices=(('否', '否'), ('是', '是')), max_length=20) engineer = models.CharField('发起人', max_length=30) engineer_display = models.CharField('发起人中文名', max_length=50, default='') diff --git a/sql/sql_workflow.py b/sql/sql_workflow.py index 8098fc572a..d73a853b50 100644 --- a/sql/sql_workflow.py +++ b/sql/sql_workflow.py @@ -139,7 +139,7 @@ def submit(request): # 验证组权限(用户是否在该组、该组是否有指定实例) try: - user_instances(request.user, type='master', db_type='mysql').get(instance_name=instance_name) + user_instances(request.user, type='master', db_type='all').get(instance_name=instance_name) except instance.DoesNotExist: context = {'errMsg': '你所在组未关联该实例!'} return render(request, 'error.html', context) diff --git a/sql/templates/queryapplylist.html b/sql/templates/queryapplylist.html index 235303aacc..3fd739fa3f 100644 --- a/sql/templates/queryapplylist.html +++ b/sql/templates/queryapplylist.html @@ -47,7 +47,7 @@ - +
diff --git a/sql/templates/sqlsubmit.html b/sql/templates/sqlsubmit.html index b1eb4975fa..748a4e0bc7 100644 --- a/sql/templates/sqlsubmit.html +++ b/sql/templates/sqlsubmit.html @@ -46,6 +46,10 @@ class="selectpicker show-tick form-control bs-select-hidden" data-name="实例" data-live-search="true" data-placeholder="请选择实例" required> + + + +
@@ -564,12 +568,22 @@ success: function (data) { if (data.status === 0) { var result = data['data']; - $("#instance_name").empty(); + $("#optgroup-mysql").empty(); + $("#optgroup-mssql").empty(); + $("#optgroup-redis").empty(); + $("#optgroup-pgsql").empty(); for (var i = 0; i < result.length; i++) { var instance = ""; - $("#instance_name").append(instance); + if (result[i]['db_type'] === 'mysql') { + $("#optgroup-mysql").append(instance); + } else if (result[i]['db_type'] === 'mssql') { + $("#optgroup-mssql").append(instance); + } else if (result[i]['db_type'] === 'redis') { + $("#optgroup-redis").append(instance); + } else if (result[i]['db_type'] === 'pgsql') { + $("#optgroup-pgsql").append(instance); + } } - $("#instance_name").prepend(""); $('#instance_name').selectpicker('render'); $('#instance_name').selectpicker('refresh'); $("#db_name").empty(); diff --git a/sql/templates/sqlworkflow.html b/sql/templates/sqlworkflow.html index 40299ed81f..d0f0a04710 100644 --- a/sql/templates/sqlworkflow.html +++ b/sql/templates/sqlworkflow.html @@ -117,7 +117,9 @@ title: '类型', field: 'syntax_type', formatter: function (value, row, index) { - if (value === 1) { + if (value === 0) { + return '其他' + } else if (value === 1) { return 'DDL' } else if (value === 2) { return 'DML'