This document demonstrates how to use the Google Python Client Library for Google Compute Engine. It describes how to authorize requests and how to create, list, and stop instances. This exercise discusses how to use the google-api-python-client library to access Google Compute Engine from outside a VM instance. It does not discuss how to build and run applications within a VM instance.
For a full list of available client libraries, including other Google client libraries and third-party open source libraries, see the Client Libraries page .
Contents
Setup
Before you can try the examples in this exercise, you need to download and install the google-api-python-client library. This contains the core Python library for accessing Google APIs and also contains the OAuth 2.0 client library. For information on how to install this library, see the installation instructions . You also need to have Python 2.5, 2.6, or 2.7 to run the Google Python Client Library.
Getting Started
The purpose of this exercise is to describe how to use OAuth 2.0 authorization, and how to perform basic instance management tasks using the google-api-python-client library. At the end of this exercise, you should be able to:
- Perform OAuth 2.0 authorization using the oauth2client library
- Create an instance using the google-python-client library
- List instances using the google-python-client library
- Stop an instance using the google-python-client library
To skip the exercise and view the full code example, visit the google-cloud-platform-samples GitHub page.
Authorizing Requests
This sample uses OAuth 2.0 authorization. You will need to create a client ID and client secret, and use both with the oauth2client library. By default, the oauth2 library is included in the google-api-python-client library, which you should have downloaded in the Setup section.
To start, all applications are managed by the Google Developers Console . If you already have a registered application, you can use the client ID and secret from that application. If you don't have a registered application or would like to register a new application, follow the application registration process . Make sure to select Installed as the application type.
Once on the application page, expand the OAuth 2.0 Client ID section and click Download JSON . Save the file as client_secrets.json . The file should look similar to the following:
{ "installed": { "client_id": "<your_client_id>", "client_secret":"<your_client_secret>", "redirect_uris": ["http://localhost", "urn:ietf:wg:oauth:2.0:oob"], "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token" } }Next, create a file called
helloworld.py
in the same directory
as the
client_secrets.json
file and provide the following code:
#!/usr/bin/env python import logging import sys import argparse import httplib2 from oauth2client.client import flow_from_clientsecrets from oauth2client.file import Storage from oauth2client import tools from oauth2client.tools import run_flow CLIENT_SECRETS = 'client_secrets.json' OAUTH2_STORAGE = 'oauth2.dat' GCE_SCOPE = 'https://www.googleapis.com/auth/compute' def main(argv): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[tools.argparser]) # Parse the command-line flags. flags = parser.parse_args(argv[1:]) # Perform OAuth 2.0 authorization. flow = flow_from_clientsecrets(CLIENT_SECRETS, scope=GCE_SCOPE) storage = Storage(OAUTH2_STORAGE) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run_flow(flow, storage, flags) http = httplib2.Http() auth_http = credentials.authorize(http) if __name__ == '__main__': main(sys.argv)
The above code uses the OAuth 2.0 scope specified
(
https://www.googleapis.com/auth/compute
) and the
client_secrets.json
information to request a refresh and access
token, which is then stored in the
oauth2.dat
file. Because the
refresh token never expires, your application can reuse the refresh token to
request new access tokens when necessary. This also eliminates further
authorization events, unless the refresh token has been explicitly revoked.
If you run
helloworld.py
now on the command line, it should automatically
open a browser window for you to authorize access.
Initializing the API
Before you can make requests, you first need to initialize an instance of the
Google Compute Engine service. Add the following bold lines to your
helloworld.py
:
#!/usr/bin/env python import logging import sys import argparse import httplib2 from oauth2client.client import flow_from_clientsecrets from oauth2client.file import Storage from oauth2client import tools from oauth2client.tools import run_flow from apiclient.discovery import build API_VERSION = 'v1' GCE_URL = 'https://www.googleapis.com/compute/%s/projects/' % (API_VERSION) PROJECT_ID = '<your_project_id>' CLIENT_SECRETS = 'client_secrets.json' OAUTH2_STORAGE = 'oauth2.dat' GCE_SCOPE = 'https://www.googleapis.com/auth/compute' def main(argv): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[tools.argparser]) # Parse the command-line flags. flags = parser.parse_args(argv[1:]) # Perform OAuth 2.0 authorization. flow = flow_from_clientsecrets(CLIENT_SECRETS, scope=GCE_SCOPE) storage = Storage(OAUTH2_STORAGE) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run_flow(flow, storage, flags) http = httplib2.Http() auth_http = credentials.authorize(http) # Build the service gce_service = build('compute', API_VERSION) project_url = '%s%s' % (GCE_URL, PROJECT_ID) if __name__ == '__main__': main(sys.argv)
Listing Instances
Next, to list your instances, call the
instances().list
method,
providing the project ID, the zone for which you want to list instances, and any optional
filters
:
#!/usr/bin/env python import logging import sys import argparse import httplib2 from oauth2client.client import flow_from_clientsecrets from oauth2client.file import Storage from oauth2client import tools from oauth2client.tools import run_flow from apiclient.discovery import build DEFAULT_ZONE = 'us-central1-a' API_VERSION = 'v1' GCE_URL = 'https://www.googleapis.com/compute/%s/projects/' % (API_VERSION) PROJECT_ID = '<your_project_id>' CLIENT_SECRETS = 'client_secrets.json' OAUTH2_STORAGE = 'oauth2.dat' GCE_SCOPE = 'https://www.googleapis.com/auth/compute' def main(argv): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[tools.argparser]) # Parse the command-line flags. flags = parser.parse_args(argv[1:]) # Perform OAuth 2.0 authorization. flow = flow_from_clientsecrets(CLIENT_SECRETS, scope=GCE_SCOPE) storage = Storage(OAUTH2_STORAGE) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run_flow(flow, storage, flags) http = httplib2.Http() auth_http = credentials.authorize(http) # Build the service gce_service = build('compute', API_VERSION) project_url = '%s%s' % (GCE_URL, PROJECT_ID) # List instances request = gce_service.instances().list(project=PROJECT_ID, filter=None, zone=DEFAULT_ZONE) response = request.execute(http=auth_http) if response and 'items' in response: instances = response['items'] for instance in instances: print instance['name'] else: print 'No instances to list.' if __name__ == '__main__': main(sys.argv)
Run
helloworld.py
on the command line and you should see a list of
instances for your specified project:
user@mymachine:~/gce_demo$ python helloworld.py
instance1
instance2
hello-world
Adding an Instance
To add an instance, use the
instances().insert()
method and
provide appropriate request body with the JSON properties described at the
API reference
documentation. 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
For this example, you are going to start an instance with the following properties:
- Zone: us-central1-a
- Machine type: n1-standard-1
- Root persistent disk: my-root-pd
-
The
default
service
account
with the following scopes:
-
https://www.googleapis.com/auth/devstorage.full_control
-
https://www.googleapis.com/auth/compute
-
Root persistent disks
All instances must boot from a root persistent disk . If you have an existing root persistent disk, you can use it for this part of the exercise. For the purposes of this guide, we're also going to demonstrate how to create a root persistent disk.
A root persistent disk contains all of the necessary files required for starting your instance. When you create a root persistent disk, you specify the name and the OS image that should be applied to the disk. You can also create a root persistent disk at the same time as your virtual machine instance, or you can create the root persistent disk separately and attach it to a new instance. For this example, you will create a Debian 7 root persistent disk as part of the instance creation request.
Add the following lines to
helloworld.py
:
#!/usr/bin/env python import logging import sys import argparse import httplib2 from oauth2client.client import flow_from_clientsecrets from oauth2client.file import Storage from oauth2client import tools from oauth2client.tools import run_flow from apiclient.discovery import build # New instance properties DEFAULT_MACHINE_TYPE = 'n1-standard-1' DEFAULT_NETWORK = 'default' DEFAULT_SERVICE_EMAIL = 'default' DEFAULT_SCOPES = ['https://www.googleapis.com/auth/devstorage.full_control', 'https://www.googleapis.com/auth/compute'] NEW_INSTANCE_NAME = 'my-new-instance' # New root persistent disk properties DEFAULT_IMAGE = 'debian' DEFAULT_IMAGES = { 'debian': 'debian-7-wheezy-v20131120', 'centos': 'centos-6-v20131120' } DEFAULT_ROOT_PD_NAME = 'my-root-pd' DEFAULT_ZONE = 'us-central1-a' API_VERSION = 'v1' GCE_URL = 'https://www.googleapis.com/compute/%s/projects/' % (API_VERSION) PROJECT_ID = '<your_project_id>' CLIENT_SECRETS = 'client_secrets.json' OAUTH2_STORAGE = 'oauth2.dat' GCE_SCOPE = 'https://www.googleapis.com/auth/compute' def main(argv): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[tools.argparser]) # Parse the command-line flags. flags = parser.parse_args(argv[1:]) # Perform OAuth 2.0 authorization. flow = flow_from_clientsecrets(CLIENT_SECRETS, scope=GCE_SCOPE) storage = Storage(OAUTH2_STORAGE) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run_flow(flow, storage, flags) http = httplib2.Http() auth_http = credentials.authorize(http) # Build the service gce_service = build('compute', API_VERSION) project_url = '%s%s' % (GCE_URL, PROJECT_ID) # List instances request = gce_service.instances().list(project=PROJECT_ID, filter=None, zone=DEFAULT_ZONE) response = request.execute(http=auth_http) if response and 'items' in response: instances = response['items'] for instance in instances: print instance['name'] else: print 'No instances exist.' # Construct URLs image_url = '%s%s/global/images/%s' % ( GCE_URL, 'debian-cloud', DEFAULT_IMAGES['debian']) machine_type_url = '%s/zones/%s/machineTypes/%s' % ( project_url, DEFAULT_ZONE, DEFAULT_MACHINE_TYPE) network_url = '%s/global/networks/%s' % (project_url, DEFAULT_NETWORK) # Construct the request body instance = { 'name': NEW_INSTANCE_NAME, 'machineType': machine_type_url, 'disks': [{ 'autoDelete': 'true', 'boot': 'true', 'type': 'PERSISTENT', 'initializeParams' : { 'diskName': DEFAULT_ROOT_PD_NAME, 'sourceImage': image_url } }], 'networkInterfaces': [{ 'accessConfigs': [{ 'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT' }], 'network': network_url }], 'serviceAccounts': [{ 'email': DEFAULT_SERVICE_EMAIL, 'scopes': DEFAULT_SCOPES }] } # Create the instance request = gce_service.instances().insert( project=PROJECT_ID, body=instance, zone=DEFAULT_ZONE) response = request.execute(http=auth_http) response = _blocking_call(gce_service, auth_http, response) print response def _blocking_call(gce_service, auth_http, response): """Blocks until the operation status is done for the given operation.""" status = response['status'] while status != 'DONE' and response: operation_id = response['name'] # Identify if this is a per-zone resource if 'zone' in response: zone_name = response['zone'].split('/')[-1] request = gce_service.zoneOperations().get( project=PROJECT_ID, operation=operation_id, zone=zone_name) else: request = gce_service.globalOperations().get( project=PROJECT_ID, operation=operation_id) response = request.execute(http=auth_http) if response: status = response['status'] return response if __name__ == '__main__': main(sys.argv)
Notice that you construct the JSON for your instance here:
instance = { 'name': NEW_INSTANCE_NAME, 'machineType': machine_type_url, 'disks': [{ 'autoDelete': 'true', 'boot': 'true', 'type': 'PERSISTENT', 'initializeParams' : { 'diskName': DEFAULT_ROOT_PD_NAME, 'sourceImage': image_url } }], 'networkInterfaces': [{ 'accessConfigs': [{ 'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT' }], 'network': network_url }], 'serviceAccounts': [{ 'email': DEFAULT_SERVICE_EMAIL, 'scopes': DEFAULT_SCOPES }]
If you would rather not hand construct the JSON for your request, many Google Compute Engine tools can automatic generate the JSON for you. For example, the Google Developers Console , which allows you to configure and create resources for your Google Compute Engine project, also provides a handy REST Request feature that constructs the JSON for the request for you. For more information, see Using the Console to Generate REST Requests .
Blocking until the operation is complete
By default, when you send a request to Google Compute Engine API, you'll
immediately receive a response describing the status of your operation as
RUNNING
. To check when the operation is finished, you need to
periodically query the server for the operation status. To eliminate this extra
step, we've included a helper method in the sample above that "blocks" and
waits for the operation status to become
DONE
before it returns a
response.
Notice when you query per-zone operations, you use the
zoneOperations.get()
method,
while querying global operations requires using the
globalOperations.get()
method.
For more information, see
zone resources
.
Run your
helloworld.py
file to create your new disk.
Adding Instance Metadata
When you create your instance, you may want to include instance metadata,
such as a startup script URL, or sshKeys. To do so, include the
metadata
field with your request body:
instance = { 'name': NEW_INSTANCE_NAME, 'machineType': machine_type_url, 'disks': [{ 'source': root_disk_url, 'boot': 'true', 'type': 'PERSISTENT', }], 'networkInterfaces': [{ 'accessConfigs': [{ 'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT' }], 'network': network_url }], 'serviceAccounts': [{ 'email': DEFAULT_SERVICE_EMAIL, 'scopes': DEFAULT_SCOPES }], 'metadata': [{ 'items': [{ 'key': <key>, 'value': <value>, ..... }] }] }
For example, you can use the metadata field to specify a startup script with your instance. Create a new file named startup.sh and populate it with the following contents:
#!/bin/bash apt-get -y install imagemagick IMAGE_URL=$(curl http://metadata/computeMetadata/v1/instance/attributes/url -H "X-Google-Metadata-Request: True") TEXT=$(curl http://metadata/computeMetadata/v1/instance/attributes/text -H "X-Google-Metadata-Request: True") CS_BUCKET=$(curl http://metadata/computeMetadata/v1/instance/attributes/cs-bucket -H "X-Google-Metadata-Request: True") mkdir image-output cd image-output wget $IMAGE_URL convert * -pointsize 30 -fill black -annotate +10+40 $TEXT output.png gsutil cp -a public-read output.png gs://$CS_BUCKET/output.png
This startup script installs the
ImageMagick
application,
downloads an image from the Internet, adds some text on top, and copies it to
Google Cloud Storage
. To try it out, make the following
changes to your
helloworld.py
file. If you do not have a Google Cloud Storage
account, you can
sign up for the service
during the limited free trial, which ends December 31, 2012. After you have
signed up for the service, create your bucket using the
Google Cloud Storage manager
.
# Construct URLs machine_type_url = '%s/zones/%s/machine-types/%s' % ( project_url, DEFAULT_ZONE, DEFAULT_MACHINE_TYPE) network_url = '%s/global/networks/%s' % (project_url, DEFAULT_NETWORK) image_url = '%s%s/global/images/%s' % ( GCE_URL, 'debian-cloud', DEFAULT_IMAGES['debian']) my_image = '<url_of_image>' # Choose an image from the Internet and put its URL here cs_bucket = '<your_google_cloud_storage_bucket>' # Must already exist, e.g. 'samplebucket' startup_script = 'startup.sh' # Construct the request body instance = { 'name': 'startup-script-demo', 'machineType': machine_type_url, 'disks': [{ 'autoDelete': 'true', 'boot': 'true', 'type': 'PERSISTENT', 'initializeParams' : { 'diskName': DEFAULT_ROOT_PD_NAME, 'sourceImage': image_url } }], 'networkInterfaces': [{ 'accessConfigs': [{ 'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT' }], 'network': network_url }], 'serviceAccounts': [{ 'email': DEFAULT_SERVICE_EMAIL, 'scopes': DEFAULT_SCOPES }], 'metadata': [{ 'items': [{ 'key': 'startup-script', 'value': open(startup_script, 'r').read() }, { 'key': 'url', 'value': my_image }, { 'key': 'text', 'value': 'AWESOME' }, { 'key': 'cs-bucket', 'value': cs_bucket }] }] } request = gce_service.instances().insert( project=PROJECT_ID, body=instance, zone=DEFAULT_ZONE) response = request.execute(http=auth_http) response = _blocking_call(gce_service, auth_http, response) print response print '\n' print 'Visit http://storage.googleapis.com/%s/output.png' % ( cs_bucket) print 'It might take a minute for the output.png file to show up.'
Try running
helloworld.py
, which creates a new instance named startup-script-demo, and viewing your image in the provided URL.
Deleting an Instance
To delete an instance, you need to call the
instances().delete()
method
and provide the name, zone, and project ID of the instance to delete. Add the
following lines to your
helloworld.py
to delete the instance. For the purposes
of this example, the following code also deletes the associated root persistent
disk:
# Delete an instance and the associated root persistent disk request = gce_service.instances().delete( project=PROJECT_ID, instance=INSTANCE_TO_DELETE, zone=DEFAULT_ZONE) response = request.execute(http=auth_http) response = _blocking_call(gce_service, auth_http, response) print response
When you set the root persistent disk in initial request to
autoDelete
, it indicated to Google Compute Engine that the root
persistent disk for this instance should also be deleted when the instance
is deleted. This setting is off by default, but can be useful so you can
delete instances and their root persistent disks in one step.
Next Steps
Now that you've completed this exercise, you can:
- Download and view the full code sample . The full sample includes a simple wrapper for the instance management methods, and is generally more cleaner than our example here. Feel free to download it, change it, and run it to suit your needs.
- Review the API reference to learn how to perform other tasks with the API.
- Start creating your own applications!