This commit is contained in:
flaskfarm
2022-10-12 01:32:51 +09:00
parent 5b6719c018
commit 751adbbedc
18 changed files with 548 additions and 96 deletions

3
.gitignore vendored
View File

@@ -140,4 +140,5 @@ export.sh
run.sh run.sh
pre_start.sh pre_start.sh
*.code-workspace *.code-workspace
false/ false
*copy.py

View File

@@ -61,9 +61,7 @@ class Framework:
self.__make_default_dir() self.__make_default_dir()
self.logger = self.get_logger(__package__) self.logger = self.get_logger(__package__)
import support
from support import set_logger
set_logger(self.logger)
self.__prepare_starting() self.__prepare_starting()
self.app = Flask(__name__) self.app = Flask(__name__)
@@ -462,6 +460,8 @@ class Framework:
def __app_close(self): def __app_close(self):
try: try:
from support import SupportSubprocess
SupportSubprocess.all_process_close()
from .init_plugin import PluginManager from .init_plugin import PluginManager
PluginManager.plugin_unload() PluginManager.plugin_unload()
self.socketio.stop() self.socketio.stop()

View File

@@ -40,6 +40,15 @@ def global_ajax(sub):
return jsonify(ret) return jsonify(ret)
except: except:
return jsonify({'ret':False}) return jsonify({'ret':False})
elif sub == 'command_modal_hide':
from tool import ToolModalCommand
ToolModalCommand.modal_close()
return jsonify('')
elif sub == 'command_modal_input':
from tool import ToolModalCommand
cmd = request.form['cmd']
ToolModalCommand.input_command(cmd)
return jsonify('')

View File

@@ -21,7 +21,10 @@ $(window).on("load resize", function (event) {
$body.css("padding-top", $navbar.outerHeight()); $body.css("padding-top", $navbar.outerHeight());
}); });
$('#command_modal').on('show.bs.modal', function (event) {
console.log('111111111')
console.log(event);
})
/////////////////////////////////////// ///////////////////////////////////////
// 사용 미확인 // 사용 미확인

View File

@@ -29,19 +29,6 @@ frameSocket.on('modal', function(data){
m_modal(data.data, data.title, false); m_modal(data.data, data.title, false);
}); });
frameSocket.on('command_modal_add_text', function(data){
document.getElementById("command_modal_textarea").innerHTML += data ;
document.getElementById("command_modal_textarea").scrollTop = document.getElementById("command_modal_textarea").scrollHeight;
});
frameSocket.on('command_modal_show', function(data){
command_modal_show(data)
});
frameSocket.on('command_modal_clear', function(data){
document.getElementById("command_modal_textarea").innerHTML = ""
});
frameSocket.on('loading_hide', function(data){ frameSocket.on('loading_hide', function(data){
$('#loading').hide(); $('#loading').hide();
}); });
@@ -52,6 +39,16 @@ frameSocket.on('refresh', function(data){
}); });
$('#command_modal').on('hide.bs.modal', function (event) {
$.ajax({
url: `/global/ajax/command_modal_hide`,
type: 'POST',
cache: false,
data: {},
dataType: 'json'
});
});
@@ -250,3 +247,50 @@ let listdir = (path = '/', only_dir = true) => {
// 파일 선택 모달 End // 파일 선택 모달 End
/////////////////////////////////////// ///////////////////////////////////////
///////////////////////////////////////
// Command MODAL
///////////////////////////////////////
frameSocket.on('command_modal_add_text', function(data){
document.getElementById("command_modal_textarea").innerHTML += data ;
document.getElementById("command_modal_textarea").scrollTop = document.getElementById("command_modal_textarea").scrollHeight;
});
frameSocket.on('command_modal_input_disable', function(data){
$('#command_modal_input').attr('disabled', true);
});
frameSocket.on('command_modal_show', function(data){
command_modal_show(data)
});
frameSocket.on('command_modal_clear', function(data){
document.getElementById("command_modal_textarea").innerHTML = ""
});
function command_modal_show(title) {
ClientHeight = window.innerHeight
document.getElementById("command_modal_title").innerHTML = title
$("#command_modal").height(ClientHeight+50);
$("#command_modal_textarea").height(ClientHeight-380);
$("#command_modal").modal({backdrop: 'static', keyboard: false}, 'show');
$('#command_modal_input').attr('disabled', false);
}
$("body").on('click', '#command_modal_input_btn', function(e) {
e.preventDefault();
$.ajax({
url: '/global/ajax/command_modal_input',
type: "POST",
cache: false,
data: {cmd:$('#command_modal_input').val()},
dataType: "json",
success: function (ret) {
$('#command_modal_input').val('');
}
});
});
///////////////////////////////////////

View File

@@ -66,13 +66,7 @@ $("body").on('click', '#global_downloader_add_btn', function(e){
}); });
}); });
function command_modal_show(title) {
ClientHeight = window.innerHeight
document.getElementById("command_modal_title").innerHTML = title
$("#command_modal").height(ClientHeight-100);
$("#command_modal_textarea").height(ClientHeight-380);
$("#command_modal").modal();
}
// 토렌트 프로그램에 다운로드 추가할 결과를 보여주는 // 토렌트 프로그램에 다운로드 추가할 결과를 보여주는

