You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by er...@apache.org on 2016/10/13 15:14:24 UTC

[2/4] libcloud git commit: [GCE] Added support for HTTP(S) proxies with BackendServices

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cffd9642/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py
index 6b00491..7eaffd3 100644
--- a/libcloud/compute/drivers/gce.py
+++ b/libcloud/compute/drivers/gce.py
@@ -318,6 +318,77 @@ class GCEAddress(UuidMixin):
             (hasattr(self.region, "name") and self.region.name or self.region))
 
 
+class GCEBackend(UuidMixin):
+    """A GCE Backend.  Only used for creating Backend Services."""
+
+    def __init__(self, instance_group, balancing_mode='UTILIZATION',
+                 max_utilization=None, max_rate=None,
+                 max_rate_per_instance=None, capacity_scaler=1,
+                 description=None):
+
+        if isinstance(instance_group, GCEInstanceGroup):
+            self.instance_group = instance_group
+        elif isinstance(instance_group, GCEInstanceGroupManager):
+            self.instance_group = instance_group.instance_group
+        else:
+            raise ValueError('instance_group must be of type GCEInstanceGroup'
+                             'or of type GCEInstanceGroupManager')
+
+        self.instance_group = instance_group
+        self.balancing_mode = balancing_mode
+        self.max_utilization = max_utilization
+        self.max_rate = max_rate
+        self.max_rate_per_instance = max_rate_per_instance
+        self.capacity_scaler = capacity_scaler
+
+        # 'id' and 'name' aren't actually used or provided by the GCE API.
+        # We create them for convenience.
+        self.id = self._gen_id()
+        self.name = self.id
+
+        self.description = description or self.name
+        UuidMixin.__init__(self)
+
+    def _gen_id(self):
+        """
+        Use the Instance Group information to fill in name and id fields.
+
+        :return: id in the format of:
+                 ZONE/instanceGroups/INSTANCEGROUPNAME
+                 Ex: us-east1-c/instanceGroups/my-instance-group
+        :rtype:  ``str``
+        """
+        zone_name = self.instance_group.zone.name
+        return "%s/instanceGroups/%s" % (zone_name, self.instance_group.name)
+
+    def to_backend_dict(self):
+        """
+        Returns dict formatted for inclusion in Backend Service Request.
+
+        :return: dict formatted as a list entry for Backend Service 'backend'.
+        :rtype: ``dict``
+        """
+        d = {}
+        d['group'] = self.instance_group.extra['selfLink']
+
+        if self.balancing_mode:
+            d['balancingMode'] = self.balancing_mode
+        if self.max_utilization:
+            d['maxUtilization'] = self.max_utilization
+        if self.max_rate:
+            d['maxRate'] = self.max_rate
+        if self.max_rate_per_instance:
+            d['maxRatePerInstance'] = self.max_rate_per_instance
+        if self.capacity_scaler:
+            d['capacityScaler'] = self.capacity_scaler
+
+        return d
+
+    def __repr__(self):
+        return '<GCEBackend instancegroup="%s" balancing_mode="%s">' % (
+            self.id, self.balancing_mode)
+
+
 class GCEBackendService(UuidMixin):
     """A GCE Backend Service."""
 
@@ -525,6 +596,67 @@ class GCENodeImage(NodeImage):
                                               deprecated, obsolete, deleted)
 
 
+class GCESslCertificate(UuidMixin):
+    """ GCESslCertificate represents the SslCertificate resource. """
+
+    def __init__(self, id, name, certificate, driver, extra, private_key=None,
+                 description=None):
+        """
+        :param  name:  Name of the resource. Provided by the client when the
+                       resource is created. The name must be 1-63 characters
+                       long, and comply with RFC1035. Specifically, the name
+                       must be 1-63 characters long and match the regular
+                       expression [a-z]([-a-z0-9]*[a-z0-9])? which means the
+                       first character must be a lowercase letter, and all
+                       following characters must be a dash, lowercase letter,
+                       or digit, except the last character, which cannot be a
+                       dash.
+        :type   name: ``str``
+
+        :param  certificate:  A local certificate file. The certificate must
+                              be in PEM format. The certificate chain must be
+                              no greater than 5 certs long. The chain must
+                              include at least one intermediate cert.
+        :type   certificate: ``str``
+
+        :param  private_key:  A write-only private key in PEM format. Only
+                              insert RPCs will include this field.
+        :type   private_key: ``str``
+
+        :keyword  description:  An optional description of this resource.
+                              Provide this property when you create the
+                              resource.
+        :type   description: ``str``
+
+        :keyword  driver:  An initialized :class: `GCENodeDriver`
+        :type   driver: :class:`:class: `GCENodeDriver``
+
+        :keyword  extra:  A dictionary of extra information.
+        :type   extra: ``:class: ``dict````
+
+        """
+
+        self.name = name
+        self.certificate = certificate
+        self.private_key = private_key
+        self.description = description
+        self.driver = driver
+        self.extra = extra
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return '<GCESslCertificate name="%s">' % (self.name)
+
+    def destroy(self):
+        """
+        Destroy this SslCertificate.
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_destroy_sslcertificate(sslcertificate=self)
+
+
 class GCESubnetwork(UuidMixin):
     """A GCE Subnetwork object class."""
 
@@ -710,8 +842,8 @@ class GCESnapshot(VolumeSnapshot):
     def __init__(self, id, name, size, status, driver, extra=None,
                  created=None):
         self.status = status
-        super(GCESnapshot, self).__init__(id, driver, size, extra,
-                                          created, name=name)
+        super(GCESnapshot, self).__init__(id, driver, size, extra, created,
+                                          name=name)
 
 
 class GCETargetHttpProxy(UuidMixin):
@@ -736,6 +868,109 @@ class GCETargetHttpProxy(UuidMixin):
         return self.driver.ex_destroy_targethttpproxy(targethttpproxy=self)
 
 
+class GCETargetHttpsProxy(UuidMixin):
+    """ GCETargetHttpsProxy represents the TargetHttpsProxy resource. """
+
+    def __init__(self, id, name, description=None, sslcertificates=None,
+                 urlmap=None, driver=None, extra=None):
+        """
+        :param  name:  Name of the resource. Provided by the client when the
+                       resource is created. The name must be 1-63 characters
+                       long, and comply with RFC1035. Specifically, the name
+                       must be 1-63 characters long and match the regular
+                       expression [a-z]([-a-z0-9]*[a-z0-9])? which means the
+                       first character must be a lowercase letter, and all
+                       following characters must be a dash, lowercase letter,
+                       or digit, except the last character, which cannot be a
+                       dash.
+        :type   name: ``str``
+
+        :param  description:  An optional description of this resource.
+                              Provide this property when you create the
+                              resource.
+        :type   description: ``str``
+
+        :param  sslcertificates:  URLs to SslCertificate resources that are
+                                   used to authenticate connections between
+                                   users and the load balancer. Currently,
+                                   exactly one SSL certificate must be
+                                   specified.
+        :type   sslcertificates: ``list`` of :class:`GCESslcertificates`
+
+        :param  urlmap:  A fully-qualified or valid partial URL to the
+                          UrlMap resource that defines the mapping from URL
+                          to the BackendService. For example, the following
+                          are all valid URLs for specifying a URL map:   - ht
+                          tps://www.googleapis.compute/v1/projects/project/gl
+                          obal/urlMaps/url-map  -
+                          projects/project/global/urlMaps/url-map  -
+                          global/urlMaps/url-map
+        :type   urlmap: :class:`GCEUrlMap`
+
+        :keyword  driver:  An initialized :class: `GCENodeDriver`
+        :type   driver: :class:`:class: `GCENodeDriver``
+
+        :keyword  extra:  A dictionary of extra information.
+        :type   extra: ``:class: ``dict````
+
+        """
+
+        self.name = name
+        self.description = description
+        self.sslcertificates = sslcertificates
+        self.urlmap = urlmap
+        self.driver = driver
+        self.extra = extra
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return '<GCETargetHttpsProxy name="%s">' % (self.name)
+
+    def set_sslcertificates(self, sslcertificates):
+        """
+        Set the SSL Certificates for this TargetHTTPSProxy
+
+        :param  sslcertificates: SSL Certificates to set.
+        :type   sslcertificates: ``list`` of :class:`GCESslCertificate`
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        return self.driver.ex_targethttpsproxy_set_sslcertificates(
+            targethttpsproxy=self, sslcertificates=sslcertificates)
+
+    def set_urlmap(self, urlmap):
+        """
+        Changes the URL map for TargetHttpsProxy.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  targethttpsproxy:  Name of the TargetHttpsProxy resource
+                                   whose URL map is to be set.
+        :type   targethttpsproxy: ``str``
+
+        :param  urlmap:  UrlMap to set.
+        :type   urlmap: :class:`GCEUrlMap`
+
+        :return:  True
+        :rtype: ``bool``
+        """
+
+        return self.driver.ex_targethttpsproxy_set_urlmap(
+            targethttpsproxy=self, urlmap=urlmap)
+
+    def destroy(self):
+        """
+        Destroy this TargetHttpsProxy.
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_destroy_targethttpsproxy(targethttpsproxy=self)
+
+
 class GCETargetInstance(UuidMixin):
     def __init__(self, id, name, zone, node, driver, extra=None):
         self.id = str(id)
@@ -803,12 +1038,21 @@ class GCEInstanceTemplate(UuidMixin):
             self.id, self.name, self.extra['properties'].get('machineType',
                                                              'UNKNOWN'))
 
+    def destroy(self):
+        """
+        Destroy this InstanceTemplate.
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_destroy_instancetemplate(instancetemplate=self)
+
 
 class GCEInstanceGroup(UuidMixin):
     """ GCEInstanceGroup represents the InstanceGroup resource. """
 
