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