View File

@@ -116,8 +116,14 @@
<!--command modal--> <!--command modal-->
{{ macros.m_modal_start('command_modal', '', 'modal-lg') }} {{ macros.m_modal_start('command_modal', '', 'modal-lg') }}
<div> <div>
<textarea id="command_modal_textarea" class="col-md-12" rows="30" disabled style="visibility:visible"></textarea> <textarea id="command_modal_textarea" class="col-md-12" rows="30" disabled style="background-color:#ffffff;visibility:visible"></textarea>
</div> </div>
<form id='command_modal_form' name='command_form'>
<div class="form-inline input-group">
<input id="command_modal_input" name="command_modal_input" type="text" class="form-control form-control-sm" placeholder="">
<button id="command_modal_input_btn" class="btn btn-sm btn-outline-success">입력</button>
</div>
</form>
{{ macros.m_modal_end() }} {{ macros.m_modal_end() }}
<!--command modal end--> <!--command modal end-->
<!-- Modal end --> <!-- Modal end -->

View File

@@ -8,32 +8,10 @@ def d(data):
else: else:
return str(data) return str(data)
def load():
from .base.aes import SupportAES
from .base.discord import SupportDiscord
from .base.file import SupportFile
from .base.process import SupportProcess
from .base.string import SupportString
from .base.subprocess import SupportSubprocess
from .base.telegram import SupportTelegram
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
default_headers, pt)
from .base.yaml import SupportYaml
import os
logger = None
if os.environ.get('FF') == 'true':
def set_logger(l):
global logger
logger = l
else:
from .logger import get_logger from .logger import get_logger
logger = get_logger() logger = get_logger()
from .base.aes import SupportAES from .base.aes import SupportAES
from .base.discord import SupportDiscord from .base.discord import SupportDiscord
from .base.file import SupportFile from .base.file import SupportFile
@@ -44,7 +22,3 @@ from .base.telegram import SupportTelegram
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil, from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
default_headers, pt) default_headers, pt)
from .base.yaml import SupportYaml from .base.yaml import SupportYaml
# 일반 cli 사용 겸용이다.
# set_logger 로 인한 진입이 아니고 import가 되면 기본 경로로 로그파일을
# 생성하기 때문에, set_logger 전에 import가 되지 않도록 주의.

View File

@@ -1,12 +1,4 @@
from support import d, logger from support import d, logger
from .aes import SupportAES import support
from .discord import SupportDiscord logger = support.logger
from .ffmpeg import SupportFfmpeg
from .file import SupportFile
from .image import SupportImage
from .process import SupportProcess
from .string import SupportString
from .util import (AlchemyEncoder, SingletonClass, SupportUtil,
default_headers, pt)
from .yaml import SupportYaml

View File

