diff --git a/sql/binlog.py b/sql/binlog.py
index eb9b2bbc24..d34c4e1917 100644
--- a/sql/binlog.py
+++ b/sql/binlog.py
@@ -4,6 +4,7 @@
import os
import time
import traceback
+import shlex
import simplejson as json
from django.conf import settings
@@ -15,7 +16,8 @@
from sql.engines import get_engine
from sql.plugins.binglog2sql import Binlog2Sql
-from sql.notify import notify_for_binlog2sql
+from sql.plugins.my2sql import My2SQL
+from sql.notify import notify_for_binlog2sql, notify_for_my2sql
from .models import Instance
logger = logging.getLogger('default')
@@ -112,7 +114,8 @@ def binlog2sql(request):
# 提交给binlog2sql进行解析
binlog2sql = Binlog2Sql()
# 准备参数
- args = {"conn_options": fr"-h{instance.host} -u{instance.user} -p'{instance.password}' -P{instance.port} ",
+ args = {"conn_options": fr"-h{shlex.quote(str(instance.host))} -u{shlex.quote(str(instance.user))} \
+ -p'{shlex.quote(str(instance.password))}' -P{shlex.quote(str(instance.port))} ",
"stop_never": False,
"no-primary-key": no_pk,
"flashback": flashback,
@@ -190,7 +193,8 @@ def binlog2sql_file(args, user):
"""
binlog2sql = Binlog2Sql()
instance = args.get('instance')
- conn_options = fr"-h{instance.host} -u{instance.user} -p'{instance.password}' -P{instance.port}"
+ conn_options = fr"-h{shlex.quote(str(instance.host))} -u{shlex.quote(str(instance.user))} \
+ -p'{shlex.quote(str(instance.password))}' -P{shlex.quote(str(instance.port))}"
args['conn_options'] = conn_options
timestamp = int(time.time())
path = os.path.join(settings.BASE_DIR, 'downloads/binlog2sql/')
@@ -208,3 +212,136 @@ def binlog2sql_file(args, user):
for c in iter(p.stdout.readline, ''):
f.write(c)
return user, filename
+
+
+@permission_required('sql.menu_my2sql', raise_exception=True)
+def my2sql(request):
+ """
+ 通过解析binlog获取SQL--使用my2sql
+ :param request:
+ :return:
+ """
+ instance_name = request.POST.get('instance_name')
+ save_sql = True if request.POST.get('save_sql') == 'true' else False
+ instance = Instance.objects.get(instance_name=instance_name)
+ work_type = 'rollback' if request.POST.get('rollback') == 'true' else '2sql'
+ num = 30 if request.POST.get('num') == '' else int(request.POST.get('num'))
+ threads = 4 if request.POST.get('threads') == '' else int(request.POST.get('threads'))
+ start_file = request.POST.get('start_file')
+ start_pos = request.POST.get('start_pos') if request.POST.get('start_pos') == '' else int(
+ request.POST.get('start_pos'))
+ end_file = request.POST.get('end_file')
+ end_pos = request.POST.get('end_pos') if request.POST.get('end_pos') == '' else int(request.POST.get('end_pos'))
+ stop_time = request.POST.get('stop_time')
+ start_time = request.POST.get('start_time')
+ only_schemas = request.POST.getlist('only_schemas')
+ only_tables = request.POST.getlist('only_tables[]')
+ sql_type = [] if request.POST.getlist('sql_type[]') == [] else request.POST.getlist('sql_type[]')
+ extra_info = True if request.POST.get('extra_info') == 'true' else False
+ ignore_primary_key = True if request.POST.get('ignore_primary_key') == 'true' else False
+ full_columns = True if request.POST.get('full_columns') == 'true' else False
+ no_db_prefix = True if request.POST.get('no_db_prefix') == 'true' else False
+ file_per_table = True if request.POST.get('file_per_table') == 'true' else False
+
+ result = {'status': 0, 'msg': 'ok', 'data': []}
+
+ # 提交给my2sql进行解析
+ my2sql = My2SQL()
+
+ # 准备参数
+ args = {"conn_options": fr"-host {shlex.quote(str(instance.host))} -user {shlex.quote(str(instance.user))} \
+ -password '{shlex.quote(str(instance.password))}' -port {shlex.quote(str(instance.port))} ",
+ "work-type": work_type,
+ "start-file": start_file,
+ "start-pos": start_pos,
+ "stop-file": end_file,
+ "stop-pos": end_pos,
+ "start-datetime": start_time,
+ "stop-datetime": stop_time,
+ "databases": ' '.join(only_schemas),
+ "tables": ','.join(only_tables),
+ "sql": ','.join(sql_type),
+ "instance": instance,
+ "threads": threads,
+ "add-extraInfo": extra_info,
+ "ignore-primaryKey-forInsert": ignore_primary_key,
+ "full-columns": full_columns,
+ "do-not-add-prifixDb": no_db_prefix,
+ "file-per-table": file_per_table,
+ "output-toScreen": True
+ }
+
+ # 参数检查
+ args_check_result = my2sql.check_args(args)
+ if args_check_result['status'] == 1:
+ return HttpResponse(json.dumps(args_check_result), content_type='application/json')
+ # 参数转换
+ cmd_args = my2sql.generate_args2cmd(args, shell=True)
+
+ # 执行命令
+ try:
+ p = my2sql.execute_cmd(cmd_args, shell=True)
+ # 读取前num行后结束
+ rows = []
+ n = 1
+ for line in iter(p.stdout.readline, ''):
+ if n <= num and isinstance(line, str):
+ if line[0:6].upper() in ('INSERT', 'DELETE', 'UPDATE'):
+ n = n + 1
+ row_info = {}
+ try:
+ row_info['sql'] = line + ';'
+ except IndexError:
+ row_info['sql'] = line + ';'
+ rows.append(row_info)
+ else:
+ break
+
+ if rows.__len__() == 0:
+ # 判断是否有异常
+ stderr = p.stderr.read()
+ if stderr and isinstance(stderr, str):
+ result['status'] = 1
+ result['msg'] = stderr
+ return HttpResponse(json.dumps(result), content_type='application/json')
+ # 终止子进程
+ p.kill()
+ result['data'] = rows
+ except Exception as e:
+ logger.error(traceback.format_exc())
+ result['status'] = 1
+ result['msg'] = str(e)
+
+ # 异步保存到文件
+ if save_sql:
+ args.pop('conn_options')
+ args.pop('output-toScreen')
+ async_task(my2sql_file, args=args, user=request.user, hook=notify_for_my2sql, timeout=-1,
+ task_name=f'my2sql-{time.time()}')
+
+ # 返回查询结果
+ return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True),
+ content_type='application/json')
+
+
+def my2sql_file(args, user):
+ """
+ 用于异步保存binlog解析的文件
+ :param args: 参数
+ :param user: 操作用户对象,用户消息推送
+ :return:
+ """
+ my2sql = My2SQL()
+ instance = args.get('instance')
+ conn_options = fr"-host {shlex.quote(str(instance.host))} -user {shlex.quote(str(instance.user))} \
+ -password '{shlex.quote(str(instance.password))}' -port {shlex.quote(str(instance.port))} "
+ args['conn_options'] = conn_options
+ path = os.path.join(settings.BASE_DIR, 'downloads/my2sql/')
+ os.makedirs(path, exist_ok=True)
+
+ # 参数转换
+ args["output-dir"] = path
+ cmd_args = my2sql.generate_args2cmd(args, shell=True)
+ # 使用output-dir参数执行命令保存sql
+ my2sql.execute_cmd(cmd_args, shell=True)
+ return user, path
diff --git a/sql/fixtures/auth_group.sql b/sql/fixtures/auth_group.sql
index fc81554df6..25e33a1a21 100644
--- a/sql/fixtures/auth_group.sql
+++ b/sql/fixtures/auth_group.sql
@@ -23,7 +23,7 @@ where codename in ('menu_dashboard','menu_sqlcheck','menu_sqlworkflow','menu_sql
insert into auth_group_permissions (group_id, permission_id)
select 3,id
from auth_permission
-where codename in ('menu_dashboard','menu_sqlcheck','menu_sqlworkflow','menu_sqlanalyze','menu_query','menu_sqlquery','menu_queryapplylist','menu_sqloptimize','menu_sqladvisor','menu_slowquery','menu_instance','menu_instance_list','menu_dbdiagnostic','menu_database','menu_instance_account','menu_param','menu_data_dictionary','menu_tools','menu_archive','menu_binlog2sql','menu_schemasync','menu_system','menu_document','sql_submit','sql_review','sql_execute_for_resource_group','sql_execute','sql_analyze','optimize_sqladvisor','optimize_sqltuning','optimize_soar','query_applypriv','query_mgtpriv','query_review','query_submit','query_all_instances','query_resource_group_instance','process_view','process_kill','tablespace_view','trx_view','trxandlocks_view','instance_account_manage','param_view','param_edit','data_dictionary_export','archive_apply','archive_review','archive_mgt');
+where codename in ('menu_dashboard','menu_sqlcheck','menu_sqlworkflow','menu_sqlanalyze','menu_query','menu_sqlquery','menu_queryapplylist','menu_sqloptimize','menu_sqladvisor','menu_slowquery','menu_instance','menu_instance_list','menu_dbdiagnostic','menu_database','menu_instance_account','menu_param','menu_data_dictionary','menu_tools','menu_archive','menu_binlog2sql','menu_my2sql','menu_schemasync','menu_system','menu_document','sql_submit','sql_review','sql_execute_for_resource_group','sql_execute','sql_analyze','optimize_sqladvisor','optimize_sqltuning','optimize_soar','query_applypriv','query_mgtpriv','query_review','query_submit','query_all_instances','query_resource_group_instance','process_view','process_kill','tablespace_view','trx_view','trxandlocks_view','instance_account_manage','param_view','param_edit','data_dictionary_export','archive_apply','archive_review','archive_mgt');
-- PM
insert into auth_group_permissions (group_id, permission_id)
diff --git a/sql/models.py b/sql/models.py
index e4ce26b05b..11920ce72a 100755
--- a/sql/models.py
+++ b/sql/models.py
@@ -692,6 +692,7 @@ class Meta:
('menu_tools', '菜单 工具插件'),
('menu_archive', '菜单 数据归档'),
('menu_binlog2sql', '菜单 Binlog2SQL'),
+ ('menu_my2sql', '菜单 My2SQL'),
('menu_schemasync', '菜单 SchemaSync'),
('menu_system', '菜单 系统管理'),
('menu_document', '菜单 相关文档'),
diff --git a/sql/notify.py b/sql/notify.py
index 6c2415d2ec..36e5380535 100755
--- a/sql/notify.py
+++ b/sql/notify.py
@@ -293,3 +293,23 @@ def notify_for_binlog2sql(task):
# 发送
msg_to = [task.kwargs['user']]
__send(msg_title, msg_content, msg_to)
+
+
+def notify_for_my2sql(task):
+ """
+ my2sql执行结束的通知
+ :param task:
+ :return:
+ """
+ # 判断是否开启消息通知,未开启直接返回
+ if not __notify_cnf_status():
+ return None
+ if task.success:
+ msg_title = '[Archery 通知]My2SQL执行结束'
+ msg_content = f'解析的SQL文件在{task.result[1]}目录下,请前往查看'
+ else:
+ msg_title = '[Archery 通知]My2SQL执行失败'
+ msg_content = f'{task.result}'
+ # 发送
+ msg_to = [task.kwargs['user']]
+ __send(msg_title, msg_content, msg_to)
diff --git a/sql/plugins/my2sql.py b/sql/plugins/my2sql.py
new file mode 100644
index 0000000000..7b3457ef23
--- /dev/null
+++ b/sql/plugins/my2sql.py
@@ -0,0 +1,52 @@
+# -*- coding: UTF-8 -*-
+from common.config import SysConfig
+from sql.plugins.plugin import Plugin
+import shlex
+
+
+class My2SQL(Plugin):
+
+ def __init__(self):
+ self.path = SysConfig().get('my2sql')
+ self.required_args = []
+ self.disable_args = []
+ super(Plugin, self).__init__()
+
+ def generate_args2cmd(self, args, shell):
+ """
+ 转换请求参数为命令行
+ :param args:
+ :param shell:
+ :return:
+ """
+ conn_options = ['conn_options']
+ args_options = ['work-type', 'threads', 'start-file', 'stop-file', 'start-pos',
+ 'stop-pos', 'databases', 'tables', 'sql', 'output-dir']
+ no_args_options = ['output-toScreen', 'add-extraInfo', 'ignore-primaryKey-forInsert',
+ 'full-columns', 'do-not-add-prifixDb', 'file-per-table']
+ datetime_options = ['start-datetime', 'stop-datetime']
+ if shell:
+ cmd_args = f'{shlex.quote(str(self.path))}' if self.path else ''
+ for name, value in args.items():
+ if name in conn_options:
+ cmd_args += f' {value}'
+ elif name in args_options and value:
+ cmd_args += f' -{name} {shlex.quote(str(value))}'
+ elif name in datetime_options and value:
+ cmd_args += f" -{name} '{shlex.quote(str(value))}'"
+ elif name in no_args_options and value:
+ cmd_args += f' -{name}'
+ else:
+ cmd_args = [self.path]
+ for name, value in args.items():
+ if name in conn_options:
+ cmd_args.append(f'{value}')
+ elif name in args_options:
+ cmd_args.append(f'-{name}')
+ cmd_args.append(f'{value}')
+ elif name in datetime_options:
+ cmd_args.append(f'-{name}')
+ cmd_args.append(f"'{value}'")
+ elif name in no_args_options:
+ cmd_args.append(f'-{name}')
+ return cmd_args
diff --git a/sql/plugins/tests.py b/sql/plugins/tests.py
index 81ea2ce5a4..c6af809634 100644
--- a/sql/plugins/tests.py
+++ b/sql/plugins/tests.py
@@ -11,6 +11,7 @@
from django.contrib.auth import get_user_model
from sql.plugins.binglog2sql import Binlog2Sql
+from sql.plugins.my2sql import My2SQL
from sql.plugins.schemasync import SchemaSync
from sql.plugins.soar import Soar
from sql.plugins.sqladvisor import SQLAdvisor
@@ -199,6 +200,36 @@ def test_binlog2ql_generate_args2cmd(self):
cmd_args = binlog2sql.generate_args2cmd(args, True)
self.assertIsInstance(cmd_args, str)
+ def test_my2sql_generate_args2cmd(self):
+ """
+ 测试my2sql参数转换
+ :return:
+ """
+ args = {'conn_options': "-host mysql -user root -password '123456' -port 3306 ",
+ 'work-type': '2sql',
+ 'start-file': 'mysql-bin.000043',
+ 'start-pos': 111,
+ 'stop-file': '',
+ 'stop-pos': '',
+ 'start-datetime': '',
+ 'stop-datetime': '',
+ 'databases': 'account_center',
+ 'tables': 'ac_apps',
+ 'sql': 'update',
+ "threads": 1,
+ "add-extraInfo": "false",
+ "ignore-primaryKey-forInsert": "false",
+ "full-columns": "false",
+ "do-not-add-prifixDb": "false",
+ "file-per-table": "false"}
+ self.sys_config.set('my2sql', '/opt/archery/src/plugins/my2sql')
+ self.sys_config.get_all_config()
+ my2sql = My2SQL()
+ cmd_args = my2sql.generate_args2cmd(args, False)
+ self.assertIsInstance(cmd_args, list)
+ cmd_args = my2sql.generate_args2cmd(args, True)
+ self.assertIsInstance(cmd_args, str)
+
def test_pt_archiver_generate_args2cmd(self):
"""
测试pt_archiver参数转换
diff --git a/sql/templates/my2sql.html b/sql/templates/my2sql.html
new file mode 100644
index 0000000000..64ee2d047e
--- /dev/null
+++ b/sql/templates/my2sql.html
@@ -0,0 +1,414 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+
+
+ 操作选项
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SQL语句
+
+
+
+ 前端展示数量由解析范围参数控制,如果勾选了保存到文件,则会异步获取完整的SQL文件,
+ 保存到项目downloads目录,在开启消息通知后,执行结束会发送通知给操作人
+
+
+
+
+
+
+
+{% endblock content %}
+
+{% block js %}
+ {% load static %}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/sql/tests.py b/sql/tests.py
index 7561966e62..c283944e9d 100644
--- a/sql/tests.py
+++ b/sql/tests.py
@@ -11,9 +11,9 @@
from common.config import SysConfig
from common.utils.const import WorkflowDict
from sql.archiver import add_archive_task, archive
-from sql.binlog import binlog2sql_file
+from sql.binlog import binlog2sql_file, my2sql_file
from sql.engines.models import ResultSet, ReviewSet, ReviewResult
-from sql.notify import notify_for_audit, notify_for_execute, notify_for_binlog2sql
+from sql.notify import notify_for_audit, notify_for_execute, notify_for_binlog2sql, notify_for_my2sql
from sql.utils.execute_sql import execute_callback
from sql.query import kill_query_conn
from sql.models import Users, Instance, QueryPrivilegesApply, QueryPrivileges, SqlWorkflow, SqlWorkflowContent, \
@@ -193,6 +193,12 @@ def test_binlog2sql(self):
r = self.client.get(f'/binlog2sql/', data=data)
self.assertEqual(r.status_code, 200)
+ def test_my2sql(self):
+ """测试my2sql页面"""
+ data = {}
+ r = self.client.get(f'/my2sql/', data=data)
+ self.assertEqual(r.status_code, 200)
+
def test_schemasync(self):
"""测试schemasync页面"""
data = {}
@@ -2071,6 +2077,32 @@ def test_binlog2sql_path_not_exist(self):
r = self.client.post(path='/binlog/binlog2sql/', data=data)
self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '可执行文件路径不能为空!', 'data': {}})
+ def test_my2sql_path_not_exist(self):
+ """
+ 测试获取解析binlog,path未设置
+ :return:
+ """
+ data = {"instance_name": "test_instance",
+ "save_sql": "false",
+ "rollback": "2sql",
+ "num": "",
+ "threads": 1,
+ "extra_info": "false",
+ "ignore_primary_key": "false",
+ "full_columns": "false",
+ "no_db_prefix": "false",
+ "file_per_table": "false",
+ "start_file": "mysql-bin.000045",
+ "start_pos": "",
+ "end_file": "mysql-bin.000045",
+ "end_pos": "",
+ "stop_time": "",
+ "start_time": "",
+ "only_schemas": "",
+ "sql_type": ""}
+ r = self.client.post(path='/binlog/my2sql/', data=data)
+ self.assertEqual(json.loads(r.content), {'status': 1, 'msg': '可执行文件路径不能为空!', 'data': {}})
+
@patch('sql.plugins.plugin.subprocess')
def test_binlog2sql(self, _subprocess):
"""
@@ -2098,6 +2130,36 @@ def test_binlog2sql(self, _subprocess):
r = self.client.post(path='/binlog/binlog2sql/', data=data)
self.assertEqual(json.loads(r.content), {"status": 0, "msg": "ok", "data": [{"sql": {}, "binlog_info": {}}]})
+ @patch('sql.plugins.plugin.subprocess')
+ def test_my2sql(self, _subprocess):
+ """
+ 测试获取解析binlog,path设置
+ :param _subprocess:
+ :return:
+ """
+ self.sys_config.set('my2sql', '/opt/my2sql')
+ self.sys_config.get_all_config()
+ data = {"instance_name": "test_instance",
+ "save_sql": "1",
+ "rollback": "2sql",
+ "num": "1",
+ "threads": 1,
+ "extra_info": "false",
+ "ignore_primary_key": "false",
+ "full_columns": "false",
+ "no_db_prefix": "false",
+ "file_per_table": "false",
+ "start_file": "mysql-bin.000045",
+ "start_pos": "",
+ "end_file": "mysql-bin.000046",
+ "end_pos": "",
+ "stop_time": "",
+ "start_time": "",
+ "only_schemas": "",
+ "sql_type": ""}
+ r = self.client.post(path='/binlog/my2sql/', data=data)
+ self.assertEqual(json.loads(r.content), {"status": 0, "msg": "ok", "data": []})
+
@patch('builtins.open')
def test_binlog2sql_file(self, _open):
"""
@@ -2124,6 +2186,35 @@ def test_binlog2sql_file(self, _open):
r = binlog2sql_file(args=args, user=self.superuser)
self.assertEqual(self.superuser, r[0])
+ @patch('builtins.open')
+ def test_my2sql_file(self, _open):
+ """
+ 测试保存文件
+ :param _subprocess:
+ :return:
+ """
+ args = {"instance_name": "test_instance",
+ "save_sql": "1",
+ "rollback": "2sql",
+ "num": "1",
+ "threads": 1,
+ "add-extraInfo": "false",
+ "ignore-primaryKey-forInsert": "false",
+ "full-columns": "false",
+ "do-not-add-prifixDb": "false",
+ "file-per-table": "false",
+ "start-file": "mysql-bin.000045",
+ "start-pos": "",
+ "stop-file": "mysql-bin.000045",
+ "stop-pos": "",
+ "stop-datetime": "",
+ "start-datetime": "",
+ "databases": "",
+ "sql": "",
+ "instance": self.master}
+ r = my2sql_file(args=args, user=self.superuser)
+ self.assertEqual(self.superuser, r[0])
+
def test_del_binlog_instance_not_exist(self):
"""
测试删除binlog,实例不存在
@@ -2598,6 +2689,18 @@ def test_notify_for_binlog2sql_disable(self, _msg_sender):
r = notify_for_execute(self.wf)
self.assertIsNone(r)
+ @patch('sql.notify.MsgSender')
+ def test_notify_for_my2sql_disable(self, _msg_sender):
+ """
+ 测试执行消息关闭
+ :return:
+ """
+ # 开启消息通知
+ self.sys_config.set('mail', 'false')
+ self.sys_config.set('ding', 'false')
+ r = notify_for_execute(self.wf)
+ self.assertIsNone(r)
+
@patch('django_q.tasks.async_task')
@patch('sql.notify.MsgSender')
def test_notify_for_binlog2sql(self, _msg_sender, _async_task):
@@ -2613,6 +2716,21 @@ def test_notify_for_binlog2sql(self, _msg_sender, _async_task):
self.assertIsNone(r)
_msg_sender.assert_called_once()
+ @patch('django_q.tasks.async_task')
+ @patch('sql.notify.MsgSender')
+ def test_notify_for_my2sql(self, _msg_sender, _async_task):
+ """
+ 测试执行消息
+ :return:
+ """
+ # 开启消息通知
+ self.sys_config.set('mail', 'true')
+ # 设置为task成功
+ _async_task.return_value.success.return_value = True
+ r = notify_for_my2sql(_async_task)
+ self.assertIsNone(r)
+ _msg_sender.assert_called_once()
+
class TestDataDictionary(TestCase):
"""
diff --git a/sql/urls.py b/sql/urls.py
index 5fe1ef7352..c21291484f 100644
--- a/sql/urls.py
+++ b/sql/urls.py
@@ -51,6 +51,7 @@
path('database/', views.database),
path('instanceparam/', views.instance_param),
path('binlog2sql/', views.binlog2sql),
+ path('my2sql/', views.my2sql),
path('schemasync/', views.schemasync),
path('archive/', views.archive),
path('archive/
/', views.archive_detail, name='archive_detail'),
@@ -125,6 +126,7 @@
path('binlog/list/', binlog.binlog_list),
path('binlog/binlog2sql/', binlog.binlog2sql),
+ path('binlog/my2sql/', binlog.my2sql),
path('binlog/del_log/', binlog.del_binlog),
path('slowquery/review/', slowlog.slowquery_review),
diff --git a/sql/views.py b/sql/views.py
index c3e281e1cc..98bdcefa92 100644
--- a/sql/views.py
+++ b/sql/views.py
@@ -325,6 +325,12 @@ def binlog2sql(request):
return render(request, 'binlog2sql.html')
+@permission_required('sql.menu_my2sql', raise_exception=True)
+def my2sql(request):
+ """my2sql页面"""
+ return render(request, 'my2sql.html')
+
+
@permission_required('sql.menu_schemasync', raise_exception=True)
def schemasync(request):
"""数据库差异对比页面"""
diff --git a/src/docker/Dockerfile b/src/docker/Dockerfile
index bedd2d36a3..1248d85909 100644
--- a/src/docker/Dockerfile
+++ b/src/docker/Dockerfile
@@ -13,7 +13,11 @@ RUN cd /opt \
&& cp /opt/archery/src/docker/supervisord.conf /etc/ \
&& mv /opt/sqladvisor /opt/archery/src/plugins/ \
&& mv /opt/soar /opt/archery/src/plugins/ \
- && mv /opt/tmp_binlog2sql /opt/archery/src/plugins/binlog2sql
+ && mv /opt/tmp_binlog2sql /opt/archery/src/plugins/binlog2sql \
+ #my2sql
+ && wget https://raw.githubusercontent.com/liuhr/my2sql/master/releases/centOS_release_7.x/my2sql -O my2sql \
+ && chmod a+x my2sql \
+ && mv /opt/my2sql /opt/archery/src/plugins/
#port
EXPOSE 9123
diff --git a/src/docker/Dockerfile-base b/src/docker/Dockerfile-base
index 0075bb99db..0ff782f6db 100644
--- a/src/docker/Dockerfile-base
+++ b/src/docker/Dockerfile-base
@@ -61,6 +61,10 @@ RUN cd /opt \
&& git clone https://github.com/danfengcao/binlog2sql.git \
&& mv binlog2sql/binlog2sql/ tmp_binlog2sql \
&& rm -rf binlog2sql \
+#my2sql
+ && cd /opt \
+ && wget https://raw.githubusercontent.com/liuhr/my2sql/master/releases/centOS_release_7.x/my2sql -O my2sql \
+ && chmod a+x my2sql \
#msodbc
&& cd /opt \
&& curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssql-release.repo \
diff --git a/src/init_sql/v1.8.3.sql b/src/init_sql/v1.8.3.sql
index 25779d6125..8a749e786a 100644
--- a/src/init_sql/v1.8.3.sql
+++ b/src/init_sql/v1.8.3.sql
@@ -8,4 +8,8 @@ CREATE TABLE `audit_log` (
`action_time` datetime(6) NOT NULL COMMENT '操作时间',
PRIMARY KEY (`id`),
KEY `idx_username` (`user_name`)
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='登录审计日志表';
\ No newline at end of file
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='登录审计日志表';
+
+-- 新增my2sql菜单权限
+set @content_type_id=(select id from django_content_type where app_label='sql' and model='permission');
+INSERT INTO auth_permission (name, content_type_id, codename) VALUES ('菜单 My2SQL', @content_type_id, 'menu_my2sql');