|
- # Copyright 2019 Huawei Technologies Co., Ltd
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- # ============================================================================
- """Start mindinsight service."""
-
- import os
- import sys
- import re
- import argparse
- from importlib import import_module
-
- import psutil
-
- from mindinsight.conf import settings
- from mindinsight.utils.command import BaseCommand
- from mindinsight.utils.hook import HookUtils
- from mindinsight.utils.hook import init
- from mindinsight.utils.exceptions import PortNotAvailableError
-
-
- class ConfigAction(argparse.Action):
- """Config action class definition."""
-
- def __call__(self, parser, namespace, values, option_string=None):
- """
- Inherited __call__ method from argparse.Action.
-
- Args:
- parser (ArgumentParser): Passed-in argument parser.
- namespace (Namespace): Namespace object to hold arguments.
- values (object): Argument values with type depending on argument definition.
- option_string (str): Optional string for specific argument name. Default: None.
- """
- config = values
- if not config:
- setattr(namespace, self.dest, config)
-
- # python:full.path.for.config.module
- if config.startswith('python:'):
- config = config[len('python:'):]
- try:
- import_module(config)
- except ModuleNotFoundError:
- parser.error(f'{option_string} {config} not exists')
-
- config = f'python:{config}'
- else:
- # file:/full/path/for/config/module.py
- if config.startswith('file:'):
- config = config[len('file:'):]
-
- if config.startswith('~'):
- config = os.path.realpath(os.path.expanduser(config))
-
- if not config.startswith('/'):
- config = os.path.realpath(os.path.join(os.getcwd(), config))
-
- if not os.path.exists(config):
- parser.error(f'{option_string} {config} not exists')
-
- if not os.access(config, os.R_OK):
- parser.error(f'{option_string} {config} not accessible')
-
- config = f'file:{config}'
-
- setattr(namespace, self.dest, config)
-
-
- class WorkspaceAction(argparse.Action):
- """Workspace action class definition."""
-
- def __call__(self, parser, namespace, values, option_string=None):
- """
- Inherited __call__ method from argparse.Action.
-
- Args:
- parser (ArgumentParser): Passed-in argument parser.
- namespace (Namespace): Namespace object to hold arguments.
- values (object): Argument values with type depending on argument definition.
- option_string (str): Optional string for specific argument name. Default: None.
- """
- workspace = os.path.realpath(values)
- setattr(namespace, self.dest, workspace)
-
-
- class PortAction(argparse.Action):
- """Port action class definition."""
-
- MIN_PORT = 1
- MAX_PORT = 65535
-
- OPEN_PORT_LIMIT = 1024
-
- def __call__(self, parser, namespace, values, option_string=None):
- """
- Inherited __call__ method from argparse.Action.
-
- Args:
- parser (ArgumentParser): Passed-in argument parser.
- namespace (Namespace): Namespace object to hold arguments.
- values (object): Argument values with type depending on argument definition.
- option_string (str): Optional string for specific argument name. Default: None.
- """
- port = values
- if not self.MIN_PORT <= port <= self.MAX_PORT:
- parser.error(f'{option_string} should be chosen from {self.MIN_PORT} to {self.MAX_PORT}')
-
- setattr(namespace, self.dest, port)
-
-
- class UrlPathPrefixAction(argparse.Action):
- """Url Path prefix action class definition."""
-
- INVALID_SEGMENTS = ('.', '..')
- REGEX = r'^[a-zA-Z0-9_\-\.]+$'
-
- def __call__(self, parser, namespace, values, option_string=None):
- """
- Inherited __call__ method from argparse.Action.
-
- Args:
- parser (ArgumentParser): Passed-in argument parser.
- namespace (Namespace): Namespace object to hold arguments.
- values (object): Argument values with type depending on argument definition.
- option_string (str): Optional string for specific argument name. Default: None.
- """
- prefix = values
- segments = prefix.split('/')
- for index, segment in enumerate(segments):
- if not segment and index in (0, len(segments) - 1):
- continue
- if segment in self.INVALID_SEGMENTS or not re.match(self.REGEX, segment):
- parser.error(f'{option_string} value is invalid url path prefix')
-
- setattr(namespace, self.dest, prefix)
-
-
- class Command(BaseCommand):
- """
- Start mindinsight service.
- """
-
- name = 'start'
- description = 'startup mindinsight service'
-
- def add_arguments(self, parser):
- """
- Add arguments to parser.
-
- Args:
- parser (ArgumentParser): Specify parser to which arguments are added.
- """
- parser.add_argument(
- '--config',
- type=str,
- action=ConfigAction,
- help="""
- Specify path for user config module or file of the form python:path.to.config.module
- or file:/path/to/config.py
- """)
-
- parser.add_argument(
- '--workspace',
- type=str,
- action=WorkspaceAction,
- help="""
- Specify path for user workspace. Default is %s.
- """ % settings.WORKSPACE)
-
- parser.add_argument(
- '--port',
- type=int,
- action=PortAction,
- help="""
- Custom port ranging from %s to %s. Default value is %s.
- """ % (PortAction.MIN_PORT, PortAction.MAX_PORT, settings.PORT))
-
- parser.add_argument(
- '--url-path-prefix',
- type=str,
- action=UrlPathPrefixAction,
- help="""
- Custom URL path prefix for web page address. URL path prefix
- consists of segments separated by slashes. Each segment supports
- alphabets / digits / underscores / dashes / dots, but cannot just
- be emtpy string / single dot / double dots. Default value is ''.
- """)
-
- for hook in HookUtils.instance().hooks():
- hook.register_startup_arguments(parser)
-
- def update_settings(self, args):
- """
- Update settings.
-
- Args:
- args (Namespace): parsed arguments to hold customized parameters.
- """
- kwargs = {}
- for key, value in args.__dict__.items():
- if value is not None:
- kwargs[key] = value
-
- init(**kwargs)
-
- def run(self, args):
- """
- Execute for start command.
-
- Args:
- args (Namespace): Parsed arguments to hold customized parameters.
- """
- for key, value in args.__dict__.items():
- if value is not None:
- self.logfile.info('%s = %s', key, value)
-
- try:
- self.check_port()
- except PortNotAvailableError as error:
- self.console.error(error.message)
- self.logfile.error(error.message)
- sys.exit(1)
-
- self.console.info('Workspace: %s', os.path.realpath(settings.WORKSPACE))
-
- run_module = import_module('mindinsight.backend.run')
- run_module.start()
-
- self.logfile.info('Start mindinsight done.')
-
- def check_port(self):
- """Check port."""
- if os.getuid() != 0 and settings.PORT < PortAction.OPEN_PORT_LIMIT:
- raise PortNotAvailableError(
- f'Port {settings.PORT} below {PortAction.OPEN_PORT_LIMIT} is not allowed by current user.')
-
- connections = psutil.net_connections()
- for connection in connections:
- if connection.status != 'LISTEN':
- continue
- if connection.laddr.port == settings.PORT:
- raise PortNotAvailableError(f'Port {settings.PORT} is not available for MindInsight')
|