@@ -1,23 +1,29 @@
import io
import json import json
import os import os
import platform import platform
import queue
import subprocess import subprocess
import threading
import time
import traceback import traceback
from . import logger from . import logger
def demote(user_uid, user_gid):
def result():
os.setgid(user_gid)
os.setuid(user_uid)
return result
class SupportSubprocess(object): class SupportSubprocess(object):
# 2021-10-25 # 2021-10-25
# timeout 적용 # timeout 적용
@classmethod @classmethod
def execute_command_return(cls, command, format=None, force_log=False, shell=False, env=None, timeout=None, uid=0, gid=0): def execute_command_return(cls, command, format=None, force_log=False, shell=False, env=None, timeout=None, uid=0, gid=0):
def demote(user_uid, user_gid):
def result():
os.setgid(user_gid)
os.setuid(user_uid)
return result
try: try:
if platform.system() == 'Windows': if platform.system() == 'Windows':
tmp = [] tmp = []
@@ -74,3 +80,162 @@ class SupportSubprocess(object):
logger.error('Exception:%s', exception) logger.error('Exception:%s', exception)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
logger.error('command : %s', command) logger.error('command : %s', command)
instance_list = []
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=0, gid=0, stdout_callback=None):
self.command = command
self.print_log = print_log
self.shell = shell
self.env = env
self.timeout = timeout
self.uid = uid
self.gid = gid
self.stdout_callback = stdout_callback
self.process = None
self.stdout_queue = None
def start(self, join=True):
try:
self.thread = threading.Thread(target=self.execute_thread_function, args=())
self.thread.setDaemon(True)
self.thread.start()
if join:
self.thread.join()
except Exception as e:
logger.error(f'Exception:{str(e)}')
logger.error(traceback.format_exc())
def execute_thread_function(self):
try:
if platform.system() == 'Windows':
tmp = []
if type(self.command) == type([]):
for x in self.command:
if x.find(' ') == -1:
tmp.append(x)
else:
tmp.append(f'"{x}"')
self.command = ' '.join(tmp)
iter_arg = ''
if platform.system() == 'Windows':
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8')
else:
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, preexec_fn=demote(self.uid, self.gid), encoding='utf8')
SupportSubprocess.instance_list.append(self)
self.start_communicate()
self.start_send_callback()
if self.process is not None:
self.process.wait()
#logger.info(f"{self.command} 정상 종료")
if self.stdout_queue != None:
self.stdout_queue.put('<END>')
except Exception as e:
logger.error(f'Exception:{str(e)}')
logger.error(traceback.format_exc())
def start_communicate(self):
self.stdout_queue = queue.Queue()
sout = io.open(self.process.stdout.fileno(), 'rb', closefd=False)
def Pump(stream):
_queue = queue.Queue()
def rdr():
while True:
if self.process == None:
break
buf = self.process.stdout.read(1)
if buf:
_queue.put( buf )
else:
_queue.put( None )
break
_queue.put( None )
time.sleep(1)
def clct():
active = True
while active:
r = _queue.get()
if r is None:
break
try:
while True:
r1 = _queue.get(timeout=0.005)
if r1 is None:
active = False
break
else:
r += r1
except:
pass
if r is not None:
if self.stdout_queue != None:
self.stdout_queue.put(r)
if self.stdout_queue != None: # 사용자 중지
self.stdout_queue.put('<END>')
for tgt in [rdr, clct]:
th = threading.Thread(target=tgt)
th.setDaemon(True)
th.start()
Pump(sout)
def start_send_callback(self):
def func():
while self.stdout_queue:
line = self.stdout_queue.get()
if line == '<END>':
if self.stdout_callback != None:
self.stdout_callback('end', None)
break
else:
if self.stdout_callback != None:
self.stdout_callback('log', line)
self.send_to_ui_thread = None
self.stdout_queue = None
self.process = None
th = threading.Thread(target=func, args=())
th.setDaemon(True)
th.start()
def process_close(self):
try:
if self.process is not None and self.process.poll() is None:
#import psutil
#process = psutil.Process(instance.process.pid)
#for proc in instance.process.children(recursive=True):
# proc.kill()
self.process.kill()
except Exception as e:
logger.error(f'Exception:{str(e)}')
logger.error(traceback.format_exc())
finally:
try:
self.stdout_queue = None
self.process.kill()
except: pass
def input_command(self, cmd):
if self.process != None:
self.process.stdin.write(f'{cmd}\n')
self.process.stdin.flush()
@classmethod
def all_process_close(cls):
for instance in cls.instance_list:
instance.process_close()
cls.instance_list = []

View File

@@ -1,6 +1,11 @@
import os, sys, logging, logging.handlers import logging
import logging.handlers
import os
import sys
from datetime import datetime from datetime import datetime
from pytz import timezone, utc from pytz import timezone, utc
""" """
ConsoleColor.Black => "\x1B[30m", ConsoleColor.Black => "\x1B[30m",
ConsoleColor.DarkRed => "\x1B[31m", ConsoleColor.DarkRed => "\x1B[31m",
@@ -48,6 +53,8 @@ class CustomFormatter(logging.Formatter):
def get_logger(name=None, log_path=None): def get_logger(name=None, log_path=None):
if os.environ.get('FF') == 'true':
name = 'framework'
if name == None: if name == None:
name = sys.argv[0].rsplit('.', 1)[0] name = sys.argv[0].rsplit('.', 1)[0]
logger = logging.getLogger(name) logger = logging.getLogger(name)

