예제 #1
0
파일: function.py 프로젝트: Kryndex/qinling
    def delete(self, id):
        """Delete the specified function."""
        LOG.info("Delete function %s.", id)

        with db_api.transaction():
            func_db = db_api.get_function(id)
            if len(func_db.jobs) > 0:
                raise exc.NotAllowedException(
                    'The function is still associated with running job(s).')
            if func_db.webhook:
                raise exc.NotAllowedException(
                    'The function is still associated with webhook.')

            # Even admin user can not delete other project's function because
            # the trust associated can only be removed by function owner.
            if func_db.project_id != context.get_ctx().projectid:
                raise exc.NotAllowedException(
                    'Function can only be deleted by its owner.')

            source = func_db.code['source']
            if source == constants.PACKAGE_FUNCTION:
                self.storage_provider.delete(func_db.project_id, id)

            # Delete all resources created by orchestrator asynchronously.
            self.engine_client.delete_function(id)

            # Delete trust if needed
            if func_db.trust_id:
                keystone_util.delete_trust(func_db.trust_id)

            # Delete etcd keys
            etcd_util.delete_function(id)

            # This will also delete function service mapping as well.
            db_api.delete_function(id)
예제 #2
0
    def delete(self, function_id, version):
        """Delete a specific function version.

        - The version should not being used by any job
        - The version should not being used by any webhook
        - Admin user can not delete normal user's version
        """
        ctx = context.get_ctx()
        acl.enforce('function_version:delete', ctx)
        LOG.info("Deleting version %s of function %s.", version, function_id)

        with db_api.transaction():
            version_db = db_api.get_function_version(function_id,
                                                     version,
                                                     insecure=False)
            latest_version = version_db.function.latest_version

            version_jobs = db_api.get_jobs(
                function_id=version_db.function_id,
                function_version=version_db.version_number,
                status={'nin': ['done', 'cancelled']})
            if len(version_jobs) > 0:
                raise exc.NotAllowedException(
                    'The function version is still associated with running '
                    'job(s).')

            version_webhook = db_api.get_webhooks(
                function_id=version_db.function_id,
                function_version=version_db.version_number,
            )
            if len(version_webhook) > 0:
                raise exc.NotAllowedException(
                    'The function version is still associated with webhook.')

            filters = rest_utils.get_filters(
                function_id=version_db.function_id,
                function_version=version_db.version_number)
            version_aliases = db_api.get_function_aliases(**filters)
            if len(version_aliases) > 0:
                raise exc.NotAllowedException(
                    'The function version is still associated with alias.')

            # Delete resources for function version
            self.engine_client.delete_function(function_id, version=version)
            etcd_util.delete_function(function_id, version=version)

            self.storage_provider.delete(ctx.projectid,
                                         function_id,
                                         None,
                                         version=version)

            db_api.delete_function_version(function_id, version)

            if latest_version == version:
                version_db.function.latest_version = latest_version - 1

        LOG.info("Version %s of function %s deleted.", version, function_id)
예제 #3
0
    def delete(self, id):
        """Delete the specified function.

        Delete function will also delete all its versions.
        """
        LOG.info("Delete function %s.", id)

        with db_api.transaction():
            func_db = db_api.get_function(id)
            if len(func_db.jobs) > 0:
                raise exc.NotAllowedException(
                    'The function is still associated with running job(s).')
            if len(func_db.webhooks) > 0:
                raise exc.NotAllowedException(
                    'The function is still associated with webhook(s).')
            if len(func_db.aliases) > 0:
                raise exc.NotAllowedException(
                    'The function is still associated with function alias(es).'
                )

            # Even admin user can not delete other project's function because
            # the trust associated can only be removed by function owner.
            if func_db.project_id != context.get_ctx().projectid:
                raise exc.NotAllowedException(
                    'Function can only be deleted by its owner.')

            # Delete trust if needed
            if func_db.trust_id:
                keystone_util.delete_trust(func_db.trust_id)

            for version_db in func_db.versions:
                # Delete all resources created by orchestrator asynchronously.
                self.engine_client.delete_function(
                    id, version=version_db.version_number)
                # Delete etcd keys
                etcd_util.delete_function(id,
                                          version=version_db.version_number)
                # Delete function version packages. Versions is only supported
                # for package type function.
                self.storage_provider.delete(func_db.project_id,
                                             id,
                                             None,
                                             version=version_db.version_number)

            # Delete resources for function version 0(func_db.versions==[])
            self.engine_client.delete_function(id)
            etcd_util.delete_function(id)

            source = func_db.code['source']
            if source == constants.PACKAGE_FUNCTION:
                self.storage_provider.delete(func_db.project_id, id,
                                             func_db.code['md5sum'])

            # This will also delete function service mapping and function
            # versions as well.
            db_api.delete_function(id)
