You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

stop.py 6.2 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # Copyright 2019-2021 Huawei Technologies Co., Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # ============================================================================
  15. """Stop mindinsight service."""
  16. import os
  17. import sys
  18. import argparse
  19. import signal
  20. import getpass
  21. import psutil
  22. from mindinsight.conf import settings
  23. from mindinsight.utils.command import BaseCommand
  24. from mindinsight.utils.hook import HookUtils
  25. class PortAction(argparse.Action):
  26. """Port action class definition."""
  27. MIN_PORT = 1
  28. MAX_PORT = 65535
  29. def __call__(self, parser, namespace, values, option_string=None):
  30. """
  31. Inherited __call__ method from argparse.Action.
  32. Args:
  33. parser (ArgumentParser): Passed-in argument parser.
  34. namespace (Namespace): Namespace object to hold arguments.
  35. values (object): Argument values with type depending on argument definition.
  36. option_string (str): Optional string for specific argument name. Default: None.
  37. """
  38. port = values
  39. if not self.MIN_PORT <= port <= self.MAX_PORT:
  40. parser.error(f'{option_string} should be chosen from {self.MIN_PORT} to {self.MAX_PORT}')
  41. setattr(namespace, self.dest, port)
  42. class Command(BaseCommand):
  43. """Stop command."""
  44. name = 'stop'
  45. description = 'stop mindinsight service'
  46. cmd_regex = 'mindinsight.backend.application:APP'
  47. def add_arguments(self, parser):
  48. """
  49. Add arguments to parser.
  50. Args:
  51. parser (ArgumentParser): Specify parser to which arguments are added.
  52. """
  53. parser.add_argument(
  54. '--port',
  55. type=int,
  56. action=PortAction,
  57. help="""
  58. Custom port ranging from %s to %s. Default value is %s
  59. """ % (PortAction.MIN_PORT, PortAction.MAX_PORT, settings.PORT))
  60. def update_settings(self, args):
  61. """
  62. Update settings.
  63. Args:
  64. args (Namespace): parsed arguments to hold customized parameters.
  65. """
  66. if args.port is None:
  67. args.port = settings.PORT
  68. pid, workspace = self.get_process(args.port)
  69. setattr(args, 'pid', pid)
  70. os.environ['MINDINSIGHT_PORT'] = str(args.port)
  71. os.environ['MINDINSIGHT_WORKSPACE'] = workspace
  72. settings.refresh()
  73. def run(self, args):
  74. """
  75. Run to stop.
  76. Args:
  77. args (Namespace): Parsed arguments to hold customized parameters.
  78. """
  79. port, pid = args.port, args.pid
  80. if not pid:
  81. msg = f'No mindinsight service started by current user found for port {port}'
  82. self.console.error(msg)
  83. sys.exit(1)
  84. self.logfile.info('Stop mindinsight with port %s and pid %s.', port, pid)
  85. process = psutil.Process(pid)
  86. processes = process.children(recursive=True)
  87. processes.append(process)
  88. try:
  89. self._send_signal(process, signal.SIGINT)
  90. # Wait 3 seconds, if not terminate, kill the worker process.
  91. exit_timeout_seconds = 3
  92. _, alive = psutil.wait_procs(processes, exit_timeout_seconds)
  93. for alive_process in alive:
  94. self.logfile.info("Stop process %d because timeout.", alive_process.pid)
  95. self._send_signal(alive_process, signal.SIGKILL)
  96. except psutil.Error as ex:
  97. self.logfile.error("Stop process %d failed. Detail: %s.", pid, str(ex))
  98. for hook in HookUtils.instance().hooks():
  99. hook.on_shutdown(self.logfile)
  100. self.console.info('Stop mindinsight service successfully')
  101. def _send_signal(self, process, proc_signal):
  102. try:
  103. process.send_signal(proc_signal)
  104. except psutil.NoSuchProcess:
  105. pass
  106. def get_process(self, port):
  107. """
  108. Get mindinsight process
  109. Args:
  110. port (int): Specified port for mindinsight process.
  111. Returns:
  112. tuple, return mindinsight process pid and workspace.
  113. """
  114. pid, workspace = 0, settings.WORKSPACE
  115. user = getpass.getuser()
  116. connections = psutil.net_connections()
  117. for connection in connections:
  118. if connection.status != 'LISTEN':
  119. continue
  120. if connection.laddr.port != port:
  121. continue
  122. try:
  123. process = psutil.Process(connection.pid)
  124. except psutil.NoSuchProcess:
  125. continue
  126. cmds = process.cmdline()
  127. if ' '.join(cmds).find(self.cmd_regex) == -1:
  128. continue
  129. if user != process.username():
  130. continue
  131. gunicorn_master_process = process
  132. # The gunicorn master process might have grand children (eg forked by process pool).
  133. while True:
  134. parent_process = gunicorn_master_process.parent()
  135. if parent_process is None or parent_process.pid == 1:
  136. break
  137. parent_cmd = parent_process.cmdline()
  138. if ' '.join(parent_cmd).find(self.cmd_regex) == -1:
  139. break
  140. gunicorn_master_process = parent_process
  141. pid = gunicorn_master_process.pid
  142. access_log_path = os.path.join('gunicorn', 'access.{}.log'.format(port))
  143. for open_file in process.open_files():
  144. if open_file.path.endswith(access_log_path):
  145. log_base_dir = open_file.path[:-len(access_log_path)]
  146. workspace = os.path.realpath(os.path.join(log_base_dir, os.pardir))
  147. break
  148. break
  149. return pid, workspace