View File

@@ -2,6 +2,7 @@ import random
import string import string
from support import SupportDiscord, SupportFile, SupportTelegram from support import SupportDiscord, SupportFile, SupportTelegram
from tool.modal_command import ToolModalCommand
from .setup import * from .setup import *
@@ -33,6 +34,10 @@ class ModuleSetting(PluginModuleBase):
'notify_advaned_use' : 'False', 'notify_advaned_use' : 'False',
'notify.yaml': '', #직접 사용하지 않으나 저장 편의상. 'notify.yaml': '', #직접 사용하지 않으나 저장 편의상.
'command_text': '', 'command_text': '',
'celery_start_by_web': 'False', #웹 실행시 celery 실행
'celery_start_command': "celery -A flaskfarm.main.celery worker --loglevel=info --pool=gevent --concurrency=2 --config_filepath={F.config['config_filepath']} --running_type=native",
} }
def __init__(self, P): def __init__(self, P):
@@ -57,6 +62,9 @@ class ModuleSetting(PluginModuleBase):
elif page == 'notify': elif page == 'notify':
arg['notify_yaml_filepath'] = F.config['notify_yaml_filepath'] arg['notify_yaml_filepath'] = F.config['notify_yaml_filepath']
arg['notify.yaml'] = SupportFile.read_file(arg['notify_yaml_filepath']) arg['notify.yaml'] = SupportFile.read_file(arg['notify_yaml_filepath'])
elif page == 'celery':
arg['use_celery'] = F.config['use_celery']
arg['running_type'] = F.config['running_type']
return render_template(f'{__package__}_{name}_{page}.html', arg=arg) return render_template(f'{__package__}_{name}_{page}.html', arg=arg)
except Exception as e: except Exception as e:
@@ -113,12 +121,21 @@ class ModuleSetting(PluginModuleBase):
elif command == 'command_run': elif command == 'command_run':
ret['msg'] = arg1 ret['msg'] = arg1
pass pass
elif command == 'celery_execute':
tmp = arg1.replace("{F.config['config_filepath']}", F.config['config_filepath']).replace('{F.config["config_filepath"]}', F.config['config_filepath'])
cmd = [
['msg', f'명령 : {tmp}'],
['msg', ''],
tmp.split(' '),
]
ToolModalCommand.start("Celery 실행", cmd)
return jsonify(ret) return jsonify(ret)
def plugin_load(self): def plugin_load(self):
try: try:
if F.config['run_flask']: if F.config['run_flask'] == False:
return
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}") F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}") F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
@@ -148,6 +165,8 @@ class ModuleSetting(PluginModuleBase):
P.logger.error(f'Exception:{str(e)}') P.logger.error(f'Exception:{str(e)}')
P.logger.error(traceback.format_exc()) P.logger.error(traceback.format_exc())
def plugin_unload(self):
ToolModalCommand.process_close()
def setting_save_after(self, change_list): def setting_save_after(self, change_list):
if 'theme' in change_list: if 'theme' in change_list:

View File

@@ -11,7 +11,7 @@ class ModuleTool(PluginModuleBase):
def __init__(self, P): def __init__(self, P):
super(ModuleTool, self).__init__(P, name=name, first_menu='celery') super(ModuleTool, self).__init__(P, name=name, first_menu='upload')
def process_menu(self, page, req): def process_menu(self, page, req):

View File

