292 lines
11 KiB
Python
292 lines
11 KiB
Python
import queue
|
|
|
|
from support import SupportSubprocess
|
|
from tool import ToolModalCommand
|
|
|
|
from .setup import *
|
|
|
|
|
|
class PageCommand(PluginPageBase):
|
|
|
|
def __init__(self, P, parent):
|
|
super(PageCommand, self).__init__(P, parent, name='command')
|
|
self.db_default = {
|
|
f'{self.parent.name}_{self.name}_recent': '',
|
|
}
|
|
|
|
def process_menu(self, req):
|
|
arg = self.P.ModelSetting.to_dict()
|
|
arg['path_data'] = F.config['path_data']
|
|
return render_template(f'{self.P.package_name}_{self.parent.name}_{self.name}.html', arg=arg)
|
|
|
|
|
|
|
|
def process_command(self, command, arg1, arg2, arg3, req):
|
|
ret = {'ret':'success'}
|
|
if command == 'foreground_command':
|
|
P.ModelSetting.set(f'{self.parent.name}_{self.name}_recent', arg1)
|
|
self.__foreground_execute(arg1, arg1.split(' '))
|
|
|
|
return jsonify('')
|
|
elif command == 'job_new':
|
|
db_item = ModelCommand.job_new(arg1)
|
|
ret['msg'] = f"ID:{db_item.id} 작업을 생성하였습니다."
|
|
elif command == 'job_list':
|
|
ret['data'] = ModelCommand.job_list()
|
|
|
|
elif command == 'job_save':
|
|
data = P.logic.arg_to_dict(arg1)
|
|
db_item = ModelCommand.get_by_id(data['job_id'])
|
|
db_item.set_command(data['job_command'])
|
|
db_item.args = data['job_command_args']
|
|
db_item.description = data['job_description']
|
|
db_item.schedule_mode = data['job_schedule_mode']
|
|
db_item.schedule_auto_start = (data.get('job_schedule_auto_start', 'False') == 'True')
|
|
db_item.schedule_interval = data.get('job_schedule_interval', '')
|
|
db_item.save()
|
|
ret['msg'] = '수정하였습니다.'
|
|
elif command == 'job_remove':
|
|
if ModelCommand.delete_by_id(arg1):
|
|
ret['msg'] = '삭제하였습니다.'
|
|
else:
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = '삭제에 실패하였습니다.'
|
|
elif command == 'job_fore_execute':
|
|
db_item = ModelCommand.get_by_id(arg1)
|
|
cmd = (db_item.command + ' ' + db_item.args).strip()
|
|
self.__foreground_execute(f"Command ID: {db_item.id}", cmd.split(' '), db_item.id)
|
|
elif command == 'job_back_execute':
|
|
self.execute_thread_start(arg1)
|
|
ret['msg'] = "실행 요청을 하였습니다.<br>로그를 확인하세요."
|
|
elif command == 'job_log':
|
|
ret['filename'] = f"command_{arg1}.log"
|
|
if os.path.exists(os.path.join(F.config['path_data'], 'log', f"command_{arg1}.log")) == False:
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = "로그 파일이 없습니다."
|
|
elif command == 'task_sched':
|
|
job_id = req.form['arg1']
|
|
flag = (req.form['arg2'] == 'true')
|
|
scheduler_id = f'command_{job_id}'
|
|
if flag and F.scheduler.is_include(scheduler_id):
|
|
ret['msg'] = '이미 스케쥴러에 등록되어 있습니다.'
|
|
elif flag and F.scheduler.is_include(scheduler_id) == False:
|
|
result = self.__sched_add(job_id)
|
|
ret['msg'] = '스케쥴러에 추가하였습니다.'
|
|
elif flag == False and scheduler.is_include(scheduler_id):
|
|
result = scheduler.remove_job(scheduler_id)
|
|
ret['msg'] = '스케쥴링 취소'
|
|
elif flag == False and scheduler.is_include(scheduler_id) == False:
|
|
ret['msg'] = '등록되어 있지 않습니다.'
|
|
elif command == 'job_process_stop':
|
|
process_ins = SupportSubprocess.get_instance_by_call_id(f"command_{arg1}")
|
|
if process_ins == None:
|
|
ret['msg'] = "실행중인 Process가 없습니다."
|
|
else:
|
|
process_ins.process_close()
|
|
ret['msg'] = "Process를 중지하였습니다."
|
|
return jsonify(ret)
|
|
|
|
|
|
def __foreground_execute(self, title, command, job_id=None):
|
|
|
|
if command[0] != 'LOAD':
|
|
ToolModalCommand.start(title, [command])
|
|
else:
|
|
F.socketio.emit("command_modal_show", title, namespace='/framework', broadcast=True)
|
|
def start_communicate_load(load_log_list):
|
|
def func():
|
|
while True:
|
|
logs = load_log_list.getvalue()
|
|
load_log_list.truncate(0)
|
|
if logs:
|
|
P.logger.error(logs)
|
|
F.socketio.emit("command_modal_add_text", logs.strip() + '\n', namespace='/framework', broadcast=True)
|
|
if logs == '<<END>>':
|
|
break
|
|
time.sleep(0.3)
|
|
th = threading.Thread(target=func)
|
|
th.setDaemon(True)
|
|
th.start()
|
|
|
|
def func():
|
|
import io
|
|
from contextlib import redirect_stdout
|
|
load_log_list = io.StringIO()
|
|
with redirect_stdout(load_log_list):
|
|
start_communicate_load(load_log_list)
|
|
if job_id is not None:
|
|
command_logger = get_logger(f'command_{job_id}')
|
|
else:
|
|
command_logger = P.logger
|
|
self.__module_load(command, logger=command_logger)
|
|
load_log_list.write("<<END>>")
|
|
load_log_list.flush()
|
|
th = threading.Thread(target=func, args=())
|
|
th.setDaemon(True)
|
|
th.start()
|
|
return 'success'
|
|
|
|
|
|
def __module_load(self, command, **kwargs):
|
|
try:
|
|
python_filename = command[1]
|
|
python_sys_path = os.path.dirname(python_filename)
|
|
if python_sys_path not in sys.path:
|
|
sys.path.append(python_sys_path)
|
|
module_name = os.path.basename(python_filename).split('.py')[0]
|
|
if module_name not in sys.path:
|
|
sys.path.append(module_name)
|
|
import importlib
|
|
mod = importlib.import_module(module_name)
|
|
importlib.reload(mod)
|
|
args = command
|
|
mod_command_load = getattr(mod, 'main')
|
|
if mod_command_load:
|
|
ret = mod_command_load(*args, **kwargs)
|
|
return ret
|
|
except Exception as e:
|
|
P.logger.error(f'Exception:{str(e)}')
|
|
P.logger.error(traceback.format_exc())
|
|
|
|
|
|
|
|
def execute_thread_start(self, job_id):
|
|
th = threading.Thread(target=self.execute_thread_function_by_job_id, args=(job_id,))
|
|
th.setDaemon(True)
|
|
th.start()
|
|
|
|
|
|
def execute_thread_function_by_job_id(self, *args, **kwargs):
|
|
P.logger.error(d(args))
|
|
P.logger.error(d(kwargs))
|
|
db_item = ModelCommand.get_by_id(args[0])
|
|
kwargs['id'] = args[0]
|
|
self.execute_thread_function((db_item.command + ' ' + db_item.args).strip(), **kwargs)
|
|
|
|
|
|
def execute_thread_function(self, command, **kwargs):
|
|
try:
|
|
cmd = command.split(' ')
|
|
|
|
if cmd[0] == 'LOAD':
|
|
command_logger = F.get_logger(f"command_{kwargs['id']}")
|
|
kwargs['logger'] = command_logger
|
|
return self.__module_load(cmd, **kwargs)
|
|
else:
|
|
class LogReceiver:
|
|
def __init__(self, logger):
|
|
self.logger = logger
|
|
|
|
def stdout_callback(self, mode, text):
|
|
if mode == 'log':
|
|
self.logger.debug(text)
|
|
else:
|
|
self.logger.debug(mode)
|
|
command_logger = F.get_logger(f"command_{kwargs['id']}", from_command=True)
|
|
receiver = LogReceiver(command_logger)
|
|
process = SupportSubprocess(cmd, stdout_callback=receiver.stdout_callback, call_id=f"command_{kwargs['id']}")
|
|
process.start()
|
|
except Exception as e:
|
|
P.logger.error(f'Exception:{str(e)}')
|
|
P.logger.error(traceback.format_exc())
|
|
|
|
|
|
def plugin_load(self):
|
|
def plugin_load_thread():
|
|
try:
|
|
db_items = ModelCommand.get_list()
|
|
for db_item in db_items:
|
|
if db_item.schedule_mode == 'startup':
|
|
self.execute_thread_start(db_item.id)
|
|
elif db_item.schedule_mode == 'scheduler' and db_item.schedule_auto_start:
|
|
self.__sched_add(db_item.id, db_item=db_item)
|
|
except Exception as exception:
|
|
logger.error('Exception:%s', exception)
|
|
logger.error(traceback.format_exc())
|
|
try:
|
|
th = threading.Thread(target=plugin_load_thread)
|
|
th.setDaemon(True)
|
|
th.start()
|
|
except Exception as e:
|
|
P.logger.error(f'Exception:{str(e)}')
|
|
P.logger.error(traceback.format_exc())
|
|
|
|
|
|
def __sched_add(self, id, db_item=None):
|
|
try:
|
|
if db_item is None:
|
|
db_item = ModelCommand.get_by_id(id)
|
|
job_id = f"command_{db_item.id}"
|
|
if scheduler.is_include(job_id):
|
|
return
|
|
job = Job(self.P.package_name, job_id, db_item.schedule_interval, self.execute_thread_function_by_job_id, db_item.description, args=db_item.id)
|
|
scheduler.add_job_instance(job)
|
|
return True
|
|
except Exception as e:
|
|
P.logger.error(f'Exception:{str(e)}')
|
|
P.logger.error(traceback.format_exc())
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ModelCommand(ModelBase):
|
|
__tablename__ = 'command_job'
|
|
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
|
|
__bind_key__ = 'system'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
command = db.Column(db.String)
|
|
filepath = db.Column(db.String)
|
|
args = db.Column(db.String)
|
|
description = db.Column(db.String)
|
|
schedule_mode = db.Column(db.String) # none, startup, scheduler
|
|
schedule_auto_start = db.Column(db.Boolean) # 시작시 스케쥴링 등록
|
|
schedule_interval = db.Column(db.String) # 주기
|
|
|
|
|
|
def __init__(self, command):
|
|
self.args = ''
|
|
self.description = ''
|
|
self.schedule_mode = 'none'
|
|
self.schedule_auto_start = False
|
|
self.schedule_interval = ''
|
|
self.set_command(command)
|
|
|
|
def set_command(self, command):
|
|
self.command = command
|
|
tmp = command.split(' ')
|
|
for t in tmp:
|
|
for ext in ['.py', '.sh', '.bat']:
|
|
if t.endswith(ext):
|
|
self.filepath = t
|
|
break
|
|
|
|
|
|
@classmethod
|
|
def job_new(cls, command):
|
|
item = ModelCommand(command)
|
|
return item.save()
|
|
|
|
|
|
@classmethod
|
|
def job_list(cls):
|
|
try:
|
|
data = cls.get_list(by_dict=True)
|
|
for item in data:
|
|
item['scheduler_is_include'] = F.scheduler.is_include(f"command_{item['id']}")
|
|
item['scheduler_is_running'] = F.scheduler.is_running(f"command_{item['id']}")
|
|
|
|
item['process'] = (SupportSubprocess.get_instance_by_call_id(f"command_{item['id']}") != None)
|
|
|
|
return data
|
|
except Exception as exception:
|
|
logger.error('Exception:%s', exception)
|
|
logger.error(traceback.format_exc())
|