Exemplo n.º 1
0
 def setUp(self):
     self.superuser = User.objects.create(username='******',
                                          is_superuser=True)
     self.user = User.objects.create(username='******')
     # 使用 travis.ci 时实例和测试service保持一致
     self.master = Instance(
         instance_name='test_instance',
         type='master',
         db_type='mysql',
         host=settings.DATABASES['default']['HOST'],
         port=settings.DATABASES['default']['PORT'],
         user=settings.DATABASES['default']['USER'],
         password=settings.DATABASES['default']['PASSWORD'])
     self.master.save()
     self.sys_config = SysConfig()
     self.client = Client()
     self.group = ResourceGroup.objects.create(group_id=1,
                                               group_name='group_name')
     self.wf1 = SqlWorkflow.objects.create(
         workflow_name='workflow_name',
         group_id=self.group.group_id,
         group_name=self.group.group_name,
         engineer=self.superuser.username,
         engineer_display=self.superuser.display,
         audit_auth_groups='audit_auth_groups',
         create_time=datetime.datetime.now(),
         status='workflow_review_pass',
         is_backup=True,
         instance=self.master,
         db_name='db_name',
         syntax_type=1,
     )
     self.wfc1 = SqlWorkflowContent.objects.create(workflow=self.wf1,
                                                   sql_content='some_sql',
                                                   execute_result='')
Exemplo n.º 2
0
 def setUp(self):
     self.superuser1 = User(username='******', display='中文显示', is_active=True, is_superuser=True,
                            email='*****@*****.**')
     self.superuser1.save()
     self.slave1 = Instance(instance_name='some_name', host='some_host', type='slave', db_type='mysql',
                            user='******', port=1234, password='******')
     self.slave1.save()
Exemplo n.º 3
0
 def setUp(self):
     self.slave1 = Instance(instance_name='test_slave_instance', type='slave', db_type='mysql',
                            host='testhost', port=3306, user='******', password='******')
     self.slave1.save()
     User = get_user_model()
     self.u1 = User(username='******', display='中文显示', is_active=True)
     self.u1.save()
     self.u2 = User(username='******', display='中文显示', is_active=True)
     self.u2.save()
     sql_query_perm = Permission.objects.get(codename='query_submit')
     self.u2.user_permissions.add(sql_query_perm)
Exemplo n.º 4
0
 def setUp(self):
     self.superuser = User(username='******', is_superuser=True)
     self.superuser.save()
     # 使用 travis.ci 时实例和测试service保持一致
     self.master = Instance(instance_name='test_instance', type='master', db_type='mysql',
                            host=settings.DATABASES['default']['HOST'],
                            port=settings.DATABASES['default']['PORT'],
                            user=settings.DATABASES['default']['USER'],
                            password=settings.DATABASES['default']['PASSWORD'])
     self.master.save()
     self.sys_config = SysConfig()
     self.client = Client()
     self.client.force_login(self.superuser)
Exemplo n.º 5
0
class QueryTest(TestCase):
    def setUp(self):
        self.slave1 = Instance(instance_name='test_slave_instance', type='slave', db_type='mysql',
                               host='testhost', port=3306, user='******', password='******')
        self.slave1.save()
        User = get_user_model()
        self.u1 = User(username='******', display='中文显示', is_active=True)
        self.u1.save()
        self.u2 = User(username='******', display='中文显示', is_active=True)
        self.u2.save()
        sql_query_perm = Permission.objects.get(codename='query_submit')
        self.u2.user_permissions.add(sql_query_perm)

    def testcorrectSQL(self):
        c = Client()
        some_sql = 'select some from some_table limit 100;'
        some_db = 'some_db'
        some_limit = 100
        c.force_login(self.u1)
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': some_sql,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        self.assertEqual(r.status_code, 403)
        c.force_login(self.u2)
        q_result = ResultSet(full_sql=some_sql, rows=['value'])
        q_result.column_list = ['some']

        mock_engine = MysqlEngine
        mock_engine.query = MagicMock(return_value=q_result)
        mock_engine.query_masking = MagicMock(return_value=q_result)

        mock_query = query
        mock_query.query_priv_check = MagicMock(return_value={'status': 0, 'data': {'limit_num': 100, 'priv_check': 1}})
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': some_sql,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        mock_engine.query.assert_called_once_with(db_name=some_db, sql=some_sql, limit_num=some_limit)
        r_json = r.json()
        self.assertEqual(r_json['data']['rows'], ['value'])
        self.assertEqual(r_json['data']['column_list'], ['some'])

    def testMasking(self):
        pass

    def tearDown(self):
        self.u1.delete()
        self.u2.delete()
        self.slave1.delete()
Exemplo n.º 6
0
 def setUpClass(cls):
     cls.u1 = User(username='******', display='用户1')
     cls.u1.save()
     cls.ins1 = Instance(instance_name='some_ins', type='master', db_type='mssql', host='some_host',
                         port=1366, user='******', password='******')
     cls.ins1.save()
     cls.wf1 = SqlWorkflow.objects.create(
         workflow_name='some_name',
         group_id=1,
         group_name='g1',
         engineer=cls.u1.username,
         engineer_display=cls.u1.display,
         audit_auth_groups='some_group',
         create_time=datetime.now() - timedelta(days=1),
         status='workflow_finish',
         is_backup='是',
         instance=cls.ins1,
         db_name='some_db',
         syntax_type=1
     )
     cls.wfc1 = SqlWorkflowContent.objects.create(
         workflow=cls.wf1,
         sql_content='some_sql',
         execute_result=json.dumps([{
             'id': 1,
             'sql': 'some_content'
         }]))
     cls.wf1.save()
Exemplo n.º 7
0
class TestSchemaSync(TestCase):
    """
    测试SchemaSync
    """
    def setUp(self):
        self.superuser = User(username='******', is_superuser=True)
        self.superuser.save()
        # 使用 travis.ci 时实例和测试service保持一致
        self.master = Instance(
            instance_name='test_instance',
            type='master',
            db_type='mysql',
            host=settings.DATABASES['default']['HOST'],
            port=settings.DATABASES['default']['PORT'],
            user=settings.DATABASES['default']['USER'],
            password=settings.DATABASES['default']['PASSWORD'])
        self.master.save()
        self.sys_config = SysConfig()
        self.client = Client()
        self.client.force_login(self.superuser)

    def tearDown(self):
        self.superuser.delete()
        self.master.delete()
        self.sys_config.replace(json.dumps({}))

    def test_schema_sync(self):
        """
        测试SchemaSync
        :return:
        """
        data = {
            "instance_name": "test_instance",
            "db_name": "*",
            "target_instance_name": "test_instance",
            "target_db_name": "*",
            "sync_auto_inc": True,
            "sync_comments": False
        }
        r = self.client.post(path='/instance/schemasync/', data=data)
        self.assertEqual(json.loads(r.content)['status'], 1)
        self.assertEqual(json.loads(r.content)['msg'], '请配置SchemaSync路径!')
        self.sys_config.set('schemasync',
                            '/opt/venv4schemasync/bin/schemasync')
        self.sys_config.get_all_config()
        r = self.client.post(path='/instance/schemasync/', data=data)
        self.assertEqual(json.loads(r.content)['status'], 0)
Exemplo n.º 8
0
 def setUpClass(cls):
     cls.ins = Instance(instance_name='some_ins',
                        type='slave',
                        db_type='pgsql',
                        host='some_host',
                        port=1366,
                        user='******',
                        password='******')
     cls.ins.save()
Exemplo n.º 9
0
 def setUp(self):
     self.slave1 = Instance(instance_name='test_slave_instance',
                            type='slave',
                            db_type='mysql',
                            host='testhost',
                            port=3306,
                            user='******',
                            password='******')
     self.slave1.save()
     User = get_user_model()
     self.u1 = User(username='******', display='中文显示', is_active=True)
     self.u1.save()
     self.u2 = User(username='******', display='中文显示', is_active=True)
     self.u2.save()
     sql_query_perm = Permission.objects.get(codename='query_submit')
     self.u2.user_permissions.add(sql_query_perm)
     tomorrow = datetime.now() + timedelta(days=1)
     self.query_apply_1 = QueryPrivilegesApply(
         group_id=1,
         group_name='some_group',
         title='some_title',
         user_name='some_user',
         instance_name='some_ins',
         db_list='some_db,some_db2',
         limit_num=100,
         valid_date=tomorrow,
         priv_type=1,
         status=0,
         audit_auth_groups='some_audit_group')
     self.query_apply_1.save()
     self.query_apply_2 = QueryPrivilegesApply(
         group_id=1,
         group_name='some_group',
         title='some_title',
         user_name='some_user',
         instance_name='some_ins',
         db_list='some_db',
         table_list='some_table,some_tb2',
         limit_num=100,
         valid_date=tomorrow,
         priv_type=2,
         status=0,
         audit_auth_groups='some_audit_group')
     self.query_apply_2.save()
Exemplo n.º 10
0
 def setUp(self):
     self.ins1 = Instance(instance_name='some_ins', type='slave', db_type='mysql', host='some_host',
                          port=1366, user='******', password='******')
     self.ins1.save()
     self.sys_config = SysConfig()
     self.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=True,
         instance=self.ins1,
         db_name='some_db',
         syntax_type=1
     )
     SqlWorkflowContent.objects.create(workflow=self.wf)
Exemplo n.º 11
0
 def setUpClass(cls):
     cls.ins1 = Instance(instance_name='some_ins',
                         type='slave',
                         db_type='mssql',
                         host='some_host',
                         port=1366,
                         user='******',
                         password='******')
     cls.ins1.save()
     cls.engine = MssqlEngine(instance=cls.ins1)
Exemplo n.º 12
0
 def setUp(self):
     self.now = datetime.now()
     self.u1 = User(username='******', display='用户1')
     self.u1.save()
     self.master1 = Instance(instance_name='test_master_instance',
                             type='master',
                             db_type='mysql',
                             host='testhost',
                             port=3306,
                             user='******',
                             password='******')
     self.master1.save()
     self.wf1 = SqlWorkflow.objects.create(
         workflow_name='some_name2',
         group_id=1,
         group_name='g1',
         engineer=self.u1.username,
         engineer_display=self.u1.display,
         audit_auth_groups='some_group',
         create_time=self.now - timedelta(days=1),
         status='workflow_executing',
         is_backup='是',
         instance=self.master1,
         db_name='some_db',
         syntax_type=1,
     )
     self.wfc1 = SqlWorkflowContent.objects.create(workflow=self.wf1,
                                                   sql_content='some_sql',
                                                   execute_result='')
     # 初始化工单执行返回对象
     self.task_result = MagicMock()
     self.task_result.args = [self.wf1.id]
     self.task_result.success = True
     self.task_result.stopped = self.now
     self.task_result.result.json.return_value = json.dumps([{
         'id':
         1,
         'sql':
         'some_content'
     }])
     self.task_result.result.warning = ''
     self.task_result.result.error = ''
