|
@@ -0,0 +1,218 @@
|
|
|
|
|
+# coding:utf-8
|
|
|
|
|
+
|
|
|
|
|
+import os
|
|
|
|
|
+import sys
|
|
|
|
|
+import fs
|
|
|
|
|
+import psutil
|
|
|
|
|
+import subprocess
|
|
|
|
|
+from utils.comm_tools import get_monitor_config, shell_cmd
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class SupervisorTools:
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ temp_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
+ utils_dir = os.path.join(temp_dir, 'utils')
|
|
|
|
|
+ self.exception_warning_file = os.path.join(utils_dir, 'exception_warning.py')
|
|
|
|
|
+ self.config_base = '/data/supervisor'
|
|
|
|
|
+ self.base_config_file = '/etc/supervisor/supervisord.conf'
|
|
|
|
|
+ self.python_bin = sys.executable
|
|
|
|
|
+ self.bin_dir = os.path.dirname(self.python_bin)
|
|
|
|
|
+ self.supervisord = os.path.join(self.bin_dir, 'supervisord')
|
|
|
|
|
+ self.supervisorctl = os.path.join(self.bin_dir, 'supervisorctl')
|
|
|
|
|
+ self.config_dir = os.path.join(self.config_base, 'config.d')
|
|
|
|
|
+ self.log_dir = os.path.join(self.config_base, 'log')
|
|
|
|
|
+
|
|
|
|
|
+ tmp_dir = os.path.dirname(self.base_config_file)
|
|
|
|
|
+ if not os.path.isdir(tmp_dir):
|
|
|
|
|
+ os.makedirs(tmp_dir)
|
|
|
|
|
+ if not os.path.isdir(self.config_dir):
|
|
|
|
|
+ os.makedirs(self.config_dir)
|
|
|
|
|
+ if not os.path.isdir(self.log_dir):
|
|
|
|
|
+ os.makedirs(self.log_dir)
|
|
|
|
|
+
|
|
|
|
|
+ def _get_supervisor_app(self):
|
|
|
|
|
+ '''获取supervisor所管理的程序'''
|
|
|
|
|
+ status, app_name_list = False, []
|
|
|
|
|
+ cmd = '{} status'.format(self.supervisorctl)
|
|
|
|
|
+ code, stdout, stderr = shell_cmd(cmd)
|
|
|
|
|
+
|
|
|
|
|
+ if code >= 0:
|
|
|
|
|
+ for line in stdout.splitlines():
|
|
|
|
|
+ tmp_line = line.split()
|
|
|
|
|
+ if len(tmp_line) > 1:
|
|
|
|
|
+ app_name = tmp_line[0].strip()
|
|
|
|
|
+ app_name_list.append(app_name)
|
|
|
|
|
+ status, msg = True, set(app_name_list)
|
|
|
|
|
+ else:
|
|
|
|
|
+ msg = '获取supervisor所管理的程序失败: {}'.format(stderr)
|
|
|
|
|
+ return status, msg
|
|
|
|
|
+
|
|
|
|
|
+ def _make_task_conf(self, app_name, dir_name, cmd):
|
|
|
|
|
+ '''生成任务配置文件'''
|
|
|
|
|
+ log_dir = os.path.join(self.log_dir, app_name)
|
|
|
|
|
+ if not os.path.isdir(log_dir):
|
|
|
|
|
+ os.makedirs(log_dir)
|
|
|
|
|
+ conf_path = os.path.join(self.config_dir, '{}.conf'.format(app_name))
|
|
|
|
|
+ log_file = os.path.join(log_dir, '{}.log'.format(app_name))
|
|
|
|
|
+
|
|
|
|
|
+ conf_str = '''
|
|
|
|
|
+#程序唯一名称
|
|
|
|
|
+[program:{app_name}]
|
|
|
|
|
+
|
|
|
|
|
+#程序路径
|
|
|
|
|
+directory={dir_name}
|
|
|
|
|
+
|
|
|
|
|
+#运行程序的命令
|
|
|
|
|
+command={cmd}
|
|
|
|
|
+
|
|
|
|
|
+#标准日志输出位置
|
|
|
|
|
+stdout_logfile={log_file}
|
|
|
|
|
+
|
|
|
|
|
+autostart=true #supervisord启动后,同时启动此程序
|
|
|
|
|
+startsecs=5 #启动5秒后没有异常退出,就表示进程正常启动了,默认为1秒
|
|
|
|
|
+autorestart=true #程序退出后自动重启
|
|
|
|
|
+startretries=3 #启动失败自动重试次数,默认是3
|
|
|
|
|
+user=root #用哪个用户启动进程,默认是root
|
|
|
|
|
+redirect_stderr=true #把stderr重定向到stdout标准输出,默认false
|
|
|
|
|
+stdout_logfile_maxbytes=50MB #stdout标准输出日志文件大小,日志文件备份数
|
|
|
|
|
+stdout_logfile_backups=10
|
|
|
|
|
+stopasgroup=true # 停止或终止进程时,同时结束进程包含的子进程
|
|
|
|
|
+killasgroup=true
|
|
|
|
|
+
|
|
|
|
|
+'''.format(app_name=app_name, dir_name=dir_name, cmd=cmd, log_file=log_file)
|
|
|
|
|
+ with open(conf_path, 'w') as fw:
|
|
|
|
|
+ fw.write(conf_str)
|
|
|
|
|
+
|
|
|
|
|
+ def _make_supervisor_conf(self):
|
|
|
|
|
+ cmd = '{} -u {}'.format(self.python_bin, self.exception_warning_file)
|
|
|
|
|
+ conf = '''
|
|
|
|
|
+[unix_http_server]
|
|
|
|
|
+file=/data/supervisor/supervisor.sock ; the path to the socket file
|
|
|
|
|
+;chmod=0700 ; socket file mode (default 0700)
|
|
|
|
|
+;chown=nobody:nogroup ; socket file uid:gid owner
|
|
|
|
|
+;username=user ; default is no username (open server)
|
|
|
|
|
+;password=123 ; default is no password (open server)
|
|
|
|
|
+
|
|
|
|
|
+;[inet_http_server] ; inet (TCP) server disabled by default
|
|
|
|
|
+;port=127.0.0.1:9001 ; ip_address:port specifier, *:port for all iface
|
|
|
|
|
+;username=user ; default is no username (open server)
|
|
|
|
|
+;password=123 ; default is no password (open server)
|
|
|
|
|
+
|
|
|
|
|
+[supervisord]
|
|
|
|
|
+logfile=/data/supervisor/supervisord.log ; main log file; default $CWD/supervisord.log
|
|
|
|
|
+logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
|
|
|
|
|
+logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
|
|
|
|
|
+loglevel=info ; log level; default info; others: debug,warn,trace
|
|
|
|
|
+pidfile=/data/supervisor/supervisord.pid ; supervisord pidfile; default supervisord.pid
|
|
|
|
|
+nodaemon=false ; start in foreground if true; default false
|
|
|
|
|
+silent=false ; no logs to stdout if true; default false
|
|
|
|
|
+minfds=1024 ; min. avail startup file descriptors; default 1024
|
|
|
|
|
+minprocs=200 ; min. avail process descriptors;default 200
|
|
|
|
|
+;umask=022 ; process file creation umask; default 022
|
|
|
|
|
+user=root ; setuid to this UNIX account at startup; recommended if root
|
|
|
|
|
+;identifier=supervisor ; supervisord identifier, default is 'supervisor'
|
|
|
|
|
+;directory=/tmp ; default is not to cd during start
|
|
|
|
|
+;nocleanup=true ; don't clean up tempfiles at start; default false
|
|
|
|
|
+;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP
|
|
|
|
|
+;environment=KEY="value" ; key value pairs to add to environment
|
|
|
|
|
+;strip_ansi=false ; strip ansi escape codes in logs; def. false
|
|
|
|
|
+
|
|
|
|
|
+[rpcinterface:supervisor]
|
|
|
|
|
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
|
|
|
|
+
|
|
|
|
|
+[supervisorctl]
|
|
|
|
|
+serverurl=unix:///data/supervisor/supervisor.sock
|
|
|
|
|
+;username=chris ; should be same as in [*_http_server] if set
|
|
|
|
|
+;password=123 ; should be same as in [*_http_server] if set
|
|
|
|
|
+;prompt=mysupervisor ; cmd line prompt (default "supervisor")
|
|
|
|
|
+;history_file=~/.sc_history ; use readline history if available
|
|
|
|
|
+
|
|
|
|
|
+[eventlistener:crash_warning]
|
|
|
|
|
+command={exception_warning_file}
|
|
|
|
|
+events=PROCESS_STATE
|
|
|
|
|
+redirect_stderr=false
|
|
|
|
|
+
|
|
|
|
|
+[include]
|
|
|
|
|
+files = /data/supervisor/config.d/*.conf
|
|
|
|
|
+
|
|
|
|
|
+'''.format(exception_warning_file=cmd)
|
|
|
|
|
+ with open(self.base_config_file, 'w') as fw:
|
|
|
|
|
+ fw.write(conf)
|
|
|
|
|
+
|
|
|
|
|
+ def _update_task(self):
|
|
|
|
|
+ '''启动添加的程序'''
|
|
|
|
|
+ status, msg = True, 'supervisor管理器启动程序成功'
|
|
|
|
|
+ cmd = '{} update all'.format(self.supervisorctl)
|
|
|
|
|
+ code, stdout, stderr = shell_cmd(cmd)
|
|
|
|
|
+ if code != 0:
|
|
|
|
|
+ status, msg = False, 'supervisor管理器启动程序失败:{}\n{}'.format(stdout, stderr)
|
|
|
|
|
+ return status, msg
|
|
|
|
|
+
|
|
|
|
|
+ def make_task(self):
|
|
|
|
|
+ '''创建supervisor任务并启动'''
|
|
|
|
|
+ status, msg = self._get_supervisor_app()
|
|
|
|
|
+ if not status:
|
|
|
|
|
+ return status, msg
|
|
|
|
|
+ app_name_set, app_name_list = msg, []
|
|
|
|
|
+ monitor_config = get_monitor_config()
|
|
|
|
|
+ with fs.open_fs('/') as root_fs:
|
|
|
|
|
+ for script_dir, item in monitor_config.items():
|
|
|
|
|
+ if not os.path.isdir(script_dir):
|
|
|
|
|
+ continue
|
|
|
|
|
+ file_filter, file_exclude = item['filter'], item.get('exclude', [])
|
|
|
|
|
+ python_bin, env_name = item['executable'], item['name']
|
|
|
|
|
+ if not file_exclude:
|
|
|
|
|
+ file_exclude = None
|
|
|
|
|
+ if not file_filter:
|
|
|
|
|
+ file_filter = None
|
|
|
|
|
+ for path in root_fs.walk.files(script_dir, filter=file_filter, exclude=file_exclude, max_depth=1):
|
|
|
|
|
+ dir_name, file_name = os.path.split(path)
|
|
|
|
|
+ index = file_name.rfind('.')
|
|
|
|
|
+ app_name = file_name[:index] if index != -1 else file_name
|
|
|
|
|
+ app_name = "{}_{}".format(env_name, app_name)
|
|
|
|
|
+ if app_name in app_name_set:
|
|
|
|
|
+ continue
|
|
|
|
|
+ cmd = '{} -u {}'.format(python_bin, path)
|
|
|
|
|
+ self._make_task_conf(app_name=app_name, dir_name=dir_name, cmd=cmd)
|
|
|
|
|
+ app_name_list.append(app_name)
|
|
|
|
|
+
|
|
|
|
|
+ status, msg = True, 'supervisor管理器启动程序成功'
|
|
|
|
|
+ if app_name_list:
|
|
|
|
|
+ status, msg = self._update_task()
|
|
|
|
|
+ return status, msg
|
|
|
|
|
+
|
|
|
|
|
+ def _check_supervisord(self):
|
|
|
|
|
+ ''' 检测supervisor管理器进程是否运行'''
|
|
|
|
|
+ is_run = False
|
|
|
|
|
+ for proc in psutil.process_iter():
|
|
|
|
|
+ proc_name = proc.name().lower()
|
|
|
|
|
+ if proc_name == 'supervisord':
|
|
|
|
|
+ is_run = True
|
|
|
|
|
+ break
|
|
|
|
|
+ return is_run
|
|
|
|
|
+
|
|
|
|
|
+ def start_supervisord(self):
|
|
|
|
|
+ '''启动supervisor管理器'''
|
|
|
|
|
+ msg = ''
|
|
|
|
|
+ if not os.path.isfile(self.supervisord):
|
|
|
|
|
+ msg = 'supervisor未安装,请先安装supervisor'
|
|
|
|
|
+ else:
|
|
|
|
|
+ is_run = self._check_supervisord()
|
|
|
|
|
+ if not is_run:
|
|
|
|
|
+ self._make_supervisor_conf()
|
|
|
|
|
+ cmd = "{} -c {}".format(self.supervisord, self.base_config_file)
|
|
|
|
|
+ result = subprocess.run(cmd, shell=True)
|
|
|
|
|
+ if result.returncode == 0:
|
|
|
|
|
+ is_run = True
|
|
|
|
|
+ else:
|
|
|
|
|
+ msg = '启动supervisor管理器失败, 请检查'
|
|
|
|
|
+
|
|
|
|
|
+ if is_run:
|
|
|
|
|
+ status, msg = self.make_task()
|
|
|
|
|
+ return msg
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ st = SupervisorTools()
|
|
|
|
|
+ msg = st.start_supervisord()
|
|
|
|
|
+ print(msg)
|