예제 #1
0
    def get(self, bento):
        """
        Get a BentoService info

        Args:
            bento: a BentoService identifier in the format of NAME:VERSION

        Returns:
            bentoml.yatai.proto.repository_pb2.Bento

        Example:

        >>> yatai_client = get_yatai_client()
        >>> bento_info = yatai_client.repository.get('my_service:version')
        """
        track('py-api-get')
        if ':' not in bento:
            raise BentoMLException(
                'BentoService name or version is missing. Please provide in the '
                'format of name:version')
        name, version = bento.split(':')
        result = self.yatai_service.GetBento(
            GetBentoRequest(bento_name=name, bento_version=version))
        if result.status.status_code != yatai_proto.status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                result.status)
            raise BentoMLException(
                f'BentoService {name}:{version} not found - '
                f'{error_code}:{error_message}')
        return result.bento
예제 #2
0
    def pull(self, bento):
        """
        Pull a BentoService from a remote yatai service. The BentoService will be saved
        and registered with local yatai service.

        Args:
            bento: a BentoService identifier in the form of NAME:VERSION

        Returns:
            BentoService saved path

        Example:

        >>> client = get_yatai_client('127.0.0.1:50051')
        >>> saved_path = client.repository.pull('MyService:')
        """
        track('py-api-pull')

        bento_pb = self.get(bento)
        with TempDirectory() as tmpdir:
            # Create a non-exist directory for safe_retrieve
            target_bundle_path = os.path.join(tmpdir, 'bundle')
            self.download_to_directory(bento_pb, target_bundle_path)

            from bentoml.yatai.client import get_yatai_client

            labels = (dict(bento_pb.bento_service_metadata.labels)
                      if bento_pb.bento_service_metadata.labels else None)

            local_yc = get_yatai_client()
            return local_yc.repository.upload_from_dir(target_bundle_path,
                                                       labels=labels)
예제 #3
0
def track_deployment_delete(deployment_operator, created_at, force_delete=False):
    operator_name = DeploymentSpec.DeploymentOperator.Name(deployment_operator)
    up_time = int((datetime.utcnow() - created_at.ToDatetime()).total_seconds())
    track(
        f'deployment-{operator_name}-stop',
        {'up_time': up_time, 'force_delete': force_delete},
    )
예제 #4
0
    def containerize(self, bento, tag=None, build_args=None, push=False):
        """
        Create a container image from a BentoService.

        Args:
            bento: string
            tag: string
            build_args: dict
            push: boolean

        Returns:
            Image tag: String
        """
        track('py-api-containerize')
        if ':' not in bento:
            raise BentoMLException(
                'BentoService name or version is missing. Please provide in the '
                'format of name:version')
        name, version = bento.split(':')
        containerize_request = ContainerizeBentoRequest(
            bento_name=name,
            bento_version=version,
            tag=tag,
            build_args=build_args,
            push=push,
        )
        result = self.yatai_service.ContainerizeBento(containerize_request)

        if result.status.status_code != yatai_proto.status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                result.status)
            raise BentoMLException(
                f'Failed to containerize {bento} - {error_code}:{error_message}'
            )
        return result.tag
예제 #5
0
    def load(self, bento):
        """
        Load bento service from bento tag or from a bento bundle path.
        Args:
            bento: string,
        Returns:
            BentoService instance

        Example:
        >>> yatai_client = get_yatai_client()
        >>> # Load BentoService bases on bento tag.
        >>> bento = yatai_client.repository.load('Service_name:version')
        >>> # Load BentoService from bento bundle path
        >>> bento = yatai_client.repository.load('/path/to/bento/bundle')
        >>> # Load BentoService from s3 storage
        >>> bento = yatai_client.repository.load('s3://bucket/path/bundle.tar.gz')
        """
        track('py-api-load')
        if os.path.isdir(bento) or is_s3_url(bento) or is_gcs_url(bento):
            saved_bundle_path = bento
        else:
            bento_pb = self.get(bento)
            saved_bundle_path = resolve_bento_bundle_uri(bento_pb)
        svc = load_from_dir(saved_bundle_path)
        return svc
