### Description

Today, Amazon provider package supports SageMaker operators for a few operations, like training, tuning, pipelines, but it lacks the support for SageMaker Notebook instances. Boto3 provides necessary APIs to [create notebook instance](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker/client/create_notebook_instance.html), [start notebook instance](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker/client/start_notebook_instance.html), [stop network instance](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker/client/stop_notebook_instance.html) and [delete notebook instance](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker/client/delete_notebook_instance.html). Leveraging these APIs, we should add new operators to SageMaker set under Amazon provider. At the same time, having a sensor (synchronous as well as deferrable) for notebook instance execution that utilizes [describe notebook instance](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker/client/describe_notebook_instance.html) and waits for Stopped/Failed status would help with observability of the execution.

### Use case/motivation

Data Scientists are orchestrating ML use cases via Apache Airflow. A key component of ML use cases is running Jupyter Notebook on SageMaker. Having built-in operators and sensors would make it easy for Airflow users to run Notebook instances on SageMaker.

### Related issues

_No response_

### Are you willing to submit a PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

"""
        Set fields which should be cast to integers.
        Child classes should override this method if they need integer fields parsed.
        """
        self.integer_fields = []
    def _get_unique_job_name(
        self, proposed_name: str, fail_if_exists: bool, describe_func: Callable[[str], Any]
    ) -> str:
        """
        Return the proposed name if it doesn't already exist, otherwise returns it with a timestamp suffix.
        :param proposed_name: Base name.
        :param fail_if_exists: Will throw an error if a job with that name already exists
            instead of finding a new name.
        :param describe_func: The `describe_` function for that kind of job.
            We use it as an O(1) way to check if a job exists.
        """
        job_name = proposed_name
        while self._check_if_job_exists(job_name, describe_func):
            
            
            if fail_if_exists:
                raise AirflowException(f"A SageMaker job with name {job_name} already exists.")
            else:
                job_name = f"{proposed_name}-{time.time_ns()//1000000}"
                self.log.info("Changed job name to '%s' to avoid collision.", job_name)
        return job_name
    def _check_if_job_exists(self, job_name, describe_func: Callable[[str], Any]) -> bool: