Files
youtube-dl/lib/system/page_command.py
flaskfarm 56419a8355 update
2022-10-19 19:50:38 +09:00

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())