예제 #6
0
    def push(self, bento, labels=None):
        """
        Push a local BentoService to a remote yatai server.

        Args:
            bento: a BentoService identifier in the format of NAME:VERSION
            labels: optional. List of labels for the BentoService.

        Returns:
            BentoService saved path

        Example:

        >>> svc = MyBentoService()
        >>> svc.save()
        >>>
        >>> remote_yatai_client = get_yatai_client('http://remote.yatai.service:50050')
        >>> bento = f'{svc.name}:{svc.version}'
        >>> remote_saved_path= remote_yatai_client.repository.push(bento)
        """
        track('py-api-push')

        from bentoml.yatai.client import get_yatai_client

        local_yc = get_yatai_client()

        local_bento_pb = local_yc.repository.get(bento)
        if local_bento_pb.uri.s3_presigned_url:
            bento_bundle_path = local_bento_pb.uri.s3_presigned_url
        elif local_bento_pb.uri.gcs_presigned_url:
            bento_bundle_path = local_bento_pb.uri.gcs_presigned_url
        else:
            bento_bundle_path = local_bento_pb.uri.uri
        return self.upload_from_dir(bento_bundle_path, labels=labels)
예제 #7
0
    def pull(self, bento):
        """
        Pull a BentoService from a remote yatai service. The BentoService will be saved
        and registered with local yatai service.

        Args:
            bento: a BentoService identifier in the form of NAME:VERSION

        Returns:
            BentoService saved path

        Example:

        >>> remote_yatai_client = get_yatai_client('remote_yatai_service_address')
        >>> saved_path = remote_yatai_client.repository.pull('MyService:version')
        """
        track('py-api-pull')
        if isinstance(self.yatai_service, YataiService):
            raise BentoMLException('need set yatai_service_url')
        bento_pb = self.get(bento)
        with TempDirectory() as tmpdir:
            # Create a non-exist directory for safe_retrieve
            target_bundle_path = os.path.join(tmpdir, 'bundle')
            self.download_to_directory(bento_pb, target_bundle_path)

            from bentoml.yatai.client import get_yatai_client

            local_yc = get_yatai_client()
            return local_yc.repository.upload_from_dir(target_bundle_path)
예제 #8
0
    def prune(self, bento_name=None, labels=None):
        """
        Delete all BentoServices that matches the specified criteria

        Args:
            bento_name: optional
            labels: optional

        Example:

        >>> yatai_client = get_yatai_client()
        >>> # Delete all bento services
        >>> yatai_client.repository.prune()
        >>> # Delete bento services that matches with the label `ci=failed`
        >>> yatai_client.repository.prune(labels='ci=failed')
        """
        track('py-api-prune')
        list_bentos_result = self.list(bento_name=bento_name, labels=labels,)
        if list_bentos_result.status.status_code != yatai_proto.status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                list_bentos_result.status
            )
            raise BentoMLException(f'{error_code}:{error_message}')
        for bento in list_bentos_result.bentos:
            bento_tag = f'{bento.name}:{bento.version}'
            try:
                self.delete(bento_tag)
            except BentoMLException as e:
                logger.error(f'Failed to delete Bento {bento_tag}: {e}')
예제 #9
0
    def delete(self, bento):
        """
        Delete bento

        Args:
            bento: a BentoService identifier in the format of NAME:VERSION

        Example:
        >>>
        >>> yatai_client = get_yatai_client()
        >>> yatai_client.repository.delete('my_service:version')
        """
        track('py-api-delete')
        if ':' not in bento:
            raise BentoMLException(
                'BentoService name or version is missing. Please provide in the '
                'format of name:version'
            )
        name, version = bento.split(':')
        result = self.yatai_service.DangerouslyDeleteBento(
            DangerouslyDeleteBentoRequest(bento_name=name, bento_version=version)
        )
        if result.status.status_code != yatai_proto.status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                result.status
            )
            raise BentoMLException(
                f'Failed to delete Bento {bento} {error_code}:{error_message}'
            )
