Beispiel #1
0
    def __init__(self, override_config_path=None, verbose_start=False):
        if verbose_start:
            print('Reading configuration...'.ljust(
                LogDecorator.get_ljust_offset()),
                  end='')
        if isinstance(override_config_path, str) and len(override_config_path):
            configPath = override_config_path
        elif 'AIDE_CONFIG_PATH' in os.environ:
            configPath = os.environ['AIDE_CONFIG_PATH']
        else:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise ValueError(
                'Neither system environment variable "AIDE_CONFIG_PATH" nor override path are set.'
            )

        self.config = None
        try:
            self.config = ConfigParser()
            self.config.read(configPath)
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(
                f'Could not read configuration file (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
    def __init__(self, config, app, dbConnector, taskCoordinator, verbose_start=False, passive_mode=False):
        self.config = config
        self.app = app

        if verbose_start:
            print('AIController'.ljust(LogDecorator.get_ljust_offset()), end='')

        try:
            self.middleware = AIMiddleware(config, dbConnector, taskCoordinator, passive_mode)
            self.login_check = None
            self._initBottle()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(f'Could not launch AIController (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #3
0
    def __init__(self, config, passiveMode=False, verbose_start=False):
        self.config = config

        if verbose_start:
            print('AIWorker'.ljust(22), end='')

        try:
            self.dbConnector = Database(config)
            self.passiveMode = passiveMode
            self._init_fileserver()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(
                f'Could not launch AIWorker (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #4
0
    def __init__(self, config, app, verbose_start=False):
        self.config = config
        self.app = app

        if verbose_start:
            print('AIController'.ljust(18), end='')

        try:
            self.middleware = AIMiddleware(config)
            self.login_check = None
            self._initBottle()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(
                f'Could not launch AIController (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #5
0
    def __init__(self, config, app, dbConnector, verbose_start=False):
        self.config = config
        self.app = app
        self.staticDir = 'modules/LabelUI/static'

        if verbose_start:
            print('LabelUI'.ljust(LogDecorator.get_ljust_offset()), end='')
        
        try:
            self.middleware = DBMiddleware(config, dbConnector)
            self.login_check = None

            self._initBottle()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(f'Could not launch LabelUI (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #6
0
    def __init__(self, config, verbose_start=False):
        self.config = config

        if verbose_start:
            print('Connecting to database...'.ljust(5), end='')

        # get DB parameters
        try:
            self.database = config.getProperty('Database', 'name').lower()
            self.host = config.getProperty('Database', 'host')
            self.port = config.getProperty('Database', 'port')
            self.user = config.getProperty('Database', 'user').lower()
            self.password = config.getProperty('Database', 'password')
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(f'Incomplete database credentials provided in configuration file (message: "{str(e)}").')

        try:
            self._createConnectionPool()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(f'Could not connect to database (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #7
0
    def __init__(self, config, verbose_start=False):
        self.config = config

        if verbose_start:
            print('Connecting to database...'.ljust(
                LogDecorator.get_ljust_offset()),
                  end='')

        # get DB parameters
        try:
            self.database = config.getProperty('Database', 'name').lower()
            self.host = config.getProperty('Database', 'host')
            self.port = config.getProperty('Database', 'port')
            self.user = config.getProperty('Database', 'user', fallback=None)
            self.password = config.getProperty('Database',
                                               'password',
                                               fallback=None)

            if self.user is None or self.password is None:
                # load from credentials file instead
                credentials = config.getProperty('Database', 'credentials')
                with open(credentials, 'r') as c:
                    lines = c.readlines()
                    for line in lines:
                        line = line.lstrip().rstrip('\r').rstrip('\n')
                        if line.startswith('#') or line.startswith(';'):
                            continue
                        tokens = line.split('=')
                        if len(tokens) >= 2:
                            idx = line.find('=') + 1
                            field = tokens[0].strip().lower()
                            if field == 'username':
                                self.user = line[idx:]
                            elif field == 'password':
                                self.password = line[idx:]

            self.user = self.user.lower()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(
                f'Incomplete database credentials provided in configuration file (message: "{str(e)}").'
            )

        try:
            self._createConnectionPool()
        except Exception as e:
            if verbose_start:
                LogDecorator.print_status('fail')
            raise Exception(
                f'Could not connect to database (message: "{str(e)}").')

        if verbose_start:
            LogDecorator.print_status('ok')
Beispiel #8
0
    def getServiceDetails(self, warn_error=False):
        '''
            Queries the indicated AIController and FileServer
            modules for availability and their version. Returns
            metadata about the setup of AIDE accordingly.
            Raises an Exception if not running on the main host.
            If "warn_error" is True, a warning statement is printed
            to the command line if the version of AIDE on the attached
            AIController and/or FileServer is not the same as on the
            host, or if the servers cannot be contacted.
        '''
        if warn_error:
            print('Contacting AIController...', end='')

        # check if running on the main host
        modules = os.environ['AIDE_MODULES'].strip().split(',')
        modules = set([m.strip() for m in modules])
        if not 'LabelUI' in modules:
            # not running on main host
            raise Exception('Not a main host; cannot query service details.')

        aic_uri = self.config.getProperty('Server',
                                          'aiController_uri',
                                          type=str,
                                          fallback=None)
        fs_uri = self.config.getProperty('Server',
                                         'dataServer_uri',
                                         type=str,
                                         fallback=None)

        if not is_localhost(aic_uri):
            # AIController runs on a different machine; poll for version of AIDE
            try:
                aic_response = requests.get(os.path.join(aic_uri, 'version'))
                aic_version = aic_response.text
                if warn_error and aic_version != AIDE_VERSION:
                    print(
                        'WARNING: AIDE version of connected AIController differs from main host.'
                    )
                    print(f'\tAIController URI: {aic_uri}')
                    print(f'\tAIController AIDE version:    {aic_version}')
                    print(f'\tAIDE version on this machine: {AIDE_VERSION}')

                elif warn_error:
                    LogDecorator.print_status('ok')
            except Exception as e:
                if warn_error:
                    LogDecorator.print_status('fail')
                    print(
                        f'WARNING: error connecting to AIController (message: "{str(e)}").'
                    )
                aic_version = None
        else:
            aic_version = AIDE_VERSION
            if warn_error:
                LogDecorator.print_status('ok')

        if not is_localhost(fs_uri):
            # same for the file server
            if warn_error:
                print('Contacting FileServer...', end='')
            try:
                fs_response = requests.get(os.path.join(fs_uri, 'version'))
                fs_version = fs_response.text
                if warn_error and fs_version != AIDE_VERSION:
                    LogDecorator.print_status('warn')
                    print(
                        'WARNING: AIDE version of connected FileServer differs from main host.'
                    )
                    print(f'\tFileServer URI: {fs_uri}')
                    print(f'\tFileServer AIDE version:       {fs_version}')
                    print(f'\tAIDE version on this machine: {AIDE_VERSION}')
                elif warn_error:
                    LogDecorator.print_status('ok')
            except Exception as e:
                if warn_error:
                    LogDecorator.print_status('fail')
                    print(
                        f'WARNING: error connecting to FileServer (message: "{str(e)}").'
                    )
                fs_version = None
        else:
            fs_version = AIDE_VERSION
            if warn_error:
                LogDecorator.print_status('ok')

        # query database
        dbVersion = self.dbConnector.execute('SHOW server_version;', None,
                                             1)[0]['server_version']
        try:
            dbVersion = dbVersion.split(' ')[0].strip()
        except:
            pass
        dbInfo = self.dbConnector.execute('SELECT version() AS version;', None,
                                          1)[0]['version']
        return {
            'aide_version': AIDE_VERSION,
            'AIController': {
                'uri': aic_uri,
                'aide_version': aic_version
            },
            'FileServer': {
                'uri': fs_uri,
                'aide_version': fs_version
            },
            'Database': {
                'version': dbVersion,
                'details': dbInfo
            }
        }
Beispiel #9
0
def assemble_server(verbose_start=True,
                    check_v1_config=True,
                    migrate_database=True,
                    force_migrate=False,
                    passive_mode=False):

    # force verbosity if any of the pre-flight checks is enabled
    verbose_start = any((verbose_start, check_v1_config, migrate_database))

    instance_args = os.environ['AIDE_MODULES'].split(',')

    if verbose_start:
        configPath = os.environ['AIDE_CONFIG_PATH']
        aideModules = ', '.join(instance_args)

        print(f'''\033[96m
#################################       
                                        version {AIDE_VERSION}
   ###    #### ########  ########       
  ## ##    ##  ##     ## ##             {platform.platform()}
 ##   ##   ##  ##     ## ##             
##     ##  ##  ##     ## ######         [config]
#########  ##  ##     ## ##             .> {configPath}
##     ##  ##  ##     ## ##             
##     ## #### ########  ########       [modules]
                                        .> {aideModules}
#################################\033[0m
''')

    statusOffset = LogDecorator.get_ljust_offset()

    # load configuration
    config = Config(None, verbose_start)
    bottle.BaseRequest.MEMFILE_MAX = 1024**3  #TODO: make hyperparameter in config?

    # connect to database
    dbConnector = Database(config, verbose_start)

    if check_v1_config:
        # check if config file points to unmigrated v1 project
        print('Checking database...'.ljust(statusOffset), end='')
        hasAdminTable = dbConnector.execute(
            '''
                SELECT EXISTS (
                    SELECT FROM information_schema.tables 
                    WHERE table_schema = 'aide_admin'
                    AND table_name = 'project'
                );
            ''', None, 1)
        if not hasAdminTable[0]['exists']:
            # not (yet) migrated, raise Exception with instructions to ensure compatibility
            LogDecorator.print_status('fail')
            print(f'''
        The current installation of AIDE:
            database host: {config.getProperty('Database', 'host')}
            database name: {config.getProperty('Database', 'name')}
            schema:        {config.getProperty('Database', 'schema', str, '(not specified)')}

        points to an installation of the legacy AIDE v1.
        If you wish to continue using AIDE v2, you have to upgrade the project accordingly.
        For instructions to do so, see here:
            https://github.com/microsoft/aerial_wildlife_detection/blob/multiProject/doc/upgrade_from_v1.md
            ''')
            sys.exit(1)

        else:
            LogDecorator.print_status('ok')

        # check if projects have been migrated
        print('Checking projects...'.ljust(statusOffset), end='')
        dbSchema = config.getProperty('Database', 'schema', str, None)
        if dbSchema is not None:
            isMigrated = dbConnector.execute(
                '''
                    SELECT COUNT(*) AS cnt
                    FROM aide_admin.project
                    WHERE shortname = %s;
                ''', (dbSchema, ), 1)
            if isMigrated is not None and len(
                    isMigrated) and isMigrated[0]['cnt'] == 0:
                LogDecorator.print_status('warn')
                print(f'''
        WARNING: the selected configuration .ini file
        ("{os.environ['AIDE_CONFIG_PATH']}")
        points to a project that has not yet been migrated to AIDE v2.
        Details:
            database host: {config.getProperty('Database', 'host')}
            database name: {config.getProperty('Database', 'name')}
            schema:        {dbSchema}

        If you wish to continue using AIDE v2 for this project, you have to upgrade it
        to v2 accordingly.
        For instructions to do so, see here:
            https://github.com/microsoft/aerial_wildlife_detection/blob/multiProject/doc/upgrade_from_v1.md
            ''')
            else:
                LogDecorator.print_status('ok')
        else:
            LogDecorator.print_status('ok')

    if migrate_database:
        # bring AIDE up-to-date
        print('Updating database...'.ljust(statusOffset), end='')
        warnings, errors = migrate_aide(force_migrate)
        if len(warnings) or len(errors):
            if len(errors):
                LogDecorator.print_status('fail')
            else:
                LogDecorator.print_status('warn')

            print(
                f'Warnings and/or errors occurred while updating AIDE to the latest version ({AIDE_VERSION}):'
            )
            print('\nWarnings:')
            for w in warnings:
                print(f'\t"{w}"')

            print('\nErrors:')
            for e in errors:
                print(f'\t"{e}"')

            if len(errors):
                sys.exit(2)

        else:
            LogDecorator.print_status('ok')

    # prepare bottle
    app = Bottle()

    # parse requested instances
    instances = {}

    # "singletons"
    dbConnector = REGISTERED_MODULES['Database'](config, verbose_start)
    userHandler = REGISTERED_MODULES['UserHandler'](config, app, dbConnector)
    taskCoordinator = REGISTERED_MODULES['TaskCoordinator'](config, app,
                                                            dbConnector,
                                                            verbose_start)
    taskCoordinator.addLoginCheckFun(userHandler.checkAuthenticated)

    for i in instance_args:

        moduleName = i.strip()
        if moduleName == 'UserHandler':
            continue

        moduleClass = REGISTERED_MODULES[moduleName]

        # verify
        _verify_unique(instances, moduleClass)

        # create instance
        if moduleName == 'AIController':
            instance = moduleClass(config, app, dbConnector, taskCoordinator,
                                   verbose_start, passive_mode)
        else:
            instance = moduleClass(config, app, dbConnector, verbose_start)
        instances[moduleName] = instance

        # add authentication functionality
        if hasattr(instance, 'addLoginCheckFun'):
            instance.addLoginCheckFun(userHandler.checkAuthenticated)

        # launch project meta modules
        if moduleName == 'LabelUI':
            aideAdmin = REGISTERED_MODULES['AIDEAdmin'](config, app,
                                                        dbConnector,
                                                        verbose_start)
            aideAdmin.addLoginCheckFun(userHandler.checkAuthenticated)
            reception = REGISTERED_MODULES['Reception'](config, app,
                                                        dbConnector)
            reception.addLoginCheckFun(userHandler.checkAuthenticated)
            configurator = REGISTERED_MODULES['ProjectConfigurator'](
                config, app, dbConnector)
            configurator.addLoginCheckFun(userHandler.checkAuthenticated)
            statistics = REGISTERED_MODULES['ProjectStatistics'](config, app,
                                                                 dbConnector)
            statistics.addLoginCheckFun(userHandler.checkAuthenticated)

        elif moduleName == 'FileServer':
            from modules.DataAdministration.backend import celery_interface as daa_int

        elif moduleName == 'AIController':
            from modules.AIController.backend import celery_interface as aic_int

            # launch model marketplace with AIController
            modelMarketplace = REGISTERED_MODULES['ModelMarketplace'](
                config, app, dbConnector, taskCoordinator)
            modelMarketplace.addLoginCheckFun(userHandler.checkAuthenticated)

        elif moduleName == 'AIWorker':
            from modules.AIWorker.backend import celery_interface as aiw_int

        # launch globally required modules
        dataAdmin = REGISTERED_MODULES['DataAdministrator'](config, app,
                                                            dbConnector,
                                                            taskCoordinator)
        dataAdmin.addLoginCheckFun(userHandler.checkAuthenticated)

        staticFiles = REGISTERED_MODULES['StaticFileServer'](config, app,
                                                             dbConnector)
        staticFiles.addLoginCheckFun(userHandler.checkAuthenticated)

    if verbose_start:
        print('\n')

    return app
Beispiel #10
0
    def getServiceDetails(self, verbose=False, raise_error=False):
        '''
            Queries the indicated AIController and FileServer
            modules for availability and their version. Returns
            metadata about the setup of AIDE accordingly.
            Raises an Exception if not running on the main host.
            If "verbose" is True, a warning statement is printed
            to the command line if the version of AIDE on the attached
            AIController and/or FileServer is not the same as on the
            host, or if the servers cannot be contacted.
            If "raise_error" is True, an Exception is being thrown at
            the first error occurrence.
        '''
        if verbose:
            print('Contacting AIController...'.ljust(
                LogDecorator.get_ljust_offset()),
                  end='')

        # check if running on the main host
        modules = os.environ['AIDE_MODULES'].strip().split(',')
        modules = set([m.strip() for m in modules])
        if not 'LabelUI' in modules:
            # not running on main host
            raise Exception('Not a main host; cannot query service details.')

        aic_uri = self.config.getProperty('Server',
                                          'aiController_uri',
                                          type=str,
                                          fallback=None)
        fs_uri = self.config.getProperty('Server',
                                         'dataServer_uri',
                                         type=str,
                                         fallback=None)

        if not is_localhost(aic_uri):
            # AIController runs on a different machine; poll for version of AIDE
            try:
                aic_response = requests.get(os.path.join(aic_uri, 'version'))
                aic_version = aic_response.text
                if verbose and aic_version != AIDE_VERSION:
                    LogDecorator.print_status('warn')
                    print(
                        'WARNING: AIDE version of connected AIController differs from main host.'
                    )
                    print(f'\tAIController URI: {aic_uri}')
                    print(f'\tAIController AIDE version:    {aic_version}')
                    print(f'\tAIDE version on this machine: {AIDE_VERSION}')

                elif verbose:
                    LogDecorator.print_status('ok')
            except Exception as e:
                message = f'ERROR: could not connect to AIController (message: "{str(e)}").'
                if verbose:
                    LogDecorator.print_status('fail')
                    print(message)
                if raise_error:
                    raise Exception(message)
                aic_version = None
        else:
            aic_version = AIDE_VERSION
            if verbose:
                LogDecorator.print_status('ok')

        if verbose:
            print('Contacting FileServer...'.ljust(
                LogDecorator.get_ljust_offset()),
                  end='')
        if not is_localhost(fs_uri):
            # same for the file server
            try:
                fs_response = requests.get(os.path.join(fs_uri, 'version'))
                fs_version = fs_response.text

                # check if version if recent enough
                if compare_versions(fs_version, MIN_FILESERVER_VERSION) < 0:
                    # FileServer version is too old
                    LogDecorator.print_status('fail')
                    print(
                        'ERROR: AIDE version of connected FileServer is too old.'
                    )
                    print(
                        f'\tMinimum required version:    {MIN_FILESERVER_VERSION}'
                    )
                    print(f'\tCurrent FileServer version:  {fs_version}')

                if verbose and fs_version != AIDE_VERSION:
                    LogDecorator.print_status('warn')
                    print(
                        'WARNING: AIDE version of connected FileServer differs from main host.'
                    )
                    print(f'\tFileServer URI: {fs_uri}')
                    print(f'\tFileServer AIDE version:       {fs_version}')
                    print(f'\tAIDE version on this machine: {AIDE_VERSION}')
                elif verbose:
                    LogDecorator.print_status('ok')
            except Exception as e:
                message = f'ERROR: could not connect to FileServer (message: "{str(e)}").'
                if verbose:
                    LogDecorator.print_status('fail')
                    print(message)
                if raise_error:
                    raise Exception(message)
                fs_version = None
        else:
            fs_version = AIDE_VERSION
            if verbose:
                LogDecorator.print_status('ok')

        # query database
        if verbose:
            print('Contacting Database...'.ljust(
                LogDecorator.get_ljust_offset()),
                  end='')
        dbVersion = None
        dbInfo = None
        try:
            dbVersion = self.dbConnector.execute('SHOW server_version;', None,
                                                 1)[0]['server_version']
            dbVersion = dbVersion.split(' ')[0].strip()
            dbInfo = self.dbConnector.execute('SELECT version() AS version;',
                                              None, 1)[0]['version']
            if verbose:
                LogDecorator.print_status('ok')
        except Exception as e:
            message = f'ERROR: database version ("{str(dbVersion)}") could not be parsed properly.'
            if verbose:
                LogDecorator.print_status('warn')
                print(message)
            if raise_error:
                raise Exception(message)

        return {
            'aide_version': AIDE_VERSION,
            'AIController': {
                'uri': aic_uri,
                'aide_version': aic_version
            },
            'FileServer': {
                'uri': fs_uri,
                'aide_version': fs_version
            },
            'Database': {
                'version': dbVersion,
                'details': dbInfo
            }
        }
Beispiel #11
0
# connect to database
dbConnector = Database(config, verbose_start=True)

# check if config file points to unmigrated v1 project
print('Checking database...'.ljust(10), end='')
hasAdminTable = dbConnector.execute(
    '''
        SELECT EXISTS (
            SELECT FROM information_schema.tables 
            WHERE table_schema = 'aide_admin'
            AND table_name = 'project'
        );
    ''', None, 1)
if not hasAdminTable[0]['exists']:
    # not (yet) migrated, raise Exception with instructions to ensure compatibility
    LogDecorator.print_status('fail')
    print(f'''
The current installation of AIDE:
    database host: {config.getProperty('Database', 'host')}
    database name: {config.getProperty('Database', 'name')}
    schema:        {config.getProperty('Database', 'schema', str, '(not specified)')}

points to an installation of the legacy AIDE v1.
If you wish to continue using AIDE v2, you have to upgrade the project accordingly.
For instructions to do so, see here:
    https://github.com/microsoft/aerial_wildlife_detection/blob/multiProject/doc/upgrade_from_v1.md
    ''')
    sys.exit(1)

else:
    LogDecorator.print_status('ok')