Exemplo n.º 13
0
 def setUpClass(cls):
     cls.u1 = User(username='******', display='用户1')
     cls.u1.save()
     cls.u2 = User(username='******', display='用户2')
     cls.u2.save()
     cls.superuser1 = User(username='******', is_superuser=True)
     cls.superuser1.save()
     cls.now = datetime.datetime.now()
     cls.slave1 = Instance(instance_name='test_slave_instance', type='slave', db_type='mysql',
                           host='testhost', port=3306, user='******', password='******')
     cls.slave1.save()
     # 批量创建数据 ddl ,u1 ,g1, yesterday 组, 2 个数据
     ddl_workflow = [SqlWorkflow(
         workflow_name='ddl %s' % i,
         group_id=1,
         group_name='g1',
         engineer=cls.u1.username,
         engineer_display=cls.u1.display,
         audit_auth_groups='some_group',
         create_time=cls.now - datetime.timedelta(days=1),
         status='workflow_finish',
         is_backup=True,
         instance=cls.slave1,
         db_name='some_db',
         syntax_type=1
     ) for i in range(2)]
     # 批量创建数据 dml ,u1 ,g2, the day before yesterday 组, 3 个数据
     dml_workflow = [SqlWorkflow(
         workflow_name='Test %s' % i,
         group_id=2,
         group_name='g2',
         engineer=cls.u2.username,
         engineer_display=cls.u2.display,
         audit_auth_groups='some_group',
         create_time=cls.now - datetime.timedelta(days=2),
         status='workflow_finish',
         is_backup=True,
         instance=cls.slave1,
         db_name='some_db',
         syntax_type=2
     ) for i in range(3)]
     SqlWorkflow.objects.bulk_create(ddl_workflow + dml_workflow)
     # 保存内容数据
     ddl_workflow_content = [SqlWorkflowContent(
         workflow=SqlWorkflow.objects.get(workflow_name='ddl %s' % i),
         sql_content='some_sql',
     ) for i in range(2)]
     dml_workflow_content = [SqlWorkflowContent(
         workflow=SqlWorkflow.objects.get(workflow_name='Test %s' % i),
         sql_content='some_sql',
     ) for i in range(3)]
     SqlWorkflowContent.objects.bulk_create(ddl_workflow_content + dml_workflow_content)
Exemplo n.º 14
0
 def setUpClass(cls):
     """
     初始化补全引擎
     :return:
     """
     # 使用 travis.ci 时实例和测试service保持一致
     cls.master = Instance(instance_name='test_instance', type='master', db_type='mysql',
                           host=settings.DATABASES['default']['HOST'],
                           port=settings.DATABASES['default']['PORT'],
                           user=settings.DATABASES['default']['USER'],
                           password=settings.DATABASES['default']['PASSWORD'])
     cls.master.save()
     cls.comp_engine = get_comp_engine(instance=cls.master, db_name=settings.DATABASES['default']['TEST']['NAME'])
     # 等待completion_refresher刷新完成
     while cls.comp_engine.completion_refresher.is_refreshing():
         import time
         time.sleep(1)
Exemplo n.º 15
0
def mirage(request):
    """迁移加密的Instance数据,保留一定版本后删除"""
    try:
        pc = Prpcrypt()
        mg_user = Migrator(app="sql", model="Instance", field="user")
        mg_password = Migrator(app="sql", model="Instance", field="password")
        # 还原密码
        for ins in Instance.objects.all():
            # 忽略解密错误的数据(本身为异常数据)
            try:
                Instance(pk=ins.pk, password=pc.decrypt(ins.password)).save(update_fields=['password'])
            except:
                pass
        # 使用django-mirage-field重新加密
        mg_user.encrypt()
        mg_password.encrypt()
        return JsonResponse({"msg": "ok"})
    except Exception as msg:
        return JsonResponse({"msg": f"{msg}"})
Exemplo n.º 16
0
class CheckTest(TestCase):
    """检查功能测试"""
    def setUp(self):
        self.superuser1 = User(username='******',
                               display='中文显示',
                               is_active=True,
                               is_superuser=True,
                               email='*****@*****.**')
        self.superuser1.save()
        self.slave1 = Instance(instance_name='some_name',
                               host='some_host',
                               type='slave',
                               db_type='mysql',
                               user='******',
                               port=1234,
                               password='******')
        self.slave1.save()

    def tearDown(self):
        self.superuser1.delete()

    @patch.object(MsgSender, '__init__', return_value=None)
    @patch.object(MsgSender, 'send_email')
    def testEmailCheck(self, send_email, mailsender):
        """邮箱配置检查"""
        mail_switch = 'true'
        smtp_ssl = 'false'
        smtp_server = 'some_server'
        smtp_port = '1234'
        smtp_user = '******'
        smtp_pass = '******'
        # 略过superuser校验
        # 未开启mail开关
        mail_switch = 'false'
        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '请先开启邮件通知!')
        mail_switch = 'true'
        # 填写非正整数端口号
        smtp_port = '-3'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '端口号只能为正整数')
        smtp_port = '1234'
        # 未填写用户邮箱
        self.superuser1.email = ''
        self.superuser1.save()
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '请先完善当前用户邮箱信息!')
        self.superuser1.email = '*****@*****.**'
        self.superuser1.save()
        # 发送失败, 显示traceback
        send_email.return_value = 'some traceback'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertIn('some traceback', r_json['msg'])
        send_email.reset_mock()  # 重置``Mock``的调用计数
        mailsender.reset_mock()
        # 发送成功
        send_email.return_value = 'success'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        mailsender.assert_called_once_with(server=smtp_server,
                                           port=int(smtp_port),
                                           user=smtp_user,
                                           password=smtp_pass,
                                           ssl=False)
        send_email.called_once_with('Archery 邮件发送测试', 'Archery 邮件发送测试...',
                                    [self.superuser1.email])
        self.assertEqual(r_json['status'], 0)
        self.assertEqual(r_json['msg'], 'ok')

    @patch('MySQLdb.connect')
    def testInstanceCheck(self, connect):
        cur = MagicMock()
        cur.return_value.execute = MagicMock()
        cur.return_value.close = MagicMock()
        connect.return_value.cursor = cur
        connect.return_value.close = MagicMock()

        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/check/instance/', data={'instance_id': self.slave1.id})
        r_json = r.json()
        self.assertEqual(r_json['status'], 0)
        connect.assert_called_once_with(host=self.slave1.host,
                                        port=self.slave1.port,
                                        user=self.slave1.user,
                                        passwd=self.slave1.raw_password,
                                        charset=ANY)
        cur.assert_called_once()
        cur.return_value.execute.assert_called_once()
        cur.return_value.close.assert_called_once()
        connect.return_value.close.assert_called_once()

        # exception
        cur.return_value.execute.side_effect = NameError('some error')
        r = c.post('/check/instance/', data={'instance_id': self.slave1.id})
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertIn('无法连接实例', r_json['msg'])
        self.assertIn('some error', r_json['msg'])
Exemplo n.º 17
0
class QueryTest(TestCase):
    def setUp(self):
        self.slave1 = Instance(instance_name='test_slave_instance',
                               type='slave',
                               db_type='mysql',
                               host='testhost',
                               port=3306,
                               user='******',
                               password='******')
        self.slave1.save()
        User = get_user_model()
        self.u1 = User(username='******', display='中文显示', is_active=True)
        self.u1.save()
        self.u2 = User(username='******', display='中文显示', is_active=True)
        self.u2.save()
        sql_query_perm = Permission.objects.get(codename='query_submit')
        self.u2.user_permissions.add(sql_query_perm)
        tomorrow = datetime.now() + timedelta(days=1)
        self.query_apply_1 = QueryPrivilegesApply(
            group_id=1,
            group_name='some_group',
            title='some_title',
            user_name='some_user',
            instance_name='some_ins',
            db_list='some_db,some_db2',
            limit_num=100,
            valid_date=tomorrow,
            priv_type=1,
            status=0,
            audit_auth_groups='some_audit_group')
        self.query_apply_1.save()
        self.query_apply_2 = QueryPrivilegesApply(
            group_id=1,
            group_name='some_group',
            title='some_title',
            user_name='some_user',
            instance_name='some_ins',
            db_list='some_db',
            table_list='some_table,some_tb2',
            limit_num=100,
            valid_date=tomorrow,
            priv_type=2,
            status=0,
            audit_auth_groups='some_audit_group')
        self.query_apply_2.save()

    def tearDown(self):
        self.u1.delete()
        self.u2.delete()
        self.slave1.delete()
        self.query_apply_1.delete()
        QueryPrivileges.objects.all().delete()

    def testQueryAuditCallback(self):
        """测试权限申请工单回调"""
        # 工单状态改为审核失败, 验证工单状态
        query.query_audit_call_back(self.query_apply_1.apply_id, 2)
        self.query_apply_1.refresh_from_db()
        self.assertEqual(self.query_apply_1.status, 2)
        for db in self.query_apply_1.db_list.split(','):
            self.assertEqual(
                len(
                    QueryPrivileges.objects.filter(
                        user_name=self.query_apply_1.user_name,
                        db_name=db,
                        limit_num=100)), 0)
        # 工单改为审核成功, 验证工单状态和权限状态
        query.query_audit_call_back(self.query_apply_1.apply_id, 1)
        self.query_apply_1.refresh_from_db()
        self.assertEqual(self.query_apply_1.status, 1)
        for db in self.query_apply_1.db_list.split(','):
            self.assertEqual(
                len(
                    QueryPrivileges.objects.filter(
                        user_name=self.query_apply_1.user_name,
                        db_name=db,
                        limit_num=100)), 1)
        # 表权限申请测试, 只测试审核成功
        query.query_audit_call_back(self.query_apply_2.apply_id, 1)
        self.query_apply_2.refresh_from_db()
        self.assertEqual(self.query_apply_2.status, 1)
        for tb in self.query_apply_2.table_list.split(','):
            self.assertEqual(
                len(
                    QueryPrivileges.objects.filter(
                        user_name=self.query_apply_2.user_name,
                        db_name=self.query_apply_2.db_list,
                        table_name=tb,
                        limit_num=self.query_apply_2.limit_num)), 1)

    def testCorrectSQL(self):
        c = Client()
        some_sql = 'select some from some_table limit 100;'
        some_db = 'some_db'
        some_limit = 100
        c.force_login(self.u1)
        r = c.post('/query/',
                   data={
                       'instance_name': self.slave1.instance_name,
                       'sql_content': some_sql,
                       'db_name': some_db,
                       'limit_num': some_limit
                   })
        self.assertEqual(r.status_code, 403)
        c.force_login(self.u2)
        q_result = ResultSet(full_sql=some_sql, rows=['value'])
        q_result.column_list = ['some']

        mock_engine = MysqlEngine
        mock_engine.query = MagicMock(return_value=q_result)
        mock_engine.query_masking = MagicMock(return_value=q_result)

        mock_query = query
        mock_query.query_priv_check = MagicMock(return_value={
            'status': 0,
            'data': {
                'limit_num': 100,
                'priv_check': 1
            }
        })
        r = c.post('/query/',
                   data={
                       'instance_name': self.slave1.instance_name,
                       'sql_content': some_sql,
                       'db_name': some_db,
                       'limit_num': some_limit
                   })
        mock_engine.query.assert_called_once_with(db_name=some_db,
                                                  sql=some_sql,
                                                  limit_num=some_limit)
        r_json = r.json()
        self.assertEqual(r_json['data']['rows'], ['value'])
        self.assertEqual(r_json['data']['column_list'], ['some'])
