docker.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import os, io, sys, platform, shutil, time, json, datetime, psutil
  2. import re, docker, requests, base64
  3. from api.utils import shell_execute
  4. from dotenv import load_dotenv, find_dotenv
  5. import dotenv
  6. from pathlib import Path
  7. from api.utils.common_log import myLogger
  8. from api.utils import shell_execute, const
  9. from api.exception.command_exception import CommandException
  10. from api.service import manage
  11. # 获取github文件内容
  12. def get_github_content(repo, path, owner='Websoft9'):
  13. url = 'https://api.github.com/repos/{owner}/{repo}/contents/{path}'
  14. url = url.format(owner=owner, repo=repo, path=path)
  15. response = requests.get(url)
  16. data = json.loads(response.text)
  17. s = data['content']
  18. contents = str(base64.b64decode(s), "utf-8")
  19. return contents
  20. # 获取指定仓库CHANGELOG
  21. def get_update_list(local_path, repo):
  22. op = shell_execute.execute_command_output_all("cat " + local_path)['result']
  23. local_version = json.loads(op)['VERSION']
  24. version_contents = get_github_content(repo, 'install/version.json')
  25. version = json.loads(version_contents)['VERSION']
  26. if local_version != version:
  27. content = []
  28. change_log_contents = get_github_content('StackHub', 'CHANGELOG.md')
  29. change_log = change_log_contents.split('## ')[1].split('\n')
  30. data = change_log[0].split()[-1]
  31. for change in change_log[1:]:
  32. if change != '':
  33. content.append(change)
  34. ret = {}
  35. ret['version'] = version
  36. ret['data'] = data
  37. ret['content'] = content
  38. return ret
  39. else:
  40. return None
  41. # 已经是running的app怎么知道它已经能够访问,如页面能进入,如mysql能被客户端连接
  42. def if_app_access(app_name):
  43. return True
  44. def if_app_exits(app_name):
  45. cmd = "docker compose ls -a"
  46. output = shell_execute.execute_command_output_all(cmd)
  47. if int(output["code"]) == 0:
  48. pattern = app_name + '$'
  49. info_list = output['result'].split()
  50. is_exist = False
  51. for info in info_list:
  52. if re.match(pattern, info) != None:
  53. is_exist = True
  54. break
  55. return is_exist
  56. else:
  57. return True
  58. def if_app_running(app_name):
  59. cmd = "docker compose ls -a"
  60. output = shell_execute.execute_command_output_all(cmd)
  61. if int(output["code"]) == 0:
  62. app_list = output['result'].split("\n")
  63. pattern = app_name + '\s*'
  64. if_running = False
  65. for app in app_list:
  66. if re.match(pattern, app) != None and re.match('running', app) != None:
  67. if_running = True
  68. break
  69. return if_running
  70. else:
  71. return False
  72. def check_appid_exist(app_id):
  73. myLogger.info_logger("Checking check_appid_exist ...")
  74. appList = manage.get_my_app()
  75. find = False
  76. for app in appList:
  77. if app_id == app.app_id:
  78. find = True
  79. break
  80. myLogger.info_logger("Check complete.")
  81. return find
  82. def check_appid_include_rq(app_id):
  83. message = ""
  84. code = None
  85. if app_id == None or app_id == "undefine":
  86. code = const.ERROR_CLIENT_PARAM_BLANK
  87. message = "AppID is null"
  88. elif re.match('^[a-z0-9]+_[a-z0-9]+$', app_id) == None:
  89. code = const.ERROR_CLIENT_PARAM_Format
  90. message = "App_id format error"
  91. elif not check_appid_exist(app_id):
  92. code = const.ERROR_CLIENT_PARAM_NOTEXIST
  93. message = "AppID is not exist"
  94. return code, message
  95. def check_app_id(app_id):
  96. message = ""
  97. code = None
  98. if app_id == None:
  99. code = const.ERROR_CLIENT_PARAM_BLANK
  100. message = "AppID is null"
  101. elif re.match('^[a-z0-9]+_[a-z0-9]+$', app_id) == None:
  102. code = const.ERROR_CLIENT_PARAM_Format
  103. message = "APP name can only be composed of numbers and lowercase letters"
  104. myLogger.info_logger(code)
  105. return code, message
  106. def check_vm_resource(app_name):
  107. myLogger.info_logger("Checking virtual memory resource ...")
  108. var_path = "/data/library/apps/" + app_name + "/variables.json"
  109. requirements_var = read_var(var_path, 'requirements')
  110. need_cpu_count = int(requirements_var['cpu'])
  111. cpu_count = int(shell_execute.execute_command_output_all("cat /proc/cpuinfo | grep \'core id\'| wc -l")["result"])
  112. if cpu_count < need_cpu_count:
  113. myLogger.info_logger("Check complete: The number of CPU cores is insufficient!")
  114. return False
  115. need_mem_total = int(requirements_var['memory'])
  116. mem_free = float(psutil.virtual_memory().available) / 1024 / 1024 / 1024
  117. if mem_free < need_mem_total * 1.2:
  118. myLogger.info_logger("Check complete: The total amount of memory is insufficient!")
  119. return False
  120. need_disk = int(requirements_var['disk'])
  121. disk_free = float(psutil.disk_usage('/').free) / 1024 / 1024 / 1024
  122. if round(disk_free) < need_disk + 2:
  123. myLogger.info_logger("Check complete: There are not enough disks left!")
  124. return False
  125. myLogger.info_logger("Check complete.")
  126. return True
  127. def check_app_websoft9(app_name):
  128. # websoft9's support applist
  129. myLogger.info_logger("Checking dir...")
  130. path = "/data/library/apps/" + app_name
  131. is_exists = check_directory(path)
  132. return is_exists
  133. def check_directory(path):
  134. try:
  135. shell_execute.execute_command_output_all("ls " + path)
  136. return True
  137. except CommandException as ce:
  138. return False
  139. def check_app_compose(app_name, customer_name):
  140. myLogger.info_logger("Set port and random password ...")
  141. library_path = "/data/library/apps/" + app_name
  142. install_path = "/data/apps/" + customer_name
  143. port_dic = read_env(library_path + '/.env', "APP_.*_PORT=")
  144. # 1.判断/data/apps/app_name/.env中的port是否占用,没有被占用,方法结束(get_start_port方法)
  145. cmd1 = "docker container inspect $(docker ps -aq) | grep HostPort | awk \'{print $2}\' | sort -u"
  146. cmd2 = "netstat -tunlp | grep \"LISTEN\" | awk '{print $4}' | awk -F \":\" '{print $NF}' | sort -u"
  147. cmd3 = "grep -r \"APP_.*_PORT=\" /data/apps/*/.env | awk -F \"=\" '{print $2}' | sort -u"
  148. s1 = shell_execute.execute_command_output_all(cmd1)['result'].replace('\"', '')
  149. s2 = shell_execute.execute_command_output_all(cmd2)['result']
  150. try:
  151. s3 = ''
  152. s3 = shell_execute.execute_command_output_all(cmd3)['result']
  153. except:
  154. pass
  155. s = s1 + '\n' + s2 + '\n' + s3
  156. shell_execute.execute_command_output_all("cp -r " + library_path + " " + install_path)
  157. env_path = install_path + "/.env"
  158. get_map(env_path)
  159. for port_name in port_dic:
  160. port_value = get_start_port(s, port_dic[port_name])
  161. modify_env(install_path + '/.env', port_name, port_value)
  162. # set random password
  163. power_password = shell_execute.execute_command_output_all("cat /data/apps/" + customer_name + "/.env")["result"]
  164. if "POWER_PASSWORD" in power_password:
  165. try:
  166. shell_execute.execute_command_output_all("docker rm -f pwgen")
  167. except Exception:
  168. pass
  169. new_password = shell_execute.execute_command_output_all("docker run --name pwgen backplane/pwgen 15")[
  170. "result"].rstrip('\n') + "!"
  171. modify_env(install_path + '/.env', 'POWER_PASSWORD', new_password)
  172. shell_execute.execute_command_output_all("docker rm -f pwgen")
  173. env_path = install_path + "/.env"
  174. get_map(env_path)
  175. myLogger.info_logger("Port check complete")
  176. return
  177. def check_app_url(customer_app_name):
  178. myLogger.info_logger("Checking app url...")
  179. # 如果app的.env文件中含有HTTP_URL项目,需要如此设置 HTTP_URL=ip:port
  180. env_path = "/data/apps/" + customer_app_name + "/.env"
  181. env_map = get_map(env_path)
  182. if env_map.get("APP_URL_REPLACE") == "true":
  183. myLogger.info_logger(customer_app_name + "need to change app url...")
  184. app_url = list(read_env(env_path, "APP_URL=").values())[0]
  185. ip = "localhost"
  186. url = ""
  187. try:
  188. ip_result = shell_execute.execute_command_output_all("cat /data/apps/w9services/w9appmanage/public_ip")
  189. ip = ip_result["result"].rstrip('\n')
  190. except Exception:
  191. ip = "127.0.0.1"
  192. http_port = list(read_env(env_path, "APP_HTTP_PORT").values())[0]
  193. if ":" in app_url:
  194. url = ip + ":" + http_port
  195. else:
  196. url = ip
  197. cmd = "sed -i 's/APP_URL=.*/APP_URL=" + url + "/g' /data/apps/" + customer_app_name + "/.env"
  198. shell_execute.execute_command_output_all(cmd)
  199. myLogger.info_logger("App url check complete")
  200. return
  201. def get_map(path):
  202. myLogger.info_logger("Read env_dic" + path)
  203. output = shell_execute.execute_command_output_all("cat " + path)
  204. code = output["code"]
  205. env_dic = {}
  206. if int(code) == 0:
  207. ret = output["result"]
  208. myLogger.info_logger(ret)
  209. env_list = ret.split("\n")
  210. for env in env_list:
  211. if "=" in env:
  212. env_dic[env.split("=")[0]] = env.split("=")[1]
  213. myLogger.info_logger(env_dic)
  214. return env_dic
  215. def read_env(path, key):
  216. myLogger.info_logger("Read " + path)
  217. output = shell_execute.execute_command_output_all("cat " + path)
  218. code = output["code"]
  219. env_dic = {}
  220. if int(code) == 0:
  221. ret = output["result"]
  222. env_list = ret.split("\n")
  223. for env in env_list:
  224. if re.match(key, env) != None:
  225. env_dic[env.split("=")[0]] = env.split("=")[1]
  226. myLogger.info_logger("Read " + path + ": " + str(env_dic))
  227. return env_dic
  228. def modify_env(path, env_name, value):
  229. myLogger.info_logger("Modify " + path + "...")
  230. output = shell_execute.execute_command_output_all("sed -n \'/^" + env_name + "/=\' " + path)
  231. if int(output["code"]) == 0 and output["result"] != "":
  232. line_num = output["result"].split("\n")[0]
  233. s = env_name + "=" + value
  234. output = shell_execute.execute_command_output_all("sed -i \'" + line_num + "c " + s + "\' " + path)
  235. if int(output["code"]) == 0:
  236. myLogger.info_logger("Modify " + path + ": Change " + env_name + " to " + value)
  237. def read_var(var_path, var_name):
  238. value = ""
  239. myLogger.info_logger("Read " + var_path)
  240. output = shell_execute.execute_command_output_all("cat " + var_path)
  241. if int(output["code"]) == 0:
  242. var = json.loads(output["result"])
  243. try:
  244. value = var[var_name]
  245. except KeyError:
  246. myLogger.warning_logger("Read " + var_path + ": No key " + var_name)
  247. else:
  248. myLogger.warning_logger(var_path + " not found")
  249. return value
  250. def get_start_port(s, port):
  251. use_port = port
  252. while True:
  253. if s.find(use_port) == -1:
  254. break
  255. else:
  256. use_port = str(int(use_port) + 1)
  257. return use_port