Skip to content

Loading…

Initial GCE Support #1390

Open
wants to merge 18 commits into from

4 participants

@shahms

This pull request picks up and subsumes the similar request from ziyadm adding initial Google Compute Engine support to boto.

@nchammas

@danielgtaylor Will boto potentially consider merging in work like this to support other cloud providers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 28, 2012
  1. @ziyadm
Commits on Dec 10, 2012
  1. @ziyadm
Commits on Feb 5, 2013
  1. @shahms

    fix GCE merge conflicts

    shahms committed
  2. @shahms

    fix GCE merge conflicts

    shahms committed
  3. @shahms

    fix GCE unintended changes

    shahms committed
Commits on Feb 15, 2013
  1. @shahms
Commits on Feb 16, 2013
  1. @shahms
Commits on Feb 22, 2013
  1. @shahms
Commits on Mar 1, 2013
  1. @shahms
  2. @shahms

    - update requirements.txt

    shahms committed
    - move GCE to API version 1beta14
    - fix GCE integration tests for above
Commits on Mar 5, 2013
  1. @shahms
Commits on Mar 11, 2013
  1. @shahms
Commits on Mar 13, 2013
  1. @shahms
  2. @shahms
  3. @shahms
  4. @shahms

    fix indentation style issues

    shahms committed
  5. @shahms
Commits on Mar 14, 2013
  1. @shahms
