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