@@ -12,6 +12,7 @@ __menu = {
{'uri': 'menu', 'name': '메뉴 구성'}, {'uri': 'menu', 'name': '메뉴 구성'},
{'uri': 'config', 'name': 'config.yaml 파일'}, {'uri': 'config', 'name': 'config.yaml 파일'},
{'uri': 'export', 'name': 'export.sh 파일'}, {'uri': 'export', 'name': 'export.sh 파일'},
{'uri': 'celery', 'name': '비동기 작업(celery)'},
{'uri': 'notify', 'name': '알림'}, {'uri': 'notify', 'name': '알림'},
], ],
@@ -22,6 +23,7 @@ __menu = {
'list': [ 'list': [
{'uri': 'setting', 'name': '설정'}, {'uri': 'setting', 'name': '설정'},
{'uri': 'list', 'name': '로딩 플러그인'}, {'uri': 'list', 'name': '로딩 플러그인'},
{'uri': 'all', 'name': '플러그인 목록'},
], ],
}, },
{ {
@@ -29,7 +31,6 @@ __menu = {
'name': '시스템 툴', 'name': '시스템 툴',
'list': [ 'list': [
{'uri': 'upload', 'name': '업로드'}, {'uri': 'upload', 'name': '업로드'},
{'uri': 'celery', 'name': 'celery 테스트'},
{'uri': 'python', 'name': 'Python'}, {'uri': 'python', 'name': 'Python'},
{'uri': 'db', 'name': 'DB'}, {'uri': 'db', 'name': 'DB'},
{'uri': 'crypt', 'name': '암호화'}, {'uri': 'crypt', 'name': '암호화'},

View File

@@ -0,0 +1,123 @@
{% extends "base.html" %}
{% block content %}
<div>
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장'], ['celery_test_btn', 'Celery 테스트']])}}
{{ macros.m_row_start('5') }}
{{ macros.m_row_end() }}
{{ macros.m_hr() }}
<div class="tab-content" id="nav-tabContent">
{{ macros.info_text('use_celery', 'use_celery 값', arg['use_celery']) }}
{{ macros.info_text('running_type', 'running_type 값', arg['running_type']) }}
{{ macros.info_text('_tmp', '설명', "Docker는 celery가 서비스로 동작하기 때문에 설정이 불필요하며 '테스트' 버튼으로 작동 여부 확인만 가능합니다.", desc=['','native로 동작하는 경우 celery 실행을 따로 하지 않고 한번에 실행하기 위한 설정', 'Redis는 설정된 Port로 동작중인 상태여야 함.']) }}
{{ macros.m_hr() }}
<form id='setting' name='setting'>
{{ macros.setting_checkbox('celery_start_by_web', '시작시 celery 실행', value=arg['celery_start_by_web']) }}
{{ macros.setting_input_textarea('celery_start_command', 'celery 실행 명령', desc=['',
'예: celery -A flaskfarm.main.celery worker --loglevel=info --pool=gevent --concurrency=2 --config_filepath="config.yaml파일경로" --running_type=native',
'패키지로 실행시 : -A flaskfarm.main.celery',
'GIT 소스로 실행시 : -A main.celery (현재 작업폴더가 flaskfarm)',
'Linux는 사용자에 따라 export C_FORCE_ROOT=true 필요'
],
value=arg['celery_start_command'], row='5') }}
</form>
{{ macros.setting_buttons([['celery_excute_btn', '실행 테스트']]) }}
</div>
</div>
<script type="text/javascript">
$("body").on('click', '#celery_excute_btn', function(e){
e.preventDefault();
globalSendCommand('celery_execute', $('#celery_start_command').val());
});
$("body").on('click', '#shutdown_btn', function(e){
e.preventDefault();
shutdown_confirm();
});
$("body").on('click', '#celery_test_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/celery_test',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret.ret == 'success') {
$.notify('<strong>'+ ret.data+'</strong>', {
type: 'success'
});
} else if (ret.ret == 'timeout' || ret.ret == 'no_celery') {
$.notify('<strong>'+ ret.data+'</strong>', {
type: 'warning'
});
}
//m_modal(ret)
}
});
});
$("body").on('click', '#ps_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/ps',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
}
});
});
$("body").on('click', '#worker_start_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/worker_start',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret.ret == 'success') {
$.notify('<strong>'+ ret.data+'</strong>', {
type: 'success'
});
} else if (ret.ret == 'timeout' || ret.ret == 'no_celery' || ret.ret == 'not_registered') {
$.notify('<strong>'+ ret.data+'</strong>', {
type: 'warning'
});
}
//m_modal(ret)
}
});
});
</script>
{% endblock %}

View File