View
1 README.rst
@@ -19,6 +19,7 @@ At the moment, boto supports:
* Amazon Elastic Compute Cloud (EC2)
* Amazon Elastic Map Reduce (EMR)
* AutoScaling
+ * Google Compute Engine (GCE)
* Content Delivery
View
14 boto/gce/__init__.py
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
View
354 boto/gce/connection.py
@@ -0,0 +1,354 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import boto
+import httplib2
+import os
+
+from apiclient.discovery import build
+from oauth2client.file import Storage
+from oauth2client.client import flow_from_clientsecrets
+from oauth2client.tools import run
+
+from boto.gce.firewall import Firewall
+from boto.gce.image import Image
+from boto.gce.instance import Instance
+from boto.gce.kernel import Kernel
+from boto.gce.network import Network
+from boto.gce.machine_type import MachineType
+from boto.gce.ramdisk import Ramdisk
+from boto.gce.zone import Zone
+from boto.gce.zone_operation import ZoneOperation
+
+
+# CLIENT_SECRETS, name of a file containing the OAuth 2.0 information for this
+# application, including client_id and client_secret, which are found
+# on the API Access tab on the Google APIs
+# Console <http://code.google.com/apis/console>
+
+# Helpful message to display if the CLIENT_SECRETS file is missing.
+MISSING_CLIENT_SECRETS_MESSAGE = """
+WARNING: Please configure OAuth 2.0
+
+To make this sample run you will need to populate the client_secrets.json file
+found at:
+
+ {0}
+
+with information from the APIs Console <https://code.google.com/apis/console>.
+"""
+
+OAUTH2_SCOPE = 'https://www.googleapis.com/auth/compute'
+GOOGLE_PROJECT = 'google'
+
+
+def get_oauth2_credentials():
+ """
+ Return oauth2 credentials, running the configured flow if necessary.
+ """
+ try:
+ client_secrets = _get_config('Credentials', 'gce_client_secrets_file')
+ credentials_file = _get_config('Credentials', 'gce_credentials_file')
+ except KeyError:
+ raise RuntimeError('OAuth2 credentials missing.')
+
+ storage = Storage(credentials_file)
+ credentials = storage.get()
+ if credentials is None or credentials.invalid:
+ message = MISSING_CLIENT_SECRETS_MESSAGE.format(client_secrets)
+ flow = flow_from_clientsecrets(client_secrets,
+ message=message,
+ scope=OAUTH2_SCOPE)
+ return run(flow, storage)
+ return credentials
+
+
+def _get_config(section, key):
+ """
+ Check boto.config and the environment for the specified key.
+ """
+ value = boto.config.get(section, key, None)
+ if value is not None:
+ return value
+ return os.environ[key.upper()]
+
+
+class GCEConnection(object):
+
+ APIVersion = 'v1beta14'
+
+ def __init__(self, gce_project=None, credentials=None):
+ """
+ Init method to create a new connection to Google Compute Engine.
+
+ :type gce_project: string
+ :param gce_project: The GCE Project name to use.
+
+ :type credentials: apiclient.Credentials
+ :param credentials: A valid OAuth2 credentials instance.
+ """
+ if gce_project is None:
+ gce_project = _get_config('Boto', 'gce_project')
+ if credentials is None:
+ credentials = get_oauth2_credentials()
+
+ self.gce_project = gce_project
+ self.credentials = credentials
+
+ self.http = self.credentials.authorize(httplib2.Http())
+ self.service = build('compute', self.APIVersion, http=self.http)
+
+ # Image methods
+
+ def get_all_images(self, global_images=True):
+ """
+ Retrieve all the Google Compute Engine images available to your
+ project.
+ """
+ if global_images:
+ project = GOOGLE_PROJECT
+ else:
+ project = self.gce_project
+
+ list_gce_images = self.service.images().list(
+ project=project).execute(http=self.http)
+
+ return [Image(i) for i in list_gce_images.get('items', [])]
+
+ def get_all_kernels(self, global_kernels=True):
+ """
+ Retrieve all the Google Compute Engine kernels available to your project.
+ """
+ if global_kernels:
+ project = GOOGLE_PROJECT
+ else:
+ project = self.gce_project
+
+ list_gce_kernels = self.service.kernels().list(
+ project=project).execute(http=self.http)
+ return [Kernel(k) for k in list_gce_kernels.get('items', [])]
+
+ def get_all_ramdisks(self, zones=None):
+ """
+ Retrieve all the Google Compute Engine disks available to your project.
+ """
+ if zones is None:
+ zones = [zone.name for zone in self.get_all_zones()]
+
+ ramdisks = []
+ for zone in zones:
+ request = self.service.disks().list(
+ project=self.gce_project, zone=zone)
+ response = request.execute(http=self.http)
+ ramdisks.extend(Ramdisk(rd) for rd in response.get('items', []))
+ return ramdisks
+
+ def get_image(self, name):
+ """
+ Shortcut method to retrieve a specific image.
+
+ :type name: string
+ :param name: The name of the Image to retrieve.
+
+ :rtype: :class:`boto.gce.image.Image`
+ :return: The Google Compute Engine Image specified, or None if the image
+ is not found
+ """
+ # TODO: Fix this to work with both global and per-project images.
+ request = self.service.images().get(project=GOOGLE_PROJECT,
+ image=name)
+ gce_image = request.execute(http=self.http)
+ return Image(gce_image)
+
+ # Instance methods
+
+ def get_all_instances(self, zones=None):
+ """
+ Retrieve all the Google Compute Engine instances available to your
+ project.
+ """
+ if zones is None:
+ zones = [zone.name for zone in self.get_all_zones()]
+
+ instances = []
+ for zone in zones:
+ request = self.service.instances().list(
+ project=self.gce_project, zone=zone)
+ response = request.execute(http=self.http)
+ instances.extend(Instance(i) for i in response.get('items', []))
+ return instances
+
+ def run_instance(self, name, machine_type, zone, image,
+ disk_types=None):
+ """
+ Insert a Google Compute Engine instance into your cluster.
+
+ :type name: string
+ :param name: The name of the instance to insert.
+
+ :rtype: :class:`boto.gce.operation.Operation`
+ :return: A Google Compute Engine operation.
+ """
+ if disk_types is None:
+ disk_types = ['EPHEMERAL']
+ network = self.get_network('default').self_link
+
+ body = {
+ 'name': name,
+ 'image': image,
+ 'machineType': machine_type,
+ 'networkInterfaces': [{
+ 'network': network
+ }],
+ 'disks': [{'type': t} for t in disk_types]
+ }
+
+ request = self.service.instances().insert(project=self.gce_project,
+ zone=zone,
+ body=body)
+ operation = request.execute(http=self.http)
+ return ZoneOperation(operation)
+
+ def terminate_instance(self, name, zone):
+ """
+ Terminate a specific Google Compute Engine instance.
+
+ :type name: string
+ :param name: The name of the instance to terminate.
+
+ :type name: string
+ :param zone: The zone in which the instance resides.
+
+ :rtype: :class:`boto.gce.operation.Operation`
+ :return: A Google Compute Engine operation.
+ """
+ request = self.service.instances().delete(zone=zone,
+ project=self.gce_project,
+ instance=name)
+ operation = request.execute(http=self.http)
+ return ZoneOperation(operation)
+
+ def get_instance(self, name, zone):
+ """
+ Get an instance from Google Compute Engine.
+
+ :type name: string
+ :param name: The name of the instance to get.
+
+ :type zone: string
+ :param zone: The name of the zone in which the instance resides.
+
+ :rtype: :class:`boto.gce.resource.Resource`
+ :return: A Resource object representing the instance information.
+ """
+ request = self.service.instances().get(
+ project=self.gce_project, instance=name, zone=zone)
+ instance = request.execute(http=self.http)
+ return Instance(instance)
+
+ # Zone methods
+
+ def get_all_zones(self):
+ """
+ Retrieve all the Google Compute Engine zones available to your project.
+ """
+ list_gce_zones = self.service.zones().list(
+ project=self.gce_project).execute(http=self.http)
+ return [Zone(z) for z in list_gce_zones.get('items', [])]
+
+ def get_zone(self, name):
+ """
+ Shortcut method to retrieve a specific zone.
+
+ :type name: string
+ :param name: The name of the Zone to retrieve.
+
+ :rtype: :class:`boto.gce.zone.Zone`
+ :return: The Google Compute Engine Zone specified, or None if the zone
+ is not found
+ """
+ gce_zone = self.service.zones().get(project=self.gce_project,
+ zone=name).execute(
+ http=self.http)
+
+ return Zone(gce_zone)
+
+ # Network methods
+
+ def get_all_networks(self):
+ """
+ Retrieve all the Google Compute Engine networks available to your
+ project.
+ """
+ list_gce_networks = self.service.networks().list(
+ project=self.gce_project).execute(http=self.http)
+ return [Network(n) for n in list_gce_networks.get('items', [])]
+
+ def get_network(self, name):
+ """
+ Shortcut method to retrieve a specific network.
+
+ :type name: string
+ :param name: The name of the Network to retrieve.
+
+ :rtype: :class:`boto.gce.network.Network`
+ :return: The Google Compute Engine Network specified, or None if the
+ network is not found
+ """
+ gce_network = self.service.networks().get(project=self.gce_project,
+ network=name).execute(
+ http=self.http)
+ return Network(gce_network)
+
+ # Firewall methods
+
+ def get_all_firewalls(self):
+ """
+ Retrieve all the Google Compute Engine firewalls available to your
+ project.
+ """
+ list_gce_firewalls = self.service.firewalls().list(
+ project=self.gce_project).execute(http=self.http)
+ return [Firewall(fw) for fw in list_gce_firewalls.get('items', [])]
+
+ def get_firewall(self, name):
+ """
+ Shortcut method to retrieve a specific firewall.
+
+ :type name: string
+ :param name: The name of the Firewall to retrieve.
+
+ :rtype: :class:`boto.gce.firewall.Firewall`
+ :return: The Google Compute Engine Firewall specified, or None if the
+ firewall is not found
+ """
+ gce_firewall = self.service.firewalls().get(
+ project=self.gce_project, firewall=name).execute(
+ http=self.http)
+
+ return Firewall(gce_firewall)
+
+ # MachineType methods
+
+ def get_all_machine_types(self):
+ """
+ Return a list of all known machine types.
+
+ :rtype: :class:`boto.gce.machine_type.MachineType`
+ :return: The GCE resource for all know machine types.
+ """
+ request = self.service.machineTypes().list(project=self.gce_project)
+ machine_list = request.execute(http=self.http)
+ return [MachineType(m) for m in machine_list.get('items', [])]
View
23 boto/gce/firewall.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class Firewall(Resource):
+ """
+ Represents a GCE Firewall.
+ """
View
24 boto/gce/image.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class Image(Resource):
+ """
+ Represents a GCE Image.
+ """
+ deprecated = None
View
28 boto/gce/instance.py
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class Instance(Resource):
+ """
+ Represents a GCE Instance.
+ """
+ def __init__(self, items):
+ assert 'zone' in items
+ super(Instance, self).__init__(items)
+ # The JSON 'zone' has a full URL, but we only ever need the name.
+ self.zone = self.zone[self.zone.rfind('/') + 1:]
View
23 boto/gce/kernel.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class Kernel(Resource):
+ """
+ Represents a GCE Kernel.
+ """
View
24 boto/gce/machine_type.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2013 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class MachineType(Resource):
+ """
+ Represents a GCE MachineType.
+ """
+ deprecated = None
View
34 boto/gce/network.py
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.utils import pythonize_name
+
+from boto.gce.resource import Resource, register_kind
+
+
+def _pythonize_network(name):
+ """
+ A pythonize_name() that special cases 'IPv4'.
+ """
+ return pythonize_name(name.replace('IPv4', 'Ip'))
+
+
+@register_kind
+class Network(Resource):
+ """
+ Represents a GCE Network.
+ """
+ def __init__(self, items, transform=_pythonize_network):
+ super(Network, self).__init__(items, transform)
View
23 boto/gce/ramdisk.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind('disk')
+class Ramdisk(Resource):
+ """
+ Represents a GCE Ramdisk.
+ """
View
103 boto/gce/resource.py
@@ -0,0 +1,103 @@
+#
+# Copyright (C) 2013 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.utils import pythonize_name
+
+
+_KIND_MAP = {}
+_DEFAULT_PREFIX = 'compute#'
+
+
+def register_kind(kind=None):
+ """
+ Class decorator for registering a resource subclass to handle a kind.
+ """
+ def decorate(klass, kind=kind):
+ if kind is None:
+ # GCE kind's are camelCase by default,
+ # class names are usually TitleCase.
+ kind = klass.__name__[0].lower() + klass.__name__[1:]
+ if '#' not in kind:
+ kind = _DEFAULT_PREFIX + kind
+
+ _KIND_MAP[kind] = klass
+ return klass
+
+ if isinstance(kind, type):
+ return decorate(kind, None)
+ return decorate
+
+
+class Resource(object):
+ """
+ Generic Python representation of a Google REST API JSON resource.
+ """
+
+ @staticmethod
+ def from_dict(values):
+ """
+ Lookup the registered class by 'kind' to instantiate a resource.
+ """
+ cls = _KIND_MAP.get(values.get('kind'), Resource)
+ return cls(values)
+
+ def __init__(self, items, transform=pythonize_name):
+ """
+ Initialize a resource from a given JSON resource dictionary.
+ """
+ for key, value in _iteritems(items):
+ attr = transform(key)
+ if isinstance(value, dict):
+ # Recursively turn dictionaries into into resources.
+ value = self.from_dict(value)
+ elif _is_resource_list(value):
+ value = [self.from_dict(v) for v in value]
+ setattr(self, attr, value)
+
+ def __repr__(self):
+ try:
+ return '%s:%s' % (self.kind[self.kind.rfind('#')].title(), self.id)
+ except AttributeError:
+ return 'Resource(%s)' % vars(self)
+
+ def __eq__(self, other):
+ if type(other) is not type(self):
+ return NotImplemented
+ return vars(self) == vars(other)
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ if result is NotImplemented:
+ return result
+ return not result
+
+
+def _iteritems(possible_dict):
+ """
+ Return an iterator over dictionary items.
+ """
+ try:
+ iteritems = possible_dict.iteritems
+ except AttributeError:
+ return possible_dict
+ else:
+ return iteritems()
+
+
+def _is_resource_list(values):
+ """
+ Return true if 'values' is a list of dicts.
+ """
+ return isinstance(values, list) and values and isinstance(values[0], dict)
View
23 boto/gce/zone.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class Zone(Resource):
+ """
+ Represents a GCE Zone.
+ """
View
23 boto/gce/zone_operation.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from boto.gce.resource import Resource, register_kind
+
+
+@register_kind
+class ZoneOperation(Resource):
+ """
+ Represents a GCE ZoneOperation.
+ """
View
1 docs/source/index.rst
@@ -19,6 +19,7 @@ Currently Supported Services
* :doc:`Auto Scaling <autoscale_tut>` -- (:doc:`API Reference <ref/autoscale>`)
* Data Pipeline -- (:doc:`API Reference <ref/datapipeline>`)
* Elastic Transcoder -- (:doc:`API Reference <ref/elastictranscoder>`)
+ * Google Compute Engine -- (:doc:`API Reference <ref/gce>`)
* **Content Delivery**
View
13 docs/source/ref/gce.rst
@@ -0,0 +1,13 @@
+.. ref-gce:
+
+===
+GCE
+===
+
+boto.gce.connection
+--------------------
+
+.. automodule:: boto.gce.connection
+ :members:
+ :undoc-members:
+
View
1 docs/source/ref/index.rst
@@ -21,6 +21,7 @@ API Reference
fps
glacier
gs
+ gce
iam
manage
mturk
View
3 requirements.txt
@@ -8,3 +8,6 @@ simplejson==2.5.2
argparse==1.2.1
unittest2==0.5.1
httpretty==0.5.5
+httplib2==0.7.7
+oauth2client==1.0
+google-api-python-client==1.0
View
6 setup.py
@@ -60,9 +60,9 @@ def readme():
"bin/dynamodb_dump", "bin/dynamodb_load"],
url = "https://github.com/boto/boto/",
packages = ["boto", "boto.sqs", "boto.s3", "boto.gs", "boto.file",
- "boto.ec2", "boto.ec2.cloudwatch", "boto.ec2.autoscale",
- "boto.ec2.elb", "boto.sdb", "boto.cacerts",
- "boto.sdb.db", "boto.sdb.db.manager",
+ "boto.gce", "boto.ec2", "boto.ec2.cloudwatch",
+ "boto.ec2.autoscale", "boto.ec2.elb", "boto.sdb",
+ "boto.cacerts", "boto.sdb.db", "boto.sdb.db.manager",
"boto.mturk", "boto.pyami",
"boto.pyami.installers", "boto.pyami.installers.ubuntu",
"boto.mashups", "boto.contrib", "boto.manage",
View
14 tests/integration/gce/__init__.py
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
View
172 tests/integration/gce/test_connection.py
@@ -0,0 +1,172 @@
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Unit tests for the GCEConnection class.
+"""
+
+import boto
+import os
+import time
+
+from boto.gce.connection import GCEConnection
+from tests.unit import unittest
+
+
+class GCEConnectionTest(unittest.TestCase):
+ gce = True
+
+ def setUp(self):
+ self.connection = GCEConnection()
+
+ # List integration tests
+
+ def test_get_all_images(self):
+ list_images = self.connection.get_all_images()
+
+ for image in list_images:
+ assert image.name
+ assert image.kind
+ assert image.description
+ assert image.raw_disk
+ assert image.preferred_kernel
+ assert image.source_type
+ assert image.self_link
+ assert image.creation_timestamp
+ assert image.id
+
+ def test_get_all_kernels(self):
+ list_kernels = self.connection.get_all_kernels()
+
+ for kernel in list_kernels:
+ assert kernel.kind
+ assert kernel.description
+ assert kernel.self_link
+ assert kernel.creation_timestamp
+ assert kernel.id
+ assert kernel.name
+
+ def test_get_all_ramdisks(self):
+ list_ramdisks = self.connection.get_all_ramdisks()
+
+ for ramdisk in list_ramdisks:
+ assert ramdisk.id
+
+ def test_get_all_instances(self):
+ list_instances = self.connection.get_all_instances()
+
+ for instance in list_instances:
+ assert instance.status
+ assert instance.kind
+ assert instance.name
+ assert instance.image
+ assert instance.machine_type
+ assert instance.self_link
+ assert instance.id
+
+ def test_get_all_zones(self):
+ list_zones = self.connection.get_all_zones()
+
+ for zone in list_zones:
+ assert zone.status
+ assert zone.kind
+ assert zone.name
+ assert zone.self_link
+ assert zone.id
+
+ def test_get_all_networks(self):
+ list_networks = self.connection.get_all_networks()
+
+ for network in list_networks:
+ assert network.kind
+ assert network.name
+ assert network.self_link
+ assert network.description
+ assert network.id
+ assert network.gateway_ip
+ assert network.creation_timestamp
+ assert network.ip_range
+
+ def test_get_all_firewalls(self):
+ list_firewalls = self.connection.get_all_firewalls()
+
+ for firewall in list_firewalls:
+ assert firewall.kind
+ assert firewall.name
+ assert firewall.self_link
+ assert firewall.description
+ assert firewall.id
+ assert firewall.network
+ assert firewall.allowed
+ assert firewall.creation_timestamp
+
+ # Get integration tests
+
+ def test_get_image(self):
+ first_image = self.connection.get_all_images()[0]
+ second_image = self.connection.get_image(first_image.name)
+ self.assertEqual(first_image, second_image)
+
+ def test_get_instance(self):
+ first_instance = self.connection.get_all_instances()[0]
+ second_instance = self.connection.get_instance(first_instance.name,
+ first_instance.zone)
+ self.assertEqual(first_instance, second_instance)
+
+ def test_get_zone(self):
+ first_zone = self.connection.get_all_zones()[0]
+ second_zone = self.connection.get_zone(first_zone.name)
+ self.assertEqual(first_zone, second_zone)
+
+ def test_get_network(self):
+ first_network = self.connection.get_all_networks()[0]
+ second_network = self.connection.get_network(first_network.name)
+ self.assertEqual(first_network, second_network)
+
+ def test_get_firewall(self):
+ first_firewall = self.connection.get_all_firewalls()[0]
+ second_firewall = self.connection.get_firewall(first_firewall.name)
+ self.assertEqual(first_firewall, second_firewall)
+
+ def test_get_all_machine_types(self):
+ machine_types = self.connection.get_all_machine_types()
+ for machine_type in machine_types:
+ assert machine_type.id
+ assert machine_type.name
+
+ def test_instance_lifecycle(self):
+ name = time.strftime('test-instance-%Y%m%d%H%M%S')
+ machine_type = None
+ for machine in self.connection.get_all_machine_types():
+ if machine.deprecated is None:
+ machine_type = machine.self_link
+ break
+ self.assertIsNotNone(machine_type)
+ image_url = None
+ for image in self.connection.get_all_images():
+ if image.deprecated is None:
+ image_url = image.self_link
+ break
+ self.assertIsNotNone(image_url)
+ zone_name = None
+ for zone in self.connection.get_all_zones():
+ if zone.status == 'UP':
+ zone_name = zone.name
+ break
+ self.assertIsNotNone(zone_name)
+ self.connection.run_instance(name, machine_type, zone_name, image_url)
+ # TODO: use the returned ZoneOperation to wait for the instance to be up.
+ time.sleep(30)
+ self.connection.terminate_instance(name, zone_name)
Something went wrong with that request. Please try again.