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.

start.py 9.3 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. # Copyright 2019 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. """Start mindinsight service."""
  16. import argparse
  17. import os
  18. import re
  19. import sys
  20. import socket
  21. from importlib import import_module
  22. import psutil
  23. from mindinsight.conf import settings
  24. from mindinsight.utils.command import BaseCommand
  25. from mindinsight.utils.exceptions import PortNotAvailableError
  26. from mindinsight.utils.hook import HookUtils
  27. from mindinsight.utils.hook import init
  28. class ConfigAction(argparse.Action):
  29. """Config action class definition."""
  30. def __call__(self, parser, namespace, values, option_string=None):
  31. """
  32. Inherited __call__ method from argparse.Action.
  33. Args:
  34. parser (ArgumentParser): Passed-in argument parser.
  35. namespace (Namespace): Namespace object to hold arguments.
  36. values (object): Argument values with type depending on argument definition.
  37. option_string (str): Optional string for specific argument name. Default: None.
  38. """
  39. config = values
  40. if not config:
  41. setattr(namespace, self.dest, config)
  42. # python:full.path.for.config.module
  43. if config.startswith('python:'):
  44. config = config[len('python:'):]
  45. try:
  46. import_module(config)
  47. except ModuleNotFoundError:
  48. parser.error(f'{option_string} {config} not exists')
  49. config = f'python:{config}'
  50. else:
  51. # file:/full/path/for/config/module.py
  52. if config.startswith('file:'):
  53. config = config[len('file:'):]
  54. if config.startswith('~'):
  55. config = os.path.realpath(os.path.expanduser(config))
  56. if not config.startswith('/'):
  57. config = os.path.realpath(os.path.join(os.getcwd(), config))
  58. if not os.path.exists(config):
  59. parser.error(f'{option_string} {config} not exists')
  60. if not os.access(config, os.R_OK):
  61. parser.error(f'{option_string} {config} not accessible')
  62. config = f'file:{config}'
  63. setattr(namespace, self.dest, config)
  64. class WorkspaceAction(argparse.Action):
  65. """Workspace action class definition."""
  66. def __call__(self, parser, namespace, values, option_string=None):
  67. """
  68. Inherited __call__ method from argparse.Action.
  69. Args:
  70. parser (ArgumentParser): Passed-in argument parser.
  71. namespace (Namespace): Namespace object to hold arguments.
  72. values (object): Argument values with type depending on argument definition.
  73. option_string (str): Optional string for specific argument name. Default: None.
  74. """
  75. workspace = os.path.realpath(values)
  76. setattr(namespace, self.dest, workspace)
  77. class PortAction(argparse.Action):
  78. """Port action class definition."""
  79. MIN_PORT = 1
  80. MAX_PORT = 65535
  81. OPEN_PORT_LIMIT = 1024
  82. def __call__(self, parser, namespace, values, option_string=None):
  83. """
  84. Inherited __call__ method from argparse.Action.
  85. Args:
  86. parser (ArgumentParser): Passed-in argument parser.
  87. namespace (Namespace): Namespace object to hold arguments.
  88. values (object): Argument values with type depending on argument definition.
  89. option_string (str): Optional string for specific argument name. Default: None.
  90. """
  91. port = values
  92. if not self.MIN_PORT <= port <= self.MAX_PORT:
  93. parser.error(f'{option_string} should be chosen from {self.MIN_PORT} to {self.MAX_PORT}')
  94. setattr(namespace, self.dest, port)
  95. class UrlPathPrefixAction(argparse.Action):
  96. """Url Path prefix action class definition."""
  97. INVALID_SEGMENTS = ('.', '..')
  98. REGEX = r'^[a-zA-Z0-9_\-\.]+$'
  99. def __call__(self, parser, namespace, values, option_string=None):
  100. """
  101. Inherited __call__ method from argparse.Action.
  102. Args:
  103. parser (ArgumentParser): Passed-in argument parser.
  104. namespace (Namespace): Namespace object to hold arguments.
  105. values (object): Argument values with type depending on argument definition.
  106. option_string (str): Optional string for specific argument name. Default: None.
  107. """
  108. prefix = values
  109. segments = prefix.split('/')
  110. for index, segment in enumerate(segments):
  111. if not segment and index in (0, len(segments) - 1):
  112. continue
  113. if segment in self.INVALID_SEGMENTS or not re.match(self.REGEX, segment):
  114. parser.error(f'{option_string} value is invalid url path prefix')
  115. setattr(namespace, self.dest, prefix)
  116. class Command(BaseCommand):
  117. """
  118. Start mindinsight service.
  119. """
  120. name = 'start'
  121. description = 'startup mindinsight service'
  122. def add_arguments(self, parser):
  123. """
  124. Add arguments to parser.
  125. Args:
  126. parser (ArgumentParser): Specify parser to which arguments are added.
  127. """
  128. parser.add_argument(
  129. '--config',
  130. type=str,
  131. action=ConfigAction,
  132. help="""
  133. Specify path for user config module or file of the form python:path.to.config.module
  134. or file:/path/to/config.py
  135. """)
  136. parser.add_argument(
  137. '--workspace',
  138. type=str,
  139. action=WorkspaceAction,
  140. help="""
  141. Specify path for user workspace. Default is %s.
  142. """ % settings.WORKSPACE)
  143. parser.add_argument(
  144. '--port',
  145. type=int,
  146. action=PortAction,
  147. help="""
  148. Custom port ranging from %s to %s. Default value is %s.
  149. """ % (PortAction.MIN_PORT, PortAction.MAX_PORT, settings.PORT))
  150. parser.add_argument(
  151. '--url-path-prefix',
  152. type=str,
  153. action=UrlPathPrefixAction,
  154. help="""
  155. Custom URL path prefix for web page address. URL path prefix
  156. consists of segments separated by slashes. Each segment supports
  157. alphabets / digits / underscores / dashes / dots, but not single
  158. dot or double dots. Default value is ''.
  159. """)
  160. for hook in HookUtils.instance().hooks():
  161. hook.register_startup_arguments(parser)
  162. def update_settings(self, args):
  163. """
  164. Update settings.
  165. Args:
  166. args (Namespace): parsed arguments to hold customized parameters.
  167. """
  168. kwargs = {}
  169. for key, value in args.__dict__.items():
  170. if value is not None:
  171. kwargs[key] = value
  172. init(**kwargs)
  173. def run(self, args):
  174. """
  175. Execute for start command.
  176. Args:
  177. args (Namespace): Parsed arguments to hold customized parameters.
  178. """
  179. for key, value in args.__dict__.items():
  180. if value is not None:
  181. self.logfile.info('%s = %s', key, value)
  182. try:
  183. self.check_port()
  184. self.check_debugger_port()
  185. except PortNotAvailableError as error:
  186. self.console.error(error.message)
  187. self.logfile.error(error.message)
  188. sys.exit(1)
  189. self.console.info('Workspace: %s', os.path.realpath(settings.WORKSPACE))
  190. run_module = import_module('mindinsight.backend.run')
  191. run_module.start()
  192. self.logfile.info('Start mindinsight done.')
  193. def check_port(self):
  194. """Check port."""
  195. if os.getuid() != 0 and settings.PORT < PortAction.OPEN_PORT_LIMIT:
  196. raise PortNotAvailableError(
  197. f'Port {settings.PORT} below {PortAction.OPEN_PORT_LIMIT} is not allowed by current user.')
  198. connections = psutil.net_connections()
  199. for connection in connections:
  200. if connection.status != 'LISTEN':
  201. continue
  202. if connection.laddr.port == settings.PORT:
  203. raise PortNotAvailableError(f'Port {settings.PORT} is no available for MindInsight')
  204. def check_debugger_port(self):
  205. """Check if the debugger_port is available"""
  206. if not settings.ENABLE_DEBUGGER:
  207. return
  208. ip = settings.HOST
  209. port = settings.DEBUGGER_PORT
  210. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  211. try:
  212. s.connect((ip, int(port)))
  213. s.shutdown(2)
  214. raise PortNotAvailableError(f'Debugger-port {ip}:{port} is not available for MindInsight')
  215. except socket.error:
  216. return