예제 #4
0
    def _create_function_version(self, project_id, function_id, **kwargs):
        with etcd_util.get_function_version_lock(function_id) as lock:
            if not lock.is_acquired():
                return False

            with db_api.transaction():
                # Get latest function package md5 and version number
                func_db = db_api.get_function(function_id, insecure=False)
                if func_db.code['source'] != constants.PACKAGE_FUNCTION:
                    raise exc.NotAllowedException(
                        "Function versioning only allowed for %s type "
                        "function." %
                        constants.PACKAGE_FUNCTION
                    )

                l_md5 = func_db.code['md5sum']
                l_version = func_db.latest_version

                if len(func_db.versions) >= constants.MAX_VERSION_NUMBER:
                    raise exc.NotAllowedException(
                        'Can not exceed maximum number(%s) of versions' %
                        constants.MAX_VERSION_NUMBER
                    )

                # Check if the latest package changed since last version
                changed = self.storage_provider.changed_since(project_id,
                                                              function_id,
                                                              l_md5,
                                                              l_version)
                if not changed:
                    raise exc.NotAllowedException(
                        'Function package not changed since the latest '
                        'version %s.' % l_version
                    )

                LOG.info("Creating %s, function_id: %s, old_version: %d",
                         self.type, function_id, l_version)

                # Create new version and copy package.
                self.storage_provider.copy(project_id, function_id, l_md5,
                                           l_version)
                version = db_api.increase_function_version(function_id,
                                                           l_version,
                                                           **kwargs)
                func_db.latest_version = l_version + 1

            LOG.info("New version %d for function %s created.", l_version + 1,
                     function_id)
            return version
예제 #5
0
    def put(self, id, **kwargs):
        """Update function.

        - Function can not being used by job.
        - Function can not being executed.
        - (TODO)Function status should be changed so no execution will create
           when function is updating.
        """
        values = {}
        for key in UPDATE_ALLOWED:
            if kwargs.get(key) is not None:
                values.update({key: kwargs[key]})

        LOG.info('Update resource, params: %s',
                 values,
                 resource={
                     'type': self.type,
                     'id': id
                 })

        ctx = context.get_ctx()

        if set(values.keys()).issubset(set(['name', 'description'])):
            func_db = db_api.update_function(id, values)
        else:
            source = values.get('code', {}).get('source')
            with db_api.transaction():
                pre_func = db_api.get_function(id)

                if len(pre_func.jobs) > 0:
                    raise exc.NotAllowedException(
                        'The function is still associated with running job(s).'
                    )

                pre_source = pre_func.code['source']
                if source and source != pre_source:
                    raise exc.InputException(
                        "The function code type can not be changed.")
                if source == constants.IMAGE_FUNCTION:
                    raise exc.InputException(
                        "The image type function code can not be changed.")
                if (pre_source == constants.PACKAGE_FUNCTION
                        and values.get('package') is not None):
                    # Update the package data.
                    data = values['package'].file.read()
                    self.storage_provider.store(ctx.projectid, id, data)
                    values.pop('package')
                if pre_source == constants.SWIFT_FUNCTION:
                    swift_info = values['code'].get('swift', {})
                    self._check_swift(swift_info.get('container'),
                                      swift_info.get('object'))

                # Delete allocated resources in orchestrator.
                db_api.delete_function_service_mapping(id)
                self.engine_client.delete_function(id)

                func_db = db_api.update_function(id, values)

        pecan.response.status = 200
        return resources.Function.from_dict(func_db.to_dict()).to_dict()
예제 #6
0
    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)
예제 #7
0
    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())
예제 #8
0
    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)
