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>