예제 #10
0
    def push(self, bento, labels=None):
        """
        Push a local BentoService to a remote yatai server.
        Args:
            bento: a BentoService identifier in the format of NAME:VERSION
            labels: optional. List of labels for the BentoService.

        Returns:
            BentoService saved path
        """
        track('py-api-push')
        if isinstance(self.yatai_service, YataiService):
            raise BentoMLException('need set yatai_service_url')

        from bentoml.yatai.client import get_yatai_client

        local_yc = get_yatai_client()

        local_bento_pb = local_yc.repository.get(bento)
        if local_bento_pb.uri.s3_presigned_url:
            bento_bundle_path = local_bento_pb.uri.s3_presigned_url
        elif local_bento_pb.uri.gcs_presigned_url:
            bento_bundle_path = local_bento_pb.uri.gcs_presigned_url
        else:
            bento_bundle_path = local_bento_pb.uri.uri
        return self.upload_from_dir(bento_bundle_path, labels=labels)
예제 #11
0
        def wrapper(do_not_track: bool, *args, **kwargs):
            if do_not_track:
                os.environ["BENTOML_DO_NOT_TRACK"] = str(True)
                logger.info("Executing '%s' command without usage tracking.",
                            command_name)

            track_properties = {
                'command_group': cmd_group.name,
                'command': command_name,
            }
            start_time = time.time()
            try:
                return_value = func(*args, **kwargs)
                track_properties['duration'] = time.time() - start_time
                track_properties['return_code'] = 0
                track(TRACK_CLI_EVENT_NAME, track_properties)
                return return_value
            except BaseException as e:
                track_properties['duration'] = time.time() - start_time
                track_properties['error_type'] = type(e).__name__
                track_properties['error_message'] = str(e)
                track_properties['return_code'] = 1
                if type(e) == KeyboardInterrupt:
                    track_properties['return_code'] = 2
                track(TRACK_CLI_EVENT_NAME, track_properties)
                raise
예제 #12
0
 def prune(self, bento_name=None, labels=None):
     track('py-api-prune')
     list_bentos_result = self.list(bento_name=bento_name, labels=labels,)
     if list_bentos_result.status.status_code != yatai_proto.status_pb2.Status.OK:
         error_code, error_message = status_pb_to_error_code_and_message(
             list_bentos_result.status
         )
         raise BentoMLException(f'{error_code}:{error_message}')
     for bento in list_bentos_result.bentos:
         bento_tag = f'{bento.name}:{bento.version}'
         try:
             self.delete(bento_tag)
         except BentoMLException as e:
             logger.error(f'Failed to delete Bento {bento_tag}: {e}')
예제 #13
0
 def delete(self, bento):
     track('py-api-delete')
     if ':' not in bento:
         raise BentoMLException(
             'BentoService name or version is missing. Please provide in the '
             'format of name:version')
     name, version = bento.split(':')
     result = self.yatai_service.DangerouslyDeleteBento(
         DangerouslyDeleteBentoRequest(bento_name=name,
                                       bento_version=version))
     if result.status.status_code != yatai_proto.status_pb2.Status.OK:
         error_code, error_message = status_pb_to_error_code_and_message(
             result.status)
         raise BentoMLException(
             f'Failed to delete Bento {bento} {error_code}:{error_message}')
예제 #14
0
    def list(
        self,
        bento_name=None,
        offset=None,
        limit=None,
        labels=None,
        order_by=None,
        ascending_order=None,
    ):
        """
        List BentoServices that satisfy the specified criteria.

        Args:
            bento_name: optional. BentoService name
            limit: optional. maximum number of returned results
            labels: optional.
            offset: optional. offset of results
            order_by: optional. order by results
            ascending_order:  optional. direction of results order

        Returns:
            [bentoml.yatai.proto.repository_pb2.Bento]

        Example:

        >>> yatai_client = get_yatai_client()
        >>> bentos_info_list = yatai_client.repository.list(
        >>>     labels='key=value,key2=value'
        >>> )
        """
        track('py-api-list')
        list_bento_request = ListBentoRequest(
            bento_name=bento_name,
            offset=offset,
            limit=limit,
            order_by=order_by,
            ascending_order=ascending_order,
        )
        if labels is not None:
            generate_gprc_labels_selector(list_bento_request.label_selectors,
                                          labels)

        result = self.yatai_service.ListBento(list_bento_request)
        if result.status.status_code != yatai_proto.status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                result.status)
            raise BentoMLException(f'{error_code}:{error_message}')
        return result.bentos