Exemplo n.º 18
0
class CheckTest(TestCase):
    """检查功能测试"""
    def setUp(self):
        self.superuser1 = User(username='******',
                               display='中文显示',
                               is_active=True,
                               is_superuser=True,
                               email='*****@*****.**')
        self.superuser1.save()
        self.slave1 = Instance(instance_name='some_name',
                               host='some_host',
                               type='slave',
                               db_type='mysql',
                               user='******',
                               port=1234,
                               password='******')
        self.slave1.save()

    def tearDown(self):
        self.superuser1.delete()

    @patch.object(MsgSender, '__init__', return_value=None)
    @patch.object(MsgSender, 'send_email')
    def testEmailCheck(self, send_email, mailsender):
        """邮箱配置检查"""
        mail_switch = 'true'
        smtp_ssl = 'false'
        smtp_server = 'some_server'
        smtp_port = '1234'
        smtp_user = '******'
        smtp_pass = '******'
        # 略过superuser校验
        # 未开启mail开关
        mail_switch = 'false'
        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '请先开启邮件通知!')
        mail_switch = 'true'
        # 填写非正整数端口号
        smtp_port = '-3'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '端口号只能为正整数')
        smtp_port = '1234'
        # 未填写用户邮箱
        self.superuser1.email = ''
        self.superuser1.save()
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertEqual(r_json['msg'], '请先完善当前用户邮箱信息!')
        self.superuser1.email = '*****@*****.**'
        self.superuser1.save()
        # 发送失败, 显示traceback
        send_email.return_value = 'some traceback'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        self.assertEqual(r_json['status'], 1)
        self.assertIn('some traceback', r_json['msg'])
        send_email.reset_mock()  # 重置``Mock``的调用计数
        mailsender.reset_mock()
        # 发送成功
        send_email.return_value = 'success'
        r = c.post('/check/email/',
                   data={
                       'mail': mail_switch,
                       'mail_ssl': smtp_ssl,
                       'mail_smtp_server': smtp_server,
                       'mail_smtp_port': smtp_port,
                       'mail_smtp_user': smtp_user,
                       'mail_smtp_password': smtp_pass
                   })
        r_json = r.json()
        mailsender.assert_called_once_with(server=smtp_server,
                                           port=int(smtp_port),
                                           user=smtp_user,
                                           password=smtp_pass,
                                           ssl=False)
        send_email.called_once_with('Archery 邮件发送测试', 'Archery 邮件发送测试...',
                                    [self.superuser1.email])
        self.assertEqual(r_json['status'], 0)
        self.assertEqual(r_json['msg'], 'ok')

    @patch('MySQLdb.connect')
    @patch('common.check.get_engine', return_value=EngineBase)
    def testInstanceCheck(self, _get_engine, _conn):
        _get_engine.return_value.get_connection = _conn
        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/check/instance/', data={'instance_id': self.slave1.id})
        r_json = r.json()
        self.assertEqual(r_json['status'], 0)

    @patch('MySQLdb.connect')
    def test_inception_check(self, _conn):
        c = Client()
        c.force_login(self.superuser1)
        data = {
            "inception_host": "inception",
            "inception_port": "6669",
            "inception_remote_backup_host": "mysql",
            "inception_remote_backup_port": 3306,
            "inception_remote_backup_user": "******",
            "inception_remote_backup_password": "******"
        }
        r = c.post('/check/inception/', data=data)
        r_json = r.json()
        self.assertEqual(r_json['status'], 0)

    @patch('MySQLdb.connect')
    def test_go_inception_check(self, _conn):
        c = Client()
        c.force_login(self.superuser1)
        data = {
            "go_inception_host": "inception",
            "go_inception_port": "6669",
            "inception_remote_backup_host": "mysql",
            "inception_remote_backup_port": 3306,
            "inception_remote_backup_user": "******",
            "inception_remote_backup_password": "******"
        }
        r = c.post('/check/go_inception/', data=data)
        r_json = r.json()
        self.assertEqual(r_json['status'], 0)
Exemplo n.º 19
0
class TestBinLog(TestCase):
    """
    测试Binlog相关
    """
    def setUp(self):
        self.superuser = User(username='******', is_superuser=True)
        self.superuser.save()
        # 使用 travis.ci 时实例和测试service保持一致
        self.master = Instance(
            instance_name='test_instance',
            type='master',
            db_type='mysql',
            host=settings.DATABASES['default']['HOST'],
            port=settings.DATABASES['default']['PORT'],
            user=settings.DATABASES['default']['USER'],
            password=settings.DATABASES['default']['PASSWORD'])
        self.master.save()
        self.sys_config = SysConfig()
        self.client = Client()
        self.client.force_login(self.superuser)

    def tearDown(self):
        self.superuser.delete()
        self.master.delete()
        self.sys_config.replace(json.dumps({}))

    def test_binlog_list_instance_not_exist(self):
        """
        测试获取binlog列表,实例不存在
        :return:
        """
        data = {"instance_name": 'some_instance'}
        r = self.client.post(path='/binlog/list/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 1,
            'msg': '实例不存在',
            'data': []
        })

    def test_binlog_list_instance(self):
        """
        测试获取binlog列表,实例存在
        :return:
        """
        data = {"instance_name": 'test_instance'}
        r = self.client.post(path='/binlog/list/', data=data)
        self.assertEqual(json.loads(r.content).get('status'), 0)

    def test_binlog2sql_path_not_exist(self):
        """
        测试获取解析binlog,path未设置
        :return:
        """
        data = {
            "instance_name": "test_instance",
            "save_sql": "false",
            "no_pk": "false",
            "flashback": "false",
            "back_interval": "",
            "num": "",
            "start_file": "mysql-bin.000045",
            "start_pos": "",
            "end_file": "",
            "end_pos": "",
            "stop_time": "",
            "start_time": "",
            "only_schemas": "",
            "only_dml": "true",
            "sql_type": ""
        }
        r = self.client.post(path='/binlog/binlog2sql/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 1,
            'msg': '可执行文件路径不能为空!',
            'data': {}
        })

    @patch('sql.plugins.plugin.subprocess')
    def test_binlog2sql(self, _subprocess):
        """
        测试获取解析binlog,path设置
        :param _subprocess:
        :return:
        """
        self.sys_config.set('binlog2sql', '/opt/binlog2sql')
        self.sys_config.get_all_config()
        data = {
            "instance_name": "test_instance",
            "save_sql": "1",
            "no_pk": "false",
            "flashback": "false",
            "back_interval": "",
            "num": "1",
            "start_file": "mysql-bin.000045",
            "start_pos": "",
            "end_file": "",
            "end_pos": "",
            "stop_time": "",
            "start_time": "",
            "only_schemas": "",
            "only_dml": "true",
            "sql_type": ""
        }
        r = self.client.post(path='/binlog/binlog2sql/', data=data)
        self.assertEqual(json.loads(r.content), {
            "status": 0,
            "msg": "ok",
            "data": [{
                "sql": {},
                "binlog_info": {}
            }]
        })

    @patch('builtins.iter')
    @patch('sql.plugins.plugin.subprocess')
    def test_binlog2sql_file(self, _subprocess, _iter):
        """
        测试保存文件
        :param _subprocess:
        :return:
        """
        args = {
            "instance_name": "test_instance",
            "save_sql": "1",
            "no_pk": "false",
            "flashback": "false",
            "back_interval": "",
            "num": "1",
            "start_file": "",
            "start_pos": "",
            "end_file": "",
            "end_pos": "",
            "stop_time": "",
            "start_time": "",
            "only_schemas": "",
            "only_dml": "true",
            "sql_type": "",
            "instance": self.master
        }
        _subprocess.Popen.return_value.stdout.return_value.readline.return_value = 'sql'
        _iter.return_value = ''
        r = binlog2sql_file(args=args, user=self.superuser)
        self.assertEqual(self.superuser, r[0])

    def test_del_binlog_instance_not_exist(self):
        """
        测试删除binlog,实例不存在
        :return:
        """
        data = {
            "instance_id": 0,
            "binlog": "mysql-bin.000001",
        }
        r = self.client.post(path='/binlog/del_log/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 1,
            'msg': '实例不存在',
            'data': []
        })

    def test_del_binlog_binlog_not_exist(self):
        """
        测试删除binlog,实例存在,binlog 不存在
        :return:
        """
        data = {"instance_id": self.master.id, "binlog": ''}
        r = self.client.post(path='/binlog/del_log/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 1,
            'msg': 'Error:未选择binlog!',
            'data': ''
        })

    @patch('sql.engines.mysql.MysqlEngine.query')
    @patch('sql.engines.get_engine')
    def test_del_binlog(self, _get_engine, _query):
        """
        测试删除binlog
        :return:
        """
        data = {"instance_id": self.master.id, "binlog": "mysql-bin.000001"}
        _query.return_value = ResultSet(full_sql='select 1')
        r = self.client.post(path='/binlog/del_log/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 0,
            'msg': '清理成功',
            'data': ''
        })

    @patch('sql.engines.mysql.MysqlEngine.query')
    @patch('sql.engines.get_engine')
    def test_del_binlog_wrong(self, _get_engine, _query):
        """
        测试删除binlog
        :return:
        """
        data = {"instance_id": self.master.id, "binlog": "mysql-bin.000001"}
        _query.return_value = ResultSet(full_sql='select 1')
        _query.return_value.error = '清理失败'
        r = self.client.post(path='/binlog/del_log/', data=data)
        self.assertEqual(json.loads(r.content), {
            'status': 2,
            'msg': '清理失败,Error:清理失败',
            'data': ''
        })
