def deploy_helper(codebase_version, expected_version, existing_service=None): with self.database.transaction(): try: ServiceManager.createOrUpdateServiceWithCodebase( service_schema.Codebase.createFromFiles( getTestServiceModule(codebase_version)), "test_service.service.Service", serviceName, targetCount=1) except Exception: pass if existing_service: self.assertTrue( self.database.waitForCondition( lambda: not existing_service.connection.exists(), timeout=5.0)) self.waitForCount(1) with self.database.transaction(): s = TestServiceLastTimestamp.aliveServices()[0] self.assertEqual(s.version, expected_version) return s
def installServices(self): with self.database.transaction(): ServiceManager.createOrUpdateService(Task.TaskService, "TaskService", target_count=1, gbRamUsed=0, coresUsed=0) ServiceManager.createOrUpdateService(Task.TaskDispatchService, "TaskDispatchService", target_count=1, gbRamUsed=0, coresUsed=0) self.waitRunning("TaskService") self.waitRunning("TaskDispatchService") with open(os.path.join(ownDir, "test_files", "TestModule1.py"), "r") as f: files = {"TestModule1.py": f.read()} with self.database.transaction(): self.testService1Object = ServiceManager.createOrUpdateServiceWithCodebase( service_schema.Codebase.createFromFiles(files), "TestModule1.TestService1", "TestService1", 0) self.testService1Codebase = self.testService1Object.codebase.instantiate( ) self.service1Conn = self.newDbConnection() self.service1Conn.setSerializationContext( self.testService1Codebase.serializationContext) self.service1Conn.subscribeToType(Task.Task) self.service1Conn.subscribeToType(Task.TaskStatus) self.service1Conn.subscribeToType( self.testService1Codebase.getClassByName("TestModule1.Record"))
def test_redeploy_hanging_services(self): with self.database.transaction(): ServiceManager.createOrUpdateService(HangingService, "HangingService", target_count=10) self.waitForCount(10) with self.database.view(): instances = service_schema.ServiceInstance.lookupAll() orig_codebase = instances[0].codebase with self.database.transaction(): ServiceManager.createOrUpdateServiceWithCodebase( service_schema.Codebase.createFromFiles( getTestServiceModule(2)), "test_service.service.Service", "HangingService", 10) # this should force a redeploy. maxProcessesEver = 0 for i in range(40): maxProcessesEver = max( maxProcessesEver, len(psutil.Process().children()[0].children())) time.sleep(.1) self.database.flush() # after 2 seconds, we should be redeployed, but give Travis a bit more time if os.environ.get('TRAVIS_CI', None) is not None: time.sleep(10.0) with self.database.view(): instances_redeployed = service_schema.ServiceInstance.lookupAll() self.assertEqual(len(instances), 10) self.assertEqual(len(instances_redeployed), 10) self.assertEqual( len(set(instances).intersection(set(instances_redeployed))), 0) self.assertTrue(orig_codebase != instances_redeployed[0].codebase) # and we never became too big! self.assertLess(maxProcessesEver, 11)
def _main(argv): configureLogging() parser = argparse.ArgumentParser("Install and configure services.") parser.add_argument("--hostname", default=os.getenv("ODB_HOST", "localhost"), required=False) parser.add_argument("--port", type=int, default=int(os.getenv("ODB_PORT", 8000)), required=False) parser.add_argument("--auth", type=str, default=os.getenv("ODB_AUTH_TOKEN", ""), required=False, help="Auth token to use to connect.") subparsers = parser.add_subparsers() connections_parser = subparsers.add_parser('connections', help='list live connections') connections_parser.set_defaults(command='connections') install_parser = subparsers.add_parser('install', help='install a service') install_parser.set_defaults(command='install') install_parser.add_argument("--path", action='append', dest='paths') install_parser.add_argument("--class") install_parser.add_argument("--name", required=False) install_parser.add_argument("--placement", required=False, default='Any', choices=['Any','Master','Worker']) install_parser.add_argument("--singleton", required=False, action='store_true') reset_parser = subparsers.add_parser('reset', help='reset a service''s boot count') reset_parser.set_defaults(command='reset') reset_parser.add_argument("name") configure_parser = subparsers.add_parser('configure', help='configure a service') configure_parser.set_defaults(command='configure') configure_parser.add_argument("name") configure_parser.add_argument("args", nargs=argparse.REMAINDER) list_parser = subparsers.add_parser('list', help='list installed services') list_parser.set_defaults(command='list') instances_parser = subparsers.add_parser('instances', help='list running service instances') instances_parser.set_defaults(command='instances') hosts_parser = subparsers.add_parser('hosts', help='list running hosts') hosts_parser.set_defaults(command='hosts') start_parser = subparsers.add_parser('start', help='Start (or change target replicas for) a service') start_parser.set_defaults(command='start') start_parser.add_argument("name") start_parser.add_argument("--count", type=int, default=1, required=False) stop_parser = subparsers.add_parser('stop', help='Stop a service') stop_parser.set_defaults(command='stop') stop_parser.add_argument("name") parsedArgs = parser.parse_args(argv[1:]) db = connect(parsedArgs.hostname, parsedArgs.port, parsedArgs.auth) db.subscribeToSchema(core_schema, service_schema, lazySubscription=True) if parsedArgs.command == 'connections': table = [['Connection ID']] with db.view(): for c in sorted(core_schema.Connection.lookupAll(), key=lambda c: c._identity): table.append([c._identity]) print(formatTable(table)) if parsedArgs.command == 'configure': try: with db.transaction(): s = service_schema.Service.lookupAny(name=parsedArgs.name) svcClass = s.instantiateServiceType() svcClass.configureFromCommandline(db, s, parsedArgs.args) except Exception as e: traceback.print_exc() return 1 if parsedArgs.command == 'reset': with db.transaction(): service = service_schema.Service.lookupAny(name=parsedArgs.name) service.timesBootedUnsuccessfully = 0 if parsedArgs.command == 'install': if parsedArgs.paths: paths = parsedArgs.paths else: paths = [findGitParent(os.getcwd())] gbRamUsed = 1 coresUsed = 1 with db.transaction(): fullClassname = getattr(parsedArgs, 'class') modulename, classname = fullClassname.rsplit(".",1) if modulename.startswith("object_database"): def _getobject(modname, attribute): mod = __import__(modname, fromlist=[attribute]) return mod.__dict__[attribute] actualClass = _getobject(modulename, classname) if not isinstance(actualClass, type): print("Named class %s is not a type." % fullClassname) return 1 if not issubclass(actualClass, ServiceBase): print("Named class %s is not a ServiceBase subclass." % fullClassname) return 1 coresUsed = actualClass.coresUsed gbRamUsed = actualClass.gbRamUsed ServiceManager.createOrUpdateService(actualClass, classname, placement=parsedArgs.placement, isSingleton=parsedArgs.singleton, coresUsed=coresUsed, gbRamUsed=gbRamUsed ) else: codebase = service_schema.Codebase.create(paths) #make sure the codebase is importable, etc module = codebase.instantiate(modulename) if module is None: print("Can't find", module, "in the codebase") actualClass = module.__dict__.get(classname, None) if actualClass is None: print("Can't find", module, "in module", modulename) if actualClass is None: print("Can't find", classname, "in the codebase") return 1 if not isinstance(actualClass, type): print("Named class %s is not a type." % fullClassname) return 1 if not issubclass(actualClass, ServiceBase): print("Named class %s is not a ServiceBase subclass." % fullClassname) return 1 coresUsed = actualClass.coresUsed gbRamUsed = actualClass.gbRamUsed if not parsedArgs.name: name = fullClassname.split(".")[-1] else: name = parsedArgs.name ServiceManager.createOrUpdateServiceWithCodebase(codebase, fullClassname, name, targetCount=None, placement=parsedArgs.placement, coresUsed=coresUsed, gbRamUsed=gbRamUsed) if parsedArgs.command == 'list': table = [['Service', 'Codebase', 'Module', 'Class', 'Placement', 'TargetCount', 'Cores', 'RAM']] with db.view(): for s in sorted(service_schema.Service.lookupAll(), key=lambda s:s.name): table.append([ s.name, str(s.codebase), s.service_module_name, s.service_class_name, s.placement, str(s.target_count), s.coresUsed, s.gbRamUsed ]) print(formatTable(table)) if parsedArgs.command == 'instances': table = [['Service', 'Host', 'Connection', 'State']] with db.view(): for s in sorted(service_schema.ServiceInstance.lookupAll(), key=lambda s:(s.service.name, s.host.hostname, s.state)): table.append([ s.service.name, s.host.hostname, s.connection if s.connection.exists() else "<DEAD>", s.state ]) print(formatTable(table)) if parsedArgs.command == 'hosts': table = [['Connection', 'IsMaster', 'Hostname', 'RAM USAGE', 'CORE USAGE', 'SERVICE COUNT']] with db.view(): for s in sorted(service_schema.ServiceHost.lookupAll(), key=lambda s:s.hostname): table.append([ s.connection._identity, str(s.isMaster), s.hostname, "%.1f / %.1f" % (s.gbRamUsed, s.maxGbRam), "%s / %s" % (s.coresUsed, s.maxCores), str(len(service_schema.ServiceInstance.lookupAll(host=s))) ]) print(formatTable(table)) if parsedArgs.command == 'start': with db.transaction(): s = service_schema.Service.lookupAny(name=parsedArgs.name) if not s: print("Can't find a service named", s) return 1 s.target_count = max(parsedArgs.count, 0) if parsedArgs.command == 'stop': with db.transaction(): s = service_schema.Service.lookupAny(name=parsedArgs.name) if not s: print("Can't find a service named", s) return 1 s.target_count = 0 return 0
def main(argv=None): if argv is not None: argv = sys.argv token = genToken() port = 8020 loglevel_name = 'INFO' with tempfile.TemporaryDirectory() as tmpDirName: try: server = start_service_manager(tmpDirName, port, token, loglevel_name=loglevel_name) database = connect("localhost", port, token, retry=True) database.subscribeToSchema(core_schema, service_schema, active_webservice_schema) with database.transaction(): service = ServiceManager.createOrUpdateService( ActiveWebService, "ActiveWebService", target_count=0) ActiveWebService.configureFromCommandline( database, service, ['--port', '8000', '--host', '0.0.0.0', '--log-level', loglevel_name] ) ActiveWebService.setLoginPlugin( database, service, LoginIpPlugin, [None], config={'company_name': 'A Testing Company'} ) with database.transaction(): ServiceManager.startService("ActiveWebService", 1) with database.transaction(): service = ServiceManager.createOrUpdateService( UninitializableService, "UninitializableService", target_count=1 ) with database.transaction(): service = ServiceManager.createOrUpdateService( HappyService, "HappyService", target_count=1 ) with database.transaction(): service = ServiceManager.createOrUpdateService( GraphDisplayService, "GraphDisplayService", target_count=1 ) with database.transaction(): service = ServiceManager.createOrUpdateService( TextEditorService, "TextEditorService", target_count=1 ) with database.transaction(): service = ServiceManager.createOrUpdateService( DropdownTestService, "DropdownTestService", target_count=1 ) with database.transaction(): service = ServiceManager.createOrUpdateService( BigGridTestService, "BigGridTestService", target_count=1 ) with database.transaction(): ServiceManager.createOrUpdateServiceWithCodebase( service_schema.Codebase.createFromFiles({ 'test_service/__init__.py': '', 'test_service/service.py': textwrap.dedent(""" from object_database.service_manager.ServiceBase import ServiceBase class TestService(ServiceBase): gbRamUsed = 0 coresUsed = 0 def initialize(self): with self.db.transaction(): self.runtimeConfig.serviceInstance.statusMessage = "Loaded" def doWork(self, shouldStop): shouldStop.wait() def display(self, queryParams=None): return "test service display message" """) }), "test_service.service.TestService", "TestService", 10 ) print("SERVER IS BOOTED") while True: time.sleep(.1) finally: server.terminate() server.wait() return 0