예제 #15
0
 def get(self, bento):
     track('py-api-get')
     if ':' not in bento:
         raise BentoMLException(
             'BentoService name or version is missing. Please provide in the '
             'format of name:version')
     name, version = bento.split(':')
     result = self.yatai_service.GetBento(
         GetBentoRequest(bento_name=name, bento_version=version))
     if result.status.status_code != yatai_proto.status_pb2.Status.OK:
         error_code, error_message = status_pb_to_error_code_and_message(
             result.status)
         raise BentoMLException(
             f'BentoService {name}:{version} not found - '
             f'{error_code}:{error_message}')
     return result.bento
예제 #16
0
 def new_behaviour(request_or_iterator, servicer_context):
     start = default_timer()
     try:
         return behaviour(request_or_iterator, servicer_context)
     finally:
         duration = max(default_timer() - start, 0)
         GRPC_SERVER_HANDLED_HISTOGRAM.labels(
             grpc_type='UNARY',
             grpc_service=grpc_service_name,
             grpc_method=grpc_method_name,
         ).observe(duration)
         track(
             YATAI_GRPC_USAGE_EVENT_NAME,
             {
                 "method": grpc_method_name,
                 "duration": duration
             },
         )
예제 #17
0
 def wrapper(*args, **kwargs):
     track_properties = {
         'command_group': cmd_group.name,
         'command': command_name,
     }
     start_time = time.time()
     try:
         return_value = func(*args, **kwargs)
         track_properties['duration'] = time.time() - start_time
         track_properties['return_code'] = 0
         track(TRACK_CLI_EVENT_NAME, track_properties)
         return return_value
     except BaseException as e:
         track_properties['duration'] = time.time() - start_time
         track_properties['error_type'] = type(e).__name__
         track_properties['return_code'] = 1
         if type(e) == KeyboardInterrupt:
             track_properties['return_code'] = 2
         track(TRACK_CLI_EVENT_NAME, track_properties)
         raise
예제 #18
0
파일: __init__.py 프로젝트: zidingz/BentoML
def deploy_bentoml(clipper_conn,
                   bundle_path,
                   api_name,
                   model_name=None,
                   labels=None,
                   build_envs=None):
    """Deploy bentoml bundle to clipper cluster

    Args:
        clipper_conn(clipper_admin.ClipperConnection): Clipper connection instance
        bundle_path(str): Path to the saved BentomlService bundle.
        api_name(str): name of the api that will be used as prediction function for
            clipper cluster
        model_name(str): Model's name for clipper cluster
        labels(:obj:`list(str)`, optional): labels for clipper model

    Returns:
        tuple: Model name and model version that deployed to clipper

    """
    track("clipper-deploy", {'bento_service_bundle_path': bundle_path})

    build_envs = {} if build_envs is None else build_envs
    # docker is required to build clipper model image
    ensure_docker_available_or_raise()

    if not clipper_conn.connected:
        raise BentoMLException(
            "No connection to Clipper cluster. CallClipperConnection.connect to "
            "connect to an existing cluster or ClipperConnection.start_clipper to "
            "create a new one")

    bento_service_metadata = load_bento_service_metadata(bundle_path)

    try:
        api_metadata = next((api for api in bento_service_metadata.apis
                             if api.name == api_name))
    except StopIteration:
        raise BentoMLException(
            "Can't find API '{}' in BentoService bundle {}".format(
                api_name, bento_service_metadata.name))

    if api_metadata.input_type not in ADAPTER_TYPE_TO_INPUT_TYPE:
        raise BentoMLException(
            "Only BentoService APIs using ClipperInput can be deployed to Clipper"
        )

    input_type = ADAPTER_TYPE_TO_INPUT_TYPE[api_metadata.input_type]
    model_name = model_name or get_clipper_compatible_string(
        bento_service_metadata.name + "-" + api_metadata.name)
    model_version = get_clipper_compatible_string(
        bento_service_metadata.version)

    with TempDirectory() as tempdir:
        entry_py_content = CLIPPER_ENTRY.format(api_name=api_name)
        build_path = os.path.join(tempdir, "bento")
        shutil.copytree(bundle_path, build_path)

        with open(os.path.join(build_path, "clipper_entry.py"), "w") as f:
            f.write(entry_py_content)

        if bento_service_metadata.env.python_version.startswith("3.6"):
            base_image = "clipper/python36-closure-container:0.4.1"
        elif bento_service_metadata.env.python_version.startswith("2.7"):
            base_image = "clipper/python-closure-container:0.4.1"
        else:
            raise BentoMLException(
                "Python version {} is not supported in Clipper".format(
                    bento_service_metadata.env.python_version))

        docker_content = CLIPPER_DOCKERFILE.format(
            model_name=model_name,
            model_version=model_version,
            base_image=base_image,
            pip_index_url=build_envs.get("PIP_INDEX_URL", ""),
            pip_trusted_url=build_envs.get("PIP_TRUSTED_HOST", ""),
        )
        with open(os.path.join(build_path, "Dockerfile-clipper"), "w") as f:
            f.write(docker_content)

        docker_api = docker.APIClient()
        clipper_model_docker_image_tag = "clipper-model-{}:{}".format(
            bento_service_metadata.name.lower(),
            bento_service_metadata.version)
        for line in docker_api.build(
                path=build_path,
                dockerfile="Dockerfile-clipper",
                tag=clipper_model_docker_image_tag,
        ):
            process_docker_api_line(line)

        logger.info(
            "Successfully built docker image %s for Clipper deployment",
            clipper_model_docker_image_tag,
        )

    clipper_conn.deploy_model(
        name=model_name,
        version=model_version,
        input_type=input_type,
        image=clipper_model_docker_image_tag,
        labels=labels,
    )

    track("clipper-deploy-success", {'bento_service_bundle_path': bundle_path})
    return model_name, model_version