-    def __init__(self, id, name, zone, driver, extra=None, description=None,
-                 network=None, subnetwork=None, named_ports=None):
+    def __init__(self, id, name, zone, driver, extra=None, network=None,
+                 subnetwork=None, named_ports=None):
         """
         :param  name:  Required. The name of the instance group. The name
                        must be 1-63 characters long, and comply with RFC1035.
@@ -818,11 +1062,6 @@ class GCEInstanceGroup(UuidMixin):
                        located.
         :type   zone: :class:`GCEZone`
 
-        :param  description:  An optional description of this resource.
-                              Provide this property when you create the
-                              resource.
-        :type   description: ``str``
-
         :param  network:  The URL of the network to which all instances in
                           the instance group belong.
         :type   network: :class:`GCENetwork`
@@ -845,7 +1084,6 @@ class GCEInstanceGroup(UuidMixin):
 
         self.name = name
         self.zone = zone
-        self.description = description
         self.network = network
         self.subnetwork = subnetwork
         self.named_ports = named_ports
@@ -855,7 +1093,7 @@ class GCEInstanceGroup(UuidMixin):
 
     def __repr__(self):
         return '<GCEInstanceGroup name="%s" zone="%s">' % (self.name,
-                                                           self.zone)
+                                                           self.zone.name)
 
     def destroy(self):
         """
