diff --git a/lib/ansible/modules/cloud/kubevirt/kubevirt_pvc.py b/lib/ansible/modules/cloud/kubevirt/kubevirt_pvc.py index c98de4ad8dd..de707723374 100644 --- a/lib/ansible/modules/cloud/kubevirt/kubevirt_pvc.py +++ b/lib/ansible/modules/cloud/kubevirt/kubevirt_pvc.py @@ -126,6 +126,21 @@ options: - "This uses the DataVolume source syntax: U(https://github.com/kubevirt/containerized-data-importer/blob/master/doc/datavolumes.md#https3registry-source)" type: dict + wait: + description: + - "If set, this module will wait for the PVC to become bound and CDI (if enabled) to finish its operation + before returning." + - "Used only if I(state) set to C(present)." + - "Unless used in conjuction with I(cdi_source), this might result in a timeout, as clusters may be configured + to not bind PVCs until first usage." + default: false + type: bool + wait_timeout: + description: + - Specifies how much time in seconds to wait for PVC creation to complete if I(wait) option is enabled. + - Default value is reasonably high due to an expectation that CDI might take a while to finish its operation. + type: int + default: 300 extends_documentation_fragment: - k8s_auth_options @@ -277,10 +292,22 @@ PVC_ARG_SPEC = { 'storage_class_name': {'type': 'str'}, 'volume_mode': {'type': 'str'}, 'volume_name': {'type': 'str'}, - 'cdi_source': {'type': 'dict'} + 'cdi_source': {'type': 'dict'}, + 'wait': { + 'type': 'bool', + 'default': False + }, + 'wait_timeout': { + 'type': 'int', + 'default': 300 + } } +class CreatePVCFailed(Exception): + pass + + class KubevirtPVC(KubernetesRawModule): def __init__(self): super(KubevirtPVC, self).__init__() @@ -291,6 +318,12 @@ class KubevirtPVC(KubernetesRawModule): argument_spec.update(PVC_ARG_SPEC) return argument_spec + @staticmethod + def fix_serialization(obj): + if obj and hasattr(obj, 'to_dict'): + return obj.to_dict() + return obj + def _parse_cdi_source(self, _cdi_src, metadata): cdi_src = copy.deepcopy(_cdi_src) annotations = metadata['annotations'] @@ -340,6 +373,35 @@ class KubevirtPVC(KubernetesRawModule): if 'certConfigMap' in src_spec: annotations['cdi.kubevirt.io/storage.import.certConfigMap'] = src_spec['certConfigMap'] + def _wait_for_creation(self, resource, uid): + return_obj = None + desired_cdi_status = 'Succeeded' + use_cdi = True if self.params.get('cdi_source') else False + if use_cdi and 'upload' in self.params['cdi_source']: + desired_cdi_status = 'Running' + + for event in resource.watch(namespace=self.namespace, timeout=self.params.get('wait_timeout')): + entity = event['object'] + metadata = entity.metadata + if not hasattr(metadata, 'uid') or metadata.uid != uid: + continue + if entity.status.phase == 'Bound': + if use_cdi and hasattr(metadata, 'annotations'): + import_status = metadata.annotations.get('cdi.kubevirt.io/storage.pod.phase') + if import_status == desired_cdi_status: + return_obj = entity + break + else: + return_obj = entity + break + elif entity.status.phase == 'Failed': + raise CreatePVCFailed("PVC creation failed") + + if not return_obj: + raise CreatePVCFailed("PVC creation timed out") + + return self.fix_serialization(return_obj) + def execute_module(self): KIND = 'PersistentVolumeClaim' API = 'v1' @@ -379,6 +441,8 @@ class KubevirtPVC(KubernetesRawModule): resource = self.find_resource(KIND, API, fail=True) definition = self.set_defaults(resource, definition) result = self.perform_action(resource, definition) + if self.params.get('wait') and self.params.get('state') == 'present': + result['result'] = self._wait_for_creation(resource, result['result']['metadata']['uid']) self.exit_json(**result)