예제 #19
0
    def delete(
        self,
        bento_tag=None,
        labels=None,
        bento_name=None,
        bento_version=None,
        prune=False,  # pylint: disable=redefined-builtin
        require_confirm=False,
    ):
        """
        Delete bentos that matches the specified criteria

        Args:
            bento_tag: string
            labels: string
            bento_name: string
            bento_version: string
            prune: boolean, Set True to delete all BentoService
            require_confirm: boolean
        Example:
        >>>
        >>> yatai_client = get_yatai_client()
        >>> # Delete all bento services
        >>> yatai_client.repository.delete(prune=True)
        >>> # Delete bento service with name is `IrisClassifier` and version `0.1.0`
        >>> yatai_client.repository.delete(
        >>>     bento_name='IrisClassifier', bento_version='0.1.0'
        >>> )
        >>> # or use bento tag
        >>> yatai_client.repository.delete('IrisClassifier:v0.1.0')
        >>> # Delete all bento services with name 'MyService`
        >>> yatai_client.repository.delete(bento_name='MyService')
        >>> # Delete all bento services with labels match `ci=failed` and `cohort=20`
        >>> yatai_client.repository.delete(labels='ci=failed, cohort=20')
        """
        track('py-api-delete')

        delete_list_limit = 50

        if (bento_tag is not None and bento_name is not None
                and bento_version is not None):
            raise BentoMLException('Too much arguments')

        if bento_tag is not None:
            logger.info(f'Deleting saved Bento bundle {bento_tag}')
            return self._delete_bento_bundle(bento_tag, require_confirm)
        elif bento_name is not None and bento_tag is not None:
            logger.info(
                f'Deleting saved Bento bundle {bento_name}:{bento_version}')
            return self._delete_bento_bundle(f'{bento_name}:{bento_version}',
                                             require_confirm)
        else:
            # list of bentos
            if prune is True:
                logger.info('Deleting all BentoML saved bundles.')
                # ignore other fields
                bento_name = None
                labels = None
            else:
                log_message = 'Deleting saved Bento bundles'
                if bento_name is not None:
                    log_message += f' with name: {bento_name},'
                if labels is not None:
                    log_message += f' with labels match to {labels}'
                logger.info(log_message)
            offset = 0
            while offset >= 0:
                bento_list = self.list(
                    bento_name=bento_name,
                    labels=labels,
                    offset=offset,
                    limit=delete_list_limit,
                )
                offset += delete_list_limit
                # Stop the loop, when no more bentos
                if len(bento_list) == 0:
                    break
                else:
                    for bento in bento_list:
                        self._delete_bento_bundle(
                            f'{bento.name}:{bento.version}', require_confirm)