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>