From 26f02eb978c9d7e50ec1b7fad1ed8aa029e55ac8 Mon Sep 17 00:00:00 2001 From: sguazt <marco.guazzone@gmail.com> Date: Tue, 5 Jan 2021 11:29:37 +0100 Subject: [PATCH] GCP: added class to use for implementing synchronized operations (e.g., for waiting until the starting of an instance is done) --- easycloud/common/gcp.py | 60 ++++++++++++++++++++++ easycloud/modules/gcp_googleapi/manager.py | 3 ++ 2 files changed, 63 insertions(+) diff --git a/easycloud/common/gcp.py b/easycloud/common/gcp.py index d24bd02..f98c620 100644 --- a/easycloud/common/gcp.py +++ b/easycloud/common/gcp.py @@ -1,4 +1,5 @@ from easycloud.core.metaconfmanager import MetaConfManager +import time class GCPBaseConfManager(MetaConfManager): @@ -52,3 +53,62 @@ class GCPBaseConfManager(MetaConfManager): "alwaysfree", "alwaysfree_instance_types").replace(" ", "").replace("\n", "").split(",") self.alwaysfree_zones = self.parser.get( "alwaysfree", "alwaysfree_zones").replace(" ", "").replace("\n", "").split(",") + + +class GCPComputeUtils: + @staticmethod + def wait_for_operation(service, project, operation, timeout=None): + """ Wait until the given operation is done. + Args: + service (googleapiclient.discovery.Resource): the Resource object for interacting with the Google Compute service. + project (str): the project identifier. + operation (dict): the operation to wait for as returned by the + original request. + timeout (float): an optional value representing the max number of + seconds to wait for; if None, no timeout is enforced. + Returns: + bool: True if the operation suceeded or False if the operation + failed or if it does not complete successfully before the + timeout. + + Requires one of the following OAuth scopes: + - https://www.googleapis.com/auth/compute + - https://www.googleapis.com/auth/cloud-platform + and the followin IAM permissions: + - compute.zoneOperations.get + + See: + - https://cloud.google.com/compute/docs/reference/rest/v1/zoneOperations/get + """ + # Inspired by: https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/compute/cmdline/src/main/java/ComputeEngineSample.java + #FIXME: the following function has not been tested yet. + op = operation['name'] + zone = None + if 'zone' in operation and operation['zone']: + zone = operation['zone'].rpartition('/')[2] + start_time = time.time() + pool_interval = 5 + ret = True + try: + while operation is not None and operation['status'] != 'DONE': + time.sleep(poll_interval) + if timeout is not None and (time.time()-start_time) >= timeout: + ret = False + break + + request = None + if zone is None: + request = service.globalOperations().get(project=project, + operation=op) + else: + request = service.zoneOperations().get(project=project, + zone=zone, + operation=op) + operation = request.execute() + + if operation is not None and ('error' in operation or 'httpErrorMessage' in operation): + msg = ', '.join([ '{} (code: {})'.format(error['message'], error['code']) for error in operation['error']['errors'] ]) + raise Exception(msg) + except Exception as e: + raise e + return ret diff --git a/easycloud/modules/gcp_googleapi/manager.py b/easycloud/modules/gcp_googleapi/manager.py index 03cf250..0bf1dba 100644 --- a/easycloud/modules/gcp_googleapi/manager.py +++ b/easycloud/modules/gcp_googleapi/manager.py @@ -4,6 +4,7 @@ EasyCloud Google Cloud Platform Manager import datetime +#from easycloud.common.gcp import GCPComputeUtils from easycloud.core.actionbinder import bind_action from easycloud.core.compute import Instance, InstanceStatus, Location, LocationKind from easycloud.core.metamanager import MetaManager @@ -148,12 +149,14 @@ class GCPInstance(Instance): self._gce_conn.instances().start(project = self._gce_proj, zone = self._zone, instance = self._gce_inst['id']).execute() + #TODO: call "GCPComputeUtils.wait_for_operation(self._gce_conn, self._gce_proj, res)" to perform a synchronous version of this method (where "res" is the value returned by the "start()" method) return True def stop(self): self._gce_conn.instances().stop(project = self._gce_proj, zone = self._zone, instance = self._gce_inst['id']).execute() + #TODO: call "GCPComputeUtils.wait_for_operation(self._gce_conn, self._gce_proj, res)" to perform a synchronous version of this method (where "res" is the value returned by the "start()" method) return True -- GitLab