Exemplo n.º 20
0
class TestOptimize(TestCase):
    """
    测试SQL优化
    """

    def setUp(self):
        self.superuser = User(username='******', is_superuser=True)
        self.superuser.save()
        # 使用 travis.ci 时实例和测试service保持一致
        self.master = Instance(instance_name='test_instance', type='master', db_type='mysql',
                               host=settings.DATABASES['default']['HOST'],
                               port=settings.DATABASES['default']['PORT'],
                               user=settings.DATABASES['default']['USER'],
                               password=settings.DATABASES['default']['PASSWORD'])
        self.master.save()
        self.sys_config = SysConfig()
        self.client = Client()
        self.client.force_login(self.superuser)

    def tearDown(self):
        self.superuser.delete()
        self.master.delete()
        self.sys_config.replace(json.dumps({}))

    def test_sqladvisor(self):
        """
        测试SQLAdvisor报告
        :return:
        """
        r = self.client.post(path='/slowquery/optimize_sqladvisor/')
        self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '页面提交参数可能为空', 'data': []})
        r = self.client.post(path='/slowquery/optimize_sqladvisor/',
                             data={"sql_content": "select 1;", "instance_name": "test_instance"})
        self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '请配置SQLAdvisor路径!', 'data': []})
        self.sys_config.set('sqladvisor', '/opt/archery/src/plugins/sqladvisor')
        self.sys_config.get_all_config()
        r = self.client.post(path='/slowquery/optimize_sqladvisor/',
                             data={"sql_content": "select 1;", "instance_name": "test_instance"})
        self.assertEqual(json.loads(r.content)['status'], 0)

    def test_soar(self):
        """
        测试SOAR报告
        :return:
        """
        r = self.client.post(path='/slowquery/optimize_soar/')
        self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '页面提交参数可能为空', 'data': []})
        r = self.client.post(path='/slowquery/optimize_soar/',
                             data={"sql": "select 1;", "instance_name": "test_instance", "db_name": "mysql"})
        self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '请配置soar_path和test_dsn!', 'data': []})
        self.sys_config.set('soar', '/opt/archery/src/plugins/soar')
        self.sys_config.set('soar_test_dsn', 'root:@127.0.0.1:3306/information_schema')
        self.sys_config.get_all_config()
        r = self.client.post(path='/slowquery/optimize_soar/',
                             data={"sql": "select 1;", "instance_name": "test_instance", "db_name": "mysql"})
        self.assertEqual(json.loads(r.content)['status'], 0)

    def test_tuning(self):
        """
        测试SQLTuning报告
        :return:
        """
        data = {"sql_content": "select * from test_archery.sql_users;",
                "instance_name": "test_instance",
                "db_name": settings.DATABASES['default']['TEST']['NAME']
                }
        r = self.client.post(path='/slowquery/optimize_sqltuning/')
        self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '实例不存在', 'data': []})

        # 获取sys_parm
        data['option[]'] = 'sys_parm'
        r = self.client.post(path='/slowquery/optimize_sqltuning/', data=data)
        self.assertListEqual(list(json.loads(r.content)['data'].keys()),
                             ['basic_information', 'sys_parameter', 'optimizer_switch', 'sqltext'])

        # 获取sql_plan
        data['option[]'] = 'sql_plan'
        r = self.client.post(path='/slowquery/optimize_sqltuning/', data=data)
        self.assertListEqual(list(json.loads(r.content)['data'].keys()),
                             ['optimizer_rewrite_sql', 'plan', 'sqltext'])

        # 获取obj_stat
        data['option[]'] = 'obj_stat'
        r = self.client.post(path='/slowquery/optimize_sqltuning/', data=data)
        self.assertListEqual(list(json.loads(r.content)['data'].keys()),
                             ['object_statistics', 'sqltext'])

        # 获取sql_profile
        data['option[]'] = 'sql_profile'
        r = self.client.post(path='/slowquery/optimize_sqltuning/', data=data)
        self.assertListEqual(list(json.loads(r.content)['data'].keys()), ['session_status', 'sqltext'])
Exemplo n.º 21
0
 def setUp(self):
     self.slave1 = Instance(instance_name='test_slave_instance', type='slave', db_type='mysql',
                            host='testhost', port=3306, user='******', password='******')
     self.slave2 = Instance(instance_name='test_instance_non_mysql', type='slave', db_type='mssql',
                            host='some_host2', port=3306, user='******', password='******')
     self.slave1.save()
     self.slave2.save()
     archer_user = get_user_model()
     self.superuser1 = archer_user(username='******', is_superuser=True)
     self.superuser1.save()
     self.u1 = archer_user(username='******', display='中文显示', is_active=True)
     self.u1.save()
     self.u2 = archer_user(username='******', display='中文显示', is_active=True)
     self.u2.save()
     self.u3 = archer_user(username='******', display='中文显示', is_active=True)
     self.u3.save()
     sql_query_perm = Permission.objects.get(codename='query_submit')
     self.u2.user_permissions.add(sql_query_perm)
     self.u3.user_permissions.add(sql_query_perm)
     tomorrow = datetime.now() + timedelta(days=1)
     self.query_apply_1 = QueryPrivilegesApply(
         group_id=1,
         group_name='some_group',
         title='some_title',
         user_name='some_user',
         instance_name='some_ins',
         db_list='some_db,some_db2',
         limit_num=100,
         valid_date=tomorrow,
         priv_type=1,
         status=0,
         audit_auth_groups='some_audit_group'
     )
     self.query_apply_1.save()
     self.query_apply_2 = QueryPrivilegesApply(
         group_id=1,
         group_name='some_group',
         title='some_title',
         user_name='some_user',
         instance_name='some_ins',
         db_list='some_db',
         table_list='some_table,some_tb2',
         limit_num=100,
         valid_date=tomorrow,
         priv_type=2,
         status=0,
         audit_auth_groups='some_audit_group'
     )
     self.query_apply_2.save()
     self.db_priv_for_user3 = QueryPrivileges(
         user_name=self.u3.username,
         user_display=self.u3.display,
         instance_name=self.slave1.instance_name,
         db_name='some_db',
         table_name='',
         valid_date=tomorrow,
         limit_num=70,
         priv_type=1)
     self.db_priv_for_user3.save()
     self.table_priv_for_user3 = QueryPrivileges(
         user_name=self.u3.username,
         user_display=self.u3.display,
         instance_name=self.slave1.instance_name,
         db_name='another_db',
         table_name='some_table',
         valid_date=tomorrow,
         limit_num=60,
         priv_type=2)
     self.table_priv_for_user3.save()
     self.db_priv_for_user3_another_instance = QueryPrivileges(
         user_name=self.u3.username,
         user_display=self.u3.display,
         instance_name=self.slave2.instance_name,
         db_name='some_db_another_instance',
         table_name='',
         valid_date=tomorrow,
         limit_num=50,
         priv_type=1)
     self.db_priv_for_user3_another_instance.save()