@@ -13,7 +13,7 @@
<form id='setting' name='setting'> <form id='setting' name='setting'>
<div class="tab-content" id="nav-tabContent"> <div class="tab-content" id="nav-tabContent">
{{ macros.m_tab_content_start('normal', true) }} {{ macros.m_tab_content_start('normal', true) }}
{{ macros.setting_checkbox('tool_crypt_use_user_key', '암호화 키 본인키 사용', value=arg['tool_crypt_use_user_key'], desc=['On : 본인 키 사용', '주의) 변경 후 일반설정-인증-로그인 암호를 새로 저장해야 합니다.', 'Off : 앱 고정 키 사용']) }} {{ macros.setting_checkbox('tool_crypt_use_user_key', '암호화 키 직접 입력', value=arg['tool_crypt_use_user_key'], desc=['On : 본인 키 사용', '주의) 변경 후 일반설정-인증-로그인 암호를 새로 저장해야 합니다.', 'Off : 앱 고정 키 사용']) }}
<div id="tool_crypt_use_user_key_div" class="collapse"> <div id="tool_crypt_use_user_key_div" class="collapse">
{{ macros.setting_input_text('tool_crypt_user_key', '암호화 키', value=arg['tool_crypt_user_key'], desc=['16진수(숫자, a~e)로 이루어진 32글자. 미만시 앞을 0으로 채움. 초과시 뒤에 무시']) }} {{ macros.setting_input_text('tool_crypt_user_key', '암호화 키', value=arg['tool_crypt_user_key'], desc=['16진수(숫자, a~e)로 이루어진 32글자. 미만시 앞을 0으로 채움. 초과시 뒤에 무시']) }}
</div> </div>

View File

@@ -1,3 +1,4 @@
from framework import logger from framework import logger
from .modal_command import ToolModalCommand
from .notify import ToolNotify from .notify import ToolNotify

113
lib/tool/modal_command.py Normal file
View File

@@ -0,0 +1,113 @@
import os
import threading
import time
import traceback
from framework import F
from support import SupportSubprocess
class ToolModalCommand(object):
__thread = None
__title = None
__commands = None
__clear = None
__show_modal = None
__wait = None
__ss_process = None
__abort = None
@classmethod
def start(cls, title, commands, clear=True, wait=False, show_modal=True):
if cls.__thread != None:
cls.__abort = True
cls.__thread = None
if cls.__ss_process != None:
cls.__ss_process.process_close()
cls.__ss_process = None
cls.__title = title
cls.__commands = commands
cls.__clear = clear
cls.__wait = wait
cls.__show_modal = show_modal
cls.__thread = None
cls.__abort = False
cls.__start()
@classmethod
def __start(cls):
try:
if cls.__show_modal:
if cls.__clear:
F.socketio.emit("command_modal_clear", None, namespace='/framework', broadcast=True)
cls.__thread = threading.Thread(target=cls.__execute_thread_function, args=())
cls.__thread.setDaemon(True)
cls.__thread.start()
if cls.__wait:
time.sleep(1)
cls.__thread.join()
except Exception as exception:
F.logger.error('Exception:%s', exception)
F.logger.error(traceback.format_exc())
@classmethod
def __execute_thread_function(cls):
try:
if cls.__show_modal:
F.socketio.emit("command_modal_show", cls.__title, namespace='/framework', broadcast=True)
F.socketio.emit("loading_hide", None, namespace='/framework', broadcast=True)
for command in cls.__commands:
if cls.__abort:
return
if command[0] == 'msg':
if cls.__show_modal:
F.socketio.emit("command_modal_add_text", '%s\n\n' % command[1], namespace='/framework', broadcast=True)
elif command[0] == 'system':
if cls.__show_modal:
F.socketio.emit("command_modal_add_text", '$ %s\n\n' % command[1], namespace='/framework', broadcast=True)
os.system(command[1])
else:
#show_command = True
#if command[0] == 'hide':
# show_command = False
# command = command[1:]
cls.__ss_process = SupportSubprocess(command, stdout_callback=cls.process_callback)
cls.__ss_process.start()
cls.__ss_process.process_close()
cls.__ss_process = None
time.sleep(1)
except Exception as exception:
if cls.__show_modal:
F.socketio.emit("command_modal_show", cls.__title, namespace='/framework', broadcast=True)
F.socketio.emit("command_modal_add_text", str(exception), namespace='/framework', broadcast=True)
F.socketio.emit("command_modal_add_text", str(traceback.format_exc()), namespace='/framework', broadcast=True)
@classmethod
def process_callback(cls, mode, text):
if cls.__show_modal == False:
return
if mode == 'end':
F.socketio.emit("command_modal_add_text", "\n\n<<프로세스 종료>>", namespace='/framework', broadcast=True)
F.socketio.emit("command_modal_input_disable", "", namespace='/framework', broadcast=True)
else:
F.socketio.emit("command_modal_add_text", text, namespace='/framework', broadcast=True)
@classmethod
def modal_close(cls):
if cls.__thread != None:
cls.__abort = True
if cls.__ss_process != None:
cls.__ss_process.process_close()
cls.__ss_process = None
@classmethod
def input_command(cls, cmd):
if cls.__ss_process != None:
cls.__ss_process.input_command(cmd)