You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/11/14 23:51:12 UTC

[25/56] [abbrv] libcloud git commit: Removed sdist

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
deleted file mode 100644
index 4aa92ac..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
+++ /dev/null
@@ -1,5860 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-"""
-Module for Google Compute Engine Driver.
-"""
-from __future__ import with_statement
-
-import datetime
-import time
-import sys
-
-from libcloud.common.base import LazyObject
-from libcloud.common.google import GoogleOAuth2Credential
-from libcloud.common.google import GoogleResponse
-from libcloud.common.google import GoogleBaseConnection
-from libcloud.common.google import GoogleBaseError
-from libcloud.common.google import ResourceNotFoundError
-from libcloud.common.google import ResourceExistsError
-from libcloud.common.types import ProviderError
-
-from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation
-from libcloud.compute.base import NodeSize, StorageVolume, VolumeSnapshot
-from libcloud.compute.base import UuidMixin
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.utils.iso8601 import parse_date
-
-API_VERSION = 'v1'
-DEFAULT_TASK_COMPLETION_TIMEOUT = 180
-
-
-def timestamp_to_datetime(timestamp):
-    """
-    Return a datetime object that corresponds to the time in an RFC3339
-    timestamp.
-
-    :param  timestamp: RFC3339 timestamp string
-    :type   timestamp: ``str``
-
-    :return:  Datetime object corresponding to timestamp
-    :rtype:   :class:`datetime.datetime`
-    """
-    # We remove timezone offset and microseconds (Python 2.5 strptime doesn't
-    # support %f)
-    ts = datetime.datetime.strptime(timestamp[:-10], '%Y-%m-%dT%H:%M:%S')
-    tz_hours = int(timestamp[-5:-3])
-    tz_mins = int(timestamp[-2:]) * int(timestamp[-6:-5] + '1')
-    tz_delta = datetime.timedelta(hours=tz_hours, minutes=tz_mins)
-    return ts + tz_delta
-
-
-class GCEResponse(GoogleResponse):
-    pass
-
-
-class GCEConnection(GoogleBaseConnection):
-    """
-    Connection class for the GCE driver.
-
-    GCEConnection extends :class:`google.GoogleBaseConnection` for 2 reasons:
-      1. modify request_path for GCE URI.
-      2. Implement gce_params functionality described below.
-
-    If the parameter gce_params is set to a dict prior to calling request(),
-    the URL parameters will be updated to include those key/values FOR A
-    SINGLE REQUEST. If the response contains a nextPageToken,
-    gce_params['pageToken'] will be set to its value. This can be used to
-    implement paging in list:
-
-    >>> params, more_results = {'maxResults': 2}, True
-    >>> while more_results:
-    ...     driver.connection.gce_params=params
-    ...     driver.ex_list_urlmaps()
-    ...     more_results = 'pageToken' in params
-    ...
-    [<GCEUrlMap id="..." name="cli-map">, <GCEUrlMap id="..." name="lc-map">]
-    [<GCEUrlMap id="..." name="web-map">]
-    """
-    host = 'www.googleapis.com'
-    responseCls = GCEResponse
-
-    def __init__(self, user_id, key, secure, auth_type=None,
-                 credential_file=None, project=None, **kwargs):
-        super(GCEConnection, self).__init__(user_id, key, secure=secure,
-                                            auth_type=auth_type,
-                                            credential_file=credential_file,
-                                            **kwargs)
-        self.request_path = '/compute/%s/projects/%s' % (API_VERSION, project)
-        self.gce_params = None
-
-    def pre_connect_hook(self, params, headers):
-        """
-        Update URL parameters with values from self.gce_params.
-
-        @inherits: :class:`GoogleBaseConnection.pre_connect_hook`
-        """
-        params, headers = super(GCEConnection, self).pre_connect_hook(params,
-                                                                      headers)
-        if self.gce_params:
-            params.update(self.gce_params)
-        return params, headers
-
-    def request(self, *args, **kwargs):
-        """
-        Perform request then do GCE-specific processing of URL params.
-
-        @inherits: :class:`GoogleBaseConnection.request`
-        """
-        response = super(GCEConnection, self).request(*args, **kwargs)
-
-        # If gce_params has been set, then update the pageToken with the
-        # nextPageToken so it can be used in the next request.
-        if self.gce_params:
-            if 'nextPageToken' in response.object:
-                self.gce_params['pageToken'] = response.object['nextPageToken']
-            elif 'pageToken' in self.gce_params:
-                del self.gce_params['pageToken']
-            self.gce_params = None
-
-        return response
-
-
-class GCEList(object):
-    """
-    An Iterator that wraps list functions to provide additional features.
-
-    GCE enforces a limit on the number of objects returned by a list operation,
-    so users with more than 500 objects of a particular type will need to use
-    filter(), page() or both.
-
-    >>> l=GCEList(driver, driver.ex_list_urlmaps)
-    >>> for sublist in l.filter('name eq ...-map').page(1):
-    ...   sublist
-    ...
-    [<GCEUrlMap id="..." name="cli-map">]
-    [<GCEUrlMap id="..." name="web-map">]
-
-    One can create a GCEList manually, but it's slightly easier to use the
-    ex_list() method of :class:`GCENodeDriver`.
-    """
-
-    def __init__(self, driver, list_fn, **kwargs):
-        """
-        :param  driver: An initialized :class:``GCENodeDriver``
-        :type   driver: :class:``GCENodeDriver``
-
-        :param  list_fn: A bound list method from :class:`GCENodeDriver`.
-        :type   list_fn: ``instancemethod``
-        """
-        self.driver = driver
-        self.list_fn = list_fn
-        self.kwargs = kwargs
-        self.params = {}
-
-    def __iter__(self):
-        list_fn = self.list_fn
-        more_results = True
-        while more_results:
-            self.driver.connection.gce_params = self.params
-            yield list_fn(**self.kwargs)
-            more_results = 'pageToken' in self.params
-
-    def __repr__(self):
-        return '<GCEList list="%s" params="%s">' % (
-            self.list_fn.__name__, repr(self.params))
-
-    def filter(self, expression):
-        """
-        Filter results of a list operation.
-
-        GCE supports server-side filtering of resources returned by a list
-        operation. Syntax of the filter expression is fully described in the
-        GCE API reference doc, but in brief it is::
-
-            FIELD_NAME COMPARISON_STRING LITERAL_STRING
-
-        where FIELD_NAME is the resource's property name, COMPARISON_STRING is
-        'eq' or 'ne', and LITERAL_STRING is a regular expression in RE2 syntax.
-
-        >>> for sublist in l.filter('name eq ...-map'):
-        ...   sublist
-        ...
-        [<GCEUrlMap id="..." name="cli-map">, \
-                <GCEUrlMap id="..." name="web-map">]
-
-        API reference: https://cloud.google.com/compute/docs/reference/latest/
-        RE2 syntax: https://github.com/google/re2/blob/master/doc/syntax.txt
-
-        :param  expression: Filter expression described above.
-        :type   expression: ``str``
-
-        :return: This :class:`GCEList` instance
-        :rtype:  :class:`GCEList`
-        """
-        self.params['filter'] = expression
-        return self
-
-    def page(self, max_results=500):
-        """
-        Limit the number of results by each iteration.
-
-        This implements the paging functionality of the GCE list methods and
-        returns this GCEList instance so that results can be chained:
-
-        >>> for sublist in GCEList(driver, driver.ex_list_urlmaps).page(2):
-        ...   sublist
-        ...
-        [<GCEUrlMap id="..." name="cli-map">, \
-                <GCEUrlMap id="..." name="lc-map">]
-        [<GCEUrlMap id="..." name="web-map">]
-
-        :keyword  max_results: Maximum number of results to return per
-                               iteration. Defaults to the GCE default of 500.
-        :type     max_results: ``int``
-
-        :return: This :class:`GCEList` instance
-        :rtype:  :class:`GCEList`
-        """
-        self.params['maxResults'] = max_results
-        return self
-
-
-class GCELicense(UuidMixin, LazyObject):
-    """A GCE License used to track software usage in GCE nodes."""
-    def __init__(self, name, project, driver):
-        UuidMixin.__init__(self)
-        self.id = name
-        self.name = name
-        self.project = project
-        self.driver = driver
-        self.charges_use_fee = None  # init in _request
-        self.extra = None  # init in _request
-
-        self._request()
-
-    def _request(self):
-        # TODO(crunkleton@google.com): create new connection? or make
-        # connection thread-safe? Saving, modifying, and restoring
-        # driver.connection.request_path is really hacky and thread-unsafe.
-        saved_request_path = self.driver.connection.request_path
-        new_request_path = saved_request_path.replace(self.driver.project,
-                                                      self.project)
-        self.driver.connection.request_path = new_request_path
-
-        request = '/global/licenses/%s' % self.name
-        response = self.driver.connection.request(request, method='GET').object
-        self.driver.connection.request_path = saved_request_path
-
-        self.extra = {
-            'selfLink': response.get('selfLink'),
-            'kind': response.get('kind')
-        }
-        self.charges_use_fee = response['chargesUseFee']
-
-    def destroy(self):
-        raise ProviderError("Can not destroy a License resource.")
-
-    def __repr__(self):
-        return '<GCELicense id="%s" name="%s" charges_use_fee="%s">' % (
-            self.id, self.name, self.charges_use_fee)
-
-
-class GCEDiskType(UuidMixin):
-    """A GCE DiskType resource."""
-    def __init__(self, id, name, zone, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.zone = zone
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        raise ProviderError("Can not destroy a DiskType resource.")
-
-    def __repr__(self):
-        return '<GCEDiskType id="%s" name="%s" zone="%s">' % (
-            self.id, self.name, self.zone)
-
-
-class GCEAddress(UuidMixin):
-    """A GCE Static address."""
-    def __init__(self, id, name, address, region, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.address = address
-        self.region = region
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this address.
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_destroy_address(address=self)
-
-    def __repr__(self):
-        return '<GCEAddress id="%s" name="%s" address="%s" region="%s">' % (
-            self.id, self.name, self.address,
-            (hasattr(self.region, "name") and self.region.name or self.region))
-
-
-class GCEBackendService(UuidMixin):
-    """A GCE Backend Service."""
-
-    def __init__(self, id, name, backends, healthchecks, port, port_name,
-                 protocol, timeout, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.backends = backends or []
-        self.healthchecks = healthchecks or []
-        self.port = port
-        self.port_name = port_name
-        self.protocol = protocol
-        self.timeout = timeout
-        self.driver = driver
-        self.extra = extra or {}
-        UuidMixin.__init__(self)
-
-    def __repr__(self):
-        return '<GCEBackendService id="%s" name="%s">' % (
-            self.id, self.name)
-
-    def destroy(self):
-        """
-        Destroy this Backend Service.
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_destroy_backendservice(backendservice=self)
-
-
-class GCEFailedDisk(object):
-    """Dummy Node object for disks that are not created."""
-    def __init__(self, name, error, code):
-        self.name = name
-        self.error = error
-        self.code = code
-
-    def __repr__(self):
-        return '<GCEFailedDisk name="%s" error_code="%s">' % (
-            self.name, self.code)
-
-
-class GCEFailedNode(object):
-    """Dummy Node object for nodes that are not created."""
-    def __init__(self, name, error, code):
-        self.name = name
-        self.error = error
-        self.code = code
-
-    def __repr__(self):
-        return '<GCEFailedNode name="%s" error_code="%s">' % (
-            self.name, self.code)
-
-
-class GCEHealthCheck(UuidMixin):
-    """A GCE Http Health Check class."""
-    def __init__(self, id, name, path, port, interval, timeout,
-                 unhealthy_threshold, healthy_threshold, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.path = path
-        self.port = port
-        self.interval = interval
-        self.timeout = timeout
-        self.unhealthy_threshold = unhealthy_threshold
-        self.healthy_threshold = healthy_threshold
-        self.driver = driver
-        self.extra = extra or {}
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this Health Check.
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_healthcheck(healthcheck=self)
-
-    def update(self):
-        """
-        Commit updated healthcheck values.
-
-        :return:  Updated Healthcheck object
-        :rtype:   :class:`GCEHealthcheck`
-        """
-        return self.driver.ex_update_healthcheck(healthcheck=self)
-
-    def __repr__(self):
-        return '<GCEHealthCheck id="%s" name="%s" path="%s" port="%s">' % (
-            self.id, self.name, self.path, self.port)
-
-
-class GCEFirewall(UuidMixin):
-    """A GCE Firewall rule class."""
-    def __init__(self, id, name, allowed, network, source_ranges, source_tags,
-                 target_tags, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.network = network
-        self.allowed = allowed
-        self.source_ranges = source_ranges
-        self.source_tags = source_tags
-        self.target_tags = target_tags
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this firewall.
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_destroy_firewall(firewall=self)
-
-    def update(self):
-        """
-        Commit updated firewall values.
-
-        :return:  Updated Firewall object
-        :rtype:   :class:`GCEFirewall`
-        """
-        return self.driver.ex_update_firewall(firewall=self)
-
-    def __repr__(self):
-        return '<GCEFirewall id="%s" name="%s" network="%s">' % (
-            self.id, self.name, self.network.name)
-
-
-class GCEForwardingRule(UuidMixin):
-    def __init__(self, id, name, region, address, protocol, targetpool, driver,
-                 extra=None):
-        self.id = str(id)
-        self.name = name
-        self.region = region
-        self.address = address
-        self.protocol = protocol
-        # TODO: 'targetpool' should more correctly be 'target' since a
-        # forwarding rule's target can be something besides a targetpool
-        self.targetpool = targetpool
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this Forwarding Rule
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_forwarding_rule(forwarding_rule=self)
-
-    def __repr__(self):
-        return '<GCEForwardingRule id="%s" name="%s" address="%s">' % (
-            self.id, self.name, self.address)
-
-
-class GCENodeImage(NodeImage):
-    """A GCE Node Image class."""
-    def __init__(self, id, name, driver, extra=None):
-        super(GCENodeImage, self).__init__(id, name, driver, extra=extra)
-
-    def delete(self):
-        """
-        Delete this image
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_delete_image(image=self)
-
-    def deprecate(self, replacement, state, deprecated=None, obsolete=None,
-                  deleted=None):
-        """
-        Deprecate this image
-
-        :param  replacement: Image to use as a replacement
-        :type   replacement: ``str`` or :class: `GCENodeImage`
-
-        :param  state: Deprecation state of this image. Possible values include
-                       \'DELETED\', \'DEPRECATED\' or \'OBSOLETE\'.
-        :type   state: ``str``
-
-        :param  deprecated: RFC3339 timestamp to mark DEPRECATED
-        :type   deprecated: ``str`` or ``None``
-
-        :param  obsolete: RFC3339 timestamp to mark OBSOLETE
-        :type   obsolete: ``str`` or ``None``
-
-        :param  deleted: RFC3339 timestamp to mark DELETED
-        :type   deleted: ``str`` or ``None``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_deprecate_image(self, replacement, state,
-                                              deprecated, obsolete, deleted)
-
-
-class GCENetwork(UuidMixin):
-    """A GCE Network object class."""
-    def __init__(self, id, name, cidr, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.cidr = cidr
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this network
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_destroy_network(network=self)
-
-    def __repr__(self):
-        return '<GCENetwork id="%s" name="%s" cidr="%s">' % (
-            self.id, self.name, self.cidr)
-
-
-class GCERoute(UuidMixin):
-    """A GCE Route object class."""
-    def __init__(self, id, name, dest_range, priority, network="default",
-                 tags=None, driver=None, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.dest_range = dest_range
-        self.priority = priority
-        self.network = network
-        self.tags = tags
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this route
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_destroy_route(route=self)
-
-    def __repr__(self):
-        return '<GCERoute id="%s" name="%s" dest_range="%s" network="%s">' % (
-            self.id, self.name, self.dest_range,
-            hasattr(self.network, 'name') and self.network.name or
-            self.network)
-
-
-class GCENodeSize(NodeSize):
-    """A GCE Node Size (MachineType) class."""
-    def __init__(self, id, name, ram, disk, bandwidth, price, driver,
-                 extra=None):
-        self.extra = extra
-        super(GCENodeSize, self).__init__(id, name, ram, disk, bandwidth,
-                                          price, driver, extra=extra)
-
-
-class GCEProject(UuidMixin):
-    """GCE Project information."""
-    def __init__(self, id, name, metadata, quotas, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.metadata = metadata
-        self.quotas = quotas
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def set_common_instance_metadata(self, metadata=None, force=False):
-        """
-        Set common instance metadata for the project. Common uses
-        are for setting 'sshKeys', or setting a project-wide
-        'startup-script' for all nodes (instances).  Passing in
-        ``None`` for the 'metadata' parameter will clear out all common
-        instance metadata *except* for 'sshKeys'. If you also want to
-        update 'sshKeys', set the 'force' parameter to ``True``.
-
-        :param  metadata: Dictionary of metadata. Can be either a standard
-                          python dictionary, or the format expected by
-                          GCE (e.g. {'items': [{'key': k1, 'value': v1}, ...}]
-        :type   metadata: ``dict`` or ``None``
-
-        :param  force: Force update of 'sshKeys'. If force is ``False`` (the
-                       default), existing sshKeys will be retained. Setting
-                       force to ``True`` will either replace sshKeys if a new
-                       a new value is supplied, or deleted if no new value
-                       is supplied.
-        :type   force: ``bool``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_set_common_instance_metadata(self, metadata)
-
-    def set_usage_export_bucket(self, bucket, prefix=None):
-        """
-        Used to retain Compute Engine resource usage, storing the CSV data in
-        a Google Cloud Storage bucket. See the
-        `docs <https://cloud.google.com/compute/docs/usage-export>`_ for more
-        information. Please ensure you have followed the necessary setup steps
-        prior to enabling this feature (e.g. bucket exists, ACLs are in place,
-        etc.)
-
-        :param  bucket: Name of the Google Cloud Storage bucket. Specify the
-                        name in either 'gs://<bucket_name>' or the full URL
-                        'https://storage.googleapis.com/<bucket_name>'.
-        :type   bucket: ``str``
-
-        :param  prefix: Optional prefix string for all reports.
-        :type   prefix: ``str`` or ``None``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        return self.driver.ex_set_usage_export_bucket(self, bucket, prefix)
-
-    def __repr__(self):
-        return '<GCEProject id="%s" name="%s">' % (self.id, self.name)
-
-
-class GCERegion(UuidMixin):
-    def __init__(self, id, name, status, zones, quotas, deprecated, driver,
-                 extra=None):
-        self.id = str(id)
-        self.name = name
-        self.status = status
-        self.zones = zones
-        self.quotas = quotas
-        self.deprecated = deprecated
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def __repr__(self):
-        return '<GCERegion id="%s" name="%s", status="%s">' % (
-            self.id, self.name, self.status)
-
-
-class GCESnapshot(VolumeSnapshot):
-    def __init__(self, id, name, size, status, driver, extra=None,
-                 created=None):
-        self.name = name
-        self.status = status
-        super(GCESnapshot, self).__init__(id, driver, size, extra, created)
-
-
-class GCETargetHttpProxy(UuidMixin):
-    def __init__(self, id, name, urlmap, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.urlmap = urlmap
-        self.driver = driver
-        self.extra = extra or {}
-        UuidMixin.__init__(self)
-
-    def __repr__(self):
-        return '<GCETargetHttpProxy id="%s" name="%s">' % (
-            self.id, self.name)
-
-    def destroy(self):
-        """
-        Destroy this Target HTTP Proxy.
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_targethttpproxy(targethttpproxy=self)
-
-
-class GCETargetInstance(UuidMixin):
-    def __init__(self, id, name, zone, node, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.zone = zone
-        self.node = node
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def destroy(self):
-        """
-        Destroy this Target Instance
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_targetinstance(targetinstance=self)
-
-    def __repr__(self):
-        return '<GCETargetInstance id="%s" name="%s" zone="%s" node="%s">' % (
-            self.id, self.name, self.zone.name,
-            (hasattr(self.node, 'name') and self.node.name or self.node))
-
-
-class GCETargetPool(UuidMixin):
-    def __init__(self, id, name, region, healthchecks, nodes, driver,
-                 extra=None):
-        self.id = str(id)
-        self.name = name
-        self.region = region
-        self.healthchecks = healthchecks
-        self.nodes = nodes
-        self.driver = driver
-        self.extra = extra
-        UuidMixin.__init__(self)
-
-    def add_node(self, node):
-        """
-        Add a node to this target pool.
-
-        :param  node: Node to add
-        :type   node: ``str`` or :class:`Node`
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_targetpool_add_node(targetpool=self, node=node)
-
-    def remove_node(self, node):
-        """
-        Remove a node from this target pool.
-
-        :param  node: Node to remove
-        :type   node: ``str`` or :class:`Node`
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_targetpool_remove_node(targetpool=self,
-                                                     node=node)
-
-    def add_healthcheck(self, healthcheck):
-        """
-        Add a healthcheck to this target pool.
-
-        :param  healthcheck: Healthcheck to add
-        :type   healthcheck: ``str`` or :class:`GCEHealthCheck`
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_targetpool_add_healthcheck(
-            targetpool=self, healthcheck=healthcheck)
-
-    def remove_healthcheck(self, healthcheck):
-        """
-        Remove a healthcheck from this target pool.
-
-        :param  healthcheck: Healthcheck to remove
-        :type   healthcheck: ``str`` or :class:`GCEHealthCheck`
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_targetpool_remove_healthcheck(
-            targetpool=self, healthcheck=healthcheck)
-
-    def set_backup_targetpool(self, backup_targetpool, failover_ratio=0.1):
-        """
-        Set a backup targetpool.
-
-        :param  backup_targetpool: The existing targetpool to use for
-                                   failover traffic.
-        :type   backup_targetpool: :class:`GCETargetPool`
-
-        :param  failover_ratio: The percentage of healthy VMs must fall at or
-                                below this value before traffic will be sent
-                                to the backup targetpool (default 0.10)
-        :type   failover_ratio: ``float``
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_targetpool_set_backup_targetpool(
-            targetpool=self, backup_targetpool=backup_targetpool,
-            failover_ratio=failover_ratio)
-
-    def get_health(self, node=None):
-        """
-        Return a hash of target pool instances and their health.
-
-        :param  node: Optional node to specify if only a specific node's
-                      health status should be returned
-        :type   node: ``str``, ``Node``, or ``None``
-
-        :return: List of hashes of nodes and their respective health
-        :rtype:  ``list`` of ``dict``
-        """
-        return self.driver.ex_targetpool_get_health(targetpool=self, node=node)
-
-    def destroy(self):
-        """
-        Destroy this Target Pool
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_targetpool(targetpool=self)
-
-    def __repr__(self):
-        return '<GCETargetPool id="%s" name="%s" region="%s">' % (
-            self.id, self.name, self.region.name)
-
-
-class GCEUrlMap(UuidMixin):
-    """A GCE URL Map."""
-
-    def __init__(self, id, name, default_service, host_rules, path_matchers,
-                 tests, driver, extra=None):
-        self.id = str(id)
-        self.name = name
-        self.default_service = default_service
-        self.host_rules = host_rules or []
-        self.path_matchers = path_matchers or []
-        self.tests = tests or []
-        self.driver = driver
-        self.extra = extra or {}
-        UuidMixin.__init__(self)
-
-    def __repr__(self):
-        return '<GCEUrlMap id="%s" name="%s">' % (
-            self.id, self.name)
-
-    def destroy(self):
-        """
-        Destroy this URL Map
-
-        :return:  True if successful
-        :rtype:   ``bool``
-        """
-        return self.driver.ex_destroy_urlmap(urlmap=self)
-
-
-class GCEZone(NodeLocation):
-    """Subclass of NodeLocation to provide additional information."""
-    def __init__(self, id, name, status, maintenance_windows, deprecated,
-                 driver, extra=None):
-        self.status = status
-        self.maintenance_windows = maintenance_windows
-        self.deprecated = deprecated
-        self.extra = extra
-        country = name.split('-')[0]
-        super(GCEZone, self).__init__(id=str(id), name=name, country=country,
-                                      driver=driver)
-
-    @property
-    def time_until_mw(self):
-        """
-        Returns the time until the next Maintenance Window as a
-        datetime.timedelta object.
-        """
-        return self._get_time_until_mw()
-
-    @property
-    def next_mw_duration(self):
-        """
-        Returns the duration of the next Maintenance Window as a
-        datetime.timedelta object.
-        """
-        return self._get_next_mw_duration()
-
-    def _now(self):
-        """
-        Returns current UTC time.
-
-        Can be overridden in unittests.
-        """
-        return datetime.datetime.utcnow()
-
-    def _get_next_maint(self):
-        """
-        Returns the next Maintenance Window.
-
-        :return:  A dictionary containing maintenance window info (or None if
-                  no maintenance windows are scheduled)
-                  The dictionary contains 4 keys with values of type ``str``
-                      - name: The name of the maintenance window
-                      - description: Description of the maintenance window
-                      - beginTime: RFC3339 Timestamp
-                      - endTime: RFC3339 Timestamp
-        :rtype:   ``dict`` or ``None``
-        """
-        begin = None
-        next_window = None
-        if not self.maintenance_windows:
-            return None
-        if len(self.maintenance_windows) == 1:
-            return self.maintenance_windows[0]
-        for mw in self.maintenance_windows:
-            begin_next = timestamp_to_datetime(mw['beginTime'])
-            if (not begin) or (begin_next < begin):
-                begin = begin_next
-                next_window = mw
-        return next_window
-
-    def _get_time_until_mw(self):
-        """
-        Returns time until next maintenance window.
-
-        :return:  Time until next maintenance window (or None if no
-                  maintenance windows are scheduled)
-        :rtype:   :class:`datetime.timedelta` or ``None``
-        """
-        next_window = self._get_next_maint()
-        if not next_window:
-            return None
-        now = self._now()
-        next_begin = timestamp_to_datetime(next_window['beginTime'])
-        return next_begin - now
-
-    def _get_next_mw_duration(self):
-        """
-        Returns the duration of the next maintenance window.
-
-        :return:  Duration of next maintenance window (or None if no
-                  maintenance windows are scheduled)
-        :rtype:   :class:`datetime.timedelta` or ``None``
-        """
-        next_window = self._get_next_maint()
-        if not next_window:
-            return None
-        next_begin = timestamp_to_datetime(next_window['beginTime'])
-        next_end = timestamp_to_datetime(next_window['endTime'])
-        return next_end - next_begin
-
-    def __repr__(self):
-        return '<GCEZone id="%s" name="%s" status="%s">' % (self.id, self.name,
-                                                            self.status)
-
-
-class GCENodeDriver(NodeDriver):
-    """
-    GCE Node Driver class.
-
-    This is the primary driver for interacting with Google Compute Engine.  It
-    contains all of the standard libcloud methods, plus additional ex_* methods
-    for more features.
-
-    Note that many methods allow either objects or strings (or lists of
-    objects/strings).  In most cases, passing strings instead of objects will
-    result in additional GCE API calls.
-    """
-    connectionCls = GCEConnection
-    api_name = 'google'
-    name = "Google Compute Engine"
-    type = Provider.GCE
-    website = 'https://cloud.google.com/'
-
-    # Google Compute Engine node states are mapped to Libcloud node states
-    # per the following dict. GCE does not have an actual 'stopped' state
-    # but instead uses a 'terminated' state to indicate the node exists
-    # but is not running. In order to better match libcloud, GCE maps this
-    # 'terminated' state to 'STOPPED'.
-    # Also, when a node is deleted from GCE, it no longer exists and instead
-    # will result in a ResourceNotFound error versus returning a placeholder
-    # node in a 'terminated' state.
-    # For more details, please see GCE's docs,
-    # https://cloud.google.com/compute/docs/instances#checkmachinestatus
-    NODE_STATE_MAP = {
-        "PROVISIONING": NodeState.PENDING,
-        "STAGING": NodeState.PENDING,
-        "RUNNING": NodeState.RUNNING,
-        "STOPPING": NodeState.PENDING,
-        "TERMINATED": NodeState.STOPPED,
-        "UNKNOWN": NodeState.UNKNOWN
-    }
-
-    AUTH_URL = "https://www.googleapis.com/auth/"
-    SA_SCOPES_MAP = {
-        # list derived from 'gcloud compute instances create --help'
-        "bigquery": "bigquery",
-        "cloud-platform": "cloud-platform",
-        "compute-ro": "compute.readonly",
-        "compute-rw": "compute",
-        "datastore": "datastore",
-        "logging-write": "logging.write",
-        "monitoring": "monitoring",
-        "sql": "sqlservice",
-        "sql-admin": "sqlservice.admin",
-        "storage-full": "devstorage.full_control",
-        "storage-ro": "devstorage.read_only",
-        "storage-rw": "devstorage.read_write",
-        "taskqueue": "taskqueue",
-        "useraccounts-ro": "cloud.useraccounts.readonly",
-        "useraccounts-rw": "cloud.useraccounts",
-        "userinfo-email": "userinfo.email"
-    }
-
-    IMAGE_PROJECTS = {
-        "centos-cloud": ["centos"],
-        "coreos-cloud": ["coreos"],
-        "debian-cloud": ["debian", "backports"],
-        "gce-nvme": ["nvme-backports"],
-        "google-containers": ["container-vm"],
-        "opensuse-cloud": ["opensuse"],
-        "rhel-cloud": ["rhel"],
-        "suse-cloud": ["sles", "suse"],
-        "ubuntu-os-cloud": ["ubuntu"],
-        "windows-cloud": ["windows"],
-    }
-
-    def __init__(self, user_id, key=None, datacenter=None, project=None,
-                 auth_type=None, scopes=None, credential_file=None, **kwargs):
-        """
-        :param  user_id: The email address (for service accounts) or Client ID
-                         (for installed apps) to be used for authentication.
-        :type   user_id: ``str``
-
-        :param  key: The RSA Key (for service accounts) or file path containing
-                     key or Client Secret (for installed apps) to be used for
-                     authentication.
-        :type   key: ``str``
-
-        :keyword  datacenter: The name of the datacenter (zone) used for
-                              operations.
-        :type     datacenter: ``str``
-
-        :keyword  project: Your GCE project name. (required)
-        :type     project: ``str``
-
-        :keyword  auth_type: Accepted values are "SA" or "IA" or "GCE"
-                             ("Service Account" or "Installed Application" or
-                             "GCE" if libcloud is being used on a GCE instance
-                             with service account enabled).
-                             If not supplied, auth_type will be guessed based
-                             on value of user_id or if the code is being
-                             executed in a GCE instance.
-        :type     auth_type: ``str``
-
-        :keyword  scopes: List of authorization URLs. Default is empty and
-                          grants read/write to Compute, Storage, DNS.
-        :type     scopes: ``list``
-
-        :keyword  credential_file: Path to file for caching authentication
-                                   information used by GCEConnection.
-        :type     credential_file: ``str``
-        """
-        if not project:
-            raise ValueError('Project name must be specified using '
-                             '"project" keyword.')
-
-        self.auth_type = auth_type
-        self.project = project
-        self.scopes = scopes
-        self.credential_file = credential_file or \
-            GoogleOAuth2Credential.default_credential_file + '.' + self.project
-
-        super(GCENodeDriver, self).__init__(user_id, key, **kwargs)
-
-        # Cache Zone and Region information to reduce API calls and
-        # increase speed
-        self.base_path = '/compute/%s/projects/%s' % (API_VERSION,
-                                                      self.project)
-        self.zone_list = self.ex_list_zones()
-        self.zone_dict = {}
-        for zone in self.zone_list:
-            self.zone_dict[zone.name] = zone
-        if datacenter:
-            self.zone = self.ex_get_zone(datacenter)
-        else:
-            self.zone = None
-
-        self.region_list = self.ex_list_regions()
-        self.region_dict = {}
-        for region in self.region_list:
-            self.region_dict[region.name] = region
-
-        if self.zone:
-            self.region = self._get_region_from_zone(self.zone)
-        else:
-            self.region = None
-
-    def ex_add_access_config(self, node, name, nic, nat_ip=None,
-                             config_type=None):
-        """
-        Add a network interface access configuration to a node.
-
-        :keyword  node: The existing target Node (instance) that will receive
-                        the new access config.
-        :type     node: ``Node``
-
-        :keyword  name: Name of the new access config.
-        :type     node: ``str``
-
-        :keyword  nat_ip: The external existing static IP Address to use for
-                          the access config. If not provided, an ephemeral
-                          IP address will be allocated.
-        :type     nat_ip: ``str`` or ``None``
-
-        :keyword  config_type: The type of access config to create. Currently
-                               the only supported type is 'ONE_TO_ONE_NAT'.
-        :type     config_type: ``str`` or ``None``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        if not isinstance(node, Node):
-            raise ValueError("Must specify a valid libcloud node object.")
-        node_name = node.name
-        zone_name = node.extra['zone'].name
-
-        config = {'name': name}
-        if config_type is None:
-            config_type = 'ONE_TO_ONE_NAT'
-        config['type'] = config_type
-
-        if nat_ip is not None:
-            config['natIP'] = nat_ip
-        params = {'networkInterface': nic}
-        request = '/zones/%s/instances/%s/addAccessConfig' % (zone_name,
-                                                              node_name)
-        self.connection.async_request(request, method='POST',
-                                      data=config, params=params)
-        return True
-
-    def ex_delete_access_config(self, node, name, nic):
-        """
-        Delete a network interface access configuration from a node.
-
-        :keyword  node: The existing target Node (instance) for the request.
-        :type     node: ``Node``
-
-        :keyword  name: Name of the access config.
-        :type     node: ``str``
-
-        :keyword  nic: Name of the network interface.
-        :type     nic: ``str``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        if not isinstance(node, Node):
-            raise ValueError("Must specify a valid libcloud node object.")
-        node_name = node.name
-        zone_name = node.extra['zone'].name
-
-        params = {'accessConfig': name, 'networkInterface': nic}
-        request = '/zones/%s/instances/%s/deleteAccessConfig' % (zone_name,
-                                                                 node_name)
-        self.connection.async_request(request, method='POST', params=params)
-        return True
-
-    def ex_set_node_metadata(self, node, metadata):
-        """
-        Set metadata for the specified node.
-
-        :keyword  node: The existing target Node (instance) for the request.
-        :type     node: ``Node``
-
-        :keyword  metadata: Set (or clear with None) metadata for this
-                            particular node.
-        :type     metadata: ``dict`` or ``None``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        if not isinstance(node, Node):
-            raise ValueError("Must specify a valid libcloud node object.")
-        node_name = node.name
-        zone_name = node.extra['zone'].name
-        if 'metadata' in node.extra and \
-                'fingerprint' in node.extra['metadata']:
-            current_fp = node.extra['metadata']['fingerprint']
-        else:
-            current_fp = 'absent'
-        body = self._format_metadata(current_fp, metadata)
-        request = '/zones/%s/instances/%s/setMetadata' % (zone_name,
-                                                          node_name)
-        self.connection.async_request(request, method='POST', data=body)
-        return True
-
-    def ex_get_serial_output(self, node):
-        """
-        Fetch the console/serial port output from the node.
-
-        :keyword  node: The existing target Node (instance) for the request.
-        :type     node: ``Node``
-
-        :return: A string containing serial port output of the node.
-        :rtype:  ``str``
-        """
-        if not isinstance(node, Node):
-            raise ValueError("Must specify a valid libcloud node object.")
-        node_name = node.name
-        zone_name = node.extra['zone'].name
-        request = '/zones/%s/instances/%s/serialPort' % (zone_name,
-                                                         node_name)
-        response = self.connection.request(request, method='GET').object
-        return response['contents']
-
-    def ex_list(self, list_fn, **kwargs):
-        """
-        Wrap a list method in a :class:`GCEList` iterator.
-
-        >>> for sublist in driver.ex_list(driver.ex_list_urlmaps).page(1):
-        ...   sublist
-        ...
-        [<GCEUrlMap id="..." name="cli-map">]
-        [<GCEUrlMap id="..." name="lc-map">]
-        [<GCEUrlMap id="..." name="web-map">]
-
-        :param  list_fn: A bound list method from :class:`GCENodeDriver`.
-        :type   list_fn: ``instancemethod``
-
-        :return: An iterator that returns sublists from list_fn.
-        :rtype: :class:`GCEList`
-        """
-        return GCEList(driver=self, list_fn=list_fn, **kwargs)
-
-    def ex_list_disktypes(self, zone=None):
-        """
-        Return a list of DiskTypes for a zone or all.
-
-        :keyword  zone: The zone to return DiskTypes from. For example:
-                        'us-central1-a'.  If None, will return DiskTypes from
-                        self.zone.  If 'all', will return all DiskTypes.
-        :type     zone: ``str`` or ``None``
-
-        :return: A list of static DiskType objects.
-        :rtype: ``list`` of :class:`GCEDiskType`
-        """
-        list_disktypes = []
-        zone = self._set_zone(zone)
-        if zone is None:
-            request = '/aggregated/diskTypes'
-        else:
-            request = '/zones/%s/diskTypes' % (zone.name)
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated result returns dictionaries for each region
-            if zone is None:
-                for v in response['items'].values():
-                    zone_disktypes = [self._to_disktype(a) for a in
-                                      v.get('diskTypes', [])]
-                    list_disktypes.extend(zone_disktypes)
-            else:
-                list_disktypes = [self._to_disktype(a) for a in
-                                  response['items']]
-        return list_disktypes
-
-    def ex_set_usage_export_bucket(self, bucket, prefix=None):
-        """
-        Used to retain Compute Engine resource usage, storing the CSV data in
-        a Google Cloud Storage bucket. See the
-        `docs <https://cloud.google.com/compute/docs/usage-export>`_ for more
-        information. Please ensure you have followed the necessary setup steps
-        prior to enabling this feature (e.g. bucket exists, ACLs are in place,
-        etc.)
-
-        :param  bucket: Name of the Google Cloud Storage bucket. Specify the
-                        name in either 'gs://<bucket_name>' or the full URL
-                        'https://storage.googleapis.com/<bucket_name>'.
-        :type   bucket: ``str``
-
-        :param  prefix: Optional prefix string for all reports.
-        :type   prefix: ``str`` or ``None``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        if bucket.startswith('https://www.googleapis.com/') or \
-                bucket.startswith('gs://'):
-            data = {'bucketName': bucket}
-        else:
-            raise ValueError("Invalid bucket name: %s" % bucket)
-        if prefix:
-            data['reportNamePrefix'] = prefix
-
-        request = '/setUsageExportBucket'
-        self.connection.async_request(request, method='POST', data=data)
-        return True
-
-    def ex_set_common_instance_metadata(self, metadata=None, force=False):
-        """
-        Set common instance metadata for the project. Common uses
-        are for setting 'sshKeys', or setting a project-wide
-        'startup-script' for all nodes (instances).  Passing in
-        ``None`` for the 'metadata' parameter will clear out all common
-        instance metadata *except* for 'sshKeys'. If you also want to
-        update 'sshKeys', set the 'force' parameter to ``True``.
-
-        :param  metadata: Dictionary of metadata. Can be either a standard
-                          python dictionary, or the format expected by
-                          GCE (e.g. {'items': [{'key': k1, 'value': v1}, ...}]
-        :type   metadata: ``dict`` or ``None``
-
-        :param  force: Force update of 'sshKeys'. If force is ``False`` (the
-                       default), existing sshKeys will be retained. Setting
-                       force to ``True`` will either replace sshKeys if a new
-                       a new value is supplied, or deleted if no new value
-                       is supplied.
-        :type   force: ``bool``
-
-        :return: True if successful
-        :rtype:  ``bool``
-        """
-        if metadata:
-            metadata = self._format_metadata('na', metadata)
-
-        request = '/setCommonInstanceMetadata'
-
-        project = self.ex_get_project()
-        current_metadata = project.extra['commonInstanceMetadata']
-        fingerprint = current_metadata['fingerprint']
-        md_items = []
-        if 'items' in current_metadata:
-            md_items = current_metadata['items']
-
-        # grab copy of current 'sshKeys' in case we want to retain them
-        current_keys = ""
-        for md in md_items:
-            if md['key'] == 'sshKeys':
-                current_keys = md['value']
-
-        new_md = self._set_project_metadata(metadata, force, current_keys)
-
-        md = {'fingerprint': fingerprint, 'items': new_md}
-        self.connection.async_request(request, method='POST', data=md)
-        return True
-
-    def ex_list_addresses(self, region=None):
-        """
-        Return a list of static addresses for a region, 'global', or all.
-
-        :keyword  region: The region to return addresses from. For example:
-                          'us-central1'.  If None, will return addresses from
-                          region of self.zone.  If 'all', will return all
-                          addresses. If 'global', it will return addresses in
-                          the global namespace.
-        :type     region: ``str`` or ``None``
-
-        :return: A list of static address objects.
-        :rtype: ``list`` of :class:`GCEAddress`
-        """
-        list_addresses = []
-        if region != 'global':
-            region = self._set_region(region)
-        if region is None:
-            request = '/aggregated/addresses'
-        elif region == 'global':
-            request = '/global/addresses'
-        else:
-            request = '/regions/%s/addresses' % (region.name)
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated result returns dictionaries for each region
-            if region is None:
-                for v in response['items'].values():
-                    region_addresses = [self._to_address(a) for a in
-                                        v.get('addresses', [])]
-                    list_addresses.extend(region_addresses)
-            else:
-                list_addresses = [self._to_address(a) for a in
-                                  response['items']]
-        return list_addresses
-
-    def ex_list_backendservices(self):
-        """
-        Return a list of backend services.
-
-        :return: A list of backend service objects.
-        :rtype: ``list`` of :class:`GCEBackendService`
-        """
-        list_backendservices = []
-        response = self.connection.request('/global/backendServices',
-                                           method='GET').object
-
-        list_backendservices = [self._to_backendservice(d) for d in
-                                response.get('items', [])]
-
-        return list_backendservices
-
-    def ex_list_healthchecks(self):
-        """
-        Return the list of health checks.
-
-        :return: A list of health check objects.
-        :rtype: ``list`` of :class:`GCEHealthCheck`
-        """
-        list_healthchecks = []
-        request = '/global/httpHealthChecks'
-        response = self.connection.request(request, method='GET').object
-        list_healthchecks = [self._to_healthcheck(h) for h in
-                             response.get('items', [])]
-        return list_healthchecks
-
-    def ex_list_firewalls(self):
-        """
-        Return the list of firewalls.
-
-        :return: A list of firewall objects.
-        :rtype: ``list`` of :class:`GCEFirewall`
-        """
-        list_firewalls = []
-        request = '/global/firewalls'
-        response = self.connection.request(request, method='GET').object
-        list_firewalls = [self._to_firewall(f) for f in
-                          response.get('items', [])]
-        return list_firewalls
-
-    def ex_list_forwarding_rules(self, region=None, global_rules=False):
-        """
-        Return the list of forwarding rules for a region or all.
-
-        :keyword  region: The region to return forwarding rules from.  For
-                          example: 'us-central1'.  If None, will return
-                          forwarding rules from the region of self.region
-                          (which is based on self.zone).  If 'all', will
-                          return forwarding rules for all regions, which does
-                          not include the global forwarding rules.
-        :type     region: ``str`` or :class:`GCERegion` or ``None``
-
-        :keyword  global_rules: List global forwarding rules instead of
-                                per-region rules.  Setting True will cause
-                                'region' parameter to be ignored.
-        :type     global_rules: ``bool``
-
-        :return: A list of forwarding rule objects.
-        :rtype: ``list`` of :class:`GCEForwardingRule`
-        """
-        list_forwarding_rules = []
-        if global_rules:
-            region = None
-            request = '/global/forwardingRules'
-        else:
-            region = self._set_region(region)
-            if region is None:
-                request = '/aggregated/forwardingRules'
-            else:
-                request = '/regions/%s/forwardingRules' % (region.name)
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated result returns dictionaries for each region
-            if not global_rules and region is None:
-                for v in response['items'].values():
-                    region_forwarding_rules = [self._to_forwarding_rule(f) for
-                                               f in v.get('forwardingRules',
-                                                          [])]
-                    list_forwarding_rules.extend(region_forwarding_rules)
-            else:
-                list_forwarding_rules = [self._to_forwarding_rule(f) for f in
-                                         response['items']]
-        return list_forwarding_rules
-
-    def list_images(self, ex_project=None, ex_include_deprecated=False):
-        """
-        Return a list of image objects. If no project is specified, a list of
-        all non-deprecated global and vendor images images is returned. By
-        default, only non-deprecated images are returned.
-
-        :keyword  ex_project: Optional alternate project name.
-        :type     ex_project: ``str``, ``list`` of ``str``, or ``None``
-
-        :keyword  ex_include_deprecated: If True, even DEPRECATED images will
-                                         be returned.
-        :type     ex_include_deprecated: ``bool``
-
-        :return:  List of GCENodeImage objects
-        :rtype:   ``list`` of :class:`GCENodeImage`
-        """
-        dep = ex_include_deprecated
-        if ex_project is not None:
-            return self.ex_list_project_images(ex_project=ex_project,
-                                               ex_include_deprecated=dep)
-        image_list = self.ex_list_project_images(ex_project=None,
-                                                 ex_include_deprecated=dep)
-        for img_proj in list(self.IMAGE_PROJECTS.keys()):
-            try:
-                image_list.extend(
-                    self.ex_list_project_images(ex_project=img_proj,
-                                                ex_include_deprecated=dep))
-            except:
-                # do not break if an OS type is invalid
-                pass
-        return image_list
-
-    def ex_list_project_images(self, ex_project=None,
-                               ex_include_deprecated=False):
-        """
-        Return a list of image objects for a project. If no project is
-        specified, only a list of 'global' images is returned.
-
-        :keyword  ex_project: Optional alternate project name.
-        :type     ex_project: ``str``, ``list`` of ``str``, or ``None``
-
-        :keyword  ex_include_deprecated: If True, even DEPRECATED images will
-                                         be returned.
-        :type     ex_include_deprecated: ``bool``
-
-        :return:  List of GCENodeImage objects
-        :rtype:   ``list`` of :class:`GCENodeImage`
-        """
-        list_images = []
-        request = '/global/images'
-        if ex_project is None:
-            response = self.connection.request(request, method='GET').object
-            for img in response.get('items', []):
-                if 'deprecated' not in img:
-                    list_images.append(self._to_node_image(img))
-                else:
-                    if ex_include_deprecated:
-                        list_images.append(self._to_node_image(img))
-        else:
-            list_images = []
-            # Save the connection request_path
-            save_request_path = self.connection.request_path
-            if isinstance(ex_project, str):
-                ex_project = [ex_project]
-            for proj in ex_project:
-                # Override the connection request path
-                new_request_path = save_request_path.replace(self.project,
-                                                             proj)
-                self.connection.request_path = new_request_path
-                try:
-                    response = self.connection.request(request,
-                                                       method='GET').object
-                except:
-                    raise
-                finally:
-                    # Restore the connection request_path
-                    self.connection.request_path = save_request_path
-                for img in response.get('items', []):
-                    if 'deprecated' not in img:
-                        list_images.append(self._to_node_image(img))
-                    else:
-                        if ex_include_deprecated:
-                            list_images.append(self._to_node_image(img))
-        return list_images
-
-    def list_locations(self):
-        """
-        Return a list of locations (zones).
-
-        The :class:`ex_list_zones` method returns more comprehensive results,
-        but this is here for compatibility.
-
-        :return: List of NodeLocation objects
-        :rtype: ``list`` of :class:`NodeLocation`
-        """
-        list_locations = []
-        request = '/zones'
-        response = self.connection.request(request, method='GET').object
-        list_locations = [self._to_node_location(l) for l in response['items']]
-        return list_locations
-
-    def ex_list_routes(self):
-        """
-        Return the list of routes.
-
-        :return: A list of route objects.
-        :rtype: ``list`` of :class:`GCERoute`
-        """
-        list_routes = []
-        request = '/global/routes'
-        response = self.connection.request(request, method='GET').object
-        list_routes = [self._to_route(n) for n in
-                       response.get('items', [])]
-        return list_routes
-
-    def ex_list_networks(self):
-        """
-        Return the list of networks.
-
-        :return: A list of network objects.
-        :rtype: ``list`` of :class:`GCENetwork`
-        """
-        list_networks = []
-        request = '/global/networks'
-        response = self.connection.request(request, method='GET').object
-        list_networks = [self._to_network(n) for n in
-                         response.get('items', [])]
-        return list_networks
-
-    def list_nodes(self, ex_zone=None):
-        """
-        Return a list of nodes in the current zone or all zones.
-
-        :keyword  ex_zone:  Optional zone name or 'all'
-        :type     ex_zone:  ``str`` or :class:`GCEZone` or
-                            :class:`NodeLocation` or ``None``
-
-        :return:  List of Node objects
-        :rtype:   ``list`` of :class:`Node`
-        """
-        list_nodes = []
-        zone = self._set_zone(ex_zone)
-        if zone is None:
-            request = '/aggregated/instances'
-        else:
-            request = '/zones/%s/instances' % (zone.name)
-
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated response returns a dict for each zone
-            if zone is None:
-                for v in response['items'].values():
-                    for i in v.get('instances', []):
-                        try:
-                            list_nodes.append(self._to_node(i))
-                        # If a GCE node has been deleted between
-                        #   - is was listed by `request('.../instances', 'GET')
-                        #   - it is converted by `self._to_node(i)`
-                        # `_to_node()` will raise a ResourceNotFoundError.
-                        #
-                        # Just ignore that node and return the list of the
-                        # other nodes.
-                        except ResourceNotFoundError:
-                            pass
-            else:
-                for i in response['items']:
-                    try:
-                        list_nodes.append(self._to_node(i))
-                    # If a GCE node has been deleted between
-                    #   - is was listed by `request('.../instances', 'GET')
-                    #   - it is converted by `self._to_node(i)`
-                    # `_to_node()` will raise a ResourceNotFoundError.
-                    #
-                    # Just ignore that node and return the list of the
-                    # other nodes.
-                    except ResourceNotFoundError:
-                        pass
-        return list_nodes
-
-    def ex_list_regions(self):
-        """
-        Return the list of regions.
-
-        :return: A list of region objects.
-        :rtype: ``list`` of :class:`GCERegion`
-        """
-        list_regions = []
-        request = '/regions'
-        response = self.connection.request(request, method='GET').object
-        list_regions = [self._to_region(r) for r in response['items']]
-        return list_regions
-
-    def list_sizes(self, location=None):
-        """
-        Return a list of sizes (machineTypes) in a zone.
-
-        :keyword  location: Location or Zone for sizes
-        :type     location: ``str`` or :class:`GCEZone` or
-                            :class:`NodeLocation` or ``None``
-
-        :return:  List of GCENodeSize objects
-        :rtype:   ``list`` of :class:`GCENodeSize`
-        """
-        list_sizes = []
-        zone = self._set_zone(location)
-        if zone is None:
-            request = '/aggregated/machineTypes'
-        else:
-            request = '/zones/%s/machineTypes' % (zone.name)
-
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated response returns a dict for each zone
-            if zone is None:
-                for v in response['items'].values():
-                    zone_sizes = [self._to_node_size(s) for s in
-                                  v.get('machineTypes', [])]
-                    list_sizes.extend(zone_sizes)
-            else:
-                list_sizes = [self._to_node_size(s) for s in response['items']]
-        return list_sizes
-
-    def ex_list_snapshots(self):
-        """
-        Return the list of disk snapshots in the project.
-
-        :return:  A list of snapshot objects
-        :rtype:   ``list`` of :class:`GCESnapshot`
-        """
-        list_snapshots = []
-        request = '/global/snapshots'
-        response = self.connection.request(request, method='GET').object
-        list_snapshots = [self._to_snapshot(s) for s in
-                          response.get('items', [])]
-        return list_snapshots
-
-    def ex_list_targethttpproxies(self):
-        """
-        Return the list of target HTTP proxies.
-
-        :return:  A list of target http proxy objects
-        :rtype:   ``list`` of :class:`GCETargetHttpProxy`
-        """
-        request = '/global/targetHttpProxies'
-        response = self.connection.request(request, method='GET').object
-        return [self._to_targethttpproxy(u) for u in
-                response.get('items', [])]
-
-    def ex_list_targetinstances(self, zone=None):
-        """
-        Return the list of target instances.
-
-        :return:  A list of target instance objects
-        :rtype:   ``list`` of :class:`GCETargetInstance`
-        """
-        list_targetinstances = []
-        zone = self._set_zone(zone)
-        if zone is None:
-            request = '/aggregated/targetInstances'
-        else:
-            request = '/zones/%s/targetInstances' % (zone.name)
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated result returns dictionaries for each region
-            if zone is None:
-                for v in response['items'].values():
-                    zone_targetinstances = [self._to_targetinstance(t) for t in
-                                            v.get('targetInstances', [])]
-                    list_targetinstances.extend(zone_targetinstances)
-            else:
-                list_targetinstances = [self._to_targetinstance(t) for t in
-                                        response['items']]
-        return list_targetinstances
-
-    def ex_list_targetpools(self, region=None):
-        """
-        Return the list of target pools.
-
-        :return:  A list of target pool objects
-        :rtype:   ``list`` of :class:`GCETargetPool`
-        """
-        list_targetpools = []
-        region = self._set_region(region)
-        if region is None:
-            request = '/aggregated/targetPools'
-        else:
-            request = '/regions/%s/targetPools' % (region.name)
-        response = self.connection.request(request, method='GET').object
-
-        if 'items' in response:
-            # The aggregated result returns dictionaries for each region
-            if region is None:
-                for v in response['items'].values():
-                    region_targetpools = [self._to_targetpool(t) for t in
-                                          v.get('targetPools', [])]
-                    list_targetpools.extend(region_targetpools)
-            else:
-                list_targetpools = [self._to_targetpool(t) for t in
-                                    response['items']]
-        return list_targetpools
-
-    def ex_list_urlmaps(self):
-        """
-        Return the list of URL Maps in the project.
-
-        :return:  A list of url map objects
-        :rtype:   ``list`` of :class:`GCEUrlMap`
-        """
-        request = '/global/urlMaps'
-        response = self.connection.request(request, method='GET').object
-        return [self._to_urlmap(u) for u in response.get('items', [])]
-
-    def list_volumes(self, ex_zone=None):
-        """
-        Return a list of volumes for a zone or all.
-
-        Will return list from provided zone, or from the default zone unless
-        given the value of 'all'.
-
-        :keyword  ex_zone: The zone to return volumes from.
-        :type     ex_zone: ``str`` or :class:`GCEZone` or
-                            :class:`NodeLocation` or ``None``
-
-        :return: A list of volume objects.
-        :rtype: ``list`` of :class:`StorageVolume`
-        """
-        list_volumes = []
-        zone = self._set_zone(ex_zone)
-        if zone is None:
-            request = '/aggregated/disks'
-        else:
-            request = '/zones/%s/disks' % (zone.name)
-
-        response = self.connection.request(request, method='GET').object
-        if 'items' in response:
-            # The aggregated response returns a dict for each zone
-            if zone is None:
-                for v in response['items'].values():
-                    zone_volumes = [self._to_storage_volume(d) for d in
-                                    v.get('disks', [])]
-                    list_volumes.extend(zone_volumes)
-            else:
-                list_volumes = [self._to_storage_volume(d) for d in
-                                response['items']]
-        return list_volumes
-
-    def ex_list_zones(self):
-        """
-        Return the list of zones.
-
-        :return: A list of zone objects.
-        :rtype: ``list`` of :class:`GCEZone`
-        """
-        list_zones = []
-        request = '/zones'
-        response = self.connection.request(request, method='GET').object
-        list_zones = [self._to_zone(z) for z in response['items']]
-        return list_zones
-
-    def ex_create_address(self, name, region=None, address=None,
-                          description=None):
-        """
-        Create a static address in a region, or a global address.
-
-        :param  name: Name of static address
-        :type   name: ``str``
-
-        :keyword  region: Name of region for the address (e.g. 'us-central1')
-                          Use 'global' to create a global address.
-        :type     region: ``str`` or :class:`GCERegion`
-
-        :keyword  address: Ephemeral IP address to promote to a static one
-                           (e.g. 'xxx.xxx.xxx.xxx')
-        :type     address: ``str`` or ``None``
-
-        :keyword  description: Optional descriptive comment.
-        :type     description: ``str`` or ``None``
-
-        :return:  Static Address object
-        :rtype:   :class:`GCEAddress`
-        """
-        region = region or self.region
-        if region != 'global' and not hasattr(region, 'name'):
-            region = self.ex_get_region(region)
-        elif region is None:
-            raise ValueError('REGION_NOT_SPECIFIED',
-                             'Region must be provided for an address')
-        address_data = {'name': name}
-        if address:
-            address_data['address'] = address
-        if description:
-            address_data['description'] = description
-        if region == 'global':
-            request = '/global/addresses'
-        else:
-            request = '/regions/%s/addresses' % (region.name)
-        self.connection.async_request(request, method='POST',
-                                      data=address_data)
-        return self.ex_get_address(name, region=region)
-
-    def ex_create_backendservice(self, name, healthchecks):
-        """
-        Create a global backend service.
-
-        :param  name: Name of the backend service
-        :type   name: ``str``
-
-        :keyword  healthchecks: A list of HTTP Health Checks to use for this
-                                service.  There must be at least one.
-        :type     healthchecks: ``list`` of (``str`` or
-                                :class:`GCEHealthCheck`)
-
-        :return:  A Backend Service object
-        :rtype:   :class:`GCEBackendService`
-        """
-        backendservice_data = {'name': name, 'healthChecks': []}
-
-        for hc in healthchecks:
-            if not hasattr(hc, 'extra'):
-                hc = self.ex_get_healthcheck(name=hc)
-            backendservice_data['healthChecks'].append(hc.extra['selfLink'])
-
-        request = '/global/backendServices'
-        self.connection.async_request(request, method='POST',
-                                      data=backendservice_data)
-        return self.ex_get_backendservice(name)
-
-    def ex_create_healthcheck(self, name, host=None, path=None, port=None,
-                              interval=None, timeout=None,
-                              unhealthy_threshold=None,
-                              healthy_threshold=None,
-                              description=None):
-        """
-        Create an Http Health Check.
-
-        :param  name: Name of health check
-        :type   name: ``str``
-
-        :keyword  host: Hostname of health check request.  Defaults to empty
-                        and public IP is used instead.
-        :type     host: ``str``
-
-        :keyword  path: The request path for the check.  Defaults to /.
-        :type     path: ``str``
-
-        :keyword  port: The TCP port number for the check.  Defaults to 80.
-        :type     port: ``int``
-
-        :keyword  interval: How often (in seconds) to check.  Defaults to 5.
-        :type     interval: ``int``
-
-        :keyword  timeout: How long to wait before failing. Defaults to 5.
-        :type     timeout: ``int``
-
-        :keyword  unhealthy_threshold: How many failures before marking
-                                       unhealthy.  Defaults to 2.
-        :type     unhealthy_threshold: ``int``
-
-        :keyword  healthy_threshold: How many successes before marking as
-                                     healthy.  Defaults to 2.
-        :type     healthy_threshold: ``int``
-
-        :keyword  description: The description of the check.  Defaults to None.
-        :type     description: ``str`` or ``None``
-
-        :return:  Health Check object
-        :rtype:   :class:`GCEHealthCheck`
-        """
-        hc_data = {}
-        hc_data['name'] = name
-        if host:
-            hc_data['host'] = host
-        if description:
-            hc_data['description'] = description
-        # As of right now, the 'default' values aren't getting set when called
-        # through the API, so set them explicitly
-        hc_data['requestPath'] = path or '/'
-        hc_data['port'] = port or 80
-        hc_data['checkIntervalSec'] = interval or 5
-        hc_data['timeoutSec'] = timeout or 5
-        hc_data['unhealthyThreshold'] = unhealthy_threshold or 2
-        hc_data['healthyThreshold'] = healthy_threshold or 2
-
-        request = '/global/httpHealthChecks'
-
-        self.connection.async_request(request, method='POST', data=hc_data)
-        return self.ex_get_healthcheck(name)
-
-    def ex_create_firewall(self, name, allowed, network='default',
-                           source_ranges=None, source_tags=None,
-                           target_tags=None):
-        """
-        Create a firewall on a network.
-
-        Firewall rules should be supplied in the "allowed" field.  This is a
-        list of dictionaries formated like so ("ports" is optional)::
-
-            [{"IPProtocol": "<protocol string or number>",
-              "ports": "<port_numbers or ranges>"}]
-
-        For example, to allow tcp on port 8080 and udp on all ports, 'allowed'
-        would be::
-
-            [{"IPProtocol": "tcp",
-              "ports": ["8080"]},
-             {"IPProtocol": "udp"}]
-
-        See `Firewall Reference <https://developers.google.com/compute/docs/
-        reference/latest/firewalls/insert>`_ for more information.
-
-        :param  name: Name of the firewall to be created
-        :type   name: ``str``
-
-        :param  allowed: List of dictionaries with rules
-        :type   allowed: ``list`` of ``dict``
-
-        :keyword  network: The network that the firewall applies to.
-        :type     network: ``str`` or :class:`GCENetwork`
-
-        :keyword  source_ranges: A list of IP ranges in CIDR format that the
-                                 firewall should apply to. Defaults to
-                                 ['0.0.0.0/0']
-        :type     source_ranges: ``list`` of ``str``
-
-        :keyword  source_tags: A list of source instance tags the rules apply
-                               to.
-        :type     source_tags: ``list`` of ``str``
-
-        :keyword  target_tags: A list of target instance tags the rules apply
-                               to.
-        :type     target_tags: ``list`` of ``str``
-
-        :return:  Firewall object
-        :rtype:   :class:`GCEFirewall`
-        """
-        firewall_data = {}
-        if not hasattr(network, 'name'):
-            nw = self.ex_get_network(network)
-        else:
-            nw = network
-
-        firewall_data['name'] = name
-        firewall_data['allowed'] = allowed
-        firewall_data['network'] = nw.extra['selfLink']
-        if source_ranges is None and source_tags is None:
-            source_ranges = ['0.0.0.0/0']
-        if source_ranges is not None:
-            firewall_data['sourceRanges'] = source_ranges
-        if source_tags is not None:
-            firewall_data['sourceTags'] = source_tags
-        if target_tags is not None:
-            firewall_data['targetTags'] = target_tags
-
-        request = '/global/firewalls'
-
-        self.connection.async_request(request, method='POST',
-                                      data=firewall_data)
-        return self.ex_get_firewall(name)
-
-    def ex_create_forwarding_rule(self, name, target=None, region=None,
-                                  protocol='tcp', port_range=None,
-                                  address=None, description=None,
-                                  global_rule=False, targetpool=None):
-        """
-        Create a forwarding rule.
-
-        :param  name: Name of forwarding rule to be created
-        :type   name: ``str``
-
-        :keyword  target: The target of this forwarding rule.  For global
-                          forwarding rules this must be a global
-                          TargetHttpProxy. For regional rules this may be
-                          either a TargetPool or TargetInstance. If passed
-                          a string instead of the object, it will be the name
-                          of a TargetHttpProxy for global rules or a
-                          TargetPool for regional rules.  A TargetInstance
-                          must be passed by object. (required)
-        :type     target: ``str`` or :class:`GCETargetHttpProxy` or
-                          :class:`GCETargetInstance` or :class:`GCETargetPool`
-
-        :keyword  region: Region to create the forwarding rule in.  Defaults to
-                          self.region.  Ignored if global_rule is True.
-        :type     region: ``str`` or :class:`GCERegion`
-
-        :keyword  protocol: Should be 'tcp' or 'udp'
-        :type     protocol: ``str``
-
-        :keyword  port_range: Single port number or range separated by a dash.
-                              Examples: '80', '5000-5999'.  Required for global
-                              forwarding rules, optional for regional rules.
-        :type     port_range: ``str``
-
-        :keyword  address: Optional static address for forwarding rule. Must be
-                           in same region.
-        :type     address: ``str`` or :class:`GCEAddress`
-
-        :keyword  description: The description of the forwarding rule.
-                               Defaults to None.
-        :type     description: ``str`` or ``None``
-
-        :keyword  targetpool: Deprecated parameter for backwards compatibility.
-                              Use target instead.
-        :type     targetpool: ``str`` or :class:`GCETargetPool`
-
-        :return:  Forwarding Rule object
-        :rtype:   :class:`GCEForwardingRule`
-        """
-        forwarding_rule_data = {'name': name}
-        if global_rule:
-            if not hasattr(target, 'name'):
-                target = self.ex_get_targethttpproxy(target)
-        else:
-            region = region or self.region
-            if not hasattr(region, 'name'):
-                region = self.ex_get_region(region)
-            forwarding_rule_data['region'] = region.extra['selfLink']
-
-            if not target:
-                target = targetpool  # Backwards compatibility
-            if not hasattr(target, 'name'):
-                target = self.ex_get_targetpool(target, region)
-
-        forwarding_rule_data['target'] = target.extra['selfLink']
-        forwarding_rule_data['IPProtocol'] = protocol.upper()
-        if address:
-            if not hasattr(address, 'name'):
-                address = self.ex_get_address(
-                    address, 'global' if global_rule else region)
-            forwarding_rule_data['IPAddress'] = address.address
-        if port_range:
-            forwarding_rule_data['portRange'] = port_range
-        if description:
-            forwarding_rule_data['description'] = description
-
-        if global_rule:
-            request = '/global/forwardingRules'
-        else:
-            request = '/regions/%s/forwardingRules' % (region.name)
-
-        self.connection.async_request(request, method='POST',
-                                      data=forwarding_rule_data)
-
-        return self.ex_get_forwarding_rule(name, global_rule=global_rule)
-
-    def ex_create_image(self, name, volume, description=None, family=None,
-                        use_existing=True, wait_for_completion=True):
-        """
-        Create an image from the provided volume.
-
-        :param  name: The name of the image to create.
-        :type   name: ``str``
-
-        :param  volume: The volume to use to create the image, or the
-                        Google Cloud Storage URI
-        :type   volume: ``str`` or :class:`StorageVolume`
-
-        :keyword    description: Description of the new Image
-        :type       description: ``str``
-
-        :keyword    family: The name of the image family to which this image
-                            belongs. If you create resources by specifying an
-                            image family instead of a specific image name, the
-                            resource uses the latest non-deprecated image that
-                            is set with that family name.
-        :type       family: ``str``
-
-        :keyword  use_existing: If True and an image with the given name
-                                already exists, return an object for that
-                                image instead of attempting to create
-                                a new image.
-        :type     use_existing: ``bool``
-
-        :keyword  wait_for_completion: If True, wait until the new image is
-                                       created before returning a new NodeImage
-                                       Otherwise, return a new NodeImage
-                                       instance, and let the user track the
-                                       creation progress
-        :type     wait_for_completion: ``bool``
-
-        :return:    A GCENodeImage object for the new image
-        :rtype:     :class:`GCENodeImage`
-
-        """
-        image_data = {}
-        image_data['name'] = name
-        image_data['description'] = description
-        image_data['family'] = family
-        if isinstance(volume, StorageVolume):
-            image_data['sourceDisk'] = volume.extra['selfLink']
-            image_data['zone'] = volume.extra['zone'].name
-        elif (isinstance(volume, str) and
-              volume.startswith('https://') and
-              volume.endswith('tar.gz')):
-            image_data['rawDisk'] = {'source': volume, 'containerType': 'TAR'}
-        else:
-            raise ValueError('Source must be instance of StorageVolume or URI')
-
-        request = '/global/images'
-
-        try:
-            if wait_for_completion:
-                self.connection.async_request(request, method='POST',
-                                              data=image_data)
-            else:
-                self.connection.request(request, method='POST',
-                                        data=image_data)
-
-        except ResourceExistsError:
-            e = sys.exc_info()[1]
-            if not use_existing:
-                raise e
-
-        return self.ex_get_image(name)
-
-    def ex_create_route(self, name, dest_range, priority=500,
-                        network="default", tags=None, next_hop=None,
-                        description=None):
-        """
-        Create a route.
-
-        :param  name: Name of route to be created
-        :type   name: ``str``
-
-        :param  dest_range: Address range of route in CIDR format.
-        :type   dest_range: ``str``
-
-        :param  priority: Priority value, lower values take precedence
-        :type   priority: ``int``
-
-        :param  network: The network the route belongs to. Can be either the
-                         full URL of the network or a libcloud object.
-        :type   network: ``str`` or ``GCENetwork``
-
-        :param  tags: List of instance-tags for routing, empty for all nodes
-        :type   tags: ``list`` of ``str`` or ``None``
-
-        :param  next_hop: Next traffic hop. Use ``None`` for the default
-                          Internet gateway, or specify an instance or IP
-                          address.
-        :type   next_hop: ``str``, ``Node``, or ``None``
-
-        :param  description: Custom description for the route.
-        :type   description: ``str`` or ``None``
-
-        :return:  Route object
-        :rtype:   :class:`GCERoute`
-        """
-        route_data = {}
-        route_data['name'] = name
-        route_data['destRange'] = dest_range
-        route_data['priority'] = priority
-        route_data['description'] = description
-        if isinstance(network, str) and network.startswith('https://'):
-            network_uri = network
-        elif isinstance(network, str):
-            network = self.ex_get_network(network)
-            network_uri = network.extra['selfLink']
-        else:
-            network_uri = network.extra['selfLink']
-        route_data['network'] = network_uri
-        route_data['tags'] = tags
-        if next_hop is None:
-            url = 'https://www.googleapis.com/compute/%s/projects/%s/%s' % (
-                  API_VERSION, self.project,
-                  "global/gateways/default-internet-gateway")
-            route_data['nextHopGateway'] = url
-        elif isinstance(next_hop, str):
-            route_data['nextHopIp'] = next_hop
-        else:
-            route_data['nextHopInstance'] = next_hop.extra['selfLink']
-
-        request = '/global/routes'
-        self.connection.async_request(request, method='POST',
-                                      data=route_data)
-
-        return self.ex_get_route(name)
-
-    def ex_create_network(self, name, cidr, description=None):
-        """
-        Create a network.
-
-        :param  name: Name of network to be created
-        :type   name: ``str``
-
-        :param  cidr: Address range of network in CIDR format.
-        :type   cidr: ``str``
-
-        :param  description: Custom description for the network.
-        :type   description: ``str`` or ``None``
-
-        :return:  Network object
-        :rtype:   :class:`GCENetwork`
-        """
-        network_data = {}
-        network_data['name'] = name
-        network_data['IPv4Range'] = cidr
-        network_data['description'] = description
-
-        request = '/global/networks'
-
-        self.connection.async_request(request, method='POST',
-                                      data=network_data)
-
-        return self.ex_get_network(name)
-
-    def create_node(self, name, size, image, location=None,
-                    ex_network='default', ex_tags=None, ex_metadata=None,
-                    ex_boot_disk=None, use_existing_disk=True,
-                    external_ip='ephemeral', ex_disk_type='pd-standard',
-                    ex_disk_auto_delete=True, ex_service_accounts=None,
-                    description=None, ex_can_ip_forward=None,
-                    ex_disks_gce_struct=None, ex_nic_gce_struct=None,
-                    ex_on_host_maintenance=None, ex_automatic_restart=None,
-                    ex_preemptible=None):
-        """
-        Create a new node and return a node object for the node.
-
-        :param  name: The name of the node to create.
-        :type   name: ``str``
-
-        :param  size: The machine type to use.
-        :type   size: ``str`` or :class:`GCENodeSize`
-
-        :param  image: The image to use to create the node (or, if attaching
-                       a persistent disk, the image used to create the disk)
-        :type   image: ``str`` or :class:`GCENodeImage` or ``None``
-
-        :keyword  location: The location (zone) to create the node in.
-        :type     location: ``str`` or :class:`NodeLocation` or
-                            :class:`GCEZone` or ``None``
-
-        :keyword  ex_network: The network to associate with the node.
-        

<TRUNCATED>