Exemplo n.º 22
0
class QueryTest(TestCase):
    def setUp(self):
        self.slave1 = Instance(instance_name='test_slave_instance', type='slave', db_type='mysql',
                               host='testhost', port=3306, user='******', password='******')
        self.slave2 = Instance(instance_name='test_instance_non_mysql', type='slave', db_type='mssql',
                               host='some_host2', port=3306, user='******', password='******')
        self.slave1.save()
        self.slave2.save()
        archer_user = get_user_model()
        self.superuser1 = archer_user(username='******', is_superuser=True)
        self.superuser1.save()
        self.u1 = archer_user(username='******', display='中文显示', is_active=True)
        self.u1.save()
        self.u2 = archer_user(username='******', display='中文显示', is_active=True)
        self.u2.save()
        self.u3 = archer_user(username='******', display='中文显示', is_active=True)
        self.u3.save()
        sql_query_perm = Permission.objects.get(codename='query_submit')
        self.u2.user_permissions.add(sql_query_perm)
        self.u3.user_permissions.add(sql_query_perm)
        tomorrow = datetime.now() + timedelta(days=1)
        self.query_apply_1 = QueryPrivilegesApply(
            group_id=1,
            group_name='some_group',
            title='some_title',
            user_name='some_user',
            instance_name='some_ins',
            db_list='some_db,some_db2',
            limit_num=100,
            valid_date=tomorrow,
            priv_type=1,
            status=0,
            audit_auth_groups='some_audit_group'
        )
        self.query_apply_1.save()
        self.query_apply_2 = QueryPrivilegesApply(
            group_id=1,
            group_name='some_group',
            title='some_title',
            user_name='some_user',
            instance_name='some_ins',
            db_list='some_db',
            table_list='some_table,some_tb2',
            limit_num=100,
            valid_date=tomorrow,
            priv_type=2,
            status=0,
            audit_auth_groups='some_audit_group'
        )
        self.query_apply_2.save()
        self.db_priv_for_user3 = QueryPrivileges(
            user_name=self.u3.username,
            user_display=self.u3.display,
            instance_name=self.slave1.instance_name,
            db_name='some_db',
            table_name='',
            valid_date=tomorrow,
            limit_num=70,
            priv_type=1)
        self.db_priv_for_user3.save()
        self.table_priv_for_user3 = QueryPrivileges(
            user_name=self.u3.username,
            user_display=self.u3.display,
            instance_name=self.slave1.instance_name,
            db_name='another_db',
            table_name='some_table',
            valid_date=tomorrow,
            limit_num=60,
            priv_type=2)
        self.table_priv_for_user3.save()
        self.db_priv_for_user3_another_instance = QueryPrivileges(
            user_name=self.u3.username,
            user_display=self.u3.display,
            instance_name=self.slave2.instance_name,
            db_name='some_db_another_instance',
            table_name='',
            valid_date=tomorrow,
            limit_num=50,
            priv_type=1)
        self.db_priv_for_user3_another_instance.save()

    def tearDown(self):
        self.u1.delete()
        self.u2.delete()
        self.u3.delete()
        self.slave1.delete()
        self.slave2.delete()
        self.query_apply_1.delete()
        self.query_apply_2.delete()
        QueryPrivileges.objects.all().delete()
        archer_config = SysConfig()
        archer_config.set('disable_star', False)

    def testQueryAuditCallback(self):
        """测试权限申请工单回调"""
        # 工单状态改为审核失败, 验证工单状态
        query.query_audit_call_back(self.query_apply_1.apply_id, 2)
        self.query_apply_1.refresh_from_db()
        self.assertEqual(self.query_apply_1.status, 2)
        for db in self.query_apply_1.db_list.split(','):
            self.assertEqual(len(QueryPrivileges.objects.filter(
                user_name=self.query_apply_1.user_name,
                db_name=db,
                limit_num=100)), 0)
        # 工单改为审核成功, 验证工单状态和权限状态
        query.query_audit_call_back(self.query_apply_1.apply_id, 1)
        self.query_apply_1.refresh_from_db()
        self.assertEqual(self.query_apply_1.status, 1)
        for db in self.query_apply_1.db_list.split(','):
            self.assertEqual(len(QueryPrivileges.objects.filter(
                user_name=self.query_apply_1.user_name,
                db_name=db,
                limit_num=100)), 1)
        # 表权限申请测试, 只测试审核成功
        query.query_audit_call_back(self.query_apply_2.apply_id, 1)
        self.query_apply_2.refresh_from_db()
        self.assertEqual(self.query_apply_2.status, 1)
        for tb in self.query_apply_2.table_list.split(','):
            self.assertEqual(len(QueryPrivileges.objects.filter(
                user_name=self.query_apply_2.user_name,
                db_name=self.query_apply_2.db_list,
                table_name=tb,
                limit_num=self.query_apply_2.limit_num)), 1)

    @patch('sql.engines.mysql.MysqlEngine.query')
    @patch('sql.engines.mysql.MysqlEngine.query_masking')
    @patch('sql.query.query_priv_check')
    def testCorrectSQL(self, _priv_check, _query_masking, _query):
        c = Client()
        some_sql = 'select some from some_table limit 100;'
        some_db = 'some_db'
        some_limit = 100
        c.force_login(self.u1)
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': some_sql,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        self.assertEqual(r.status_code, 403)
        c.force_login(self.u2)
        q_result = ResultSet(full_sql=some_sql, rows=['value'])
        q_result.column_list = ['some']

        _query.return_value = q_result
        _query_masking.return_value = q_result
        _priv_check.return_value = {'status': 0, 'data': {'limit_num': 100, 'priv_check': 1}}
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': some_sql,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        _query.assert_called_once_with(db_name=some_db, sql=some_sql, limit_num=some_limit)
        r_json = r.json()
        self.assertEqual(r_json['data']['rows'], ['value'])
        self.assertEqual(r_json['data']['column_list'], ['some'])

    @patch('sql.engines.mysql.MysqlEngine.query')
    @patch('sql.engines.mysql.MysqlEngine.query_masking')
    @patch('sql.query.query_priv_check')
    def testSQLWithoutLimit(self, _priv_check, _query_masking, _query):
        c = Client()
        some_limit = 100
        sql_without_limit = 'select some from some_table'
        sql_with_limit = 'select some from some_table limit {0};'.format(some_limit)
        some_db = 'some_db'
        c.force_login(self.u2)
        q_result = ResultSet(full_sql=sql_without_limit, rows=['value'])
        q_result.column_list = ['some']
        _query.return_value = q_result
        _query_masking.return_value = q_result
        _priv_check.return_value = {'status': 0, 'data': {'limit_num': 100, 'priv_check': 1}}
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': sql_without_limit,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        _query.assert_called_once_with(db_name=some_db, sql=sql_with_limit, limit_num=some_limit)
        r_json = r.json()
        self.assertEqual(r_json['data']['rows'], ['value'])
        self.assertEqual(r_json['data']['column_list'], ['some'])

        # 带 * 且不带 limit 的sql
        sql_with_star = 'select * from some_table'
        filtered_sql_with_star = 'select * from some_table limit {0};'.format(some_limit)
        _query.reset_mock()
        c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                'sql_content': sql_with_star,
                                'db_name': some_db,
                                'limit_num': some_limit})
        _query.assert_called_once_with(db_name=some_db, sql=filtered_sql_with_star, limit_num=some_limit)

    @patch('sql.query.query_priv_check')
    def testStarOptionOn(self, _priv_check):
        c = Client()
        c.force_login(self.u2)
        some_limit = 100
        sql_with_star = 'select * from some_table'
        some_db = 'some_db'
        _priv_check.return_value = {'status': 0, 'data': {'limit_num': 100, 'priv_check': 1}}
        archer_config = SysConfig()
        archer_config.set('disable_star', True)
        r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
                                    'sql_content': sql_with_star,
                                    'db_name': some_db,
                                    'limit_num': some_limit})
        archer_config.set('disable_star', False)
        r_json = r.json()
        self.assertEqual(1, r_json['status'])

    @patch('sql.query.Masking')
    def test_query_priv_check(self, mock_masking):
        # 超级用户直接返回
        superuser_limit = query.query_priv_check(self.superuser1, self.slave1.instance_name,
                                                 'some_db', 'some_sql', 100)
        self.assertEqual(superuser_limit['status'], 0)
        self.assertEqual(superuser_limit['data']['limit_num'], 100)

        # 无语法树解析,只校验db_name
        limit_without_tree_analyse = query.query_priv_check(self.u3, self.slave2,
                                                            'some_db_another_instance','some_sql', 1000)
        self.assertEqual(limit_without_tree_analyse['data']['limit_num'], self.db_priv_for_user3_another_instance.limit_num)

        # 无语法树解析, 无权限的情况
        limit_without_tree_analyse = query.query_priv_check(self.u3, self.slave2,
                                                            'some_db_does_not_exist','some_sql', 1000)
        self.assertEqual(limit_without_tree_analyse['status'],1)
        self.assertIn('some_db_does_not_exist', limit_without_tree_analyse['msg'])

        # 有语法树解析, 有库权限
        mock_masking.return_value.query_table_ref.return_value = {
             'status': 0,
             'data': [{
                 'db': 'another_db',
                 'table': 'some_table'
             }]}
        limit_with_tree_analyse = query.query_priv_check(self.u3, self.slave1,
                                                         'some_db','some_sql',1000)
        mock_masking.return_value.query_table_ref.assert_called_once()
        self.assertEqual(limit_with_tree_analyse['data']['limit_num'], self.table_priv_for_user3.limit_num)
Exemplo n.º 23
0
class WorkflowViewTest(TransactionTestCase):
    def setUp(self):
        self.now = datetime.now()
        can_view_permission = Permission.objects.get(
            codename='menu_sqlworkflow')
        self.u1 = User(username='******', display='用户1')
        self.u1.save()
        self.u1.user_permissions.add(can_view_permission)
        self.u2 = User(username='******', display='用户2')
        self.u2.save()
        self.u2.user_permissions.add(can_view_permission)
        self.u3 = User(username='******', display='用户3')
        self.u3.save()
        self.u3.user_permissions.add(can_view_permission)
        self.superuser1 = User(username='******', is_superuser=True)
        self.superuser1.save()
        self.master1 = Instance(instance_name='test_master_instance',
                                type='master',
                                db_type='mysql',
                                host='testhost',
                                port=3306,
                                user='******',
                                password='******')
        self.master1.save()
        self.wf1 = SqlWorkflow.objects.create(
            workflow_name='some_name',
            group_id=1,
            group_name='g1',
            engineer=self.u1.username,
            engineer_display=self.u1.display,
            audit_auth_groups='some_group',
            create_time=self.now - timedelta(days=1),
            status='workflow_finish',
            is_backup='是',
            instance=self.master1,
            db_name='some_db',
            syntax_type=1,
        )
        self.wfc1 = SqlWorkflowContent.objects.create(
            workflow=self.wf1,
            sql_content='some_sql',
            execute_result=json.dumps([{
                'id': 1,
                'sql': 'some_content'
            }]))
        self.wf2 = SqlWorkflow.objects.create(workflow_name='some_name2',
                                              group_id=1,
                                              group_name='g1',
                                              engineer=self.u2.username,
                                              engineer_display=self.u2.display,
                                              audit_auth_groups='some_group',
                                              create_time=self.now -
                                              timedelta(days=1),
                                              status='workflow_manreviewing',
                                              is_backup='是',
                                              instance=self.master1,
                                              db_name='some_db',
                                              syntax_type=1)
        self.wfc2 = SqlWorkflowContent.objects.create(
            workflow=self.wf2,
            sql_content='some_sql',
            execute_result=json.dumps([{
                'id': 1,
                'sql': 'some_content'
            }]))
        self.resource_group1 = ResourceGroup(group_name='some_group')
        self.resource_group1.save()

    def tearDown(self):
        SqlWorkflowContent.objects.all().delete()
        SqlWorkflow.objects.all().delete()
        self.master1.delete()
        self.u1.delete()
        self.superuser1.delete()
        self.resource_group1.delete()

    def testWorkflowStatus(self):
        c = Client(header={})
        c.force_login(self.u1)
        r = c.post('/getWorkflowStatus/', {'workflow_id': self.wf1.id})
        r_json = r.json()
        self.assertEqual(r_json['status'], 'workflow_finish')

    @patch('sql.utils.workflow_audit.Audit.logs')
    @patch('sql.utils.workflow_audit.Audit.detail_by_workflow_id')
    @patch('sql.utils.workflow_audit.Audit.review_info')
    @patch('sql.utils.workflow_audit.Audit.can_review')
    def testWorkflowDetailView(self, _can_review, _review_info, _detail_by_id,
                               _logs):
        _review_info.return_value = ('some_auth_group', 'current_auth_group')
        _can_review.return_value = False
        _detail_by_id.return_value.audit_id = 123
        _logs.return_value.latest('id').operation_info = ''
        c = Client()
        c.force_login(self.u1)
        r = c.get('/detail/{}/'.format(self.wf1.id))
        expected_status_display = r"""id="workflow_detail_disaply">已正常结束"""
        self.assertContains(r, expected_status_display)
        exepcted_status = r"""id="workflow_detail_status">workflow_finish"""
        self.assertContains(r, exepcted_status)

    def testWorkflowListView(self):
        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/sqlworkflow_list/', {
            'limit': 10,
            'offset': 0,
            'navStatus': 'all'
        })
        r_json = r.json()
        self.assertEqual(r_json['total'], 2)
        # 列表按创建时间倒序排列, 第二个是wf1 , 是已正常结束
        self.assertEqual(r_json['rows'][1]['status'], 'workflow_finish')

        # u1拿到u1的
        c.force_login(self.u1)
        r = c.post('/sqlworkflow_list/', {
            'limit': 10,
            'offset': 0,
            'navStatus': 'all'
        })
        r_json = r.json()
        self.assertEqual(r_json['total'], 1)
        self.assertEqual(r_json['rows'][0]['id'], self.wf1.id)

        # u3拿到None
        c.force_login(self.u3)
        r = c.post('/sqlworkflow_list/', {
            'limit': 10,
            'offset': 0,
            'navStatus': 'all'
        })
        r_json = r.json()
        self.assertEqual(r_json['total'], 0)

    @patch('sql.utils.workflow_audit.Audit.detail_by_workflow_id')
    @patch('sql.utils.workflow_audit.Audit.audit')
    @patch('sql.utils.workflow_audit.Audit.can_review')
    def testWorkflowPassedView(self, _can_review, _audit, _detail_by_id):
        c = Client()
        c.force_login(self.superuser1)
        r = c.post('/passed/')
        self.assertContains(r, 'workflow_id参数为空.')
        _can_review.return_value = False
        r = c.post('/passed/', {'workflow_id': self.wf1.id})
        self.assertContains(r, '你无权操作当前工单!')
        _can_review.return_value = True
        _detail_by_id.return_value.audit_id = 123
        _audit.return_value = {
            "data": {
                "workflow_status": 1  # TODO 改为audit_success
            }
        }
        r = c.post('/passed/',
                   data={
                       'workflow_id': self.wf1.id,
                       'audit_remark': 'some_audit'
                   },
                   follow=False)
        self.assertRedirects(r,
                             '/detail/{}/'.format(self.wf1.id),
                             fetch_redirect_response=False)
        self.wf1.refresh_from_db()
        self.assertEqual(self.wf1.status, 'workflow_review_pass')

    @patch('sql.sql_workflow.Audit.add_log')
    @patch('sql.sql_workflow.Audit.detail_by_workflow_id')
    @patch('sql.sql_workflow.Audit.audit')
    # patch view里的can_cancel 而不是原始位置的can_cancel ,因为在调用时, 已经 import 了真的 can_cancel ,会导致mock失效
    # 在import 静态函数时需要注意这一点, 动态对象因为每次都会重新生成,也可以 mock 原函数/方法/对象
    # 参见 : https://docs.python.org/3/library/unittest.mock.html#where-to-patch
    @patch('sql.sql_workflow.can_cancel')
    def testWorkflowCancelView(self, _can_cancel, _audit, _detail_by_id,
                               _add_log):
        c = Client()
        c.force_login(self.u2)
        r = c.post('/cancel/')
        self.assertContains(r, 'workflow_id参数为空.')
        r = c.post('/cancel/', data={'workflow_id': self.wf2.id})
        self.assertContains(r, '终止原因不能为空')
        _can_cancel.return_value = False
        r = c.post('/cancel/',
                   data={
                       'workflow_id': self.wf2.id,
                       'cancel_remark': 'some_reason'
                   })
        self.assertContains(r, '你无权操作当前工单!')
        _can_cancel.return_value = True
        _detail_by_id = 123
        r = c.post('/cancel/',
                   data={
                       'workflow_id': self.wf2.id,
                       'cancel_remark': 'some_reason'
                   })
        self.wf2.refresh_from_db()
        self.assertEqual('workflow_abort', self.wf2.status)

    @patch('sql.sql_workflow.async_task')
    @patch('sql.sql_workflow.Audit')
    @patch('sql.sql_workflow.get_engine')
    @patch('sql.sql_workflow.user_instances')
    def test_workflow_auto_review_view(self, mock_user_instances,
                                       mock_get_engine, mock_audit,
                                       mock_async_task):
        c = Client()
        c.force_login(self.superuser1)
        request_data = {
            'sql_content': "update some_db set some_key=\'some value\';",
            'workflow_name': 'some_title',
            'group_name': self.resource_group1.group_name,
            'group_id': self.resource_group1.group_id,
            'instance_name': self.master1.instance_name,
            'db_name': 'some_db',
            'is_backup': '是',
            'notify_users': ''
        }
        mock_user_instances.return_value.get.return_value = None
        mock_get_engine.return_value.execute_check.return_value.rows = []
        mock_get_engine.return_value.execute_check.return_value.json.return_value = json.dumps(
            [{
                "id": 1,
                "stage": "CHECKED",
                "errlevel": 0,
                "stagestatus": "Audit completed",
                "errormessage": "None",
                "sql": "use thirdservice_db",
                "affected_rows": 0,
                "sequence": "'0_0_0'",
                "backup_dbname": "None",
                "execute_time": "0",
                "sqlsha1": "",
                "actual_affected_rows": None
            }])
        mock_audit.settings.return_value = 'some_group,another_group'
        mock_audit.add.return_value = None
        mock_async_task.return_value = None
        r = c.post('/autoreview/', data=request_data, follow=False)
        self.assertIn('detail', r.url)
        workflow_id = int(re.search(r'\/detail\/(\d+)\/', r.url).groups()[0])
        self.assertEqual(request_data['workflow_name'],
                         SqlWorkflow.objects.get(id=workflow_id).workflow_name)
