manage.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. import os
  2. import io
  3. import sys
  4. import platform
  5. import shutil
  6. import time
  7. import subprocess
  8. import json
  9. import datetime
  10. import socket
  11. import re
  12. from threading import Thread
  13. from api.utils import shell_execute, docker, const
  14. from api.model.app import App
  15. from api.model.response import Response
  16. from api.utils import lock
  17. from api.utils.common_log import myLogger
  18. # 获取所有app的信息
  19. def get_my_app():
  20. ret = Response(code=const.RETURN_FAIL, message="App query failed!")
  21. # get all info
  22. cmd = "docker compose ls -a --format json"
  23. output = shell_execute.execute_command_output_all(cmd)
  24. if int(output["code"]) == 0:
  25. output_list = json.loads(output["result"])
  26. app_list, has_add = get_apps_from_compose(output_list)
  27. list = get_apps_from_queue(app_list, has_add)
  28. ret = Response(code=const.RETURN_SUCCESS, message="The app query is successful.", data=list)
  29. ret = ret.dict()
  30. return ret
  31. # 获取具体某个app的信息
  32. def get_app_detail(app_id):
  33. ret = {}
  34. ret['code'] = const.RETURN_FAIL
  35. ret['message'] = 'App query failed!'
  36. ret['data'] = None
  37. if docker.check_app_id(app_id):
  38. # get all info
  39. cmd = "docker compose ls -a --format json"
  40. output = shell_execute.execute_command_output_all(cmd)
  41. if int(output["code"]) == 0:
  42. output_list = json.loads(output["result"])
  43. app_list, has_add = get_apps_from_compose(output_list)
  44. list = get_apps_from_queue(app_list, has_add)
  45. flag = 0
  46. app_info = None
  47. for app in list:
  48. if app["app_id"] == app_id:
  49. list.clear()
  50. list.append(app)
  51. app_info = app
  52. flag = 1
  53. break
  54. if flag == 1:
  55. ret['code'] = const.RETURN_SUCCESS
  56. ret['message'] = "The app query is successful."
  57. ret['data'] = app_info
  58. else:
  59. ret['message'] = "AppID is not legal!"
  60. return ret
  61. # 查询某个正在安装的app的 具体状态:waiting(等待安装)pulling(拉取镜像)initializing(初始化)running(正常运行)
  62. def install_app_process(app_id):
  63. app_name = split_app_id(app_id)
  64. var_path = "/data/apps/" + app_name + "/variables.json"
  65. real_name = docker.read_var(var_path, 'name')
  66. if docker.check_app_directory(real_name):
  67. percentage = docker.get_process_perc(app_name, real_name)
  68. ret = Response(code=const.RETURN_SUCCESS, message=percentage)
  69. ret = ret.dict()
  70. else:
  71. ret = Response(code=const.RETURN_FAIL, message="This app is not currently installed.")
  72. ret = ret.dict()
  73. return ret
  74. def install_app(app_name, customer_app_name, app_version):
  75. ret = Response(code=const.RETURN_FAIL, message=" ")
  76. ret.code, ret.message = check_app(app_name, customer_app_name, app_version)
  77. if ret.code == const.RETURN_SUCCESS:
  78. ret.code, ret.message = prepare_app(app_name, customer_app_name)
  79. if ret.code == const.RETURN_SUCCESS:
  80. t1 = Thread(target=install_app_job, args=(customer_app_name, app_version,))
  81. t1.start()
  82. ret.message = "The app is starting, please check again in a few minutes."
  83. ret = ret.dict()
  84. return ret
  85. def start_app(app_id):
  86. ret = Response(code=const.RETURN_FAIL, message="")
  87. if docker.check_app_id(app_id):
  88. app_name = split_app_id(app_id)
  89. info, code = if_app_exits(app_name)
  90. if code:
  91. app_path = info.split()[-1].rsplit('/', 1)[0]
  92. docker.check_app_compose(app_path + '/.env')
  93. cmd = "docker compose -f " + app_path + "/docker-compose.yml start"
  94. output = shell_execute.execute_command_output_all(cmd)
  95. if int(output["code"]) == 0:
  96. ret.code = const.RETURN_SUCCESS
  97. ret.message = "The app starts successfully."
  98. else:
  99. ret.message = "The app failed to start!"
  100. else:
  101. ret.message = "The app is not installed!"
  102. else:
  103. ret.message = "AppID is not legal!"
  104. ret = ret.dict()
  105. return ret
  106. def stop_app(app_id):
  107. ret = Response(code=const.RETURN_FAIL, message="")
  108. if docker.check_app_id(app_id):
  109. app_name = split_app_id(app_id)
  110. info, code = if_app_exits(app_name)
  111. if code:
  112. app_path = info.split()[-1].rsplit('/', 1)[0]
  113. cmd = "docker compose -f " + app_path + "/docker-compose.yml stop"
  114. output = shell_execute.execute_command_output_all(cmd)
  115. if int(output["code"]) == 0:
  116. ret.code = const.RETURN_SUCCESS
  117. ret.message = "The app stopped successfully."
  118. else:
  119. ret.message = "App stop failed!"
  120. else:
  121. ret.message = "The app is not installed!"
  122. else:
  123. ret.message = 'AppID is not legal!'
  124. ret = ret.dict()
  125. return ret
  126. def restart_app(app_id):
  127. ret = Response(code=const.RETURN_FAIL, message="")
  128. if docker.check_app_id(app_id):
  129. app_name = split_app_id(app_id)
  130. info, code = if_app_exits(app_name)
  131. if code:
  132. app_path = info.split()[-1].rsplit('/', 1)[0]
  133. cmd = "docker compose -f " + app_path + "/docker-compose.yml restart"
  134. output = shell_execute.execute_command_output_all(cmd)
  135. if int(output["code"]) == 0:
  136. ret.code = const.RETURN_SUCCESS
  137. ret.message = "The app restarts successfully."
  138. else:
  139. ret.message = "App restart failed!"
  140. else:
  141. ret.message = "The app is not installed!"
  142. else:
  143. ret.message = 'AppID is not legal!'
  144. ret = ret.dict()
  145. return ret
  146. def uninstall_app(app_id):
  147. ret = Response(code=const.RETURN_FAIL, message="")
  148. if docker.check_app_id(app_id):
  149. app_name = split_app_id(app_id)
  150. info, code = if_app_exits(app_name)
  151. if code:
  152. app_path = info.split()[-1].rsplit('/', 1)[0]
  153. if_stopped = stop_app(app_id) # stop_app
  154. app_name = split_app_id(app_id)
  155. if if_stopped["code"] == 0:
  156. cmd = "docker compose -f " + app_path + "/docker-compose.yml down -v"
  157. lib_path = '/data/library/apps/' + app_name
  158. if app_path != lib_path:
  159. cmd = cmd + " && sudo rm -rf " + app_path
  160. output = shell_execute.execute_command_output_all(cmd)
  161. if int(output["code"]) == 0:
  162. ret.code = 0
  163. ret.message = "The app is deleted successfully"
  164. else:
  165. ret.message = "App deletion failed!"
  166. else:
  167. ret.message = if_stopped["message"]
  168. else:
  169. ret.message = 'AppID is not legal!'
  170. ret = ret.dict()
  171. return ret
  172. def check_app(app_name, customer_app_name, app_version):
  173. message = " "
  174. code = const.RETURN_FAIL
  175. if app_name == None or customer_app_name == None or app_version == None:
  176. message = "Please fill in the APP information completely!"
  177. elif not docker.check_app_directory(app_name):
  178. message = "Installing the app is not supported!"
  179. elif re.match('^[a-z0-9]+$', customer_app_name) == None:
  180. message = "App names must be lowercase letters and numbers!"
  181. elif docker.check_directory("/data/apps/" + customer_app_name):
  182. message = "The APP name is already in use, please specify a different name to reinstall."
  183. elif not docker.check_vm_resource(app_name):
  184. message = "System resources (memory, CPU, disk) are insufficient, and continuing to install may cause the app to not run or the server to be abnormal!"
  185. else:
  186. code = const.RETURN_SUCCESS
  187. return code, message
  188. def prepare_app(app_name, customer_app_name):
  189. library_path = "/data/library/apps/" + app_name
  190. install_path = "/data/apps/" + customer_app_name
  191. message = " "
  192. code = const.RETURN_SUCCESS
  193. output = shell_execute.execute_command_output_all("cp -r " + library_path + " " + install_path)
  194. if int(output["code"]) != 0:
  195. message = "creating" + customer_app_name + "directory failed!"
  196. code = const.RETURN_FAIL
  197. return code, message
  198. def install_app_job(customer_app_name, app_version):
  199. # write running_apps.txt
  200. file_path = "/data/apps/running_apps.txt"
  201. shell_execute.execute_command_output_all("echo " + customer_app_name + " >> " + file_path)
  202. # modify env
  203. env_path = "/data/apps/" + customer_app_name + "/.env"
  204. docker.modify_env(env_path, 'APP_NAME', customer_app_name)
  205. docker.modify_env(env_path, "APP_VERSION", app_version)
  206. # check port
  207. docker.check_app_compose(env_path)
  208. # modify running_apps.txt
  209. cmd = "cd /data/apps/" + customer_app_name + " && sudo docker compose up --pull always -d"
  210. shell_execute.execute_command_output_all(cmd)
  211. # delete
  212. output = shell_execute.execute_command_output_all("sed -n \'/^" + customer_app_name + "/=\' " + file_path)
  213. if int(output["code"]) == 0 and output["result"] != "":
  214. line_num = output["result"].split("\n")[0]
  215. shell_execute.execute_command_output_all("sed -i \'" + line_num + "d\' " + file_path)
  216. def if_app_exits(app_name):
  217. info = ""
  218. cmd = "docker compose ls -a | grep \'/" + app_name + "/\'"
  219. output = shell_execute.execute_command_output_all(cmd)
  220. if int(output["code"]) == -1:
  221. return info, False
  222. else:
  223. info = output["result"]
  224. myLogger.info_logger("APP info: " + info)
  225. return info, True
  226. def split_app_id(app_id):
  227. return app_id.split("_")[1]
  228. def get_apps_from_compose(output_list):
  229. ip_result = shell_execute.execute_command_output_all("curl ifconfig.me")
  230. ip = ip_result["result"]
  231. app_list = []
  232. has_add = []
  233. for app_info in output_list:
  234. volume = app_info["ConfigFiles"] # volume
  235. app_path = volume.rsplit('/', 1)[0]
  236. app_name = volume.split('/')[-2]
  237. app_id = app_name + "_" + app_name # app_id
  238. real_name = ""
  239. trade_mark = ""
  240. port = 0
  241. url = ""
  242. admin_url = ""
  243. image_url = ""
  244. user_name = ""
  245. password = ""
  246. official_app = False
  247. if app_name in ['appmanage', 'nginxproxymanager']:
  248. continue
  249. # get code
  250. case = app_info["Status"].split("(")[0] # case
  251. if case == "running":
  252. case_code = const.APP_RUNNING # case_code
  253. elif case == "exited":
  254. case = "stop"
  255. case_code = const.APP_STOP
  256. elif case == "created":
  257. case_code = const.APP_READY
  258. case = "installing"
  259. else:
  260. case_code = const.APP_ERROR
  261. var_path = app_path + "/variables.json"
  262. official_app = check_if_official_app(var_path)
  263. if official_app:
  264. real_name = docker.read_var(var_path, 'name')
  265. app_id = real_name + "_" + app_name # app_id
  266. # get trade_mark
  267. trade_mark = docker.read_var(var_path, 'trademark')
  268. image_url = get_Image_url(real_name)
  269. # get env info
  270. path = app_path + "/.env"
  271. # get port and url
  272. try:
  273. http_port = list(docker.read_env(
  274. path, "APP_HTTP_PORT").values())[0]
  275. port = int(http_port)
  276. easy_url = "http://" + ip + ":" + str(port)
  277. url = get_url(real_name, easy_url)
  278. admin_url = get_admin_url(real_name, url)
  279. except IndexError:
  280. try:
  281. db_port = list(docker.read_env(
  282. path, "APP_DB.*_PORT").values())[0]
  283. port = int(db_port)
  284. except IndexError:
  285. pass
  286. # get user_name
  287. try:
  288. user_name = list(docker.read_env(path, "APP_USER").values())[0]
  289. except IndexError:
  290. pass
  291. # get password
  292. try:
  293. password = list(docker.read_env(
  294. path, "POWER_PASSWORD").values())[0]
  295. except IndexError:
  296. pass
  297. has_add.append(app_name)
  298. app = App(app_id=app_id, name=real_name, customer_name=app_name, status_code=case_code, status=case, port=port,
  299. volume=volume, url=url,
  300. image_url=image_url, admin_url=admin_url, trade_mark=trade_mark, user_name=user_name,
  301. password=password, official_app=official_app)
  302. app_list.append(app.dict())
  303. return app_list, has_add
  304. def check_if_official_app(var_path):
  305. if docker.check_directory(var_path):
  306. if docker.read_var(var_path, 'name') != "" and docker.read_var(var_path, 'trademark') != "" and docker.read_var(
  307. var_path, 'requirements') != "":
  308. requirements = docker.read_var(var_path, 'requirements')
  309. try:
  310. cpu = requirements['cpu']
  311. mem = requirements['memory']
  312. return True
  313. except:
  314. return False
  315. else:
  316. return False
  317. def get_apps_from_queue(app_list, has_add):
  318. file_path = "/data/apps/running_apps.txt"
  319. if docker.check_directory(file_path):
  320. output = shell_execute.execute_command_output_all("cat " + file_path)
  321. apps = output["result"].split("\n")
  322. for running_app_name in apps:
  323. running_app_name = re.sub("\n", "", running_app_name)
  324. if running_app_name not in has_add and running_app_name != "":
  325. var_path = "/data/apps/" + running_app_name + "/variables.json"
  326. trade_mark = docker.read_var(var_path, 'trademark')
  327. real_name = docker.read_var(var_path, 'name')
  328. image_url = get_Image_url(real_name)
  329. app = App(app_id=real_name + "_" + running_app_name, name=real_name, customer_name=running_app_name,
  330. status_code=const.APP_READY, status="installing", port=0, volume="",
  331. url="", image_url=image_url, admin_url="", trade_mark=trade_mark, user_name="",
  332. password="", official_app=True)
  333. app_list.append(app.dict())
  334. return app_list
  335. def get_Image_url(app_name):
  336. image_url = "static/images/" + app_name + "-websoft9.png"
  337. return image_url
  338. def get_url(app_name, easy_url):
  339. url = easy_url
  340. if app_name == "joomla":
  341. url = easy_url + "/administrator"
  342. elif app_name == "other":
  343. url = easy_url + "/administrator"
  344. else:
  345. url = easy_url
  346. return url
  347. def get_admin_url(app_name, url):
  348. admin_url = ""
  349. if app_name == "wordpress":
  350. admin_url = url + "/wp-admin"
  351. elif app_name == "other":
  352. admin_url = url + "/admin"
  353. else:
  354. admin_url = ""
  355. return admin_url