456 lines
21 KiB
Python
456 lines
21 KiB
Python
import os
|
|
import platform
|
|
import shutil
|
|
import sys
|
|
import threading
|
|
import traceback
|
|
import zipfile
|
|
|
|
import requests
|
|
from support import SupportFile, SupportSubprocess, SupportYaml
|
|
|
|
from framework import F
|
|
|
|
|
|
class PluginManager:
|
|
plugin_list = {}
|
|
plugin_menus = {}
|
|
setting_menus = []
|
|
all_package_list = {}
|
|
|
|
|
|
@classmethod
|
|
def get_plugin_name_list(cls):
|
|
#if not app.config['config']['auth_status']:
|
|
# return
|
|
"""
|
|
plugin_path = os.path.join(frame.config['path_app'], 'plugins')
|
|
sys.path.insert(0, plugin_path)
|
|
from system import SystemModelSetting
|
|
plugins = os.listdir(plugin_path)
|
|
"""
|
|
plugins = []
|
|
|
|
|
|
#2019-07-17
|
|
|
|
try:
|
|
plugin_path = os.path.join(F.config['path_data'], 'plugins')
|
|
if os.path.exists(plugin_path) == True and os.path.isdir(plugin_path) == True:
|
|
sys.path.insert(1, plugin_path)
|
|
tmps = os.listdir(plugin_path)
|
|
add_plugin_list = []
|
|
for t in tmps:
|
|
if not t.startswith('_') and os.path.isdir(os.path.join(plugin_path, t)):
|
|
add_plugin_list.append(t)
|
|
cls.all_package_list[t] = {'pos':'normal', 'path':os.path.join(plugin_path, t), 'loading':(F.config.get('plugin_loading_only_devpath', None) != True)}
|
|
|
|
plugins = plugins + add_plugin_list
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
|
|
if F.config.get('plugin_loading_only_devpath', None) == True:
|
|
plugins = []
|
|
|
|
# 2018-09-04
|
|
try:
|
|
#plugin_path = F.SystemModelSetting.get('plugin_dev_path')
|
|
plugin_path = F.config['path_dev']
|
|
if plugin_path != None and plugin_path != '':
|
|
if os.path.exists(plugin_path):
|
|
sys.path.insert(0, plugin_path)
|
|
tmps = os.listdir(plugin_path)
|
|
add_plugin_list = []
|
|
for t in tmps:
|
|
if not t.startswith('_') and os.path.isdir(os.path.join(plugin_path, t)):
|
|
add_plugin_list.append(t)
|
|
cls.all_package_list[t] = {'pos':'dev', 'path':os.path.join(plugin_path, t), 'loading':True}
|
|
plugins = plugins + add_plugin_list
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
|
|
# plugin_loading_list
|
|
try:
|
|
plugin_loading_list = F.config.get('plugin_loading_list', None)
|
|
if plugin_loading_list != None and (type(plugin_loading_list) == type([]) and len(plugin_loading_list)) > 0:
|
|
new_plugins = []
|
|
for _ in plugins:
|
|
if _ in plugin_loading_list:
|
|
new_plugins.append(_)
|
|
else:
|
|
cls.all_package_list[t]['loading'] = False
|
|
cls.all_package_list[t]['status'] = 'not_include_loading_list'
|
|
plugins = new_plugins
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
|
|
# plugin_except_list
|
|
try:
|
|
plugin_except_list = F.config.get('plugin_except_list', None)
|
|
if plugin_except_list != None and (type(plugin_except_list) == type([]) and len(plugin_except_list)) > 0:
|
|
new_plugins = []
|
|
for _ in plugins:
|
|
if _ not in plugin_except_list:
|
|
new_plugins.append(_)
|
|
else:
|
|
cls.all_package_list[t]['loading'] = False
|
|
cls.all_package_list[t]['status'] = 'include_except_list'
|
|
plugins = new_plugins
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
return plugins
|
|
|
|
|
|
|
|
# menu, blueprint, plugin_info, plugin_load, plugin_unload
|
|
@classmethod
|
|
def plugin_init(cls):
|
|
try:
|
|
plugins = cls.get_plugin_name_list()
|
|
plugins = sorted(plugins)
|
|
|
|
F.logger.debug(plugins)
|
|
for plugin_name in plugins:
|
|
|
|
#logger.debug(len(system.LogicPlugin.current_loading_plugin_list))
|
|
#if plugin_name.startswith('_'):
|
|
# continue
|
|
#if plugin_name == 'terminal' and platform.system() == 'Windows':
|
|
# continue
|
|
#if plugin_name in except_plugin_list:
|
|
# F.logger.debug('Except plugin : %s' % frame.plugin_menu)
|
|
# continue
|
|
F.logger.debug(f'[+] PLUGIN LOADING Start.. [{plugin_name}]')
|
|
entity = cls.all_package_list[plugin_name]
|
|
entity['version'] = '3'
|
|
try:
|
|
mod = __import__('%s' % (plugin_name), fromlist=[])
|
|
mod_plugin_info = None
|
|
# 2021-12-31
|
|
#import system
|
|
#if plugin_name not in system.LogicPlugin.current_loading_plugin_list:
|
|
# system.LogicPlugin.current_loading_plugin_list[plugin_name] = {'status':'loading'}
|
|
|
|
try:
|
|
mod_plugin_info = getattr(mod, 'plugin_info')
|
|
entity['module'] = mod
|
|
"""
|
|
if 'category' not in mod_plugin_info and 'category_name' in mod_plugin_info:
|
|
mod_plugin_info['category'] = mod_plugin_info['category_name']
|
|
if 'policy_point' in mod_plugin_info:
|
|
if mod_plugin_info['policy_point'] > app.config['config']['point']:
|
|
system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_policy_point'
|
|
continue
|
|
if 'policy_level' in mod_plugin_info:
|
|
if mod_plugin_info['policy_level'] > app.config['config']['level']:
|
|
system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_policy_level'
|
|
continue
|
|
if 'category' in mod_plugin_info and mod_plugin_info['category'] == 'beta':
|
|
if SystemModelSetting.get_bool('use_beta') == False:
|
|
system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_beta'
|
|
continue
|
|
"""
|
|
except Exception as exception:
|
|
#logger.error('Exception:%s', exception)
|
|
#logger.error(traceback.format_exc())
|
|
|
|
#mod_plugin_info = getattr(mod, 'setup')
|
|
F.logger.info(f'[!] PLUGIN_INFO not exist : [{plugin_name}] - is FF')
|
|
if mod_plugin_info == None:
|
|
try:
|
|
mod = __import__(f'{plugin_name}.setup', fromlist=['setup'])
|
|
entity['version'] = '4'
|
|
except Exception as e:
|
|
F.logger.error(f'Exception:{str(e)}')
|
|
F.logger.error(traceback.format_exc())
|
|
F.logger.warning(f'[!] NOT normal plugin : [{plugin_name}]')
|
|
#entity['version'] = 'not_plugin'
|
|
|
|
|
|
try:
|
|
if entity['version'] != '4':
|
|
mod_blue_print = getattr(mod, 'blueprint')
|
|
|
|
else:
|
|
entity['setup_mod'] = mod
|
|
entity['P'] = getattr(mod, 'P')
|
|
mod_blue_print = getattr(entity['P'], 'blueprint')
|
|
if mod_blue_print:
|
|
F.app.register_blueprint(mod_blue_print)
|
|
except Exception as exception:
|
|
#logger.error('Exception:%s', exception)
|
|
#logger.error(traceback.format_exc())
|
|
F.logger.warning(f'[!] BLUEPRINT not exist : [{plugin_name}]')
|
|
cls.plugin_list[plugin_name] = entity
|
|
#system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'success'
|
|
#system.LogicPlugin.current_loading_plugin_list[plugin_name]['info'] = mod_plugin_info
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
F.logger.debug('no blueprint')
|
|
cls.all_package_list[plugin_name]['loading'] = False
|
|
cls.all_package_list[plugin_name]['status'] = 'import fail'
|
|
cls.all_package_list[plugin_name]['log'] = traceback.format_exc()
|
|
|
|
#from tool_base import d
|
|
#logger.error(d(system.LogicPlugin.current_loading_plugin_list))
|
|
# 2021-07-01 모듈에 있는 DB 테이블 생성이 안되는 문제
|
|
# 기존 구조 : db.create_all() => 모듈 plugin_load => celery task 등록 후 리턴
|
|
# 변경 구조 : 모듈 plugin_load => db.create_all() => celery인 경우 리턴
|
|
|
|
# plugin_load 를 해야 하위 로직에 있는 DB가 로딩된다.
|
|
# plugin_load 에 db는 사용하는 코드가 있으면 안된다. (테이블도 없을 때 에러발생)
|
|
try:
|
|
#logger.warning('module plugin_load in celery ')
|
|
cls.plugin_list['mod']['module'].plugin_load()
|
|
except Exception as exception:
|
|
F.logger.debug(f'mod plugin_load error!!')
|
|
#logger.error('Exception:%s', exception)
|
|
#logger.error(traceback.format_exc())
|
|
|
|
# import가 끝나면 DB를 만든다.
|
|
# 플러그인 로드시 DB 초기화를 할 수 있다.
|
|
|
|
if not F.config['run_celery']:
|
|
try:
|
|
with F.app.app_context():
|
|
F.db.create_all()
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
F.logger.debug('db.create_all error')
|
|
|
|
if not F.config['run_flask']:
|
|
# 2021-06-03
|
|
# 모듈의 로직에 있는 celery 함수는 등록해주어야한다.
|
|
#try:
|
|
# logger.warning('module plugin_load in celery ')
|
|
# plugin_instance_list['mod'].plugin_load()
|
|
#except Exception as exception:
|
|
# logger.error('module plugin_load error')
|
|
# logger.error('Exception:%s', exception)
|
|
# logger.error(traceback.format_exc())
|
|
# 2021-07-01
|
|
# db때문에 위에서 로딩함.
|
|
return
|
|
|
|
for key, entity in cls.plugin_list.items():
|
|
try:
|
|
mod_plugin_load = None
|
|
if entity['version'] == '3':
|
|
mod_plugin_load = getattr(entity['module'], 'plugin_load')
|
|
elif entity['version'] == '4':
|
|
mod_plugin_load = getattr(entity['P'], 'plugin_load')
|
|
if mod_plugin_load:
|
|
def func(mod_plugin_load, key):
|
|
try:
|
|
F.logger.debug(f'[!] plugin_load threading start : [{key}]')
|
|
#mod.plugin_load()
|
|
mod_plugin_load()
|
|
F.logger.debug(f'[!] plugin_load threading end : [{key}]')
|
|
except Exception as exception:
|
|
F.logger.error('### plugin_load exception : %s', key)
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
cls.all_package_list[key]['loading'] = False
|
|
cls.all_package_list[key]['status'] = 'plugin_load error'
|
|
cls.all_package_list[key]['log'] = traceback.format_exc()
|
|
# mod는 위에서 로딩
|
|
if key != 'mod':
|
|
t = threading.Thread(target=func, args=(mod_plugin_load, key))
|
|
t.setDaemon(True)
|
|
t.start()
|
|
#if key == 'mod':
|
|
# t.join()
|
|
except Exception as exception:
|
|
F.logger.debug(f'[!] PLUGIN_LOAD function not exist : [{key}]')
|
|
#logger.error('Exception:%s', exception)
|
|
#logger.error(traceback.format_exc())
|
|
#logger.debug('no init_scheduler')
|
|
try:
|
|
mod_menu = None
|
|
if entity['version'] == '3':
|
|
mod_menu = getattr(entity['module'], 'menu')
|
|
elif entity['version'] == '4':
|
|
mod_menu = getattr(entity['P'], 'menu')
|
|
|
|
if mod_menu:
|
|
cls.plugin_menus[key]= {'menu':mod_menu, 'match':False}
|
|
if entity['version'] == '4':
|
|
setting_menu = getattr(entity['P'], 'setting_menu')
|
|
if setting_menu != None:
|
|
cls.setting_menus.append(setting_menu)
|
|
|
|
|
|
except Exception as exception:
|
|
F.logger.debug('no menu')
|
|
F.logger.debug('### plugin_load threading all start.. : %s ', len(cls.plugin_list))
|
|
# 모든 모듈을 로드한 이후에 app 등록, table 생성, start
|
|
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|
|
|
|
|
|
|
|
@classmethod
|
|
def plugin_unload(cls):
|
|
for key, entity in cls.plugin_list.items():
|
|
try:
|
|
if entity['version'] == '3':
|
|
mod_plugin_unload = getattr(entity['module'], 'plugin_unload')
|
|
elif entity['version'] == '4':
|
|
mod_plugin_unload = getattr(entity['P'], 'plugin_unload')
|
|
|
|
#if plugin_name == 'rss':
|
|
# continue
|
|
#mod_plugin_unload = getattr(mod, 'plugin_unload')
|
|
if mod_plugin_unload:
|
|
mod_plugin_unload()
|
|
#mod.plugin_unload()
|
|
except Exception as e:
|
|
F.logger.error('module:%s', key)
|
|
F.logger.error(f'Exception:{str(e)}')
|
|
F.logger.error(traceback.format_exc())
|
|
try:
|
|
from system.setup import P
|
|
P.plugin_unload()
|
|
except Exception as e:
|
|
F.logger.error(f'Exception:{str(e)}')
|
|
F.logger.error(traceback.format_exc())
|
|
|
|
@classmethod
|
|
def plugin_install(cls, plugin_git, zip_url=None, zip_filename=None):
|
|
is_git = True if plugin_git != None and plugin_git != '' else False
|
|
ret = {}
|
|
try:
|
|
if is_git:
|
|
name = plugin_git.split('/')[-1]
|
|
else:
|
|
name = zip_filename.split('.')[0]
|
|
|
|
plugin_all_path = os.path.join(F.config['path_data'], 'plugins')
|
|
os.makedirs(plugin_all_path, exist_ok=True)
|
|
plugin_path = os.path.join(plugin_all_path, name)
|
|
plugin_info = None
|
|
if os.path.exists(plugin_path):
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = '이미 설치되어 있습니다.'
|
|
ret['status'] = 'already_exist'
|
|
return ret
|
|
|
|
if plugin_git and plugin_git.startswith('http'):
|
|
for tag in ['main', 'master']:
|
|
try:
|
|
info_url = plugin_git.replace('github.com', 'raw.githubusercontent.com') + f'/{tag}/info.yaml'
|
|
plugin_info = requests.get(info_url).json()
|
|
if plugin_info is not None:
|
|
break
|
|
except:
|
|
pass
|
|
|
|
if zip_filename and zip_filename != '':
|
|
zip_filepath = os.path.join(F.config['path_data'], 'tmp', zip_filename)
|
|
extract_filepath = os.path.join(F.config['path_data'], 'tmp', name)
|
|
if SupportFile.download(zip_url, zip_filepath):
|
|
with zipfile.ZipFile(zip_filepath, 'r') as zip_ref:
|
|
zip_ref.extractall(extract_filepath)
|
|
plugin_info_filepath = os.path.join(extract_filepath, 'info.yaml')
|
|
if os.path.exists(plugin_info_filepath):
|
|
plugin_info = SupportYaml.read_yaml(plugin_info_filepath)
|
|
|
|
if plugin_info == None:
|
|
plugin_info = {}
|
|
|
|
flag = True
|
|
tmp = plugin_info.get('require_os', '')
|
|
if tmp != '' and type(tmp) == type([]) and platform.system() not in tmp:
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = '설치 가능한 OS가 아닙니다.'
|
|
ret['status'] = 'not_support_os'
|
|
flag = False
|
|
|
|
tmp = plugin_info.get('require_running_type', '')
|
|
if tmp != '' and type(tmp) == type([]) and F.config['running_type'] not in tmp:
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = '설치 가능한 실행타입이 아닙니다.'
|
|
ret['status'] = 'not_support_running_type'
|
|
flag = False
|
|
|
|
|
|
if flag:
|
|
if plugin_git and plugin_git.startswith('http'):
|
|
command = ['git', '-C', plugin_all_path, 'clone', plugin_git + '.git', '--depth', '1']
|
|
log = SupportSubprocess.execute_command_return(command, log=True)
|
|
F.logger.debug(log)
|
|
if os.path.exists(plugin_path):
|
|
ret['ret'] = 'success'
|
|
ret['msg'] = '정상적으로 설치하였습니다. 재시작시 적용됩니다.<br>' + '<br>'.join(log['log'].split('\n'))
|
|
else:
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = '설치 실패.<br>' + '<br>'.join(log['log'].split('\n'))
|
|
if zip_filename and zip_filename != '':
|
|
|
|
if os.path.exists(plugin_path) == False:
|
|
shutil.move(extract_filepath, plugin_path)
|
|
else:
|
|
for tmp in os.listdir(extract_filepath):
|
|
shutil.move(os.path.join(extract_filepath, tmp), plugin_path)
|
|
log = ''
|
|
|
|
# 2021-12-31
|
|
tmp = plugin_info.get('require_plugin', '')
|
|
if tmp != '' and type(tmp) == type([]) and len(tmp) > 0:
|
|
for need_plugin in plugin_info['require_plugin']:
|
|
if need_plugin['package_name'] in cls.plugin_init:
|
|
F.logger.debug(f"Dependency 설치 - 이미 설치됨 : {need_plugin['package_name']}")
|
|
continue
|
|
else:
|
|
F.logger.debug(f"Dependency 설치 : {need_plugin['package_name']}")
|
|
cls.plugin_install(need_plugin['home'], None, None)
|
|
|
|
|
|
#ret['msg'] = '<br>'.join(log)
|
|
|
|
except Exception as e:
|
|
F.logger.error(f'Exception:{str(e)}')
|
|
F.logger.error(traceback.format_exc())
|
|
ret['ret'] = 'danger'
|
|
ret['msg'] = str(e)
|
|
return ret
|
|
|
|
|
|
@classmethod
|
|
def plugin_update(cls):
|
|
try:
|
|
if os.environ.get('UPDATE_STOP') == 'true':
|
|
return
|
|
if os.environ.get('PLUGIN_UPDATE_FROM_PYTHON') == 'false':
|
|
return
|
|
if F.config['plugin_update'] != True:
|
|
return
|
|
plugins_path = os.path.join(F.config['path_data'], 'plugins')
|
|
if os.path.exists(plugins_path) == False:
|
|
return
|
|
tmps = os.listdir(plugins_path)
|
|
for t in tmps:
|
|
plugin_path = os.path.join(plugins_path, t)
|
|
if t.startswith('_'):
|
|
continue
|
|
if os.path.exists(os.path.join(plugin_path, '.git')):
|
|
command = ['git', '-C', plugin_path, 'reset', '--hard', 'HEAD']
|
|
ret = SupportSubprocess.execute_command_return(command)
|
|
F.logger.debug(ret)
|
|
command = ['git', '-C', plugin_path, 'pull']
|
|
ret = SupportSubprocess.execute_command_return(command)
|
|
F.logger.debug(ret)
|
|
else:
|
|
F.logger.debug(f"{plugin_path} not git repo")
|
|
except Exception as exception:
|
|
F.logger.error('Exception:%s', exception)
|
|
F.logger.error(traceback.format_exc())
|