Exemplo n.º 24
0
 def setUp(self):
     self.now = datetime.now()
     can_view_permission = Permission.objects.get(
         codename='menu_sqlworkflow')
     self.u1 = User(username='******', display='用户1')
     self.u1.save()
     self.u1.user_permissions.add(can_view_permission)
     self.u2 = User(username='******', display='用户2')
     self.u2.save()
     self.u2.user_permissions.add(can_view_permission)
     self.u3 = User(username='******', display='用户3')
     self.u3.save()
     self.u3.user_permissions.add(can_view_permission)
     self.superuser1 = User(username='******', is_superuser=True)
     self.superuser1.save()
     self.master1 = Instance(instance_name='test_master_instance',
                             type='master',
                             db_type='mysql',
                             host='testhost',
                             port=3306,
                             user='******',
                             password='******')
     self.master1.save()
     self.wf1 = SqlWorkflow.objects.create(
         workflow_name='some_name',
         group_id=1,
         group_name='g1',
         engineer=self.u1.username,
         engineer_display=self.u1.display,
         audit_auth_groups='some_group',
         create_time=self.now - timedelta(days=1),
         status='workflow_finish',
         is_backup='是',
         instance=self.master1,
         db_name='some_db',
         syntax_type=1,
     )
     self.wfc1 = SqlWorkflowContent.objects.create(
         workflow=self.wf1,
         sql_content='some_sql',
         execute_result=json.dumps([{
             'id': 1,
             'sql': 'some_content'
         }]))
     self.wf2 = SqlWorkflow.objects.create(workflow_name='some_name2',
                                           group_id=1,
                                           group_name='g1',
                                           engineer=self.u2.username,
                                           engineer_display=self.u2.display,
                                           audit_auth_groups='some_group',
                                           create_time=self.now -
                                           timedelta(days=1),
                                           status='workflow_manreviewing',
                                           is_backup='是',
                                           instance=self.master1,
                                           db_name='some_db',
                                           syntax_type=1)
     self.wfc2 = SqlWorkflowContent.objects.create(
         workflow=self.wf2,
         sql_content='some_sql',
         execute_result=json.dumps([{
             'id': 1,
             'sql': 'some_content'
         }]))
     self.resource_group1 = ResourceGroup(group_name='some_group')
     self.resource_group1.save()
