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')
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')
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')
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')
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')
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')
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 } }
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
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 } }
# 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')