# Using the Cloud Client Libraries for Python

This document demonstrates how to use the Cloud Client Libraries for Python for Compute Engine. 
It describes how to authorize requests and how to create, list, and delete instances. 
This exercise discusses how to use the `google-api-python-client` library to access Compute Engine 
resources. You can run this sample from your local machine or on a VM instance, provided that 
you have authorized the sample correctly.

For a full list of available client libraries, including other Google client libraries and 
third-party open source libraries, see the [client libraries page](https://cloud.google.com/compute/docs/api/libraries).

To view the full code example with all of the necessary imports, see the [create_instance.py file](create_instance.py).

## Objectives

 * Perform OAuth 2.0 authorization using the `oauth2client` library
 * Create, list and delete instances using the `google-api-python-client` library

## Costs

This tutorial uses billable components of Google Cloud including Compute Engine.

## Before you begin

1. In the Google Cloud Console, on the project selector page, select or create a Google Cloud project.
   [Go to project selector](https://console.cloud.google.com/projectselector2/home/dashboard).
1. Make sure that billing is enabled for your cloud project. 
   [Learn how to confirm that billing is enabled for your project.](https://cloud.google.com/billing/docs/how-to/modify-project)
1. [Install Google Cloud SDK and `gcloud`](https://cloud.google.com/sdk)
1. After the SDK is installed, run `gcloud auth application-default login`.
1. Install the [google-api-python-client](http://github.com/googleapis/google-api-python-client) library. Typically, you can run:
   
   ```bash
   pip install --upgrade google-api-python-client
   ```

1. Enable the Cloud Storage API.
   ```bash
   gcloud services enable storage.googleapis.com
   ```
1. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) and note the bucket name for later.

## Authorizing requests

This sample uses OAuth 2.0 authorization. There are many ways to authorize requests using OAuth 2.0,
but for the example use [application default credentials](https://developers.google.com/accounts/docs/application-default-credentials). This lets you reuse the credentials from 
the `gcloud` tool if you are running the sample on a local workstation or reuse credentials from a 
service account if you are running the sample from within Compute Engine or App Engine. You should 
have installed and authorized the `gcloud` tool in the "Before you begin" section.

Application default credentials are provided in Google API Client Libraries automatically. 
You just have to build and initialize the API:

```python
import googleapiclient.discovery
compute = googleapiclient.discovery.build('compute', 'v1')
```

See the `main()` method in the [create_instance.py](create_instance.py) script, to see how an API
client is built and used.

## Listing instances

Using `google-api-python-client`, you can list instances by using the `compute.instances().list()` method. 
You need to provide the project ID and the zone for which you want to list instances. For example:

```python
def list_instances(compute, project, zone):
    result = compute.instances().list(project=project, zone=zone).execute()
    return result.get('items')
```

## Adding an instance

To add an instance, use the `compute.instances().insert()` method and specify the properties of the new 
instance. These properties are specified in the request body; for details about each property see 
the [API reference for `instances.insert`](https://cloud.google.com/compute/docs/reference/latest/instances/insert).

At a minimum, your request must provide values for the following properties when you create a new 
instance:

* Instance name
* Root persistent disk
* Machine type
* Zone
* Network Interfaces

This sample starts an instance with the following properties in a zone of your choice:

* Machine type: e2-standard-2
* Root persistent disk: a new persistent disk based on the latest Debian 8 image
* The Compute Engine default service account with the following scopes:
  * https://www.googleapis.com/auth/devstorage.read_write, so the instance can read and write files in Cloud Storage
  * https://www.googleapis.com/auth/logging.write, so the instances logs can upload to Cloud Logging
* Metadata to specify commands that the instance should execute upon startup

You can see an example of instance creation in the `create_instance` method in [create_instance.py](create_instance.py) file.

### Root persistent disks

All instances must boot from a [root persistent disk](https://cloud.google.com/compute/docs/disks/create-root-persistent-disks).
The root persistent disk contains all of the necessary files required for starting an instance. 
When you create a root persistent disk you must select a public image or a custom image to apply to 
the disk. In the example above, a new root persistent disk is created based on Debian 8 at the same 
time as the instance. However, it is also possible to create a disk beforehand and attach it to the 
instance.

To create an instance using your own custom OS image, you need to provide a different URL than 
the one included in the example. For more information about starting an instance with your own 
images, see [Creating an instance from a custom image](https://cloud.google.com/compute/docs/instances/create-start-instance#creating_an_instance_from_a_custom_image).



### Instance metadata

When you create your instance, you might want to include instance metadata such as a [startup script](https://cloud.google.com/compute/docs/startupscript), 
configuration variables, and SSH keys. In the example above, you used the `metadata` field in your 
request body to specify a startup script for the instance and some configuration variables as 
key/values pairs. The [startup-script.sh](startup-script.sh) shows how to read these variables and use them 
to apply text to an image and upload it to [Cloud Storage](https://cloud.google.com/storage).

## Deleting an Instance

To delete an instance, you need to call the `compute.instances().delete()` method and provide the name, 
zone, and project ID of the instance to delete. When the `autoDelete` parameter is set to `true` for the 
boot disk it is also deleted with the instance. This setting is off by default but is 
useful when your use case calls for disks and instances to be deleted together.

```python
def delete_instance(compute, project, zone, name):
    return compute.instances().delete(
        project=project,
        zone=zone,
        instance=name).execute()
```

## Running the sample

You can run the full sample by downloading the code and running it on the command line. Make sure 
to download the `create_instance.py` file and the `startup-script.sh` file. To run the sample:

```bash
python create_instance.py --name [INSTANCE_NAME] --zone [ZONE] [PROJECT_ID] [CLOUD_STORAGE_BUCKET]
```

where:

* `[INSTANCE_NAME]` is the name of the instance to create.
* `[ZONE]` is the desired zone for this request.
* `[PROJECT_ID]` is our project ID.
* `[CLOUD_STORAGE_BUCKET]` is the name of the bucket you initially set up but without the `gs://` prefix.

For example:

```bash
python python-example.py --name example-instance --zone us-central1-a example-project my-gcs-bucket
```

## Waiting for operations to complete

Requests to the Compute Engine API that modify resources such as instances immediately return a 
response acknowledging your request. The acknowledgement lets you check the status of the requested 
operation. Operations can take a few minutes to complete, so it's often easier to wait for the 
operation to complete before continuing. This helper method waits until the operation completes 
before returning:

```python
def wait_for_operation(compute, project, zone, operation):
    print('Waiting for operation to finish...')
    while True:
        result = compute.zoneOperations().get(
            project=project,
            zone=zone,
            operation=operation).execute()

        if result['status'] == 'DONE':
            print("done.")
            if 'error' in result:
                raise Exception(result['error'])
            return result

        time.sleep(1)
```

When you query per-zone operations, use the `compute.zoneOperations.get()` method. When you query global 
operations, use the `compute.globalOperations.get()` method. For more information, see zone resources.

## Cleaning up

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, 
either delete the project that contains the resources, or keep the project and delete the 
individual resources.

### Delete your Cloud Storage bucket

To delete a Cloud Storage bucket:
1. In the Cloud Console, go to the Cloud Storage [Browser page](https://console.cloud.google.com/storage/browser).
1. Click the checkbox for the bucket that you want to delete.
1. To delete the bucket, click `Delete`, and then follow the instructions.