Exemplo n.º 25
0
class TestMysql(TestCase):
    def setUp(self):
        self.ins1 = Instance(instance_name='some_ins',
                             type='slave',
                             db_type='mysql',
                             host='some_host',
                             port=1366,
                             user='******',
                             password='******')
        self.ins1.save()
        self.sys_config = SysConfig()
        self.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.ins1,
                                             db_name='some_db',
                                             syntax_type=1)
        SqlWorkflowContent.objects.create(workflow=self.wf)

    def tearDown(self):
        self.ins1.delete()
        self.sys_config.replace(json.dumps({}))
        SqlWorkflow.objects.all().delete()
        SqlWorkflowContent.objects.all().delete()

    @patch('MySQLdb.connect')
    def testGetConnection(self, connect):
        new_engine = MysqlEngine(instance=self.ins1)
        new_engine.get_connection()
        connect.assert_called_once()

    @patch('MySQLdb.connect')
    def testQuery(self, connect):
        cur = Mock()
        connect.return_value.cursor = cur
        cur.return_value.execute = Mock()
        cur.return_value.fetchmany.return_value = (('v1', 'v2'), )
        cur.return_value.description = (('k1', 'some_other_des'),
                                        ('k2', 'some_other_des'))
        new_engine = MysqlEngine(instance=self.ins1)
        query_result = new_engine.query(sql='some_str', limit_num=100)
        cur.return_value.execute.assert_called()
        cur.return_value.fetchmany.assert_called_once_with(size=100)
        connect.return_value.close.assert_called_once()
        self.assertIsInstance(query_result, ResultSet)

    @patch.object(MysqlEngine, 'query')
    def testAllDb(self, mock_query):
        db_result = ResultSet()
        db_result.rows = [('db_1', ), ('db_2', )]
        mock_query.return_value = db_result
        new_engine = MysqlEngine(instance=self.ins1)
        dbs = new_engine.get_all_databases()
        self.assertEqual(dbs, ['db_1', 'db_2'])

    @patch.object(MysqlEngine, 'query')
    def testAllTables(self, mock_query):
        table_result = ResultSet()
        table_result.rows = [('tb_1', 'some_des'), ('tb_2', 'some_des')]
        mock_query.return_value = table_result
        new_engine = MysqlEngine(instance=self.ins1)
        tables = new_engine.get_all_tables('some_db')
        mock_query.assert_called_once_with(db_name='some_db', sql=ANY)
        self.assertEqual(tables, ['tb_1', 'tb_2'])

    @patch.object(MysqlEngine, 'query')
    def testAllColumns(self, mock_query):
        db_result = ResultSet()
        db_result.rows = [('col_1', 'type'), ('col_2', 'type2')]
        mock_query.return_value = db_result
        new_engine = MysqlEngine(instance=self.ins1)
        dbs = new_engine.get_all_columns_by_tb('some_db', 'some_tb')
        self.assertEqual(dbs, ['col_1', 'col_2'])

    @patch.object(MysqlEngine, 'query')
    def testDescribe(self, mock_query):
        new_engine = MysqlEngine(instance=self.ins1)
        new_engine.describe_table('some_db', 'some_db')
        mock_query.assert_called_once()

    def testQueryCheck(self):
        new_engine = MysqlEngine(instance=self.ins1)
        sql_without_limit = '-- 测试\n select user from usertable'
        check_result = new_engine.query_check(db_name='some_db',
                                              sql=sql_without_limit)
        self.assertEqual(check_result['filtered_sql'],
                         'select user from usertable')

    def test_query_check_wrong_sql(self):
        new_engine = MysqlEngine(instance=self.ins1)
        wrong_sql = '-- 测试'
        check_result = new_engine.query_check(db_name='some_db', sql=wrong_sql)
        self.assertDictEqual(
            check_result, {
                'msg': '不支持的查询语法类型!',
                'bad_query': True,
                'filtered_sql': '-- 测试',
                'has_star': True
            })

    def test_query_check_update_sql(self):
        new_engine = MysqlEngine(instance=self.ins1)
        update_sql = 'update user set id=0'
        check_result = new_engine.query_check(db_name='some_db',
                                              sql=update_sql)
        self.assertDictEqual(
            check_result, {
                'msg': '不支持的查询语法类型!',
                'bad_query': True,
                'filtered_sql': 'update user set id=0',
                'has_star': False
            })

    def test_filter_sql_with_delimiter(self):
        new_engine = MysqlEngine(instance=self.ins1)
        sql_without_limit = 'select user from usertable;'
        check_result = new_engine.filter_sql(sql=sql_without_limit,
                                             limit_num=100)
        self.assertEqual(check_result, 'select user from usertable limit 100;')

    def test_filter_sql_without_delimiter(self):
        new_engine = MysqlEngine(instance=self.ins1)
        sql_without_limit = 'select user from usertable'
        check_result = new_engine.filter_sql(sql=sql_without_limit,
                                             limit_num=100)
        self.assertEqual(check_result, 'select user from usertable limit 100;')

    def test_filter_sql_with_limit(self):
        new_engine = MysqlEngine(instance=self.ins1)
        sql_without_limit = 'select user from usertable limit 10'
        check_result = new_engine.filter_sql(sql=sql_without_limit,
                                             limit_num=1)
        self.assertEqual(check_result, 'select user from usertable limit 10;')

    @patch('sql.engines.mysql.data_masking', return_value=ResultSet())
    def test_query_masking(self, _data_masking):
        query_result = ResultSet()
        new_engine = MysqlEngine(instance=self.ins1)
        masking_result = new_engine.query_masking(db_name='archery',
                                                  sql='select 1',
                                                  resultset=query_result)
        self.assertIsInstance(masking_result, ResultSet)

    @patch('sql.engines.mysql.data_masking', return_value=ResultSet())
    def test_query_masking_not_select(self, _data_masking):
        query_result = ResultSet()
        new_engine = MysqlEngine(instance=self.ins1)
        masking_result = new_engine.query_masking(db_name='archery',
                                                  sql='explain select 1',
                                                  resultset=query_result)
        self.assertEqual(masking_result, query_result)

    def test_execute_check_select_sql(self):
        sql = 'select * from user'
        row = ReviewResult(id=1,
                           errlevel=2,
                           stagestatus='驳回高危SQL',
                           errormessage='仅支持DML和DDL语句,查询语句请使用SQL查询功能!',
                           sql=sql)
        new_engine = MysqlEngine(instance=self.ins1)
        check_result = new_engine.execute_check(db_name='archery', sql=sql)
        self.assertIsInstance(check_result, ReviewSet)
        self.assertEqual(check_result.rows[0].__dict__, row.__dict__)

    def test_execute_check_critical_sql(self):
        self.sys_config.set('critical_ddl_regex', '^|update')
        self.sys_config.get_all_config()
        sql = 'update user set id=1'
        row = ReviewResult(id=1,
                           errlevel=2,
                           stagestatus='驳回高危SQL',
                           errormessage='禁止提交匹配' + '^|update' + '条件的语句!',
                           sql=sql)
        new_engine = MysqlEngine(instance=self.ins1)
        check_result = new_engine.execute_check(db_name='archery', sql=sql)
        self.assertIsInstance(check_result, ReviewSet)
        self.assertEqual(check_result.rows[0].__dict__, row.__dict__)

    @patch('sql.engines.mysql.InceptionEngine')
    def test_execute_check_normal_sql(self, _inception_engine):
        sql = 'update user set id=1'
        row = ReviewResult(
            id=1,
            errlevel=0,
            stagestatus='Audit completed',
            errormessage='None',
            sql=sql,
            affected_rows=0,
            execute_time=0,
        )
        _inception_engine.return_value.execute_check.return_value = ReviewSet(
            full_sql=sql, rows=[row])
        new_engine = MysqlEngine(instance=self.ins1)
        check_result = new_engine.execute_check(db_name='archery', sql=sql)
        self.assertIsInstance(check_result, ReviewSet)
        self.assertEqual(check_result.rows[0].__dict__, row.__dict__)

    @patch('sql.engines.mysql.InceptionEngine')
    def test_execute_check_normal_sql_with_Exception(self, _inception_engine):
        sql = 'update user set id=1'
        _inception_engine.return_value.execute_check.side_effect = RuntimeError(
        )
        new_engine = MysqlEngine(instance=self.ins1)
        with self.assertRaises(RuntimeError):
            new_engine.execute_check(db_name=0, sql=sql)

    @patch('sql.engines.mysql.InceptionEngine')
    def test_execute_workflow(self, _inception_engine):
        sql = 'update user set id=1'
        _inception_engine.return_value.execute.return_value = ReviewSet(
            full_sql=sql)
        new_engine = MysqlEngine(instance=self.ins1)
        execute_result = new_engine.execute_workflow(self.wf)
        self.assertIsInstance(execute_result, ReviewSet)

    @patch('MySQLdb.connect.cursor.execute')
    @patch('MySQLdb.connect.cursor')
    @patch('MySQLdb.connect')
    def test_execute(self, _connect, _cursor, _execute):
        new_engine = MysqlEngine(instance=self.ins1)
        execute_result = new_engine.execute(self.wf)
        self.assertIsInstance(execute_result, ResultSet)

    @patch.object(MysqlEngine, 'query')
    def test_server_version(self, _query):
        _query.return_value.rows = (('5.7.20', ), )
        new_engine = MysqlEngine(instance=self.ins1)
        server_version = new_engine.server_version
        self.assertTupleEqual(server_version, (5, 7, 20))

    @patch.object(MysqlEngine, 'query')
    def test_get_variables_not_filter(self, _query):
        new_engine = MysqlEngine(instance=self.ins1)
        new_engine.get_variables()
        _query.assert_called_once()

    @patch.object(MysqlEngine, 'query')
    def test_get_variables_filter(self, _query):
        new_engine = MysqlEngine(instance=self.ins1)
        new_engine.get_variables(variables=['binlog_format'])
        _query.assert_called()

    @patch.object(MysqlEngine, 'query')
    def test_set_variable(self, _query):
        new_engine = MysqlEngine(instance=self.ins1)
        new_engine.set_variable('binlog_format', 'ROW')
        _query.assert_called_once()
Exemplo n.º 26
0
class AsyncTest(TestCase):
    def setUp(self):
        self.now = datetime.now()
        self.u1 = User(username='******', display='用户1')
        self.u1.save()
        self.master1 = Instance(instance_name='test_master_instance',
                                type='master',
                                db_type='mysql',
                                host='testhost',
                                port=3306,
                                user='******',
                                password='******')
        self.master1.save()
        self.wf1 = SqlWorkflow.objects.create(
            workflow_name='some_name2',
            group_id=1,
            group_name='g1',
            engineer=self.u1.username,
            engineer_display=self.u1.display,
            audit_auth_groups='some_group',
            create_time=self.now - timedelta(days=1),
            status='workflow_executing',
            is_backup='是',
            instance=self.master1,
            db_name='some_db',
            syntax_type=1,
        )
        self.wfc1 = SqlWorkflowContent.objects.create(workflow=self.wf1,
                                                      sql_content='some_sql',
                                                      execute_result='')
        # 初始化工单执行返回对象
        self.task_result = MagicMock()
        self.task_result.args = [self.wf1.id]
        self.task_result.success = True
        self.task_result.stopped = self.now
        self.task_result.result.json.return_value = json.dumps([{
            'id':
            1,
            'sql':
            'some_content'
        }])
        self.task_result.result.warning = ''
        self.task_result.result.error = ''

    def tearDown(self):
        self.wf1.delete()
        self.u1.delete()
        self.task_result = None
        self.master1.delete()

    @patch('sql.utils.execute_sql.notify_for_execute')
    @patch('sql.utils.execute_sql.Audit')
    def test_call_back(self, mock_audit, mock_notify):
        mock_audit.detail_by_workflow_id.return_value.audit_id = 123
        mock_audit.add_log.return_value = 'any thing'
        execute_callback(self.task_result)
        mock_audit.detail_by_workflow_id.assert_called_with(
            workflow_id=self.wf1.id, workflow_type=ANY)
        mock_audit.add_log.assert_called_with(
            audit_id=123,
            operation_type=ANY,
            operation_type_desc=ANY,
            operation_info="执行结果:已正常结束",
            operator=ANY,
            operator_display=ANY,
        )
        mock_notify.assert_called_once()
