main.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import traceback
  4. import signal
  5. import time
  6. from datetime import timedelta
  7. import jimit as ji
  8. import json
  9. import os
  10. import threading
  11. from flask import g, request, redirect, url_for, Response, session
  12. from flask.ext.session import Session
  13. from models import Utils
  14. from models.event_processor import EventProcessor
  15. from models.initialize import socketio, logger, q_ws, Init
  16. import api_route_table
  17. import views_route_table
  18. from models import Database as db
  19. from models import Config
  20. from models import User
  21. from api.user import blueprint as user_blueprint
  22. from api.boot_job import blueprint as boot_job_blueprint
  23. from api.boot_job import blueprints as boot_job_blueprints
  24. from api.operate_rule import blueprint as operate_rule_blueprint
  25. from api.operate_rule import blueprints as operate_rule_blueprints
  26. from api.os_template import blueprint as os_template_blueprint
  27. from api.os_template import blueprints as os_template_blueprints
  28. from api.guest import blueprint as guest_blueprint
  29. from api.guest import blueprints as guest_blueprints
  30. from api.disk import blueprint as disk_blueprint
  31. from api.disk import blueprints as disk_blueprints
  32. from api.config import blueprint as config_blueprint
  33. from api.log import blueprint as log_blueprint
  34. from api.log import blueprints as log_blueprints
  35. from api.host import blueprint as host_blueprint
  36. from api.host import blueprints as host_blueprints
  37. from api.performance import blueprint as performance_blueprint
  38. from api.performance import blueprints as performance_blueprints
  39. from api.host_performance import blueprint as host_performance_blueprint
  40. from api.host_performance import blueprints as host_performance_blueprints
  41. from views.error_pages import *
  42. from views.config import blueprint as view_config_blueprint
  43. from views.misc import blueprint as view_misc_blueprint
  44. from views.dashboard import blueprint as view_dashboard_blueprint
  45. from views.guest import blueprint as view_guest_blueprint
  46. from views.guest import blueprints as view_guest_blueprints
  47. from views.disk import blueprint as view_disk_blueprint
  48. from views.disk import blueprints as view_disk_blueprints
  49. from views.log import blueprint as view_log_blueprint
  50. from views.log import blueprints as view_log_blueprints
  51. from views.os_template import blueprint as view_os_template_blueprint
  52. from views.os_template import blueprints as view_os_template_blueprints
  53. from views.boot_job import blueprint as view_boot_job_blueprint
  54. from views.boot_job import blueprints as view_boot_job_blueprints
  55. from views.operate_rule import blueprint as view_operate_rule_blueprint
  56. from views.operate_rule import blueprints as view_operate_rule_blueprints
  57. from views.host import blueprint as view_host_blueprint
  58. from views.host import blueprints as view_host_blueprints
  59. from websockify.websocketproxy import WebSocketProxy
  60. __author__ = 'James Iter'
  61. __date__ = '2017/3/31'
  62. __contact__ = 'james.iter.cn@gmail.com'
  63. __copyright__ = '(c) 2017 by James Iter.'
  64. # 替换为Flask-Session
  65. app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=app.config['PERMANENT_SESSION_LIFETIME'])
  66. Session(app)
  67. def instantiation_ws_vnc(listen_port, target_host, target_port):
  68. # 用于 Web noVNC 代理
  69. ws = WebSocketProxy(listen_host="0.0.0.0", listen_port=listen_port, target_host=target_host,
  70. target_port=target_port, run_once=True, daemon=True, idle_timeout=10)
  71. ws.start_server()
  72. def ws_engine_for_vnc():
  73. while True:
  74. payload = q_ws.get()
  75. payload = json.loads(payload)
  76. c_pid = os.fork()
  77. if c_pid == 0:
  78. instantiation_ws_vnc(payload['listen_port'], payload['target_host'], payload['target_port'])
  79. # 因为 WebSocketProxy 使用了 daemon 参数,所以当执行到 ws.start_server() 时,会退出其所在的子进程,
  80. # 故而这里设置wait来处理结束的子进程的环境,避免出现僵尸进程。
  81. os.wait()
  82. q_ws.task_done()
  83. def is_not_need_to_auth(endpoint):
  84. not_auth_table = [
  85. 'api_config.r_get',
  86. 'api_config.r_create',
  87. 'v_config.create',
  88. 'v_misc.login',
  89. 'api_user.r_sign_in'
  90. ]
  91. if endpoint in not_auth_table:
  92. return True
  93. return False
  94. @app.before_request
  95. @Utils.dumps2response
  96. def r_before_request():
  97. try:
  98. g.ts = ji.Common.ts()
  99. if not is_not_need_to_auth(request.endpoint) and request.blueprint is not None and request.method != 'OPTIONS':
  100. g.config = Config()
  101. g.config.id = 1
  102. g.config.get()
  103. token = session.get('token', '')
  104. g.token = Utils.verify_token(token)
  105. user = User()
  106. user.id = g.token['uid']
  107. try:
  108. user.get()
  109. except ji.PreviewingError, e:
  110. # 如果该用户获取失败,则清除该用户对应的session。因为该用户可能已经被删除。
  111. for key in session.keys():
  112. session.pop(key=key)
  113. return json.loads(e.message)
  114. except ji.JITError, e:
  115. ret = json.loads(e.message)
  116. if ret['state']['code'] == '404':
  117. return redirect(location=url_for('v_config.create'), Response=Response)
  118. if ret['state']['sub']['code'] in ['41208']:
  119. return redirect(location=url_for('v_misc.login'), Response=Response)
  120. return ret
  121. @app.after_request
  122. @Utils.dumps2response
  123. def r_after_request(response):
  124. try:
  125. # https://developer.mozilla.org/en/HTTP_access_control
  126. # (中文版) https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials
  127. # http://www.w3.org/TR/cors/
  128. # 由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
  129. if request.referrer is None:
  130. # 跑测试脚本时,用该规则。
  131. response.headers['Access-Control-Allow-Origin'] = '*'
  132. else:
  133. # 生产环境中,如果前后端分离。那么请指定具体的前端域名地址,不要用如下在开发环境中的便捷方式。
  134. # -- Access-Control-Allow-Credentials为true,携带cookie时,不允许Access-Control-Allow-Origin为通配符,是浏览器对用户的一种安全保护。
  135. # -- 至少能避免登录山寨网站,骗取用户相关信息。
  136. response.headers['Access-Control-Allow-Origin'] = '/'.join(request.referrer.split('/')[:3])
  137. response.headers['Access-Control-Allow-Credentials'] = 'true'
  138. response.headers['Access-Control-Allow-Methods'] = 'HEAD, GET, POST, DELETE, OPTIONS, PATCH, PUT'
  139. response.headers['Access-Control-Allow-Headers'] = 'X-Request-With, Content-Type'
  140. response.headers['Access-Control-Expose-Headers'] = 'Set-Cookie'
  141. # 少于session生命周期一半时,自动对其续期
  142. if not is_not_need_to_auth(request.endpoint) and hasattr(g, 'token') and \
  143. g.token['exp'] < (ji.Common.ts() + (app.config['token_ttl'] / 2)):
  144. token = Utils.generate_token(g.token['uid'])
  145. # 清除原有session,由新session代替
  146. for key in session.keys():
  147. session.pop(key=key)
  148. session['token'] = token
  149. return response
  150. except ji.JITError, e:
  151. return json.loads(e.message)
  152. # noinspection PyBroadException
  153. try:
  154. db.init_conn_mysql()
  155. db.init_conn_redis()
  156. app.register_blueprint(user_blueprint)
  157. app.register_blueprint(boot_job_blueprint)
  158. app.register_blueprint(boot_job_blueprints)
  159. app.register_blueprint(operate_rule_blueprint)
  160. app.register_blueprint(operate_rule_blueprints)
  161. app.register_blueprint(os_template_blueprint)
  162. app.register_blueprint(os_template_blueprints)
  163. app.register_blueprint(guest_blueprint)
  164. app.register_blueprint(guest_blueprints)
  165. app.register_blueprint(disk_blueprint)
  166. app.register_blueprint(disk_blueprints)
  167. app.register_blueprint(config_blueprint)
  168. app.register_blueprint(log_blueprint)
  169. app.register_blueprint(log_blueprints)
  170. app.register_blueprint(host_blueprint)
  171. app.register_blueprint(host_blueprints)
  172. app.register_blueprint(performance_blueprint)
  173. app.register_blueprint(performance_blueprints)
  174. app.register_blueprint(host_performance_blueprint)
  175. app.register_blueprint(host_performance_blueprints)
  176. app.register_blueprint(view_config_blueprint)
  177. app.register_blueprint(view_misc_blueprint)
  178. app.register_blueprint(view_dashboard_blueprint)
  179. app.register_blueprint(view_guest_blueprint)
  180. app.register_blueprint(view_guest_blueprints)
  181. app.register_blueprint(view_disk_blueprint)
  182. app.register_blueprint(view_disk_blueprints)
  183. app.register_blueprint(view_log_blueprint)
  184. app.register_blueprint(view_log_blueprints)
  185. app.register_blueprint(view_os_template_blueprint)
  186. app.register_blueprint(view_os_template_blueprints)
  187. app.register_blueprint(view_boot_job_blueprint)
  188. app.register_blueprint(view_boot_job_blueprints)
  189. app.register_blueprint(view_operate_rule_blueprint)
  190. app.register_blueprint(view_operate_rule_blueprints)
  191. app.register_blueprint(view_host_blueprint)
  192. app.register_blueprint(view_host_blueprints)
  193. except:
  194. logger.error(traceback.format_exc())
  195. if __name__ == '__main__':
  196. # noinspection PyBroadException
  197. try:
  198. signal.signal(signal.SIGTERM, Utils.signal_handle)
  199. signal.signal(signal.SIGINT, Utils.signal_handle)
  200. pid = os.fork()
  201. if pid == 0:
  202. ws_engine_for_vnc()
  203. else:
  204. # 父进程退出时,会清理所有提前退出的子进程的环境。所以这里无需对子进程做等待操作。
  205. # 即:即使子进程提前退出,且因父进程没有做wait处理,使其变成了僵尸进程。但当父进程退出时,会对因其所产生的僵尸进程做统一清理操作。
  206. threads = []
  207. t_ = threading.Thread(target=EventProcessor.launch, args=())
  208. threads.append(t_)
  209. t_ = threading.Thread(target=Init.pub_sub_ping_pong, args=())
  210. threads.append(t_)
  211. for t in threads:
  212. t.start()
  213. app.run(host=app.config['listen'], port=app.config['port'], use_reloader=False, threaded=True)
  214. while True:
  215. if Utils.exit_flag:
  216. # 主线程即将结束
  217. break
  218. time.sleep(1)
  219. # 等待子线程结束
  220. for t in threads:
  221. t.join()
  222. print 'Main say bye-bye!'
  223. except:
  224. logger.error(traceback.format_exc())
  225. exit(-1)