예제 #9
0
    def delete(self, id):
        """Delete the specified function."""
        LOG.info("Delete resource.", resource={'type': self.type, 'id': id})

        with db_api.transaction():
            func_db = db_api.get_function(id)
            if len(func_db.jobs) > 0:
                raise exc.NotAllowedException(
                    'The function is still associated with running job(s).')

            source = func_db.code['source']
            if source == 'package':
                self.storage_provider.delete(context.get_ctx().projectid, id)

            # Delete all resources created by orchestrator asynchronously.
            self.engine_client.delete_function(id)

            # This will also delete function service mapping as well.
            db_api.delete_function(id)
예제 #10
0
파일: runtime.py 프로젝트: sampathP/qinling
    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)
예제 #11
0
    def put(self, id, **kwargs):
        """Update function.

        - Function can not being used by job.
        - Function can not being executed.
        - (TODO)Function status should be changed so no execution will create
           when function is updating.
        """
        values = {}

        try:
            for key in UPDATE_ALLOWED:
                if kwargs.get(key) is not None:
                    if key == "code":
                        kwargs[key] = json.loads(kwargs[key])
                    values.update({key: kwargs[key]})
        except Exception as e:
            raise exc.InputException("Invalid input, %s" % str(e))

        LOG.info('Update function %s, params: %s', id, values)
        ctx = context.get_ctx()

        if values.get('timeout'):
            common.validate_int_in_range('timeout', values['timeout'],
                                         CONF.resource_limits.min_timeout,
                                         CONF.resource_limits.max_timeout)

        db_update_only = set(['name', 'description', 'timeout'])
        if set(values.keys()).issubset(db_update_only):
            func_db = db_api.update_function(id, values)
        else:
            source = values.get('code', {}).get('source')
            md5sum = values.get('code', {}).get('md5sum')
            cpu = values.get('cpu')
            memory_size = values.get('memory_size')

            # Check cpu and memory_size values when updating.
            if cpu is not None:
                common.validate_int_in_range('cpu', values['cpu'],
                                             CONF.resource_limits.min_cpu,
                                             CONF.resource_limits.max_cpu)
            if memory_size is not None:
                common.validate_int_in_range('memory', values['memory_size'],
                                             CONF.resource_limits.min_memory,
                                             CONF.resource_limits.max_memory)

            with db_api.transaction():
                pre_func = db_api.get_function(id)

                if len(pre_func.jobs) > 0:
                    raise exc.NotAllowedException(
                        'The function is still associated with running job(s).'
                    )

                pre_source = pre_func.code['source']
                pre_md5sum = pre_func.code.get('md5sum')

                if source and source != pre_source:
                    raise exc.InputException(
                        "The function code type can not be changed.")

                if pre_source == constants.IMAGE_FUNCTION:
                    raise exc.InputException(
                        "The image type function code can not be changed.")

                # Package type function. 'code' and 'entry' make sense only if
                # 'package' is provided
                package_updated = False
                if (pre_source == constants.PACKAGE_FUNCTION
                        and values.get('package') is not None):
                    if md5sum and md5sum == pre_md5sum:
                        raise exc.InputException(
                            "The function code checksum is not changed.")

                    # Update the package data.
                    data = values['package'].file.read()
                    package_updated, md5sum = self.storage_provider.store(
                        ctx.projectid, id, data, md5sum=md5sum)
                    values.setdefault('code', {}).update({
                        "md5sum": md5sum,
                        "source": pre_source
                    })
                    values.pop('package')

                # Swift type function
                if (pre_source == constants.SWIFT_FUNCTION
                        and "swift" in values.get('code', {})):
                    swift_info = values['code']["swift"]

                    if not (swift_info.get('container')
                            or swift_info.get('object')):
                        raise exc.InputException(
                            "Either container or object must be provided for "
                            "swift type function update.")

                    new_swift_info = pre_func.code['swift']
                    new_swift_info.update(swift_info)

                    self._check_swift(new_swift_info.get('container'),
                                      new_swift_info.get('object'))

                    values['code'] = {
                        "source": pre_source,
                        "swift": new_swift_info
                    }

                # Delete allocated resources in orchestrator and etcd.
                self.engine_client.delete_function(id)
                etcd_util.delete_function(id)

                func_db = db_api.update_function(id, values)

            # Delete the old function package if needed
            if package_updated:
                self.storage_provider.delete(ctx.projectid, id, pre_md5sum)

        pecan.response.status = 200
        return resources.Function.from_db_obj(func_db).to_dict()