Exemplo n.º 27
0
class TestSQLReview(TestCase):
    """
    测试sql review内的方法
    """
    def setUp(self):
        self.superuser = User.objects.create(username='******',
                                             is_superuser=True)
        self.user = User.objects.create(username='******')
        # 使用 travis.ci 时实例和测试service保持一致
        self.master = Instance(
            instance_name='test_instance',
            type='master',
            db_type='mysql',
            host=settings.DATABASES['default']['HOST'],
            port=settings.DATABASES['default']['PORT'],
            user=settings.DATABASES['default']['USER'],
            password=settings.DATABASES['default']['PASSWORD'])
        self.master.save()
        self.sys_config = SysConfig()
        self.client = Client()
        self.group = ResourceGroup.objects.create(group_id=1,
                                                  group_name='group_name')
        self.wf1 = SqlWorkflow.objects.create(
            workflow_name='workflow_name',
            group_id=self.group.group_id,
            group_name=self.group.group_name,
            engineer=self.superuser.username,
            engineer_display=self.superuser.display,
            audit_auth_groups='audit_auth_groups',
            create_time=datetime.datetime.now(),
            status='workflow_review_pass',
            is_backup=True,
            instance=self.master,
            db_name='db_name',
            syntax_type=1,
        )
        self.wfc1 = SqlWorkflowContent.objects.create(workflow=self.wf1,
                                                      sql_content='some_sql',
                                                      execute_result='')

    def tearDown(self):
        self.wf1.delete()
        self.group.delete()
        self.superuser.delete()
        self.user.delete()
        self.master.delete()
        self.sys_config.replace(json.dumps({}))

    @patch('sql.engines.get_engine')
    def test_auto_review_hit_review_regex(
        self,
        _get_engine,
    ):
        """
        测试自动审批通过的判定条件,命中判断正则
        :return:
        """
        # 开启自动审批设置
        self.sys_config.set('auto_review', 'true')
        self.sys_config.set('auto_review_regex', '^drop')  # drop语句需要审批
        self.sys_config.set('auto_review_max_update_rows',
                            '50')  # update影响行数大于50需要审批
        self.sys_config.get_all_config()
        # 修改工单为drop
        self.wfc1.sql_content = "drop table users;"
        self.wfc1.save(update_fields=('sql_content', ))
        r = is_auto_review(self.wfc1.workflow_id)
        self.assertFalse(r)

    @patch('sql.engines.mysql.MysqlEngine.execute_check')
    @patch('sql.engines.get_engine')
    def test_auto_review_gt_max_update_rows(self, _get_engine, _execute_check):
        """
        测试自动审批通过的判定条件,影响行数大于auto_review_max_update_rows
        :return:
        """
        # 开启自动审批设置
        self.sys_config.set('auto_review', 'true')
        self.sys_config.set('auto_review_regex', '^drop')  # drop语句需要审批
        self.sys_config.set('auto_review_max_update_rows',
                            '2')  # update影响行数大于2需要审批
        self.sys_config.get_all_config()
        # 修改工单为update
        self.wfc1.sql_content = "update table users set email='';"
        self.wfc1.save(update_fields=('sql_content', ))
        # mock返回值,update影响行数=3
        _execute_check.return_value.to_dict.return_value = [{
            "id":
            1,
            "stage":
            "CHECKED",
            "errlevel":
            0,
            "stagestatus":
            "Audit completed",
            "errormessage":
            "None",
            "sql":
            "use archer_test",
            "affected_rows":
            0,
            "sequence":
            "'0_0_0'",
            "backup_dbname":
            "None",
            "execute_time":
            "0",
            "sqlsha1":
            "",
            "actual_affected_rows":
            'null'
        }, {
            "id":
            2,
            "stage":
            "CHECKED",
            "errlevel":
            0,
            "stagestatus":
            "Audit completed",
            "errormessage":
            "None",
            "sql":
            "update table users set email=''",
            "affected_rows":
            3,
            "sequence":
            "'0_0_1'",
            "backup_dbname":
            "mysql_3306_archer_test",
            "execute_time":
            "0",
            "sqlsha1":
            "",
            "actual_affected_rows":
            'null'
        }]
        r = is_auto_review(self.wfc1.workflow_id)
        self.assertFalse(r)

    @patch('sql.engines.mysql.MysqlEngine.execute_check')
    @patch('sql.engines.get_engine')
    def test_auto_review_true(self, _get_engine, _execute_check):
        """
        测试自动审批通过的判定条件,
        :return:
        """
        # 开启自动审批设置
        self.sys_config.set('auto_review', 'true')
        self.sys_config.set('auto_review_regex', '^drop')  # drop语句需要审批
        self.sys_config.set('auto_review_max_update_rows',
                            '2')  # update影响行数大于2需要审批
        self.sys_config.get_all_config()
        # 修改工单为update
        self.wfc1.sql_content = "update table users set email='';"
        self.wfc1.save(update_fields=('sql_content', ))
        # mock返回值,update影响行数=3
        _execute_check.return_value.to_dict.return_value = [{
            "id":
            1,
            "stage":
            "CHECKED",
            "errlevel":
            0,
            "stagestatus":
            "Audit completed",
            "errormessage":
            "None",
            "sql":
            "use archer_test",
            "affected_rows":
            0,
            "sequence":
            "'0_0_0'",
            "backup_dbname":
            "None",
            "execute_time":
            "0",
            "sqlsha1":
            "",
            "actual_affected_rows":
            'null'
        }, {
            "id":
            2,
            "stage":
            "CHECKED",
            "errlevel":
            0,
            "stagestatus":
            "Audit completed",
            "errormessage":
            "None",
            "sql":
            "update table users set email=''",
            "affected_rows":
            1,
            "sequence":
            "'0_0_1'",
            "backup_dbname":
            "mysql_3306_archer_test",
            "execute_time":
            "0",
            "sqlsha1":
            "",
            "actual_affected_rows":
            'null'
        }]
        r = is_auto_review(self.wfc1.workflow_id)
        self.assertTrue(r)

    def test_can_execute_for_resource_group(self, ):
        """
        测试是否能执行的判定条件,登录用户有资源组粒度执行权限,并且为组内用户
        :return:
        """
        # 修改工单为workflow_review_pass,登录用户有资源组粒度执行权限,并且为组内用户
        self.wf1.status = 'workflow_review_pass'
        self.wf1.save(update_fields=('status', ))
        sql_execute_for_resource_group = Permission.objects.get(
            codename='sql_execute_for_resource_group')
        self.user.user_permissions.add(sql_execute_for_resource_group)
        ResourceGroupRelations.objects.create(object_type=0,
                                              object_id=self.user.id,
                                              group_id=self.group.group_id)
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    def test_can_execute_true(self, ):
        """
        测试是否能执行的判定条件,当前登录用户为提交人,并且有执行权限,工单状态为审核通过
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人,并且有执行权限
        self.wf1.status = 'workflow_review_pass'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        sql_execute = Permission.objects.get(codename='sql_execute')
        self.user.user_permissions.add(sql_execute)
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    def test_can_execute_workflow_timing_task(self, ):
        """
        测试是否能执行的判定条件,当前登录用户为提交人,并且有执行权限,工单状态为定时执行
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人,并且有执行权限
        self.wf1.status = 'workflow_timingtask'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        sql_execute = Permission.objects.get(codename='sql_execute')
        self.user.user_permissions.add(sql_execute)
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    def test_can_execute_false_no_permission(self, ):
        """
        当前登录用户为提交人,但是没有执行权限
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人,并且有执行权限
        self.wf1.status = 'workflow_timingtask'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertFalse(r)

    def test_can_execute_false_not_in_group(self, ):
        """
        当前登录用户为提交人,有资源组粒度执行权限,但是不是组内用户
        :return:
        """
        # 修改工单为workflow_review_pass,有资源组粒度执行权限,但是不是组内用户
        self.wf1.status = 'workflow_review_pass'
        self.wf1.save(update_fields=('status', ))
        sql_execute_for_resource_group = Permission.objects.get(
            codename='sql_execute_for_resource_group')
        self.user.user_permissions.add(sql_execute_for_resource_group)
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertFalse(r)

    def test_can_execute_false_wrong_status(self, ):
        """
        当前登录用户为提交人,前登录用户为提交人,并且有执行权限,但是工单状态为待审核
        :return:
        """
        # 修改工单为workflow_manreviewing,当前登录用户为提交人,并且有执行权限, 但是工单状态为待审核
        self.wf1.status = 'workflow_manreviewing'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        sql_execute = Permission.objects.get(codename='sql_execute')
        self.user.user_permissions.add(sql_execute)
        r = can_execute(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertFalse(r)

    def test_can_timingtask_true(self, ):
        """
        测试是否能定时执行的判定条件,当前登录用户为提交人,并且有执行权限,工单状态为审核通过
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人,并且有执行权限
        self.wf1.status = 'workflow_review_pass'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        sql_execute = Permission.objects.get(codename='sql_execute')
        self.user.user_permissions.add(sql_execute)
        r = can_timingtask(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    def test_can_timingtask_false(self, ):
        """
        测试是否能定时执行的判定条件,当前登录有执行权限,工单状态为审核通过,但用户不是提交人
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人,并且有执行权限
        self.wf1.status = 'workflow_review_pass'
        self.wf1.engineer = self.superuser.username
        self.wf1.save(update_fields=('status', 'engineer'))
        sql_execute = Permission.objects.get(codename='sql_execute')
        self.user.user_permissions.add(sql_execute)
        r = can_timingtask(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertFalse(r)

    @patch('sql.utils.workflow_audit.Audit.can_review')
    def test_can_cancel_true_for_apply_user(self, _can_review):
        """
        测试是否能取消,审核中的工单,提交人可终止
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人
        self.wf1.status = 'workflow_manreviewing'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        _can_review.return_value = False
        r = can_cancel(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    @patch('sql.utils.workflow_audit.Audit.can_review')
    def test_can_cancel_true_for_audit_user(self, _can_review):
        """
        测试是否能取消,审核中的工单,审核人可终止
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人
        self.wf1.status = 'workflow_manreviewing'
        self.wf1.engineer = self.superuser.username
        self.wf1.save(update_fields=('status', 'engineer'))
        _can_review.return_value = True
        r = can_cancel(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    @patch('sql.utils.sql_review.can_execute')
    def test_can_cancel_true_for_execute_user(self, _can_execute):
        """
        测试是否能取消,审核通过但未执行的工单,有执行权限的用户终止
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人
        self.wf1.status = 'workflow_review_pass'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        _can_execute.return_value = True
        r = can_cancel(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertTrue(r)

    @patch('sql.utils.sql_review.can_execute')
    def test_can_cancel_false(self, _can_execute):
        """
        测试是否能取消,审核通过但未执行的工单,无执行权限的用户无法终止
        :return:
        """
        # 修改工单为workflow_review_pass,当前登录用户为提交人
        self.wf1.status = 'workflow_review_pass'
        self.wf1.engineer = self.user.username
        self.wf1.save(update_fields=('status', 'engineer'))
        _can_execute.return_value = False
        r = can_cancel(user=self.user, workflow_id=self.wfc1.workflow_id)
        self.assertFalse(r)
Exemplo n.º 28
0
class TestSQLAnalyze(TestCase):
    """
    测试SQL分析
    """
    def setUp(self):
        self.superuser = User(username='******', is_superuser=True)
        self.superuser.save()
        # 使用 travis.ci 时实例和测试service保持一致
        self.master = Instance(
            instance_name='test_instance',
            type='master',
            db_type='mysql',
            host=settings.DATABASES['default']['HOST'],
            port=settings.DATABASES['default']['PORT'],
            user=settings.DATABASES['default']['USER'],
            password=settings.DATABASES['default']['PASSWORD'])
        self.master.save()
        self.sys_config = SysConfig()
        self.client = Client()
        self.client.force_login(self.superuser)

    def tearDown(self):
        self.superuser.delete()
        self.master.delete()
        self.sys_config.replace(json.dumps({}))

    def test_generate_text_None(self):
        """
        测试解析SQL,text为空
        :return:
        """
        r = self.client.post(path='/sql_analyze/generate/', data={})
        self.assertEqual(json.loads(r.content), {'rows': [], 'total': 0})

    def test_generate_text_not_None(self):
        """
        测试解析SQL,text不为空
        :return:
        """
        text = "select * from sql_user;select * from sql_workflow;"
        r = self.client.post(path='/sql_analyze/generate/',
                             data={"text": text})
        self.assertEqual(
            json.loads(r.content), {
                "total":
                2,
                "rows": [{
                    "sql_id": 1,
                    "sql": "select * from sql_user;"
                }, {
                    "sql_id": 2,
                    "sql": "select * from sql_workflow;"
                }]
            })

    def test_analyze_text_None(self):
        """
        测试分析SQL,text为空
        :return:
        """
        r = self.client.post(path='/sql_analyze/analyze/', data={})
        self.assertEqual(json.loads(r.content), {'rows': [], 'total': 0})

    def test_analyze_text_not_None(self):
        """
        测试分析SQL,text不为空
        :return:
        """
        text = "select * from sql_user;select * from sql_workflow;"
        instance_name = self.master.instance_name
        db_name = settings.DATABASES['default']['TEST']['NAME']
        r = self.client.post(path='/sql_analyze/analyze/',
                             data={
                                 "text": text,
                                 "instance_name": instance_name,
                                 "db_name": db_name
                             })
        self.assertListEqual(list(json.loads(r.content)['rows'][0].keys()),
                             ['sql_id', 'sql', 'report'])