@@ -866,6 +1104,91 @@ class GCEInstanceGroup(UuidMixin):
         """
         return self.driver.ex_destroy_instancegroup(instancegroup=self)
 
+    def add_instances(self, node_list):
+        """
+        Adds a list of instances to the specified instance group. All of the
+        instances in the instance group must be in the same
+        network/subnetwork. Read  Adding instances for more information.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The Instance Group where you are
+                                adding instances.
+        :type   instancegroup: :class:``GCEInstanceGroup``
+
+        :param  node_list: List of nodes to add.
+        :type   node_list: ``list`` of :class:`Node` or ``list`` of
+                           :class:`GCENode`
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_instancegroup_add_instances(instancegroup=self,
+                                                          node_list=node_list)
+
+    def list_instances(self):
+        """
+        Lists the instances in the specified instance group.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+        * https://www.googleapis.com/auth/compute.readonly
+
+        :return:  List of :class:`GCENode` objects.
+        :rtype: ``list`` of :class:`GCENode` objects.
+        """
+        return self.driver.ex_instancegroup_list_instances(instancegroup=self)
+
+    def remove_instances(self, node_list):
+        """
+        Removes one or more instances from the specified instance group,
+        but does not delete those instances.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The Instance Group where you are
+                                removng instances.
+        :type   instancegroup: :class:``GCEInstanceGroup``
+
+        :param  node_list: List of nodes to add.
+        :type   node_list: ``list`` of :class:`Node` or ``list`` of
+                           :class:`GCENode`
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_instancegroup_remove_instances(
+            instancegroup=self, node_list=node_list)
+
+    def set_named_ports(self, named_ports):
+        """
+        Sets the named ports for the specified instance group.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  named_ports:  Assigns a name to a port number. For example:
+                              {name: "http", port: 80}  This allows the
+                              system to reference ports by the assigned name
+                              instead of a port number. Named ports can also
+                              contain multiple ports. For example: [{name:
+                              "http", port: 80},{name: "http", port: 8080}]
+                              Named ports apply to all instances in this
+                              instance group.
+        :type   named_ports: ``list`` of {'name': ``str``, 'port`: ``int``}
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_instancegroup_set_named_ports(
+            instancegroup=self, named_ports=named_ports)
+
 
 class GCEInstanceGroupManager(UuidMixin):
     """
@@ -982,6 +1305,30 @@ class GCEInstanceGroupManager(UuidMixin):
         return self.driver.ex_instancegroupmanager_resize(manager=self,
                                                           size=size)
 
+    def set_named_ports(self, named_ports):
+        """
+        Sets the named ports for the instance group controlled by this manager.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  named_ports:  Assigns a name to a port number. For example:
+                              {name: "http", port: 80}  This allows the
+                              system to reference ports by the assigned name
+                              instead of a port number. Named ports can also
+                              contain multiple ports. For example: [{name:
+                              "http", port: 80},{name: "http", port: 8080}]
+                              Named ports apply to all instances in this
+                              instance group.
+        :type   named_ports: ``list`` of {'name': ``str``, 'port`: ``int``}
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        return self.driver.ex_instancegroup_set_named_ports(
+            instancegroup=self.instance_group, named_ports=named_ports)
+
     def __repr__(self):
         return '<GCEInstanceGroupManager name="%s" zone="%s" size="%d">' % (
             self.name, self.zone.name, self.size)
@@ -1297,6 +1644,7 @@ class GCENodeDriver(NodeDriver):
         "windows-cloud": ["windows"],
     }
 
+    BACKEND_SERVICE_PROTOCOLS = ['HTTP', 'HTTPS', 'HTTP2', 'TCP', 'SSL']
     GUEST_OS_FEATURES = ['VIRTIO_SCSI_MULTIQUEUE', 'WINDOWS']
 
     def __init__(self, user_id, key=None, datacenter=None, project=None,
@@ -1858,6 +2206,26 @@ class GCENodeDriver(NodeDriver):
         list_routes = [self._to_route(n) for n in response.get('items', [])]
         return list_routes
 
+    def ex_list_sslcertificates(self):
+        """
+        Retrieves the list of SslCertificate resources available to the
+        specified project.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+        * https://www.googleapis.com/auth/compute.readonly
+
+        :return: A list of SSLCertificate objects.
+        :rtype: ``list`` of :class:`GCESslCertificate`
+        """
+        list_data = []
+        request = '/global/sslCertificates'
+        response = self.connection.request(request, method='GET').object
+        list_data = [self._to_sslcertificate(a)
+                     for a in response.get('items', [])]
+        return list_data
+
     def ex_list_subnetworks(self, region=None):
         """
         Return the list of subnetworks.
@@ -2029,6 +2397,18 @@ class GCENodeDriver(NodeDriver):
         response = self.connection.request(request, method='GET').object
         return [self._to_targethttpproxy(u) for u in response.get('items', [])]
 
+    def ex_list_targethttpsproxies(self):
+        """
+        Return the list of target HTTPs proxies.
+
+        :return:  A list of target https proxy objects
+        :rtype:   ``list`` of :class:`GCETargetHttpsProxy`
+        """
+        request = '/global/targetHttpsProxies'
+        response = self.connection.request(request, method='GET').object
+        return [self._to_targethttpsproxy(x)
+                for x in response.get('items', [])]
+
     def ex_list_targetinstances(self, zone=None):
         """
         Return the list of target instances.
@@ -2337,28 +2717,160 @@ class GCENodeDriver(NodeDriver):
                                       data=autoscaler_data)
         return self.ex_get_autoscaler(name, zone)
 
-    def ex_create_backendservice(self, name, healthchecks):
+    def ex_create_backend(self, instance_group, balancing_mode='UTILIZATION',
+                          max_utilization=None, max_rate=None,
+                          max_rate_per_instance=None, capacity_scaler=1,
+                          description=None):
+        """
+        Helper Object to create a backend.
+
+        :param  instance_group: The Instance Group for this Backend.
+        :type   instance_group: :class: `GCEInstanceGroup`
+
+        :param  balancing_mode: Specifies the balancing mode for this backend.
+                                For global HTTP(S) load balancing, the valid
+                                values are UTILIZATION (default) and RATE.
+                                For global SSL load balancing, the valid
+                                values are UTILIZATION (default) and
+                                CONNECTION.
+        :type   balancing_mode: ``str``
+
+        :param  max_utilization: Used when balancingMode is UTILIZATION.
+                                 This ratio defines the CPU utilization
+                                 target for the group. The default is 0.8.
+                                 Valid range is [0.0, 1.0].
+        :type   max_utilization: ``float``
+
+        :param  max_rate: The max requests per second (RPS) of the group.
+                          Can be used with either RATE or UTILIZATION balancing
+                          modes, but required if RATE mode. For RATE mode,
+                          either maxRate or maxRatePerInstance must be set.
+        :type   max_rate: ``int``
+
+        :param  max_rate_per_instance: The max requests per second (RPS) that
+                                       a single backend instance can handle.
+                                       This is used to calculate the capacity
+                                       of the group. Can be used in either
+                                       balancing mode. For RATE mode, either
+                                       maxRate or maxRatePerInstance must be
+                                       set.
+        :type   max_rate_per_instance: ``float``
+
+        :param  capacity_scaler: A multiplier applied to the group's maximum
+                                 servicing capacity (based on UTILIZATION,
+                                 RATE, or CONNECTION). Default value is 1,
+                                 which means the group will serve up to 100%
+                                 of its configured capacity (depending on
+                                 balancingMode). A setting of 0 means the
+                                 group is completely drained, offering 0%
+                                 of its available capacity. Valid range is
+                                 [0.0,1.0].
+        :type   capacity_scaler: ``float``
+
+        :param  description: An optional description of this resource.
+                             Provide this property when you create the
+                             resource.
+        :type   description: ``str``
+
+        :return: A GCEBackend object.
+        :rtype: :class: `GCEBackend`
+        """
+
+        return GCEBackend(
+            instance_group=instance_group, balancing_mode=balancing_mode,
+            max_utilization=max_utilization, max_rate=max_rate,
+            max_rate_per_instance=max_rate_per_instance,
+            capacity_scaler=capacity_scaler, description=description)
+
+    def ex_create_backendservice(self, name, healthchecks, backends=[],
+                                 protocol=None, description=None,
+                                 timeout_sec=None, enable_cdn=False, port=None,
+                                 port_name=None):
         """
         Create a global Backend Service.
 
-        :param    name: Name of the Backend Service
-        :type     name: ``str``
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  name:  Name of the resource. Provided by the client when the
+                       resource is created. The name must be 1-63 characters
+                       long, and comply with RFC1035. Specifically, the name
+                       must be 1-63 characters long and match the regular
+                       expression [a-z]([-a-z0-9]*[a-z0-9])? which means the
+                       first character must be a lowercase letter, and all
+                       following characters must be a dash, lowercase letter,
+                       or digit, except the last character, which cannot be a
+                       dash.
+        :type   name: ``str``
 
         :param    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`)
 
+        :keyword  backends:  The list of backends that serve this
+                             BackendService.
+        :type   backends: ``list`` of :class `GCEBackend` or list of ``dict``
+
+        :keyword  timeout_sec:  How many seconds to wait for the backend
+                                before considering it a failed request.
+                                Default is 30 seconds.
+        :type   timeout_sec: ``integer``
+
+        :keyword  enable_cdn:  If true, enable Cloud CDN for this
+                                 BackendService.  When the load balancing
+                                 scheme is INTERNAL, this field is not used.
+        :type   enable_cdn: ``bool``
+
+        :keyword  port:  Deprecated in favor of port_name. The TCP port to
+                         connect on the backend. The default value is 80.
+                         This cannot be used for internal load balancing.
+        :type   port: ``integer``
+
+        :keyword  port_name: Name of backend port. The same name should appear
+                             in the instance groups referenced by this service.
+        :type     port_name: ``str``
+
+        :keyword  protocol: The protocol this Backend Service uses to
+                            communicate with backends.
+                            Possible values are HTTP, HTTPS, HTTP2, TCP
+                            and SSL.
+        :type     protocol: ``str``
+
         :return:  A Backend Service object.
         :rtype:   :class:`GCEBackendService`
         """
-        backendservice_data = {'name': name, 'healthChecks': []}
+        backendservice_data = {'name': name,
+                               'healthChecks': [],
+                               'backends': [],
+                               'enableCDN': enable_cdn}
 
         for hc in healthchecks:
             if not hasattr(hc, 'extra'):
                 hc = self.ex_get_healthcheck(name=hc)
             backendservice_data['healthChecks'].append(hc.extra['selfLink'])
 
+        for be in backends:
+            if isinstance(be, GCEBackend):
+                backendservice_data['backends'].append(be.to_backend_dict())
+            else:
+                backendservice_data['backends'].append(be)
+        if port:
+            backendservice_data['port'] = port
+        if port_name:
+            backendservice_data['portName'] = port_name
+        if timeout_sec:
+            backendservice_data['timeoutSec'] = timeout_sec
+        if protocol:
+            if protocol in self.BACKEND_SERVICE_PROTOCOLS:
+                backendservice_data['protocol'] = protocol
+            else:
+                raise ValueError('Protocol must be one of %s' %
+                                 ','.join(self.BACKEND_SERVICE_PROTOCOLS))
+        if description:
+            backendservice_data['description'] = description
+
         request = '/global/backendServices'
         self.connection.async_request(request, method='POST',
                                       data=backendservice_data)
@@ -2715,6 +3227,72 @@ class GCENodeDriver(NodeDriver):
         self.connection.async_request(request, method='POST', data=image_data)
         return self.ex_get_image(name)
 
+    def ex_create_instancegroup(self, name, zone, description=None,
+                                network=None, subnetwork=None,
+                                named_ports=None):
+        """
+        Creates an instance group in the specified project using the
+        parameters that are included in the request.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  name:  Required. The name of the instance group. The name
+                       must be 1-63 characters long, and comply with RFC1035.
+        :type   name: ``str``
+
+        :param  zone:  The URL of the zone where the instance group is
+                       located.
+        :type   zone: :class:`GCEZone`
+
+        :keyword  description:  An optional description of this resource.
+                                Provide this property when you create the
+                                resource.
+        :type   description: ``str``
+
+        :keyword  network:  The URL of the network to which all instances in
+                            the instance group belong.
+        :type   network: :class:`GCENetwork`
+
+        :keyword  subnetwork:  The URL of the subnetwork to which all
+                               instances in the instance group belong.
+        :type   subnetwork: :class:`GCESubnetwork`
+
+        :keyword  named_ports:  Assigns a name to a port number. For example:
+                                {name: "http", port: 80}  This allows the
+                                system to reference ports by the assigned
+                                name instead of a port number. Named ports
+                                can also contain multiple ports. For example:
+                                [{name: "http", port: 80},{name: "http",
+                                port: 8080}]   Named ports apply to all
+                                instances in this instance group.
+        :type   named_ports: ``list`` of {'name': ``str``, 'port`: ``int``}
+
+        :return:  `GCEInstanceGroup` object.
+        :rtype: :class:`GCEInstanceGroup`
+        """
+        zone = zone or self.zone
+        if not hasattr(zone, 'name'):
+            zone = self.ex_get_zone(zone)
+        request = "/zones/%s/instanceGroups" % (zone.name)
+        request_data = {}
+        request_data['name'] = name
+        request_data['zone'] = zone.extra['selfLink']
+        if description:
+            request_data['description'] = description
+        if network:
+            request_data['network'] = network.extra['selfLink']
+        if subnetwork:
+            request_data['subnetwork'] = subnetwork.extra['selfLink']
+        if named_ports:
+            request_data['namedPorts'] = named_ports
+
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return self.ex_get_instancegroup(name, zone)
+
     def ex_create_instancegroupmanager(self, name, zone, template, size,
                                        base_instance_name=None,
                                        description=None):
@@ -2830,7 +3408,61 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_route(name)
 
-    def ex_create_subnetwork(self, name, cidr=None, network=None, region=None,
+    def ex_create_sslcertificate(self, name, certificate=None,
+                                 private_key=None, description=None):
+        """
+        Creates a SslCertificate resource in the specified project using the
+        data included in the request.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  name:  Name of the resource. Provided by the client when the
+                       resource is created. The name must be 1-63 characters
+                       long, and comply with RFC1035. Specifically, the name
+                       must be 1-63 characters long and match the regular
+                       expression [a-z]([-a-z0-9]*[a-z0-9])? which means the
+                       first character must be a lowercase letter, and all
+                       following characters must be a dash, lowercase letter,
+                       or digit, except the last character, which cannot be a
+                       dash.
+        :type   name: ``str``
+
+        :param  certificate:  A string containing local certificate file in
+                              PEM format. The certificate chain
+                              must be no greater than 5 certs long. The
+                              chain must include at least one intermediate
+                              cert.
+        :type   certificate: ``str``
+
+        :param  private_key:  A string containing a write-only private key
+                              in PEM format. Only insert RPCs will include
+                              this field.
+        :type   private_key: ``str``
+
+        :keyword  description:  An optional description of this resource.
+                                Provide this property when you create the
+                                resource.
+        :type   description: ``str``
+
+        :return:  `GCESslCertificate` object.
+        :rtype: :class:`GCESslCertificate`
+        """
+
+        request = "/global/sslCertificates" % ()
+        request_data = {}
+        request_data['name'] = name
+        request_data['certificate'] = certificate
+        request_data['privateKey'] = private_key
+        request_data['description'] = description
+
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return self.ex_get_sslcertificate(name)
+
+    def ex_create_subnetwork(self, name, cidr=None, network=None, region=None,
                              description=None):
         """
         Create a subnetwork.
@@ -3133,6 +3765,679 @@ class GCENodeDriver(NodeDriver):
         self.connection.async_request(request, method='POST', data=node_data)
         return self.ex_get_node(name, location.name)
 
+    def ex_create_instancetemplate(
+            self, name, size, source=None, image=None, disk_type='pd-standard',
+            disk_auto_delete=True, network='default', subnetwork=None,
+            can_ip_forward=None, external_ip='ephemeral',
+            service_accounts=None, on_host_maintenance=None,
+            automatic_restart=None, preemptible=None, tags=None, metadata=None,
+            description=None, disks_gce_struct=None, nic_gce_struct=None):
+        """
+        Creates an instance template in the specified project using the data
+        that is included in the request. If you are creating a new template to
+        update an existing instance group, your new instance template must
+        use the same network or, if applicable, the same subnetwork as the
+        original template.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :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  network: The network to associate with the template.
+        :type     network: ``str`` or :class:`GCENetwork`
+
+        :keyword  subnetwork: The subnetwork to associate with the node.
+        :type     subnetwork: ``str`` or :class:`GCESubnetwork`
+
+        :keyword  tags: A list of tags to associate with the node.
+        :type     tags: ``list`` of ``str`` or ``None``
+
+        :keyword  metadata: Metadata dictionary for instance.
+        :type     metadata: ``dict`` or ``None``
+
+        :keyword  external_ip: The external IP address to use.  If 'ephemeral'
+                               (default), a new non-static address will be
+                               used.  If 'None', then no external address will
+                               be used.  To use an existing static IP address,
+                               a GCEAddress object should be passed in.
+        :type     external_ip: :class:`GCEAddress` or ``str`` or ``None``
+
+        :keyword  disk_type: Specify a pd-standard (default) disk or pd-ssd
+                                for an SSD disk.
+        :type     disk_type: ``str`` or :class:`GCEDiskType`
+
+        :keyword  disk_auto_delete: Indicate that the boot disk should be
+                                       deleted when the Node is deleted. Set to
+                                       True by default.
+        :type     disk_auto_delete: ``bool``
+
+        :keyword  service_accounts: Specify a list of serviceAccounts when
+                                       creating the instance. The format is a
+                                       list of dictionaries containing email
+                                       and list of scopes, e.g.
+                                       [{'email':'default',
+                                       'scopes':['compute', ...]}, ...]
+                                       Scopes can either be full URLs or short
+                                       names. If not provided, use the
+                                       'default' service account email and a
+                                       scope of 'devstorage.read_only'. Also
+                                       accepts the aliases defined in
+                                       'gcloud compute'.
+        :type     service_accounts: ``list``
+
+        :keyword  description: The description of the node (instance).
+        :type     description: ``str`` or ``None``
+
+        :keyword  can_ip_forward: Set to ``True`` to allow this node to
+                                  send/receive non-matching src/dst packets.
+        :type     can_ip_forward: ``bool`` or ``None``
+
+        :keyword  disks_gce_struct: Support for passing in the GCE-specific
+                                       formatted disks[] structure. No attempt
+                                       is made to ensure proper formatting of
+                                       the disks[] structure. Using this
+                                       structure obviates the need of using
+                                       other disk params like 'ex_boot_disk',
+                                       etc. See the GCE docs for specific
+                                       details.
+        :type     disks_gce_struct: ``list`` or ``None``
+
+        :keyword  nic_gce_struct: Support passing in the GCE-specific
+                                     formatted networkInterfaces[] structure.
+                                     No attempt is made to ensure proper
+                                     formatting of the networkInterfaces[]
+                                     data. Using this structure obviates the
+                                     need of using 'external_ip' and
+                                     'ex_network'.  See the GCE docs for
+                                     details.
+        :type     nic_gce_struct: ``list`` or ``None``
+
+        :keyword  on_host_maintenance: Defines whether node should be
+                                          terminated or migrated when host
+                                          machine goes down. Acceptable values
+                                          are: 'MIGRATE' or 'TERMINATE' (If
+                                          not supplied, value will be reset to
+                                          GCE default value for the instance
+                                          type.)
+        :type     ex_on_host_maintenance: ``str`` or ``None``
+
+        :keyword  automatic_restart: Defines whether the instance should be
+                                        automatically restarted when it is
+                                        terminated by Compute Engine. (If not
+                                        supplied, value will be set to the GCE
+                                        default value for the instance type.)
+        :type     automatic_restart: ``bool`` or ``None``
+
+        :keyword  preemptible: Defines whether the instance is preemptible.
+                                  (If not supplied, the instance will not be
+                                  preemptible)
+        :type     preemptible: ``bool`` or ``None``
+
+        :return:  An Instance Template object.
+        :rtype:   :class:`GCEInstanceTemplate`
+        """
+        request = "/global/instanceTemplates"
+
+        properties = self._create_instance_properties(
+            name, node_size=size, source=source, image=image,
+            disk_type='pd-standard', disk_auto_delete=True,
+            external_ip=external_ip, network=network, subnetwork=subnetwork,
+            can_ip_forward=can_ip_forward, service_accounts=service_accounts,
+            on_host_maintenance=on_host_maintenance,
+            automatic_restart=automatic_restart, preemptible=preemptible,
+            tags=tags, metadata=metadata, description=description,
+            disks_gce_struct=disks_gce_struct, nic_gce_struct=nic_gce_struct,
+            use_selflinks=False)
+
+        request_data = {'name': name,
+                        'description': description,
+                        'properties': properties}
+
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return self.ex_get_instancetemplate(name)
+
+    def _create_instance_properties(
+            self, name, node_size, source=None, image=None,
+            disk_type='pd-standard', disk_auto_delete=True, network='default',
+            subnetwork=None, external_ip='ephemeral', can_ip_forward=None,
+            service_accounts=None, on_host_maintenance=None,
+            automatic_restart=None, preemptible=None, tags=None, metadata=None,
+            description=None, disks_gce_struct=None, nic_gce_struct=None,
+            use_selflinks=True):
+        """
+        Create the GCE instance properties needed for instance templates.
+
+        :param    node_size: The machine type to use.
+        :type     node_size: ``str`` or :class:`GCENodeSize`
+
+        :keyword  source: A source disk to attach to the instance. Cannot
+                          specify both 'image' and 'source'.
+        :type     source: :class:`StorageVolume` or ``str`` or ``None``
+
+        :param    image: The image to use to create the node. Cannot specify
+                         both 'image' and 'source'.
+        :type     image: ``str`` or :class:`GCENodeImage` or ``None``
+
+        :keyword  disk_type: Specify a pd-standard (default) disk or pd-ssd
+                             for an SSD disk.
+        :type     disk_type: ``str`` or :class:`GCEDiskType`
+
+        :keyword  disk_auto_delete: Indicate that the boot disk should be
+                                    deleted when the Node is deleted. Set to
+                                    True by default.
+        :type     disk_auto_delete: ``bool``
+
+        :keyword  network: The network to associate with the node.
+        :type     network: ``str`` or :class:`GCENetwork`
+
+        :keyword  subnetwork: The Subnetwork resource for this instance. If
+                              the network resource is in legacy mode, do not
+                              provide this property. If the network is in auto
+                              subnet mode, providing the subnetwork is
+                              optional. If the network is in custom subnet
+                              mode, then this field should be specified.
+        :type     subnetwork: :class: `GCESubnetwork` or None
+
+        :keyword  external_ip: The external IP address to use.  If 'ephemeral'
+                               (default), a new non-static address will be
+                               used.  If 'None', then no external address will
+                               be used.  To use an existing static IP address,
+                               a GCEAddress object should be passed in.
+        :type     external_ip: :class:`GCEAddress` or ``str`` or ``None``
+
+        :keyword  can_ip_forward: Set to ``True`` to allow this node to
+                                  send/receive non-matching src/dst packets.
+        :type     can_ip_forward: ``bool`` or ``None``
+
+        :keyword  service_accounts: Specify a list of serviceAccounts when
+                                    creating the instance. The format is a
+                                    list of dictionaries containing email
+                                    and list of scopes, e.g.
+                                    [{'email':'default',
+                                    'scopes':['compute', ...]}, ...]
+                                    Scopes can either be full URLs or short
+                                    names. If not provided, use the
+                                    'default' service account email and a
+                                    scope of 'devstorage.read_only'. Also
+                                    accepts the aliases defined in
+                                    'gcloud compute'.
+        :type     service_accounts: ``list``
+
+        :keyword  on_host_maintenance: Defines whether node should be
+                                       terminated or migrated when host
+                                       machine goes down. Acceptable values
+                                       are: 'MIGRATE' or 'TERMINATE' (If
+                                       not supplied, value will be reset to
+                                       GCE default value for the instance
+                                       type.)
+        :type     on_host_maintenance: ``str`` or ``None``
+
+        :keyword  automatic_restart: Defines whether the instance should be
+                                     automatically restarted when it is
+                                     terminated by Compute Engine. (If not
+                                     supplied, value will be set to the GCE
+                                     default value for the instance type.)
+        :type     automatic_restart: ``bool`` or ``None``
+
+        :keyword  preemptible: Defines whether the instance is preemptible.
+                               (If not supplied, the instance will not be
+                               preemptible)
+        :type     preemptible: ``bool`` or ``None``
+
+        :keyword  tags: A list of tags to associate with the node.
+        :type     tags: ``list`` of ``str`` or ``None``
+
+        :keyword  metadata: Metadata dictionary for instance.
+        :type     metadata: ``dict`` or ``None``
+
+        :keyword  description: The description of the node (instance).
+        :type     description: ``str`` or ``None``
+
+        :keyword  disks_gce_struct: Support for passing in the GCE-specific
+                                    formatted disks[] structure. No attempt
+                                    is made to ensure proper formatting of
+                                    the disks[] structure. Using this
+                                    structure obviates the need of using
+                                    other disk params like 'boot_disk',
+                                    etc. See the GCE docs for specific
+                                    details.
+        :type     disks_gce_struct: ``list`` or ``None``
+
+        :keyword  nic_gce_struct: Support passing in the GCE-specific
+                                  formatted networkInterfaces[] structure.
+                                  No attempt is made to ensure proper
+                                  formatting of the networkInterfaces[]
+                                  data. Using this structure obviates the
+                                  need of using 'external_ip' and
+                                  'network'.  See the GCE docs for
+                                  details.
+        :type     nic_gce_struct: ``list`` or ``None``
+
+        :return:  A dictionary formatted for use with the GCE API.
+        :rtype:   ``dict``
+        """
+        instance_properties = {}
+
+        # build disks
+        if not image and not source and not disks_gce_struct:
+            raise ValueError("Missing root device or image. Must specify an "
+                             "'image', source, or use the "
+                             "'disks_gce_struct'.")
+
+        if source and disks_gce_struct:
+            raise ValueError("Cannot specify both 'source' and "
+                             "'disks_gce_struct'. Use one or the other.")
+
+        if disks_gce_struct:
+            instance_properties['disks'] = disks_gce_struct
+        else:
+            disk_name = None
+            device_name = None
+            if source:
+                disk_name = source.name
+                # TODO(supertom): what about device name?
+                device_name = source.name
+                image = None
+
+            instance_properties['disks'] = [self._build_disk_gce_struct(
+                device_name, source=source, disk_type=disk_type, image=image,
+                disk_name=disk_name, usage_type='PERSISTENT',
+                mount_mode='READ_WRITE', auto_delete=disk_auto_delete,
+                is_boot=True, use_selflinks=use_selflinks)]
+
+        # build network interfaces
+        if nic_gce_struct is not None:
+            if hasattr(external_ip, 'address'):
+                raise ValueError("Cannot specify both a static IP address "
+                                 "and 'nic_gce_struct'. Use one or the "
+                                 "other.")
+            if hasattr(network, 'name'):
+                if network.name == 'default':
+                    # assume this is just the default value from create_node()
+                    # and since the user specified ex_nic_gce_struct, the
+                    # struct should take precedence
+                    network = None
+                else:
+                    raise ValueError("Cannot specify both 'network' and "
+                                     "'nic_gce_struct'. Use one or the "
+                                     "other.")
+            instance_properties['networkInterfaces'] = nic_gce_struct
+        else:
+            instance_properties['networkInterfaces'] = [
+                self._build_network_gce_struct(
+                    network=network, subnetwork=subnetwork,
+                    external_ip=external_ip, use_selflinks=True)
+            ]
+
+        # build scheduling
+        scheduling = self._build_scheduling_gce_struct(
+            on_host_maintenance, automatic_restart, preemptible)
+        if scheduling:
+            instance_properties['scheduling'] = scheduling
+
+        # build service accounts/scopes
+        instance_properties[
+            'serviceAccounts'] = self._build_service_accounts_gce_list(
+                service_accounts)
+
+        # include general properties
+        if description:
+            instance_properties['description'] = str(description)
+        if tags:
+            instance_properties['tags'] = {'items': tags}
+        if metadata:
+            instance_properties['metadata'] = self._format_metadata(
+                fingerprint='na', metadata=metadata)
+        if can_ip_forward:
+            instance_properties['canIpForward'] = True
+
+        instance_properties['machineType'] = self._get_selflink_or_name(
+            obj=node_size, get_selflinks=use_selflinks, objname='size')
+
+        return instance_properties
+
+    def _build_disk_gce_struct(
+            self, device_name, source=None, disk_type=None, disk_size=None,
+            image=None, disk_name=None, is_boot=True, mount_mode='READ_WRITE',
+            usage_type='PERSISTENT', auto_delete=True, use_selflinks=True):
+        """
+        Generates the GCP dict for a disk.
+
+        :param    device_name: Specifies a unique device name of your
+                               choice that is reflected into the
+                               /dev/disk/by-id/google-* tree
+                               of a Linux operating system running within the
+                               instance. This name can be used to reference the
+                               device for mounting, resizing, and so on, from
+                               within the instance.  Defaults to disk_name.
+        :type      device_name: ``str``
+
+        :keyword   source: The disk to attach to the instance.
+        :type      source: ``str`` of selfLink, :class:`StorageVolume` or None
+
+        :keyword   disk_type: Specify a URL or DiskType object.
+        :type      disk_type: ``str`` or :class:`GCEDiskType` or ``None``
+
+        :keyword   image: The image to use to create the disk.
+        :type      image: :class:`GCENodeImage` or ``None``
+
+        :keyword   disk_size: Integer in gigabytes.
+        :type      disk_size: ``int``
+
+        :param     disk_name: Specifies the disk name. If not specified, the
+                              default is to use the device_name.
+        :type      disk_name: ``str``
+
+        :keyword   mount_mode: The mode in which to attach this disk, either
+                               READ_WRITE or READ_ONLY. If not specified,
+                               the default is to attach the disk in READ_WRITE
+                               mode.
+        :type      mount_mode: ``str``
+
+        :keyword   usage_type: Specifies the type of the disk, either SCRATCH
+                               or PERSISTENT. If not specified, the default
+                               is PERSISTENT.
+        :type      usage_type: ``str``
+
+        :keyword   auto_delete: Indicate that the boot disk should be
+                                deleted when the Node is deleted. Set to
+                                True by default.
+        :type      auto_delete: ``bool``
+
+        :return:   Dictionary to be used in disk-portion of
+                   instance API call.
+        :rtype:    ``dict``
+        """
+        # validation
+        if source is None and image is None:
+            raise ValueError(
+                "Either the 'source' or 'image' argument must be specified.")
+
+        if not isinstance(auto_delete, bool):
+            raise ValueError("auto_delete field is not a bool.")
+
+        if disk_size is not None and not disk_size.isdigit():
+            raise ValueError("disk_size must be a digit, '%s' provided." %
+                             (disk_size))
+
+        mount_modes = ['READ_WRITE', 'READ_ONLY']
+        if mount_mode not in mount_modes:
+            raise ValueError("mount mode must be one of: %s." %
+                             (','.join(mount_modes)))
+        usage_types = ['PERSISTENT', 'SCRATCH']
+        if usage_type not in usage_types:
+            raise ValueError("usage type must be one of: %s." %
+                             (','.join(usage_types)))
+
+        disk = {}
+        if not disk_name:
+            disk_name = device_name
+
+        if source is not None:
+            disk['source'] = self._get_selflink_or_name(
+                obj=source, get_selflinks=use_selflinks, objname='volume')
+
+        else:
+            # create new disk
+            # we need the URL of the image, always.
+            image = self._get_selflink_or_name(obj=image, get_selflinks=True,
+                                               objname='image')
+            disk_type = self._get_selflink_or_name(
+                obj=disk_type, get_selflinks=use_selflinks, objname='disktype')
+
+            disk['initializeParams'] = {
+                'diskName': disk_name,
+                'diskType': disk_type,
+                'sourceImage': image,
+            }
+            if disk_size is not None:
+                disk['initializeParams']['diskSizeGb'] = disk_size
+
+        # add in basic attributes
+        disk.update({'boot': is_boot,
+                     'type': usage_type,
+                     'mode': mount_mode,
+                     'deviceName': device_name,
+                     'autoDelete': auto_delete})
+        return disk
+
+    def _get_selflink_or_name(self, obj, get_selflinks=True, objname=None):
+        """
+        Return the selflink or name, given a name or object.
+
+        Will try to fetch the appropriate object if necessary (assumes
+        we only need one parameter to fetch the object, no introspection
+        is performed).
+
+        :param    obj: object to test.
+        :type     obj: ``str`` or ``object``
+
+        :param    get_selflinks: Inform if we should return selfLinks or just
+                              the name.  Default is True.
+        :param    get_selflinks: ``bool``
+
+        :param    objname: string to use in constructing method call
+        :type     objname: ``str`` or None
+
+        :return:  URL from extra['selfLink'] or name
+        :rtype:   ``str``
+        """
+        if get_selflinks:
+            if not hasattr(obj, 'name'):
+                if objname:
+                    getobj = getattr(self, 'ex_get_%s' % (objname))
+                    obj = getobj(obj)
+                else:
+                    raise ValueError(
+                        "objname must be set if selflinks is True.")
+            return obj.extra['selfLink']
+        else:
+            if not hasattr(obj, 'name'):
+                return obj
+            else:
+                return obj.name
+
+    def _build_network_gce_struct(self, network, subnetwork=None,
+                                  external_ip=None, use_selflinks=True):
+        """
+        Build network interface dict for use in the GCE API.
+
+        Note: Must be wrapped in a list before passing to the GCE API.
+
+        :param    network: The network to associate with the node.
+        :type     network: :class:`GCENetwork`
+
+        :keyword  subnetwork: The subnetwork to include.
+        :type     subnetwork: :class:`GCESubNetwork`
+
+        :keyword  external_ip: The external IP address to use.  If 'ephemeral'
+                               (default), a new non-static address will be
+                               used.  If 'None', then no external address will
+                               be used.  To use an existing static IP address,
+                               a GCEAddress object should be passed in.
+        :type     external_ip: :class:`GCEAddress`
+
+        :return:  network interface dict
+        :rtype:   ``dict``
+        """
+        ni = {}
+        ni = {'kind': 'compute#instanceNetworkInterface'}
+        if network is None:
+            network = 'default'
+
+        ni['network'] = self._get_selflink_or_name(
+            obj=network, get_selflinks=use_selflinks, objname='network')
+
+        if subnetwork:
+            ni['subnetwork'] = self._get_selflink_or_name(
+                obj=subnetwork, get_selflinks=use_selflinks,
+                objname='subnetwork')
+
+        if external_ip:
+            access_configs = [{'name': 'External NAT',
+                               'type': 'ONE_TO_ONE_NAT'}]
+            if hasattr(external_ip, 'address'):
+                access_configs[0]['natIP'] = external_ip.address
+            ni['accessConfigs'] = access_configs
+
+        return ni
+
+    def _build_service_account_gce_struct(
+            self, service_account, default_email='default',
+            default_scope='devstorage.read_only'):
+        """
+        Helper to create Service Account dict.  Use
+        _build_service_accounts_gce_list to create a list ready for the
+        GCE API.
+
+        :param: service_account: dictionarie containing email
+                                 and list of scopes, e.g.
+                                 [{'email':'default',
+                                 'scopes':['compute', ...]}, ...]
+                                 Scopes can either be full URLs or short
+                                 names. If not provided, use the
+                                 'default' service account email and a
+                                 scope of 'devstorage.read_only'. Also
+                                 accepts the aliases defined in
+                                 'gcloud compute'.
+       :type    service_account: ``dict`` or None
+
+       :return: dict usable in GCE API call.
+       :rtype:  ``dict``
+       """
+        if not isinstance(service_account, dict):
+            raise ValueError(
+                "service_account not in the correct format,"
+                "'%s - %s'" %
+                (str(type(service_account)), str(service_account)))
+        sa = {}
+        if 'email' not in service_account:
+            sa['email'] = default_email
+
+        if 'scopes' not in service_account:
+            sa['scopes'] = [self.AUTH_URL + default_scope]
+        else:
+            ps = []
+            for scope in service_account['scopes']:
+                if scope.startswith(self.AUTH_URL):
+                    ps.append(scope)
+                elif scope in self.SA_SCOPES_MAP:
+                    ps.append(self.AUTH_URL + self.SA_SCOPES_MAP[scope])
+                else:
+                    ps.append(self.AUTH_URL + scope)
+            sa['scopes'] = ps
+
+        return sa
+
+    def _build_service_accounts_gce_list(self, service_accounts=None,
+                                         default_email='default',
+                                         default_scope='devstorage.read_only'):
+        """
+        Helper to create service account list for GCE API.
+
+        :keyword  service_accounts: Specify a list of serviceAccounts when
+                                       creating the instance. The format is a
+                                       list of dictionaries containing email
+                                       and list of scopes, e.g.
+                                       [{'email':'default',
+                                       'scopes':['compute', ...]}, ...]
+                                       Scopes can either be full URLs or short
+                                       names. If not provided, use the
+                                       'default' service account email and a
+                                       scope of 'devstorage.read_only'. Also
+                                       accepts the aliases defined in
+                                       'gcloud compute'.
+
+        :type     service_accounts: ``list`` of ``dict`` or None
+
+        :return:  list of dictionaries usable in the GCE API.
+        :rtype:   ``list`` of ``dict``
+        """
+        gce_service_accounts = []
+        if not service_accounts:
+            gce_service_accounts = [{
+                'email': default_email,
+                'scopes': [self.AUTH_URL + default_scope]
+            }]
+        elif not isinstance(service_accounts, list):
+            raise ValueError("service_accounts field is not a list.")
+        else:
+            for sa in service_accounts:
+                gce_service_accounts.append(
+                    self._build_service_account_gce_struct(service_account=sa))
+
+        return gce_service_accounts
+
+    def _build_scheduling_gce_struct(self, on_host_maintenance=None,
+                                     automatic_restart=None, preemptible=None):
+        """
+        Build the scheduling dict suitable for use with the GCE API.
+
+        :param    on_host_maintenance: Defines whether node should be
+                                          terminated or migrated when host
+                                          machine goes down. Acceptable values
+                                          are: 'MIGRATE' or 'TERMINATE' (If
+                                          not supplied, value will be reset to
+                                          GCE default value for the instance
+                                          type.)
+        :type     on_host_maintenance: ``str`` or ``None``
+
+        :param    automatic_restart: Defines whether the instance should be
+                                        automatically restarted when it is
+                                        terminated by Compute Engine. (If not
+                                        supplied, value will be set to the GCE
+                                        default value for the instance type.)
+        :type     automatic_restart: ``bool`` or ``None``
+
+        :param    preemptible: Defines whether the instance is preemptible.
+                                        (If not supplied, the instance will
+                                         not be preemptible)
+        :type     preemptible: ``bool`` or ``None``
+
+        :return:  A dictionary of scheduling options for the GCE API.
+        :rtype:   ``dict``
+        """
+        scheduling = {}
+        if preemptible is not None:
+            if isinstance(preemptible, bool):
+                scheduling['preemptible'] = preemptible
+            else:
+                raise ValueError("boolean expected for preemptible")
+        if on_host_maintenance is not None:
+            maint_opts = ['MIGRATE', 'TERMINATE']
+            if isinstance(on_host_maintenance,
+                          str) and on_host_maintenance in maint_opts:
+                if preemptible is True and on_host_maintenance is 'MIGRATE':
+                    raise ValueError(("host maintenance cannot be 'MIGRATE' "
+                                      "if instance is preemptible."))
+                scheduling['onHostMaintenance'] = on_host_maintenance
+            else:
+                raise ValueError("host maintenance must be one of %s" %
+                                 (','.join(maint_opts)))
+        if automatic_restart is not None:
+            if isinstance(automatic_restart, bool):
+                if automatic_restart is True and preemptible is True:
+                    raise ValueError(
+                        "instance cannot be restarted if it is preemptible.")
+                scheduling['automaticRestart'] = automatic_restart
+
+            else:
+                raise ValueError("boolean expected for automatic")
+
+        return scheduling
+
     def ex_create_multiple_nodes(
             self, base_name, size, image, number, location=None,
             ex_network='default', ex_tags=None, ex_metadata=None,
@@ -3376,6 +4681,61 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_targethttpproxy(name)
 
+    def ex_create_targethttpsproxy(self, name, urlmap, sslcertificates,
+                                   description=None):
+        """
+        Creates a TargetHttpsProxy resource in the specified project
+        using the data included in the request.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  name:  Name of the resource. Provided by the client when the
+                       resource is created. The name must be 1-63 characters
+                       long, and comply with RFC1035. Specifically, the name
+                       must be 1-63 characters long and match the regular
+                       expression [a-z]([-a-z0-9]*[a-z0-9])? which means the
+                       first character must be a lowercase letter, and all
+                       following characters must be a dash, lowercase letter,
+                       or digit, except the last character, which cannot be a
+                       dash.
+        :type   name: ``str``
+
+        :param  sslcertificates:  URLs to SslCertificate resources that
+                                     are used to authenticate connections
+                                     between users and the load balancer.
+                                     Currently, exactly one SSL certificate
+                                     must be specified.
+        :type   sslcertificates: ``list`` of :class:`GCESslcertificates`
+
+        :param  urlmap:  A fully-qualified or valid partial URL to the
+                            UrlMap resource that defines the mapping from URL
+                            to the BackendService.
+        :type   urlmap: :class:`GCEUrlMap`
+
+        :keyword  description:  An optional description of this resource.
+                                Provide this property when you create the
+                                resource.
+        :type   description: ``str``
+
+        :return:  `GCETargetHttpsProxy` object.
+        :rtype: :class:`GCETargetHttpsProxy`
+        """
+
+        request = "/global/targetHttpsProxies" % ()
+        request_data = {}
+        request_data['name'] = name
+        request_data['description'] = description
+        request_data['sslCertificates'] = [x.extra['selfLink']
+                                           for x in sslcertificates]
+        request_data['urlMap'] = urlmap.extra['selfLink']
+
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return self.ex_get_targethttpsproxy(name)
+
     def ex_create_targetinstance(self, name, zone=None, node=None,
                                  description=None, nat_policy="NO_NAT"):
         """
@@ -3705,6 +5065,61 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_firewall(firewall.name)
 
+    def ex_targethttpsproxy_set_sslcertificates(self, targethttpsproxy,
+                                                sslcertificates):
+        """
+        Replaces SslCertificates for TargetHttpsProxy.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  targethttpsproxy:  Name of the TargetHttpsProxy resource to
+                                   set an SslCertificates resource for.
+        :type   targethttpsproxy: ``str``
+
+        :param  sslcertificates:  sslcertificates to set.
+        :type   sslcertificates: ``list`` of :class:`GCESslCertificates`
+
+        :return:  True
+        :rtype: ``bool``
+        """
+
+        request = "/targetHttpsProxies/%s/setSslCertificates" % (
+            targethttpsproxy.name)
+        request_data = {'sslCertificates': [x.extra['selfLink']
+                                            for x in sslcertificates]}
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return True
+
+    def ex_targethttpsproxy_set_urlmap(self, targethttpsproxy, urlmap):
+        """
+        Changes the URL map for TargetHttpsProxy.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  targethttpsproxy:  Name of the TargetHttpsProxy resource
+                                   whose URL map is to be set.
+        :type   targethttpsproxy: ``str``
+
+        :param  urlmap:  urlmap to set.
+        :type   urlmap: :class:`GCEUrlMap`
+
+        :return:  True
+        :rtype: ``bool``
+        """
+
+        request = "/targetHttpsProxies/%s/setUrlMap" % (targethttpsproxy.name)
+        request_data = {'urlMap': urlmap.extra['selfLink']}
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+
+        return True
+
     def ex_targetpool_get_health(self, targetpool, node=None):
         """
         Return a hash of target pool instances and their health.
@@ -3920,6 +5335,158 @@ class GCENodeDriver(NodeDriver):
             targetpool.healthchecks.pop(index)
         return True
 
+    def ex_instancegroup_add_instances(self, instancegroup, node_list):
+        """
+        Adds a list of instances to the specified instance group. All of the
+        instances in the instance group must be in the same
+        network/subnetwork. Read  Adding instances for more information.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The Instance Group where you are
+                                adding instances.
+        :type   instancegroup: :class:``GCEInstanceGroup``
+
+        :param  node_list: List of nodes to add.
+        :type   node_list: ``list`` of :class:`Node` or ``list`` of
+                           :class:`GCENode`
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+        request = "/zones/%s/instanceGroups/%s/addInstances" % (
+            instancegroup.zone.name, instancegroup.name)
+        request_data = {'instances': [{'instance': x.extra['selfLink']}
+                                      for x in node_list]}
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+        return True
+
+    def ex_instancegroup_remove_instances(self, instancegroup, node_list):
+        """
+        Removes one or more instances from the specified instance group,
+        but does not delete those instances.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The Instance Group where the
+                                specified instances will be removed.
+        :type   instancegroup: :class:``GCEInstanceGroup``
+
+        :param  node_list: List of nodes to add.
+        :type   node_list: ``list`` of :class:`Node` or ``list`` of
+                           :class:`GCENode`
+
+        :return:  True if successful.
+        :rtype: ``bool``
+        """
+        request = "/zones/%s/instanceGroups/%s/removeInstances" % (
+            instancegroup.zone.name, instancegroup.name)
+        request_data = {'instances': [{'instance': x.extra['selfLink']}
+                                      for x in node_list]}
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+        return True
+
+    def ex_instancegroup_list_instances(self, instancegroup):
+        """
+        Lists the instances in the specified instance group.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+        * https://www.googleapis.com/auth/compute.readonly
+
+        :param  instancegroup:  The Instance Group where from which you
+                                want to generate a list of included
+                                instances.
+        :type   instancegroup: :class:``GCEInstanceGroup``
+
+        :return:  List of :class:`GCENode` objects.
+        :rtype: ``list`` of :class:`GCENode` objects.
+        """
+        request = "/zones/%s/instanceGroups/%s/listInstances" % (
+            instancegroup.zone.name, instancegroup.name)
+
+        # Note: This API requires a 'POST'.
+        response = self.connection.request(request, method='POST').object
+
+        list_data = []
+        if 'items' in response:
+            for v in response['items']:
+                instance_info = self._get_components_from_path(v['instance'])
+                list_data.append(
+                    self.ex_get_node(instance_info['name'], instance_info[
+                        'zone']))
+        return list_data
+
+    def ex_instancegroup_set_named_ports(self, instancegroup, named_ports=[]):
+        """
+        Sets the named ports for the specified instance group.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The Instance Group where where the
+                                named ports are updated.
+        :type   instancegroup: :class:`GCEInstanceGroup`
+
+        :param  named_ports:  Assigns a name to a port number. For example:
+                              {name: "http", port: 80}  This allows the
+                              system to reference ports by the assigned name
+                              instead of a port number. Named ports can also
+                              contain multiple ports. For example: [{name:
+                              "http", port: 80},{name: "http", port: 8080}]
+                              Named ports apply to all instances in this
+                              instance group.
+        :type   named_ports: ``list`` of {'name': ``str``, 'port`: ``int``}
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+
+        if not isinstance(named_ports, list):
+            raise ValueError("'named_ports' must be a list of name/port"
+                             " dictionaries.")
+
+        request = "/zones/%s/instanceGroups/%s/setNamedPorts" % (
+            instancegroup.zone.name, instancegroup.name)
+        request_data = {'namedPorts': named_ports,
+                        'fingerprint': instancegroup.extra['fingerprint']}
+        self.connection.async_request(request, method='POST',
+                                      data=request_data)
+        return True
+
+    def ex_destroy_instancegroup(self, instancegroup):
+        """
+        Deletes the specified instance group. The instances in the group
+        are not deleted. Note that instance group must not belong to a backend
+        service. Read  Deleting an instance group for more information.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancegroup:  The name of the instance group to delete.
+        :type   instancegroup: :class:`GCEInstanceGroup`
+
+        :return:  Return True if successful.
+        :rtype: ``bool``
+        """
+
+        request = "/zones/%s/instanceGroups/%s" % (instancegroup.zone.name,
+                                                   instancegroup.name)
+        request_data = {}
+        self.connection.async_request(request, method='DELETE',
+                                      data=request_data)
+
+        return True
+
     def ex_instancegroupmanager_list_managed_instances(self, manager):
         """
         Lists all of the instances in the Managed Instance Group.
@@ -4479,8 +6046,8 @@ class GCENodeDriver(NodeDriver):
                 try:
                     timestamp_to_datetime(value)
                 except:
-                    raise ValueError('%s must be an RFC3339 timestamp'
-                                     % attribute)
+                    raise ValueError('%s must be an RFC3339 timestamp' %
+                                     attribute)
                 image_data[attribute] = value
 
         request = '/global/images/%s/deprecate' % (image.name)
@@ -4634,6 +6201,33 @@ class GCENodeDriver(NodeDriver):
         self.connection.async_request(request, method='DELETE')
         return True
 
+    def ex_destroy_instancetemplate(self, instancetemplate):
+        """
+        Deletes the specified instance template. If you delete an instance
+        template that is being referenced from another instance group, the
+        instance group will not be able to create or recreate virtual machine
+        instances. Deleting an instance template is permanent and cannot be
+        undone.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  instancetemplate:  The name of the instance template to
+                                   delete.
+        :type   instancetemplate: ``str``
+
+        :return  instanceTemplate:  Return True if successful.
+        :rtype   instanceTemplate: ````bool````
+        """
+
+        request = "/global/instanceTemplates/%s" % (instancetemplate.name)
+        request_data = {}
+        self.connection.async_request(request, method='DELETE',
+                                      data=request_data)
+
+        return True
+
     def ex_destroy_autoscaler(self, autoscaler):
         """
         Destroy an Autoscaler.
@@ -4792,6 +6386,29 @@ class GCENodeDriver(NodeDriver):
         self.connection.async_request(request, method='DELETE')
         return True
 
+    def ex_destroy_targethttpsproxy(self, targethttpsproxy):
+        """
+        Deletes the specified TargetHttpsProxy resource.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  targethttpsproxy:  Name of the TargetHttpsProxy resource to
+                                   delete.
+        :type   targethttpsproxy: ``str``
+
+        :return  targetHttpsProxy:  Return True if successful.
+        :rtype   targetHttpsProxy: ````bool````
+        """
+
+        request = "/global/targetHttpsProxies/%s" % (targethttpsproxy.name)
+        request_data = {}
+        self.connection.async_request(request, method='DELETE',
+                                      data=request_data)
+
+        return True
+
     def ex_destroy_targetinstance(self, targetinstance):
         """
         Destroy a target instance.
@@ -5109,6 +6726,29 @@ class GCENodeDriver(NodeDriver):
         response = self.connection.request(request, method='GET').object
         return self._to_route(response)
 
+    def ex_destroy_sslcertificate(self, sslcertificate):
+        """
+        Deletes the specified SslCertificate resource.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+
+        :param  sslcertificate:  Name of the SslCertificate resource to
+                                 delete.
+        :type   sslcertificate: ``str``
+
+        :return  sslCertificate:  Return True if successful.
+        :rtype   sslCertificate: ````bool````
+        """
+
+        request = "/global/sslCertificates/%s" % (sslcertificate.name)
+        request_data = {}
+        self.connection.async_request(request, method='DELETE',
+                                      data=request_data)
+
+        return True
+
     def ex_destroy_subnetwork(self, name, region=None):
         """
         Delete a Subnetwork object based on name and region.
@@ -5318,6 +6958,29 @@ class GCENodeDriver(NodeDriver):
         response = self.connection.request(request, method='GET').object
         return self._to_region(response)
 
+    def ex_get_sslcertificate(self, name):
+        """
+        Returns the specified SslCertificate resource. Get a list of available
+        SSL certificates by making a list() request.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+        * https://www.googleapis.com/auth/compute.readonly
+
+        :param  name:  Name of the SslCertificate resource to
+                                 return.
+        :type   name: ``str``
+
+        :return:  `GCESslCertificate` object.
+        :rtype: :class:`GCESslCertificate`
+        """
+
+        request = "/global/sslCertificates/%s" % (name)
+        response = self.connection.request(request, method='GET').object
+
+        return self._to_sslcertificate(response)
+
     def ex_get_targethttpproxy(self, name):
         """
         Return a Target HTTP Proxy object based on its name.
@@ -5332,6 +6995,29 @@ class GCENodeDriver(NodeDriver):
         response = self.connection.request(request, method='GET').object
         return self._to_targethttpproxy(response)
 
+    def ex_get_targethttpsproxy(self, name):
+        """
+        Returns the specified TargetHttpsProxy resource. Get a list of
+        available target HTTPS proxies by making a list() request.
+
+        Scopes needed - one of the following:
+        * https://www.googleapis.com/auth/cloud-platform
+        * https://www.googleapis.com/auth/compute
+        * https://www.googleapis.com/auth/compute.readonly
+
+        :param  name:  Name of the TargetHttpsProxy resource to
+                                   return.
+        :type   name: ``str``
+
+        :return:  `GCETargetHttpsProxy` object.
+        :rtype: :class:`GCETargetHttpsProxy`
+        """
+
+        request = "/global/targetHttpsProxies/%s" % (name)
+        response = self.connection.request(request, method='GET').object
+
+        return self._to_targethttpsproxy(response)
+
     def ex_get_targetinstance(self, name, zone=None):
         """
         Return a TargetInstance object based on a name and optional zone.
@@ -5700,8 +7386,9 @@ class GCENodeDriver(NodeDriver):
             ex_on_host_maintenance=None, ex_automatic_restart=None,
             ex_preemptible=None, ex_subnetwork=None):
         """
-        Returns a request and body to create a new node.  This is a helper
-        method to support both :class:`create_node` and
+        Returns a request and body to create a new node.
+
+        This is a helper method to support both :class:`create_node` and
         :class:`ex_create_multiple_nodes`.
 
         :param  name: The name of the node to create.
@@ -5815,140 +7502,34 @@ class GCENodeDriver(NodeDriver):
         :return:  A tuple containing a request string and a node_data dict.
         :rtype:   ``tuple`` of ``str`` and ``dict``
         """
-        node_data = {}
-        node_data['machineType'] = size.extra['selfLink']
-        node_data['name'] = name
-        if tags:
-            node_data['tags'] = {'items': tags}
-        if metadata:
-            node_data['metadata'] = self._format_metadata(fingerprint='na',
-                                                          metadata=metadata)
-
-        # by default, new instances will match the same serviceAccount and
-        # scope set in the Developers Console and Cloud SDK
-        if not ex_service_accounts:
-            set_scopes = [{
-                'email': 'default',
-                'scopes': [self.AUTH_URL + 'devstorage.read_only']
-            }]
-        elif not isinstance(ex_service_accounts, list):
-            raise ValueError("ex_service_accounts field is not a list.")
-        else:
-            set_scopes = []
-            for sa in ex_service_accounts:
-                if not isinstance(sa, dict):
-                    raise ValueError("ex_service_accounts needs to be a list "
-                                     "of dicts, got: '%s - %s'" %
-                                     (str(type(sa)), str(sa)))
-                if 'email' not in sa:
-                    sa['email'] = 'default'
-                if 'scopes' not in sa:
-                    sa['scopes'] = [self.AUTH_URL + 'devstorage.read_only']
-                ps = []
-                for scope in sa['scopes']:
-                    if scope.startswith(self.AUTH_URL):
-                        ps.append(scope)
-                    elif scope in self.SA_SCOPES_MAP:
-                        ps.append(self.AUTH_URL + self.SA_SCOPES_MAP[scope])
-                    else:
-                        ps.append(self.AUTH_URL + scope)
-                sa['scopes'] = ps
-                set_scopes.append(sa)
-        node_data['serviceAccounts'] = set_scopes
-
-        if boot_disk and ex_disks_gce_struct:
-            raise ValueError("Cannot specify both 'boot_disk' and "
-                             "'ex_disks_gce_s

<TRUNCATED>