def post(self, **kwargs): LOG.info("Creating function, params=%s", kwargs) if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s not available.' % kwargs['runtime_id']) values = { 'name': kwargs['name'], 'description': kwargs.get('description', None), 'runtime_id': kwargs['runtime_id'], 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main'), } if values['code'].get('package', False): data = kwargs['package'].file.read() ctx = context.get_ctx() with db_api.transaction(): func_db = db_api.create_function(values) self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def create_execution(self, ctx, execution_id, function_id, runtime_id, input=None): LOG.info( 'Creating execution. execution_id=%s, function_id=%s, ' 'runtime_id=%s', execution_id, function_id, runtime_id) with db_api.transaction(): execution = db_api.get_execution(execution_id) runtime = db_api.get_runtime(runtime_id) identifier = '%s-%s' % (runtime_id, runtime.name) labels = {'runtime_name': runtime.name, 'runtime_id': runtime_id} service_url = self.orchestrator.prepare_execution( function_id, identifier=identifier, labels=labels) output = self.orchestrator.run_execution(function_id, input=input, service_url=service_url) LOG.debug('Finished execution. execution_id=%s, output=%s', execution_id, output) execution.output = output execution.status = 'success' mapping = {'function_id': function_id, 'service_url': service_url} db_api.create_function_service_mapping(mapping)
def post(self, **kwargs): LOG.info("Creating %s, params: %s", self.type, kwargs) # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) values = { 'name': kwargs.get('name'), 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), } source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != constants.IMAGE_FUNCTION: if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False if values['code']['source'] == constants.PACKAGE_FUNCTION: store = True data = kwargs['package'].file.read() elif values['code']['source'] == constants.SWIFT_FUNCTION: swift_info = values['code'].get('swift', {}) self._check_swift(swift_info.get('container'), swift_info.get('object')) if cfg.CONF.pecan.auth_enable: try: values['trust_id'] = keystone_util.create_trust().id LOG.debug('Trust %s created', values['trust_id']) except Exception: raise exc.TrustFailedException( 'Trust creation failed for function.') with db_api.transaction(): func_db = db_api.create_function(values) if store: ctx = context.get_ctx() self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def post(self, **kwargs): LOG.info("Creating %s, params: %s", self.type, kwargs) # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) values = { 'name': kwargs['name'], 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), } source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != 'image': if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False if values['code']['source'] == 'package': store = True data = kwargs['package'].file.read() elif values['code']['source'] == 'swift': # Auth needs to be enabled because qinling needs to check swift # object using user's credential. if not CONF.pecan.auth_enable: raise exc.InputException('Swift object not supported.') container = values['code']['swift'].get('container') object = values['code']['swift'].get('object') if not swift_util.check_object(container, object): raise exc.InputException('Object does not exist in Swift.') with db_api.transaction(): func_db = db_api.create_function(values) if store: ctx = context.get_ctx() self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def put(self, id, runtime): """Update runtime. Currently, we support update name, description, image. When updating image, send message to engine for asynchronous handling. """ acl.enforce('runtime:update', context.get_ctx()) values = {} for key in UPDATE_ALLOWED: if runtime.to_dict().get(key) is not None: values.update({key: runtime.to_dict()[key]}) LOG.info('Update resource, params: %s', values, resource={ 'type': self.type, 'id': id }) image = values.get('image') with db_api.transaction(): if image is not None: pre_runtime = db_api.get_runtime(id) if pre_runtime.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % id) pre_image = pre_runtime.image if pre_image != image: # Ensure there is no function running in the runtime. db_funcs = db_api.get_functions(insecure=True, fields=['id'], runtime_id=id) func_ids = [func.id for func in db_funcs] for id in func_ids: if etcd_util.get_service_url(id): raise exc.NotAllowedException( 'Runtime %s is still in use by functions.' % id) values['status'] = status.UPGRADING self.engine_client.update_runtime( id, image=image, pre_image=pre_image, ) runtime_db = db_api.update_runtime(id, values) return resources.Runtime.from_db_obj(runtime_db)
def test_create_runtime(self): runtime = self.create_runtime() runtime_id = runtime.id # Set status to verify it is changed during creation. db_api.update_runtime(runtime_id, {'status': status.CREATING}) self.default_engine.create_runtime(mock.Mock(), runtime_id) self.orchestrator.create_pool.assert_called_once_with( runtime_id, runtime.image) runtime = db_api.get_runtime(runtime_id) self.assertEqual(status.AVAILABLE, runtime.status)
def delete_runtime(self, ctx, runtime_id): LOG.info('Start to delete runtime, id=%s', runtime_id) with db_api.transaction(): runtime = db_api.get_runtime(runtime_id) identifier = '%s-%s' % (runtime_id, runtime.name) labels = {'runtime_name': runtime.name, 'runtime_id': runtime_id} self.orchestrator.delete_pool(identifier, labels=labels) db_api.delete_runtime(runtime_id) LOG.info('Runtime %s deleted.', runtime_id)
def test_create_runtime_failed(self): runtime = self.create_runtime() runtime_id = runtime.id # Set status to verify it is changed during creation. db_api.update_runtime(runtime_id, {'status': status.CREATING}) self.orchestrator.create_pool.side_effect = RuntimeError self.default_engine.create_runtime(mock.Mock(), runtime_id) self.orchestrator.create_pool.assert_called_once_with( runtime_id, runtime.image, trusted=True) runtime = db_api.get_runtime(runtime_id) self.assertEqual(status.ERROR, runtime.status)
def put(self, id, runtime): """Update runtime. Currently, we only support update name, description, image. When updating image, send message to engine for asynchronous handling. """ values = {} for key in UPDATE_ALLOWED: if runtime.to_dict().get(key) is not None: values.update({key: runtime.to_dict()[key]}) LOG.info('Update resource, params: %s', values, resource={ 'type': self.type, 'id': id }) with db_api.transaction(): if 'image' in values: pre_runtime = db_api.get_runtime(id) if pre_runtime.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % id) pre_image = pre_runtime.image if pre_image != values['image']: # Ensure there is no function running in the runtime. db_funcs = db_api.get_functions(insecure=True, fields=['id'], runtime_id=id) func_ids = [func.id for func in db_funcs] mappings = db_api.get_function_service_mappings( insecure=True, function_id={'in': func_ids}) if mappings: raise exc.NotAllowedException( 'Runtime %s is still in use by functions.' % id) values['status'] = status.UPGRADING self.engine_client.update_runtime(id, image=values['image'], pre_image=pre_image) else: values.pop('image') runtime_db = db_api.update_runtime(id, values) return resources.Runtime.from_dict(runtime_db.to_dict())
def create_runtime(self, ctx, runtime_id): LOG.info('Start to create runtime %s.', runtime_id) with db_api.transaction(): runtime = db_api.get_runtime(runtime_id) try: self.orchestrator.create_pool(runtime_id, runtime.image) runtime.status = status.AVAILABLE LOG.info('Runtime %s created.', runtime_id) except Exception as e: LOG.exception( 'Failed to create pool for runtime %s. Error: %s', runtime_id, str(e)) runtime.status = status.ERROR
def test_update_runtime(self): runtime = self.create_runtime() runtime_id = runtime.id # Set status to verify it is changed during update. db_api.update_runtime(runtime_id, {'status': status.UPGRADING}) image = self.rand_name('new_image', prefix=self.prefix) pre_image = self.rand_name('pre_image', prefix=self.prefix) self.orchestrator.update_pool.return_value = True self.default_engine.update_runtime(mock.Mock(), runtime_id, image, pre_image) self.orchestrator.update_pool.assert_called_once_with(runtime_id, image=image) runtime = db_api.get_runtime(runtime_id) self.assertEqual(runtime.status, status.AVAILABLE)
def delete(self, id): LOG.info("Delete resource.", resource={'type': self.type, 'id': id}) with db_api.transaction(): runtime_db = db_api.get_runtime(id) # Runtime can not be deleted if still associate with functions. funcs = db_api.get_functions(runtime_id={'eq': id}) if len(funcs): raise exc.NotAllowedException('Runtime %s is still in use.' % id) runtime_db.status = status.DELETING # Clean related resources asynchronously self.engine_client.delete_runtime(id)
def delete(self, id): """Delete runtime.""" LOG.info("Delete runtime [id=%s]", id) with db_api.transaction(): runtime_db = db_api.get_runtime(id) # Runtime can not be deleted if still associate with functions. funcs = db_api.get_functions(runtime_id={'eq': id}) if len(funcs): raise exc.NotAllowedException( 'Runtime %s is still in use.' % id ) runtime_db.status = 'deleting' # Clean related resources asynchronously self.engine_client.delete_runtime(id)
def create_runtime(self, ctx, runtime_id): LOG.info('Start to create runtime, id=%s', runtime_id) with db_api.transaction(): runtime = db_api.get_runtime(runtime_id) identifier = '%s-%s' % (runtime_id, runtime.name) labels = {'runtime_name': runtime.name, 'runtime_id': runtime_id} try: self.orchestrator.create_pool( identifier, runtime.image, labels=labels, ) runtime.status = 'available' except Exception as e: LOG.exception( 'Failed to create pool for runtime %s. Error: %s', runtime_id, str(e)) runtime.status = 'error'
def create_runtime(self, ctx, runtime_id): LOG.info('Start to create.', resource={ 'type': 'runtime', 'id': runtime_id }) with db_api.transaction(): runtime = db_api.get_runtime(runtime_id) labels = {'runtime_id': runtime_id} try: self.orchestrator.create_pool( runtime_id, runtime.image, labels=labels, ) runtime.status = status.AVAILABLE except Exception as e: LOG.exception( 'Failed to create pool for runtime %s. Error: %s', runtime_id, str(e)) runtime.status = status.ERROR
def get(self, id): LOG.info("Get resource.", resource={'type': self.type, 'id': id}) runtime_db = db_api.get_runtime(id) return resources.Runtime.from_db_obj(runtime_db)
def get(self, id): LOG.info("Fetch runtime [id=%s]", id) runtime_db = db_api.get_runtime(id) return resources.Runtime.from_dict(runtime_db.to_dict())
def post(self, **kwargs): # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) LOG.info("Creating function, params: %s", kwargs) values = { 'name': kwargs.get('name'), 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), 'cpu': kwargs.get('cpu', CONF.resource_limits.default_cpu), 'memory_size': kwargs.get('memory_size', CONF.resource_limits.default_memory), 'timeout': kwargs.get('timeout', CONF.resource_limits.default_timeout), } common.validate_int_in_range('timeout', values['timeout'], CONF.resource_limits.min_timeout, CONF.resource_limits.max_timeout) common.validate_int_in_range('cpu', values['cpu'], CONF.resource_limits.min_cpu, CONF.resource_limits.max_cpu) common.validate_int_in_range('memory', values['memory_size'], CONF.resource_limits.min_memory, CONF.resource_limits.max_memory) source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != constants.IMAGE_FUNCTION: if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False create_trust = True if source == constants.PACKAGE_FUNCTION: store = True md5sum = values['code'].get('md5sum') data = kwargs['package'].file.read() elif source == constants.SWIFT_FUNCTION: swift_info = values['code'].get('swift', {}) if not (swift_info.get('container') and swift_info.get('object')): raise exc.InputException("Both container and object must be " "provided for swift type function.") self._check_swift(swift_info.get('container'), swift_info.get('object')) else: create_trust = False values['entry'] = None if cfg.CONF.pecan.auth_enable and create_trust: try: values['trust_id'] = keystone_util.create_trust().id LOG.debug('Trust %s created', values['trust_id']) except Exception: raise exc.TrustFailedException( 'Trust creation failed for function.') # Create function and store the package data inside a db transaction so # that the function won't be created if any error happened during # package store. with db_api.transaction(): func_db = db_api.create_function(values) if store: try: ctx = context.get_ctx() _, actual_md5 = self.storage_provider.store(ctx.projectid, func_db.id, data, md5sum=md5sum) values['code'].update({"md5sum": actual_md5}) func_db = db_api.update_function(func_db.id, values) except Exception as e: LOG.exception("Failed to store function package.") keystone_util.delete_trust(values['trust_id']) raise e pecan.response.status = 201 return resources.Function.from_db_obj(func_db).to_dict()
def get(self, id): LOG.info("Fetch resource.", resource={'type': self.type, 'id': id}) runtime_db = db_api.get_runtime(id) return resources.Runtime.from_dict(runtime_db.to_dict())
def create_execution(engine_client, params): function_id = params['function_id'] is_sync = params.get('sync', True) input = params.get('input') version = params.get('function_version', 0) func_db = db_api.get_function(function_id) runtime_id = func_db.runtime_id # Image type function does not need runtime if runtime_id: runtime_db = db_api.get_runtime(runtime_id) if runtime_db and runtime_db.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % func_db.runtime_id) if version > 0: if func_db.code['source'] != constants.PACKAGE_FUNCTION: raise exc.InputException( "Can not specify version for %s type function." % constants.PACKAGE_FUNCTION) # update version count version_db = db_api.get_function_version(function_id, version) pre_version_count = version_db.count _update_function_version_db(version_db.id, pre_version_count) else: pre_count = func_db.count _update_function_db(function_id, pre_count) # input in params should be a string. if input: try: params['input'] = json.loads(input) except ValueError: params['input'] = {'__function_input': input} params.update({'status': status.RUNNING}) db_model = db_api.create_execution(params) try: engine_client.create_execution(db_model.id, function_id, version, runtime_id, input=params.get('input'), is_sync=is_sync) except exc.QinlingException: # Catch RPC errors for executions: # - for RemoteError in an RPC call, the execution status would be # handled in the engine side; # - for other exceptions in an RPC call or cast, the execution status # would remain RUNNING so we should update it. db_model = db_api.get_execution(db_model.id) if db_model.status == status.RUNNING: db_model = db_api.update_execution(db_model.id, {'status': status.ERROR}) return db_model if is_sync: # The execution should already be updated by engine service for sync # execution. db_model = db_api.get_execution(db_model.id) return db_model