manage.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. from ast import Constant
  2. import os
  3. import io
  4. import sys
  5. import platform
  6. import shutil
  7. import time
  8. import subprocess
  9. import requests
  10. import json
  11. import datetime
  12. import socket
  13. import re
  14. from threading import Thread
  15. from api.utils import shell_execute, docker, const
  16. from api.model.app import App
  17. from api.service import db
  18. from api.model.response import Response
  19. from api.model.config import Config
  20. from api.model.status_reason import StatusReason
  21. from api.utils.common_log import myLogger
  22. from redis import Redis
  23. from rq import Queue, Worker, Connection
  24. from rq.registry import StartedJobRegistry, FinishedJobRegistry, DeferredJobRegistry, FailedJobRegistry, ScheduledJobRegistry, CanceledJobRegistry
  25. from api.exception.command_exception import CommandException
  26. from apscheduler.schedulers.background import BackgroundScheduler
  27. from apscheduler.triggers.date import DateTrigger
  28. from apscheduler.triggers.cron import CronTrigger
  29. from apscheduler.triggers.interval import IntervalTrigger
  30. from apscheduler.events import EVENT_SCHEDULER_PAUSED, EVENT_SCHEDULER_RESUMED,EVENT_SCHEDULER_STARTED,EVENT_SCHEDULER_SHUTDOWN
  31. # 指定 Redis 容器的主机名和端口
  32. redis_conn = Redis(host='websoft9-redis', port=6379)
  33. # 使用指定的 Redis 连接创建 RQ 队列
  34. q = Queue(connection=redis_conn, default_timeout=3600)
  35. def auto_update():
  36. myLogger.info_logger("auto update start...")
  37. local_path = '/usr/share/cockpit/appstore/appstore.json'
  38. local_version = "0"
  39. try:
  40. op = shell_execute.execute_command_output_all("cat " + local_path)['result']
  41. local_version = json.loads(op)['Version']
  42. except:
  43. local_version = "0.0.0"
  44. download_url = const.ARTIFACT_URL + "/plugin/appstore-latest.zip"
  45. cmd = "cd /opt && wget " + download_url + " && unzip appstore-latest.zip "
  46. shell_execute.execute_command_output_all(cmd)
  47. latest = shell_execute.execute_command_output_all("cat /opt/appstore/appstore.json")
  48. version = json.loads(latest)['Version']
  49. ret = {}
  50. ret['current_version'] = local_version
  51. if compared_version(local_version, version) == -1:
  52. shell_execute.execute_command_output_all("rm -rf /usr/share/cockpit/appstore && cp /opt/appstore /usr/share/cockpit")
  53. shell_execute.execute_command_output_all("rm -rf /opt/appstore*")
  54. library_url = const.ARTIFACT_URL + "/plugin/library-latest.zip"
  55. library_cmd = "cd /opt && wget " + library_url + " && unzip library-latest.zip "
  56. shell_execute.execute_command_output_all(library_cmd)
  57. shell_execute.execute_command_output_all("rm -rf /data/library && cp /opt/library /data")
  58. shell_execute.execute_command_output_all("rm -rf /opt/library*")
  59. myLogger.info_logger("auto update success...")
  60. else:
  61. shell_execute.execute_command_output_all("rm -rf /opt/appstore*")
  62. myLogger.info_logger("Appstore is latest, not need to update.")
  63. scheduler = BackgroundScheduler()
  64. scheduler.add_job(auto_update, 'cron', hour=1)
  65. scheduler.start()
  66. # 获取github文件内容
  67. def get_github_content(repo, path):
  68. url = 'https://websoft9.github.io/{repo}/{path}'
  69. url = url.format(repo=repo, path=path)
  70. response = requests.get(url)
  71. response.encoding = 'utf-8'
  72. contents = response.text
  73. return contents
  74. def AppAutoUpdate(auto_update):
  75. myLogger.info_logger(scheduler.state)
  76. myLogger.info_logger(scheduler.get_jobs())
  77. if auto_update == "true" or auto_update == "True":
  78. scheduler.resume()
  79. return "true"
  80. elif auto_update == "false" or auto_update == "False":
  81. scheduler.pause()
  82. return "false"
  83. elif auto_update == None or auto_update == "" or auto_update == "undefine":
  84. state = scheduler.state
  85. if state == 1:
  86. return "true"
  87. else:
  88. return "false"
  89. # 更新软件商店
  90. def AppStoreUpdate():
  91. local_path = '/data/library/install/version.json'
  92. local_version = "0"
  93. try:
  94. op = shell_execute.execute_command_output_all("cat " + local_path)['result']
  95. local_version = json.loads(op)['VERSION']
  96. except:
  97. local_version = "0.0.1"
  98. repo = 'docker-library'
  99. version_contents = get_github_content(repo, 'install/version.json')
  100. version = json.loads(version_contents)['VERSION']
  101. if compared_version(local_version, version) == -1:
  102. content = []
  103. change_log_contents = get_github_content(repo, 'CHANGELOG.md')
  104. change_log = change_log_contents.split('## ')[1].split('\n')
  105. for change in change_log[1:]:
  106. if change != '':
  107. content.append(change)
  108. shell_execute.execute_command_output_all("rm -rf /tmp/update_appstore.sh")
  109. shell_execute.execute_command_output_all("cd /tmp && wget https://websoft9.github.io/websoft9/install/update_appstore.sh")
  110. shell_execute.execute_command_output_all("bash /tmp/update_appstore.sh 1>/dev/null 2>&1")
  111. return content
  112. else:
  113. return None
  114. # 获取 update info
  115. def get_update_list():
  116. local_path = '/data/apps/websoft9/install/version.json'
  117. repo = 'websoft9'
  118. local_version = "0"
  119. try:
  120. op = shell_execute.execute_command_output_all("cat " + local_path)['result']
  121. local_version = json.loads(op)['VERSION']
  122. except:
  123. local_version = "0.0.1"
  124. version_contents = get_github_content(repo, 'install/version.json')
  125. version = json.loads(version_contents)['VERSION']
  126. ret = {}
  127. ret['current_version'] = local_version
  128. if compared_version(local_version, version) == -1:
  129. content = []
  130. change_log_contents = get_github_content(repo, 'CHANGELOG.md')
  131. change_log = change_log_contents.split('## ')[1].split('\n')
  132. date = change_log[0].split()[-1]
  133. for change in change_log[1:]:
  134. if change != '':
  135. content.append(change)
  136. ret2= {}
  137. ret2['latest_version'] = version
  138. ret2['date'] = date
  139. ret2['content'] = content
  140. ret['Update_content']=ret2
  141. return ret
  142. else:
  143. ret['Update_content']=None
  144. return ret
  145. # 获取 appstore update info
  146. def get_appstore_update_list():
  147. local_path = '/usr/share/cockpit/appstore/appstore.json'
  148. local_version = "0"
  149. try:
  150. op = shell_execute.execute_command_output_all("cat " + local_path)['result']
  151. local_version = json.loads(op)['Version']
  152. except:
  153. local_version = "0.0.0"
  154. download_url = const.ARTIFACT_URL + "/plugin/appstore-latest.zip"
  155. cmd = "cd /opt && wget " + download_url + " && unzip appstore-latest.zip "
  156. shell_execute.execute_command_output_all(cmd)
  157. latest = shell_execute.execute_command_output_all("cat /opt/appstore/appstore.json")
  158. version = json.loads(latest)['Version']
  159. ret = {}
  160. ret['current_version'] = local_version
  161. if compared_version(local_version, version) == -1:
  162. content = []
  163. change_log_contents = shell_execute.execute_command_output_all("cat /opt/appstore/CHANGELOG.md")['result']
  164. change_log = change_log_contents.split('## ')[1].split('\n')
  165. date = change_log[0].split()[-1]
  166. for change in change_log[1:]:
  167. if change != '':
  168. content.append(change)
  169. ret2= {}
  170. ret2['latest_version'] = version
  171. ret2['date'] = date
  172. ret2['content'] = content
  173. ret['Update_content']=ret2
  174. shell_execute.execute_command_output_all("rm -rf /opt/appstore*")
  175. return ret
  176. else:
  177. ret['Update_content']=None
  178. shell_execute.execute_command_output_all("rm -rf /opt/appstore*")
  179. return ret
  180. def conbine_list(installing_list, installed_list):
  181. app_list = installing_list + installed_list
  182. result_list = []
  183. appid_list = []
  184. for app in app_list:
  185. app_id = app['app_id']
  186. if app_id in appid_list:
  187. continue
  188. else:
  189. appid_list.append(app_id)
  190. result_list.append(app)
  191. return result_list
  192. # 获取所有app的信息
  193. def get_my_app(app_id):
  194. installed_list = get_apps_from_compose()
  195. installing_list = get_apps_from_queue()
  196. app_list = conbine_list(installing_list, installed_list)
  197. find = False
  198. ret = {}
  199. if app_id != None:
  200. for app in app_list:
  201. if app_id == app['app_id']:
  202. ret = app
  203. find = True
  204. break
  205. if not find:
  206. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "This App doesn't exist!", "")
  207. else:
  208. ret = app_list
  209. myLogger.info_logger("app list result ok")
  210. return ret
  211. # 获取具体某个app的信息
  212. def get_app_status(app_id):
  213. code, message = docker.check_app_id(app_id)
  214. if code == None:
  215. app = get_my_app(app_id)
  216. # 将app_list 过滤出app_id的app,并缩减信息,使其符合文档的要求
  217. ret = {}
  218. ret['app_id'] = app['app_id']
  219. ret['status'] = app['status']
  220. ret['status_reason'] = app['status_reason']
  221. else:
  222. raise CommandException(code, message, '')
  223. return ret
  224. def install_app(app_name, customer_name, app_version):
  225. myLogger.info_logger("Install app ...")
  226. ret = {}
  227. ret['ResponseData'] = {}
  228. app_id = app_name + "_" + customer_name
  229. ret['ResponseData']['app_id'] = app_id
  230. code, message = check_app(app_name, customer_name, app_version)
  231. if code == None:
  232. q.enqueue(install_app_delay, app_name, customer_name, app_version, job_id=app_id)
  233. else:
  234. ret['Error'] = get_error_info(code, message, "")
  235. return ret
  236. def start_app(app_id):
  237. info, flag = app_exits_in_docker(app_id)
  238. if flag:
  239. app_path = info.split()[-1].rsplit('/', 1)[0]
  240. cmd = "docker compose -f " + app_path + "/docker-compose.yml start"
  241. shell_execute.execute_command_output_all(cmd)
  242. else:
  243. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  244. def stop_app(app_id):
  245. info, flag = app_exits_in_docker(app_id)
  246. if flag:
  247. app_path = info.split()[-1].rsplit('/', 1)[0]
  248. cmd = "docker compose -f " + app_path + "/docker-compose.yml stop"
  249. shell_execute.execute_command_output_all(cmd)
  250. else:
  251. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  252. def restart_app(app_id):
  253. code, message = docker.check_app_id(app_id)
  254. if code == None:
  255. info, flag = app_exits_in_docker(app_id)
  256. if flag:
  257. app_path = info.split()[-1].rsplit('/', 1)[0]
  258. cmd = "docker compose -f " + app_path + "/docker-compose.yml restart"
  259. shell_execute.execute_command_output_all(cmd)
  260. else:
  261. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  262. else:
  263. raise CommandException(code, message, "")
  264. def delete_app_failedjob(job_id):
  265. myLogger.info_logger("delete_app_failedjob")
  266. failed = FailedJobRegistry(queue=q)
  267. failed.remove(job_id, delete_job=True)
  268. def delete_app(app_id):
  269. try:
  270. app_name = app_id.split('_')[0]
  271. customer_name = app_id.split('_')[1]
  272. app_path = ""
  273. info, code_exist = app_exits_in_docker(app_id)
  274. if code_exist:
  275. app_path = info.split()[-1].rsplit('/', 1)[0]
  276. cmd = "docker compose -f " + app_path + "/docker-compose.yml down -v"
  277. lib_path = '/data/library/apps/' + app_name
  278. if app_path != lib_path:
  279. cmd = cmd + " && sudo rm -rf " + app_path
  280. try:
  281. myLogger.info_logger("Intall fail, down app and delete files")
  282. shell_execute.execute_command_output_all(cmd)
  283. except Exception:
  284. myLogger.info_logger("Delete app compose exception")
  285. # 强制删除失败又无法通过docker compose down 删除的容器
  286. try:
  287. myLogger.info_logger("IF delete fail, force to delete containers")
  288. force_cmd = "docker rm -f $(docker ps -f name=^" + customer_name + " -aq)"
  289. shell_execute.execute_command_output_all(force_cmd)
  290. except Exception:
  291. myLogger.info_logger("force delete app compose exception")
  292. else:
  293. if check_app_rq(app_id):
  294. delete_app_failedjob(app_id)
  295. else:
  296. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "AppID is not exist", "")
  297. cmd = " sudo rm -rf /data/apps/" + customer_name
  298. shell_execute.execute_command_output_all(cmd)
  299. except CommandException as ce:
  300. myLogger.info_logger("Delete app compose exception")
  301. def uninstall_app(app_id):
  302. app_name = app_id.split('_')[0]
  303. customer_name = app_id.split('_')[1]
  304. app_path = ""
  305. info, code_exist = app_exits_in_docker(app_id)
  306. if code_exist:
  307. app_path = info.split()[-1].rsplit('/', 1)[0]
  308. cmd = "docker compose -f " + app_path + "/docker-compose.yml down -v"
  309. lib_path = '/data/library/apps/' + app_name
  310. if app_path != lib_path:
  311. cmd = cmd + " && sudo rm -rf " + app_path
  312. shell_execute.execute_command_output_all(cmd)
  313. else:
  314. if check_app_rq(app_id):
  315. delete_app_failedjob(app_id)
  316. else:
  317. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "AppID is not exist", "")
  318. # Force to delete docker compose
  319. try:
  320. cmd = " sudo rm -rf /data/apps/" + customer_name
  321. shell_execute.execute_command_output_all(cmd)
  322. except CommandException as ce:
  323. myLogger.info_logger("Delete app compose exception")
  324. # Delete proxy config when uninstall app
  325. app_proxy_delete(app_id)
  326. def check_app(app_name, customer_name, app_version):
  327. message = ""
  328. code = None
  329. app_id = app_name + "_" + customer_name
  330. if app_name == None:
  331. code = const.ERROR_CLIENT_PARAM_BLANK
  332. message = "app_name is null"
  333. elif customer_name == None:
  334. code = const.ERROR_CLIENT_PARAM_BLANK
  335. message = "customer_name is null"
  336. elif len(customer_name) < 2:
  337. code = const.ERROR_CLIENT_PARAM_BLANK
  338. message = "customer_name must be longer than 2 chars"
  339. elif app_version == None:
  340. code = const.ERROR_CLIENT_PARAM_BLANK
  341. message = "app_version is null"
  342. elif app_version == "undefined" or app_version == "":
  343. code = const.ERROR_CLIENT_PARAM_BLANK
  344. message = "app_version is null"
  345. elif not docker.check_app_websoft9(app_name):
  346. code = const.ERROR_CLIENT_PARAM_NOTEXIST
  347. message = "It is not support to install " + app_name
  348. elif re.match('^[a-z0-9]+$', customer_name) == None:
  349. code = const.ERROR_CLIENT_PARAM_Format
  350. message = "APP name can only be composed of numbers and lowercase letters"
  351. elif docker.check_directory("/data/apps/" + customer_name):
  352. code = const.ERROR_CLIENT_PARAM_REPEAT
  353. message = "Repeat installation: " + customer_name
  354. elif not docker.check_vm_resource(app_name):
  355. code = const.ERROR_SERVER_RESOURCE
  356. message = "Insufficient system resources (cpu, memory, disk space)"
  357. elif check_app_docker(app_id):
  358. code = const.ERROR_CLIENT_PARAM_REPEAT
  359. message = "Repeat installation: " + customer_name
  360. elif check_app_rq(app_id):
  361. code = const.ERROR_CLIENT_PARAM_REPEAT
  362. message = "Repeat installation: " + customer_name
  363. return code, message
  364. def prepare_app(app_name, customer_name):
  365. library_path = "/data/library/apps/" + app_name
  366. install_path = "/data/apps/" + customer_name
  367. shell_execute.execute_command_output_all("cp -r " + library_path + " " + install_path)
  368. def install_app_delay(app_name, customer_name, app_version):
  369. myLogger.info_logger("-------RQ install start --------")
  370. job_id = app_name + "_" + customer_name
  371. try:
  372. # 因为这个时候还没有复制文件夹,是从/data/library里面文件读取json来检查的,应该是app_name,而不是customer_name
  373. resource_flag = docker.check_vm_resource(app_name)
  374. if resource_flag == True:
  375. myLogger.info_logger("job check ok, continue to install app")
  376. env_path = "/data/apps/" + customer_name + "/.env"
  377. # prepare_app(app_name, customer_name)
  378. docker.check_app_compose(app_name, customer_name)
  379. myLogger.info_logger("start JobID=" + job_id)
  380. docker.modify_env(env_path, 'APP_NAME', customer_name)
  381. docker.modify_env(env_path, "APP_VERSION", app_version)
  382. docker.check_app_url(customer_name)
  383. cmd = "cd /data/apps/" + customer_name + " && sudo docker compose pull && sudo docker compose up -d"
  384. output = shell_execute.execute_command_output_all(cmd)
  385. myLogger.info_logger("-------Install result--------")
  386. myLogger.info_logger(output["code"])
  387. myLogger.info_logger(output["result"])
  388. try:
  389. shell_execute.execute_command_output_all("bash /data/apps/" + customer_name + "/src/after_up.sh")
  390. except Exception as e:
  391. myLogger.info_logger(str(e))
  392. else:
  393. error_info = "##websoft9##" + const.ERROR_SERVER_RESOURCE + "##websoft9##" + "Insufficient system resources (cpu, memory, disk space)" + "##websoft9##" + "Insufficient system resources (cpu, memory, disk space)"
  394. myLogger.info_logger(error_info)
  395. raise Exception(error_info)
  396. except CommandException as ce:
  397. myLogger.info_logger(customer_name + " install failed(docker)!")
  398. delete_app(job_id)
  399. error_info = "##websoft9##" + ce.code + "##websoft9##" + ce.message + "##websoft9##" + ce.detail
  400. myLogger.info_logger(error_info)
  401. raise Exception(error_info)
  402. except Exception as e:
  403. myLogger.info_logger(customer_name + " install failed(system)!")
  404. delete_app(job_id)
  405. error_info = "##websoft9##" + const.ERROR_SERVER_SYSTEM + "##websoft9##" + 'system original error' + "##websoft9##" + str(
  406. e)
  407. myLogger.info_logger(error_info)
  408. raise Exception(error_info)
  409. def app_exits_in_docker(app_id):
  410. customer_name = app_id.split('_')[1]
  411. app_name = app_id.split('_')[0]
  412. flag = False
  413. info = ""
  414. cmd = "docker compose ls -a | grep \'/" + customer_name + "/\'"
  415. try:
  416. output = shell_execute.execute_command_output_all(cmd)
  417. if int(output["code"]) == 0:
  418. info = output["result"]
  419. app_path = info.split()[-1].rsplit('/', 1)[0]
  420. is_official = check_if_official_app(app_path + '/variables.json')
  421. if is_official:
  422. name = docker.read_var(app_path + '/variables.json', 'name')
  423. if name == app_name:
  424. flag = True
  425. elif app_name == customer_name:
  426. flag = True
  427. myLogger.info_logger("APP in docker")
  428. except CommandException as ce:
  429. myLogger.info_logger("APP not in docker")
  430. return info, flag
  431. def split_app_id(app_id):
  432. return app_id.split("_")[1]
  433. def get_createtime(official_app, app_path, customer_name):
  434. data_time = ""
  435. try:
  436. if official_app:
  437. cmd = "docker ps -f name=" + customer_name + " --format {{.RunningFor}} | head -n 1"
  438. result = shell_execute.execute_command_output_all(cmd)["result"].rstrip('\n')
  439. data_time = result
  440. else:
  441. cmd_all = "cd " + app_path + " && docker compose ps -a --format json"
  442. output = shell_execute.execute_command_output_all(cmd_all)
  443. container_name = json.loads(output["result"])[0]["Name"]
  444. cmd = "docker ps -f name=" + container_name + " --format {{.RunningFor}} | head -n 1"
  445. result = shell_execute.execute_command_output_all(cmd)["result"].rstrip('\n')
  446. data_time = result
  447. except Exception as e:
  448. myLogger.info_logger(str(e))
  449. myLogger.info_logger("get_createtime get success" + data_time)
  450. return data_time
  451. def get_apps_from_compose():
  452. myLogger.info_logger("Search all of apps ...")
  453. cmd = "docker compose ls -a --format json"
  454. output = shell_execute.execute_command_output_all(cmd)
  455. output_list = json.loads(output["result"])
  456. myLogger.info_logger(len(output_list))
  457. ip = "localhost"
  458. try:
  459. ip_result = shell_execute.execute_command_output_all("cat /data/apps/w9services/w9appmanage/public_ip")
  460. ip = ip_result["result"].rstrip('\n')
  461. except Exception:
  462. ip = "127.0.0.1"
  463. app_list = []
  464. for app_info in output_list:
  465. volume = app_info["ConfigFiles"]
  466. app_path = volume.rsplit('/', 1)[0]
  467. customer_name = volume.split('/')[-2]
  468. app_id = ""
  469. app_name = ""
  470. trade_mark = ""
  471. port = 0
  472. url = ""
  473. admin_url = ""
  474. image_url = ""
  475. user_name = ""
  476. password = ""
  477. official_app = False
  478. app_version = ""
  479. create_time = ""
  480. volume_data = ""
  481. config_path = app_path
  482. app_https = False
  483. app_replace_url = False
  484. default_domain = ""
  485. admin_path = ""
  486. admin_domain_url = ""
  487. if customer_name in ['w9appmanage', 'w9nginxproxymanager', 'w9redis', 'w9kopia',
  488. 'w9portainer'] or app_path == '/data/apps/w9services/' + customer_name:
  489. continue
  490. var_path = app_path + "/variables.json"
  491. official_app = check_if_official_app(var_path)
  492. status_show = app_info["Status"]
  493. status = app_info["Status"].split("(")[0]
  494. if status == "running" or status == "exited" or status == "restarting":
  495. if "exited" in status_show and "running" in status_show:
  496. if status == "exited":
  497. cmd = "docker ps -a -f name=" + customer_name + " --format {{.Names}}#{{.Status}}|grep Exited"
  498. result = shell_execute.execute_command_output_all(cmd)["result"].rstrip('\n')
  499. container = result.split("#Exited")[0]
  500. if container != customer_name:
  501. status = "running"
  502. if "restarting" in status_show:
  503. about_time = get_createtime(official_app, app_path, customer_name)
  504. if "seconds" in about_time:
  505. status = "restarting"
  506. else:
  507. status = "failed"
  508. elif status == "created":
  509. status = "failed"
  510. else:
  511. continue
  512. if official_app:
  513. app_name = docker.read_var(var_path, 'name')
  514. app_id = app_name + "_" + customer_name # app_id
  515. # get trade_mark
  516. trade_mark = docker.read_var(var_path, 'trademark')
  517. image_url = get_Image_url(app_name)
  518. # get env info
  519. path = app_path + "/.env"
  520. env_map = docker.get_map(path)
  521. try:
  522. myLogger.info_logger("get domain for APP_URL")
  523. domain = env_map.get("APP_URL")
  524. if "appname.example.com" in domain or ip in domain:
  525. default_domain = ""
  526. else:
  527. default_domain = domain
  528. except Exception:
  529. myLogger.info_logger("domain exception")
  530. try:
  531. app_version = env_map.get("APP_VERSION")
  532. volume_data = "/data/apps/" + customer_name + "/data"
  533. user_name = env_map.get("APP_USER", "")
  534. password = env_map.get("POWER_PASSWORD", "")
  535. admin_path = env_map.get("APP_ADMIN_PATH")
  536. if admin_path:
  537. myLogger.info_logger(admin_path)
  538. admin_path = admin_path.replace("\"", "")
  539. else:
  540. admin_path = ""
  541. if default_domain != "" and admin_path != "":
  542. admin_domain_url = "http://" + default_domain + admin_path
  543. except Exception:
  544. myLogger.info_logger("APP_USER POWER_PASSWORD exception")
  545. try:
  546. replace = env_map.get("APP_URL_REPLACE", "false")
  547. myLogger.info_logger("replace=" + replace)
  548. if replace == "true":
  549. app_replace_url = True
  550. https = env_map.get("APP_HTTPS_ACCESS", "false")
  551. if https == "true":
  552. app_https = True
  553. except Exception:
  554. myLogger.info_logger("APP_HTTPS_ACCESS exception")
  555. try:
  556. http_port = env_map.get("APP_HTTP_PORT", "0")
  557. if http_port:
  558. port = int(http_port)
  559. except Exception:
  560. pass
  561. if port != 0:
  562. try:
  563. if app_https:
  564. easy_url = "https://" + ip + ":" + str(port)
  565. else:
  566. easy_url = "http://" + ip + ":" + str(port)
  567. url = easy_url
  568. admin_url = get_admin_url(customer_name, url)
  569. except Exception:
  570. pass
  571. else:
  572. try:
  573. db_port = list(docker.read_env(path, "APP_DB.*_PORT").values())[0]
  574. port = int(db_port)
  575. except Exception:
  576. pass
  577. else:
  578. app_name = customer_name
  579. app_id = customer_name + "_" + customer_name
  580. create_time = get_createtime(official_app, app_path, customer_name)
  581. if status in ['running', 'exited']:
  582. config = Config(port=port, compose_file=volume, url=url, admin_url=admin_url,
  583. admin_domain_url=admin_domain_url,
  584. admin_path=admin_path, admin_username=user_name, admin_password=password,
  585. default_domain=default_domain)
  586. else:
  587. config = None
  588. if status == "failed":
  589. status_reason = StatusReason(Code=const.ERROR_SERVER_SYSTEM, Message="system original error",
  590. Detail="unknown error")
  591. else:
  592. status_reason = None
  593. app = App(app_id=app_id, app_name=app_name, customer_name=customer_name, trade_mark=trade_mark,
  594. app_version=app_version, create_time=create_time, volume_data=volume_data, config_path=config_path,
  595. status=status, status_reason=status_reason, official_app=official_app, image_url=image_url,
  596. app_https=app_https, app_replace_url=app_replace_url, config=config)
  597. app_list.append(app.dict())
  598. return app_list
  599. def check_if_official_app(var_path):
  600. if docker.check_directory(var_path):
  601. if docker.read_var(var_path, 'name') != "" and docker.read_var(var_path, 'trademark') != "" and docker.read_var(
  602. var_path, 'requirements') != "":
  603. requirements = docker.read_var(var_path, 'requirements')
  604. try:
  605. cpu = requirements['cpu']
  606. mem = requirements['memory']
  607. disk = requirements['disk']
  608. return True
  609. except KeyError:
  610. return False
  611. else:
  612. return False
  613. def check_app_docker(app_id):
  614. customer_name = app_id.split('_')[1]
  615. app_name = app_id.split('_')[0]
  616. flag = False
  617. cmd = "docker compose ls -a | grep \'/" + customer_name + "/\'"
  618. try:
  619. shell_execute.execute_command_output_all(cmd)
  620. flag = True
  621. myLogger.info_logger("APP in docker")
  622. except CommandException as ce:
  623. myLogger.info_logger("APP not in docker")
  624. return flag
  625. def check_app_rq(app_id):
  626. myLogger.info_logger("check_app_rq")
  627. started = StartedJobRegistry(queue=q)
  628. failed = FailedJobRegistry(queue=q)
  629. run_job_ids = started.get_job_ids()
  630. failed_job_ids = failed.get_job_ids()
  631. queue_job_ids = q.job_ids
  632. myLogger.info_logger(queue_job_ids)
  633. myLogger.info_logger(run_job_ids)
  634. myLogger.info_logger(failed_job_ids)
  635. if queue_job_ids and app_id in queue_job_ids:
  636. myLogger.info_logger("App in RQ")
  637. return True
  638. if failed_job_ids and app_id in failed_job_ids:
  639. myLogger.info_logger("App in RQ")
  640. return True
  641. if run_job_ids and app_id in run_job_ids:
  642. myLogger.info_logger("App in RQ")
  643. return True
  644. myLogger.info_logger("App not in RQ")
  645. return False
  646. def get_apps_from_queue():
  647. myLogger.info_logger("get queque apps...")
  648. # 获取 StartedJobRegistry 实例
  649. started = StartedJobRegistry(queue=q)
  650. finish = FinishedJobRegistry(queue=q)
  651. deferred = DeferredJobRegistry(queue=q)
  652. failed = FailedJobRegistry(queue=q)
  653. scheduled = ScheduledJobRegistry(queue=q)
  654. cancel = CanceledJobRegistry(queue=q)
  655. # 获取正在执行的作业 ID 列表
  656. run_job_ids = started.get_job_ids()
  657. finish_job_ids = finish.get_job_ids()
  658. wait_job_ids = deferred.get_job_ids()
  659. failed_jobs = failed.get_job_ids()
  660. scheduled_jobs = scheduled.get_job_ids()
  661. cancel_jobs = cancel.get_job_ids()
  662. myLogger.info_logger(q.jobs)
  663. myLogger.info_logger(run_job_ids)
  664. myLogger.info_logger(failed_jobs)
  665. myLogger.info_logger(cancel_jobs)
  666. myLogger.info_logger(wait_job_ids)
  667. myLogger.info_logger(finish_job_ids)
  668. myLogger.info_logger(scheduled_jobs)
  669. installing_list = []
  670. for job_id in run_job_ids:
  671. app = get_rq_app(job_id, 'installing', "", "", "")
  672. installing_list.append(app)
  673. for job in q.jobs:
  674. app = get_rq_app(job.id, 'installing', "", "", "")
  675. installing_list.append(app)
  676. for job_id in failed_jobs:
  677. job = q.fetch_job(job_id)
  678. exc_info = job.exc_info
  679. code = exc_info.split('##websoft9##')[1]
  680. message = exc_info.split('##websoft9##')[2]
  681. detail = exc_info.split('##websoft9##')[3]
  682. app = get_rq_app(job_id, 'failed', code, message, detail)
  683. installing_list.append(app)
  684. return installing_list
  685. def get_rq_app(id, status, code, message, detail):
  686. app_name = id.split('_')[0]
  687. customer_name = id.split('_')[1]
  688. # 当app还在RQ时,可能文件夹还没创建,无法获取trade_mark
  689. trade_mark = ""
  690. app_version = ""
  691. create_time = ""
  692. volume_data = ""
  693. config_path = ""
  694. image_url = get_Image_url(app_name)
  695. config = None
  696. if status == "installing":
  697. status_reason = None
  698. else:
  699. status_reason = StatusReason(Code=code, Message=message, Detail=detail)
  700. app = App(app_id=id, app_name=app_name, customer_name=customer_name, trade_mark=trade_mark,
  701. app_version=app_version, create_time=create_time, volume_data=volume_data, config_path=config_path,
  702. status=status, status_reason=status_reason, official_app=True, image_url=image_url,
  703. app_https=False, app_replace_url=False, config=config)
  704. return app.dict()
  705. def get_Image_url(app_name):
  706. image_url = "static/images/" + app_name + "-websoft9.png"
  707. return image_url
  708. def get_url(app_name, easy_url):
  709. url = easy_url
  710. return url
  711. def get_admin_url(customer_name, url):
  712. admin_url = ""
  713. path = "/data/apps/" + customer_name + "/.env"
  714. try:
  715. admin_path = list(docker.read_env(path, "APP_ADMIN_PATH").values())[0]
  716. admin_path = admin_path.replace("\"", "")
  717. admin_url = url + admin_path
  718. except IndexError:
  719. pass
  720. return admin_url
  721. def get_error_info(code, message, detail):
  722. error = {}
  723. error['Code'] = code
  724. error['Message'] = message
  725. error['Detail'] = detail
  726. return error
  727. def app_domain_list(app_id):
  728. code, message = docker.check_app_id(app_id)
  729. if code == None:
  730. info, flag = app_exits_in_docker(app_id)
  731. if flag:
  732. myLogger.info_logger("Check app_id ok[app_domain_list]")
  733. else:
  734. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  735. else:
  736. raise CommandException(code, message, "")
  737. domains = get_all_domains(app_id)
  738. myLogger.info_logger(domains)
  739. ret = {}
  740. ret['domains'] = domains
  741. default_domain = ""
  742. if domains != None and len(domains) > 0:
  743. customer_name = app_id.split('_')[1]
  744. app_url = shell_execute.execute_command_output_all("cat /data/apps/" + customer_name + "/.env")["result"]
  745. if "APP_URL" in app_url:
  746. url = shell_execute.execute_command_output_all("cat /data/apps/" + customer_name + "/.env |grep APP_URL=")[
  747. "result"].rstrip('\n')
  748. default_domain = url.split('=')[1]
  749. ret['default_domain'] = default_domain
  750. myLogger.info_logger(ret)
  751. return ret
  752. def app_proxy_delete(app_id):
  753. customer_name = app_id.split('_')[1]
  754. proxy_host = None
  755. token = get_token()
  756. url = const.NGINX_URL+"/api/nginx/proxy-hosts"
  757. headers = {
  758. 'Authorization': token,
  759. 'Content-Type': 'application/json'
  760. }
  761. response = requests.get(url, headers=headers)
  762. for proxy in response.json():
  763. portainer_name = proxy["forward_host"]
  764. if customer_name == portainer_name:
  765. proxy_id = proxy["id"]
  766. token = get_token()
  767. url = const.NGINX_URL+"/api/nginx/proxy-hosts/" + str(proxy_id)
  768. headers = {
  769. 'Authorization': token,
  770. 'Content-Type': 'application/json'
  771. }
  772. response = requests.delete(url, headers=headers)
  773. def app_domain_delete(app_id, domain):
  774. code, message = docker.check_app_id(app_id)
  775. if code == None:
  776. info, flag = app_exits_in_docker(app_id)
  777. if flag:
  778. myLogger.info_logger("Check app_id ok[app_domain_delete]")
  779. else:
  780. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  781. else:
  782. raise CommandException(code, message, "")
  783. if domain is None or domain == "undefined":
  784. raise CommandException(const.ERROR_CLIENT_PARAM_BLANK, "Domains is blank", "")
  785. old_all_domains = get_all_domains(app_id)
  786. if domain not in old_all_domains:
  787. myLogger.info_logger("delete domain is not binded")
  788. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "Domain is not bind.", "")
  789. myLogger.info_logger("Start to delete " + domain)
  790. proxy = get_proxy_domain(app_id, domain)
  791. if proxy != None:
  792. myLogger.info_logger(proxy)
  793. myLogger.info_logger("before update")
  794. domains_old = proxy["domain_names"]
  795. myLogger.info_logger(domains_old)
  796. domains_old.remove(domain)
  797. myLogger.info_logger("after update")
  798. myLogger.info_logger(domains_old)
  799. if len(domains_old) == 0:
  800. proxy_id = proxy["id"]
  801. token = get_token()
  802. url = const.NGINX_URL+"/api/nginx/proxy-hosts/" + str(proxy_id)
  803. headers = {
  804. 'Authorization': token,
  805. 'Content-Type': 'application/json'
  806. }
  807. response = requests.delete(url, headers=headers)
  808. try:
  809. if response.json().get("error"):
  810. raise CommandException(const.ERROR_CONFIG_NGINX, response.json().get("error").get("message"), "")
  811. except Exception:
  812. myLogger.info_logger(response.json())
  813. set_domain("", app_id)
  814. else:
  815. proxy_id = proxy["id"]
  816. token = get_token()
  817. url = const.NGINX_URL+"/api/nginx/proxy-hosts/" + str(proxy_id)
  818. headers = {
  819. 'Authorization': token,
  820. 'Content-Type': 'application/json'
  821. }
  822. port = get_container_port(app_id.split('_')[1])
  823. host = app_id.split('_')[1]
  824. data = {
  825. "domain_names": domains_old,
  826. "forward_scheme": "http",
  827. "forward_host": host,
  828. "forward_port": port,
  829. "access_list_id": "0",
  830. "certificate_id": 0,
  831. "meta": {
  832. "letsencrypt_agree": False,
  833. "dns_challenge": False
  834. },
  835. "advanced_config": "",
  836. "locations": [],
  837. "block_exploits": False,
  838. "caching_enabled": False,
  839. "allow_websocket_upgrade": False,
  840. "http2_support": False,
  841. "hsts_enabled": False,
  842. "hsts_subdomains": False,
  843. "ssl_forced": False
  844. }
  845. response = requests.put(url, data=json.dumps(data), headers=headers)
  846. if response.json().get("error"):
  847. raise CommandException(const.ERROR_CONFIG_NGINX, response.json().get("error").get("message"), "")
  848. domain_set = app_domain_list(app_id)
  849. default_domain = domain_set['default_domain']
  850. # 如果被删除的域名是默认域名,删除后去剩下域名的第一个
  851. if default_domain == domain:
  852. set_domain(domains_old[0], app_id)
  853. else:
  854. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "Delete domain is not bind", "")
  855. def app_domain_update(app_id, domain_old, domain_new):
  856. myLogger.info_logger("app_domain_update")
  857. domain_list = []
  858. domain_list.append(domain_old)
  859. domain_list.append(domain_new)
  860. check_domains(domain_list)
  861. code, message = docker.check_app_id(app_id)
  862. if code == None:
  863. info, flag = app_exits_in_docker(app_id)
  864. if flag:
  865. myLogger.info_logger("Check app_id ok")
  866. else:
  867. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  868. else:
  869. raise CommandException(code, message, "")
  870. proxy = get_proxy_domain(app_id, domain_old)
  871. if proxy != None:
  872. domains_old = proxy["domain_names"]
  873. index = domains_old.index(domain_old)
  874. domains_old[index] = domain_new
  875. proxy_id = proxy["id"]
  876. token = get_token()
  877. url = const.NGINX_URL+"/api/nginx/proxy-hosts/" + str(proxy_id)
  878. headers = {
  879. 'Authorization': token,
  880. 'Content-Type': 'application/json'
  881. }
  882. port = get_container_port(app_id.split('_')[1])
  883. host = app_id.split('_')[1]
  884. data = {
  885. "domain_names": domains_old,
  886. "forward_scheme": "http",
  887. "forward_host": host,
  888. "forward_port": port,
  889. "access_list_id": "0",
  890. "certificate_id": 0,
  891. "meta": {
  892. "letsencrypt_agree": False,
  893. "dns_challenge": False
  894. },
  895. "advanced_config": "",
  896. "locations": [],
  897. "block_exploits": False,
  898. "caching_enabled": False,
  899. "allow_websocket_upgrade": False,
  900. "http2_support": False,
  901. "hsts_enabled": False,
  902. "hsts_subdomains": False,
  903. "ssl_forced": False
  904. }
  905. response = requests.put(url, data=json.dumps(data), headers=headers)
  906. if response.json().get("error"):
  907. raise CommandException(const.ERROR_CONFIG_NGINX, response.json().get("error").get("message"), "")
  908. domain_set = app_domain_list(app_id)
  909. default_domain = domain_set['default_domain']
  910. myLogger.info_logger("default_domain=" + default_domain + ",domain_old=" + domain_old)
  911. # 如果被修改的域名是默认域名,修改后也设置为默认域名
  912. if default_domain == domain_old:
  913. set_domain(domain_new, app_id)
  914. else:
  915. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "edit domain is not exist", "")
  916. def app_domain_add(app_id, domain):
  917. temp_domains = []
  918. temp_domains.append(domain)
  919. check_domains(temp_domains)
  920. code, message = docker.check_app_id(app_id)
  921. if code == None:
  922. info, flag = app_exits_in_docker(app_id)
  923. if flag:
  924. myLogger.info_logger("Check app_id ok")
  925. else:
  926. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  927. else:
  928. raise CommandException(code, message, "")
  929. old_domains = get_all_domains(app_id)
  930. if domain in old_domains:
  931. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "Domain is in use", "")
  932. proxy = get_proxy(app_id)
  933. if proxy != None:
  934. domains_old = proxy["domain_names"]
  935. domain_list = domains_old
  936. domain_list.append(domain)
  937. proxy_id = proxy["id"]
  938. token = get_token()
  939. url = const.NGINX_URL+"/api/nginx/proxy-hosts/" + str(proxy_id)
  940. headers = {
  941. 'Authorization': token,
  942. 'Content-Type': 'application/json'
  943. }
  944. port = get_container_port(app_id.split('_')[1])
  945. host = app_id.split('_')[1]
  946. data = {
  947. "domain_names": domain_list,
  948. "forward_scheme": "http",
  949. "forward_host": host,
  950. "forward_port": port,
  951. "access_list_id": "0",
  952. "certificate_id": 0,
  953. "meta": {
  954. "letsencrypt_agree": False,
  955. "dns_challenge": False
  956. },
  957. "advanced_config": "",
  958. "locations": [],
  959. "block_exploits": False,
  960. "caching_enabled": False,
  961. "allow_websocket_upgrade": False,
  962. "http2_support": False,
  963. "hsts_enabled": False,
  964. "hsts_subdomains": False,
  965. "ssl_forced": False
  966. }
  967. response = requests.put(url, data=json.dumps(data), headers=headers)
  968. if response.json().get("error"):
  969. raise CommandException(const.ERROR_CONFIG_NGINX, response.json().get("error").get("message"), "")
  970. else:
  971. # 追加
  972. token = get_token()
  973. url = const.NGINX_URL+"/api/nginx/proxy-hosts"
  974. headers = {
  975. 'Authorization': token,
  976. 'Content-Type': 'application/json'
  977. }
  978. port = get_container_port(app_id.split('_')[1])
  979. host = app_id.split('_')[1]
  980. data = {
  981. "domain_names": temp_domains,
  982. "forward_scheme": "http",
  983. "forward_host": host,
  984. "forward_port": port,
  985. "access_list_id": "0",
  986. "certificate_id": 0,
  987. "meta": {
  988. "letsencrypt_agree": False,
  989. "dns_challenge": False
  990. },
  991. "advanced_config": "",
  992. "locations": [],
  993. "block_exploits": False,
  994. "caching_enabled": False,
  995. "allow_websocket_upgrade": False,
  996. "http2_support": False,
  997. "hsts_enabled": False,
  998. "hsts_subdomains": False,
  999. "ssl_forced": False
  1000. }
  1001. response = requests.post(url, data=json.dumps(data), headers=headers)
  1002. if response.json().get("error"):
  1003. raise CommandException(const.ERROR_CONFIG_NGINX, response.json().get("error").get("message"), "")
  1004. set_domain(domain, app_id)
  1005. return domain
  1006. def check_domains(domains):
  1007. myLogger.info_logger(domains)
  1008. if domains is None or len(domains) == 0:
  1009. raise CommandException(const.ERROR_CLIENT_PARAM_BLANK, "Domains is blank", "")
  1010. else:
  1011. for domain in domains:
  1012. if is_valid_domain(domain):
  1013. if check_real_domain(domain) == False:
  1014. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "Domain and server not match", "")
  1015. else:
  1016. raise CommandException(const.ERROR_CLIENT_PARAM_Format, "Domains format error", "")
  1017. def is_valid_domain(domain):
  1018. if domain.startswith("http"):
  1019. return False
  1020. return True
  1021. def check_real_domain(domain):
  1022. domain_real = True
  1023. try:
  1024. cmd = "ping -c 1 " + domain + " | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | uniq"
  1025. domain_ip = shell_execute.execute_command_output_all(cmd)["result"].rstrip('\n')
  1026. ip_result = shell_execute.execute_command_output_all("cat /data/apps/w9services/w9appmanage/public_ip")
  1027. ip_save = ip_result["result"].rstrip('\n')
  1028. if domain_ip == ip_save:
  1029. myLogger.info_logger("Domain check ok!")
  1030. else:
  1031. domain_real = False
  1032. except CommandException as ce:
  1033. domain_real = False
  1034. return domain_real
  1035. def get_token():
  1036. url = const.NGINX_URL+"/api/tokens"
  1037. headers = {'Content-type': 'application/json'}
  1038. password = db.AppSearchUsers("nginx").get("password")
  1039. myLogger.info_logger("token_password: " + password)
  1040. param = {
  1041. "identity": "help@websoft9.com",
  1042. "scope": "user",
  1043. "secret": password
  1044. }
  1045. response = requests.post(url, data=json.dumps(param), headers=headers)
  1046. nginx_token = response.json()
  1047. myLogger.info_logger(nginx_token)
  1048. token = "Bearer " + response.json()["token"]
  1049. return token
  1050. def get_proxy(app_id):
  1051. customer_name = app_id.split('_')[1]
  1052. proxy_host = None
  1053. token = get_token()
  1054. url = const.NGINX_URL+"/api/nginx/proxy-hosts"
  1055. headers = {
  1056. 'Authorization': token,
  1057. 'Content-Type': 'application/json'
  1058. }
  1059. response = requests.get(url, headers=headers)
  1060. for proxy in response.json():
  1061. portainer_name = proxy["forward_host"]
  1062. if customer_name == portainer_name:
  1063. proxy_host = proxy
  1064. break
  1065. return proxy_host
  1066. def get_proxy_domain(app_id, domain):
  1067. customer_name = app_id.split('_')[1]
  1068. proxy_host = None
  1069. token = get_token()
  1070. url = const.NGINX_URL+"/api/nginx/proxy-hosts"
  1071. headers = {
  1072. 'Authorization': token,
  1073. 'Content-Type': 'application/json'
  1074. }
  1075. response = requests.get(url, headers=headers)
  1076. myLogger.info_logger(response.json())
  1077. for proxy in response.json():
  1078. portainer_name = proxy["forward_host"]
  1079. domain_list = proxy["domain_names"]
  1080. if customer_name == portainer_name:
  1081. myLogger.info_logger("-------------------")
  1082. if domain in domain_list:
  1083. myLogger.info_logger("find the domain proxy")
  1084. proxy_host = proxy
  1085. break
  1086. return proxy_host
  1087. def get_all_domains(app_id):
  1088. customer_name = app_id.split('_')[1]
  1089. domains = []
  1090. token = get_token()
  1091. url = const.NGINX_URL+"/api/nginx/proxy-hosts"
  1092. headers = {
  1093. 'Authorization': token,
  1094. 'Content-Type': 'application/json'
  1095. }
  1096. response = requests.get(url, headers=headers)
  1097. for proxy in response.json():
  1098. portainer_name = proxy["forward_host"]
  1099. if customer_name == portainer_name:
  1100. for domain in proxy["domain_names"]:
  1101. domains.append(domain)
  1102. return domains
  1103. def app_domain_set(domain, app_id):
  1104. temp_domains = []
  1105. temp_domains.append(domain)
  1106. check_domains(temp_domains)
  1107. code, message = docker.check_app_id(app_id)
  1108. if code == None:
  1109. info, flag = app_exits_in_docker(app_id)
  1110. if flag:
  1111. myLogger.info_logger("Check app_id ok")
  1112. else:
  1113. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  1114. else:
  1115. raise CommandException(code, message, "")
  1116. set_domain(domain, app_id)
  1117. def set_domain(domain, app_id):
  1118. myLogger.info_logger("set_domain start")
  1119. old_domains = get_all_domains(app_id)
  1120. if domain != "":
  1121. if domain not in old_domains:
  1122. message = domain + " is not in use"
  1123. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, message, "")
  1124. customer_name = app_id.split('_')[1]
  1125. app_url = shell_execute.execute_command_output_all("cat /data/apps/" + customer_name + "/.env")["result"]
  1126. if "APP_URL" in app_url:
  1127. myLogger.info_logger("APP_URL is exist")
  1128. if domain == "":
  1129. ip_result = shell_execute.execute_command_output_all("cat /data/apps/w9services/w9appmanage/public_ip")
  1130. domain = ip_result["result"].rstrip('\n')
  1131. cmd = "sed -i 's/APP_URL=.*/APP_URL=" + domain + "/g' /data/apps/" + customer_name + "/.env"
  1132. shell_execute.execute_command_output_all(cmd)
  1133. if "APP_URL_REPLACE=true" in app_url:
  1134. myLogger.info_logger("need up")
  1135. shell_execute.execute_command_output_all("cd /data/apps/" + customer_name + " && docker compose up -d")
  1136. else:
  1137. cmd = "sed -i 's/APP_URL=.*/APP_URL=" + domain + "/g' /data/apps/" + customer_name + "/.env"
  1138. shell_execute.execute_command_output_all(cmd)
  1139. if "APP_URL_REPLACE=true" in app_url:
  1140. myLogger.info_logger("need up")
  1141. shell_execute.execute_command_output_all("cd /data/apps/" + customer_name + " && docker compose up -d")
  1142. else:
  1143. myLogger.info_logger("APP_URL is not exist")
  1144. if domain == "":
  1145. ip_result = shell_execute.execute_command_output_all("cat /data/apps/w9services/w9appmanage/public_ip")
  1146. domain = ip_result["result"].rstrip('\n')
  1147. cmd = "sed -i '/APP_NETWORK/a APP_URL=" + domain + "' /data/apps/" + customer_name + "/.env"
  1148. shell_execute.execute_command_output_all(cmd)
  1149. myLogger.info_logger("set_domain success")
  1150. def get_container_port(container_name):
  1151. port = "80"
  1152. cmd = "docker port " + container_name + " |grep ::"
  1153. result = shell_execute.execute_command_output_all(cmd)["result"]
  1154. myLogger.info_logger(result)
  1155. port = result.split('/')[0]
  1156. myLogger.info_logger(port)
  1157. return port
  1158. def compared_version(ver1, ver2):
  1159. list1 = str(ver1).split(".")
  1160. list2 = str(ver2).split(".")
  1161. # 循环次数为短的列表的len
  1162. for i in range(len(list1)) if len(list1) < len(list2) else range(len(list2)):
  1163. if int(list1[i]) == int(list2[i]):
  1164. pass
  1165. elif int(list1[i]) < int(list2[i]):
  1166. return -1
  1167. else:
  1168. return 1
  1169. # 循环结束,哪个列表长哪个版本号高
  1170. if len(list1) == len(list2):
  1171. return 0
  1172. elif len(list1) < len(list2):
  1173. return -1
  1174. else:
  1175. return 1