docker_remote_service.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # app/services/docker_remote_service.py
  2. import json
  3. from typing import Optional, Dict, Any, List
  4. from app.utils.docker_remote_control import DockerRemoteController
  5. from app.schemas.docker_remote import (
  6. RemoteServerConfig,
  7. DockerContainerStatus,
  8. DockerLogsRequest,
  9. ConfigUpdateRequest,
  10. LogReadRequest,
  11. )
  12. from app.core.logger import logger
  13. from app.core.config import settings
  14. from fastapi import HTTPException
  15. from sqlalchemy.ext.asyncio import AsyncSession
  16. from sqlalchemy import select
  17. from app.models.remote_server import RemoteServer
  18. class DockerRemoteService:
  19. """Docker远程控制服务"""
  20. @staticmethod
  21. async def get_server_config(db: AsyncSession, server_id: str, override_path: Optional[str] = None) -> RemoteServerConfig:
  22. """从数据库中获取服务器连接信息"""
  23. result = await db.execute(select(RemoteServer).filter(RemoteServer.server_id == server_id))
  24. server = result.scalar_one_or_none()
  25. if not server:
  26. raise HTTPException(status_code=404, detail=f"Server {server_id} not found in database")
  27. return RemoteServerConfig(
  28. host=server.host,
  29. port=server.port,
  30. username=server.username,
  31. password=server.password,
  32. key_file=server.key_file,
  33. project_path=override_path or server.project_path
  34. )
  35. @staticmethod
  36. async def get_container_status(config: RemoteServerConfig) -> Dict[str, DockerContainerStatus]:
  37. """获取容器状态"""
  38. controller = DockerRemoteController(
  39. host=config.host,
  40. port=config.port,
  41. username=config.username,
  42. password=config.password,
  43. key_file=config.key_file,
  44. project_path=config.project_path
  45. )
  46. try:
  47. if not controller.connect():
  48. raise Exception("连接失败")
  49. containers = controller.docker_status()
  50. result = {}
  51. for name, info in containers.items():
  52. result[name] = DockerContainerStatus(
  53. name=name,
  54. status=info['status'],
  55. image=info['image']
  56. )
  57. return result
  58. except Exception as e:
  59. logger.error(f"获取容器状态失败: {e}")
  60. raise
  61. finally:
  62. controller.disconnect()
  63. @staticmethod
  64. async def docker_compose_up(config: RemoteServerConfig, services: Optional[List[str]] = None) -> bool:
  65. """启动Docker Compose服务"""
  66. controller = DockerRemoteController(
  67. host=config.host,
  68. port=config.port,
  69. username=config.username,
  70. password=config.password,
  71. key_file=config.key_file,
  72. project_path=config.project_path
  73. )
  74. try:
  75. if not controller.connect():
  76. raise Exception("连接失败")
  77. return controller.docker_compose_up(services)
  78. except Exception as e:
  79. logger.error(f"启动Docker服务失败: {e}")
  80. raise
  81. finally:
  82. controller.disconnect()
  83. @staticmethod
  84. async def docker_compose_down(config: RemoteServerConfig, services: Optional[List[str]] = None) -> bool:
  85. """停止Docker Compose服务"""
  86. controller = DockerRemoteController(
  87. host=config.host,
  88. port=config.port,
  89. username=config.username,
  90. password=config.password,
  91. key_file=config.key_file,
  92. project_path=config.project_path
  93. )
  94. try:
  95. if not controller.connect():
  96. raise Exception("连接失败")
  97. return controller.docker_compose_down(services)
  98. except Exception as e:
  99. logger.error(f"停止Docker服务失败: {e}")
  100. raise
  101. finally:
  102. controller.disconnect()
  103. @staticmethod
  104. async def docker_restart(config: RemoteServerConfig, container_name: str) -> bool:
  105. """重启容器"""
  106. controller = DockerRemoteController(
  107. host=config.host,
  108. port=config.port,
  109. username=config.username,
  110. password=config.password,
  111. key_file=config.key_file,
  112. project_path=config.project_path
  113. )
  114. try:
  115. if not controller.connect():
  116. raise Exception("连接失败")
  117. return controller.docker_restart(container_name)
  118. except Exception as e:
  119. logger.error(f"重启容器失败: {e}")
  120. raise
  121. finally:
  122. controller.disconnect()
  123. @staticmethod
  124. async def docker_start(config: RemoteServerConfig, container_name: str) -> bool:
  125. """启动容器"""
  126. controller = DockerRemoteController(
  127. host=config.host,
  128. port=config.port,
  129. username=config.username,
  130. password=config.password,
  131. key_file=config.key_file,
  132. project_path=config.project_path
  133. )
  134. try:
  135. if not controller.connect():
  136. raise Exception("连接失败")
  137. return controller.docker_start(container_name)
  138. except Exception as e:
  139. logger.error(f"启动容器失败: {e}")
  140. raise
  141. finally:
  142. controller.disconnect()
  143. @staticmethod
  144. async def docker_stop(config: RemoteServerConfig, container_name: str) -> bool:
  145. """停止容器"""
  146. controller = DockerRemoteController(
  147. host=config.host,
  148. port=config.port,
  149. username=config.username,
  150. password=config.password,
  151. key_file=config.key_file,
  152. project_path=config.project_path
  153. )
  154. try:
  155. if not controller.connect():
  156. raise Exception("连接失败")
  157. return controller.docker_stop(container_name)
  158. except Exception as e:
  159. logger.error(f"停止容器失败: {e}")
  160. raise
  161. finally:
  162. controller.disconnect()
  163. @staticmethod
  164. async def docker_logs(config: RemoteServerConfig, request: DockerLogsRequest) -> str:
  165. """查看容器日志"""
  166. controller = DockerRemoteController(
  167. host=config.host,
  168. port=config.port,
  169. username=config.username,
  170. password=config.password,
  171. key_file=config.key_file,
  172. project_path=config.project_path
  173. )
  174. try:
  175. if not controller.connect():
  176. raise Exception("连接失败")
  177. return controller.docker_logs(request.container_name, request.lines, request.follow)
  178. except Exception as e:
  179. logger.error(f"查看容器日志失败: {e}")
  180. raise
  181. finally:
  182. controller.disconnect()
  183. @staticmethod
  184. async def read_config(config: RemoteServerConfig, config_file: str) -> Dict[str, Any]:
  185. """读取配置文件"""
  186. controller = DockerRemoteController(
  187. host=config.host,
  188. port=config.port,
  189. username=config.username,
  190. password=config.password,
  191. key_file=config.key_file,
  192. project_path=config.project_path
  193. )
  194. try:
  195. if not controller.connect():
  196. raise Exception("连接失败")
  197. result = controller.read_config(config_file)
  198. if result is None:
  199. raise Exception("配置文件不存在或读取失败")
  200. return result
  201. except Exception as e:
  202. logger.error(f"读取配置文件失败: {e}")
  203. raise
  204. finally:
  205. controller.disconnect()
  206. @staticmethod
  207. async def update_config(config: RemoteServerConfig, request: ConfigUpdateRequest) -> bool:
  208. """更新配置文件"""
  209. controller = DockerRemoteController(
  210. host=config.host,
  211. port=config.port,
  212. username=config.username,
  213. password=config.password,
  214. key_file=config.key_file,
  215. project_path=config.project_path
  216. )
  217. try:
  218. if not controller.connect():
  219. raise Exception("连接失败")
  220. return controller.update_config(request.config_file, request.key_path, request.value)
  221. except Exception as e:
  222. logger.error(f"更新配置文件失败: {e}")
  223. raise
  224. finally:
  225. controller.disconnect()
  226. @staticmethod
  227. async def read_log(config: RemoteServerConfig, request: LogReadRequest) -> str:
  228. """读取日志文件"""
  229. controller = DockerRemoteController(
  230. host=config.host,
  231. port=config.port,
  232. username=config.username,
  233. password=config.password,
  234. key_file=config.key_file,
  235. project_path=config.project_path
  236. )
  237. try:
  238. if not controller.connect():
  239. raise Exception("连接失败")
  240. return controller.read_log(
  241. request.log_file,
  242. lines=request.lines,
  243. from_head=request.from_head,
  244. full=request.full
  245. )
  246. except Exception as e:
  247. logger.error(f"读取日志文件失败: {e}")
  248. raise
  249. finally:
  250. controller.disconnect()
  251. @staticmethod
  252. async def list_logs(config: RemoteServerConfig) -> List[str]:
  253. """列出所有日志文件"""
  254. controller = DockerRemoteController(
  255. host=config.host,
  256. port=config.port,
  257. username=config.username,
  258. password=config.password,
  259. key_file=config.key_file,
  260. project_path=config.project_path
  261. )
  262. try:
  263. if not controller.connect():
  264. raise Exception("连接失败")
  265. return controller.list_logs()
  266. except Exception as e:
  267. logger.error(f"列出日志文件失败: {e}")
  268. raise
  269. finally:
  270. controller.disconnect()