Blame SOURCES/0048-o-rhv-upload-wait-for-VM-creation-task.patch

e21fe6
From 8036ab4bc8f37030fcaceda14678cb14dbbed547 Mon Sep 17 00:00:00 2001
e21fe6
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= <tgolembi@redhat.com>
e21fe6
Date: Wed, 20 Apr 2022 17:14:26 +0200
e21fe6
Subject: [PATCH] -o rhv-upload: wait for VM creation task
e21fe6
MIME-Version: 1.0
e21fe6
Content-Type: text/plain; charset=UTF-8
e21fe6
Content-Transfer-Encoding: 8bit
e21fe6
e21fe6
oVirt API call for VM creation finishes before the VM is actually
e21fe6
created. Entities may be still locked after virt-v2v terminates and if
e21fe6
user tries to perform (scripted) actions after virt-v2v those operations
e21fe6
may fail. To prevent this it is useful to monitor the task and wait for
e21fe6
the completion. This will also help to prevent some corner case
e21fe6
scenarios (that would be difficult to debug) when the VM creation job
e21fe6
fails after virt-v2v already termintates with success.
e21fe6
e21fe6
Thanks: Nir Soffer
e21fe6
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1985827
e21fe6
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
e21fe6
Reviewed-by: Arik Hadas <ahadas@redhat.com>
e21fe6
Reviewed-by: Nir Soffer <nsoffer@redhat.com>
e21fe6
(cherry picked from commit 291edb363e841e1c555954a070def671a651cfab)
e21fe6
---
e21fe6
 .../ovirtsdk4/__init__.py                     | 10 +++-
e21fe6
 .../ovirtsdk4/types.py                        | 19 +++++++
e21fe6
 v2v/rhv-upload-createvm.py                    | 57 ++++++++++++++++++-
e21fe6
 3 files changed, 84 insertions(+), 2 deletions(-)
e21fe6
e21fe6
diff --git a/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py b/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py
e21fe6
index abb7050c..ba0649cb 100644
e21fe6
--- a/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py
e21fe6
+++ b/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py
e21fe6
@@ -63,6 +63,9 @@ class SystemService(object):
e21fe6
     def disks_service(self):
e21fe6
         return DisksService()
e21fe6
 
e21fe6
+    def jobs_service(self):
e21fe6
+        return JobsService()
e21fe6
+
e21fe6
     def image_transfers_service(self):
e21fe6
         return ImageTransfersService()
e21fe6
 
e21fe6
@@ -108,6 +111,11 @@ class DisksService(object):
e21fe6
         return DiskService(disk_id)
e21fe6
 
e21fe6
 
e21fe6
+class JobsService(object):
e21fe6
+    def list(self, search=None):
e21fe6
+        return [types.Job()]
e21fe6
+
e21fe6
+
e21fe6
 class ImageTransferService(object):
e21fe6
     def __init__(self):
e21fe6
         self._finalized = False
e21fe6
@@ -139,7 +147,7 @@ class StorageDomainsService(object):
e21fe6
 
e21fe6
 
e21fe6
 class VmsService(object):
e21fe6
-    def add(self, vm):
e21fe6
+    def add(self, vm, query=None):
e21fe6
         return vm
e21fe6
 
e21fe6
     def list(self, search=None):
e21fe6
diff --git a/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py b/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py
e21fe6
index 732887aa..8e734756 100644
e21fe6
--- a/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py
e21fe6
+++ b/tests/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py
e21fe6
@@ -138,6 +138,25 @@ class Initialization(object):
e21fe6
         pass
e21fe6
 
e21fe6
 
e21fe6
+class JobStatus(Enum):
e21fe6
+    ABORTED = "aborted"
e21fe6
+    FAILED = "failed"
e21fe6
+    FINISHED = "finished"
e21fe6
+    STARTED = "started"
e21fe6
+    UNKNOWN = "unknown"
e21fe6
+
e21fe6
+    def __init__(self, image):
e21fe6
+        self._image = image
e21fe6
+
e21fe6
+    def __str__(self):
e21fe6
+        return self._image
e21fe6
+
e21fe6
+
e21fe6
+class Job(object):
e21fe6
+    description = "Fake job"
e21fe6
+    status = JobStatus.FINISHED
e21fe6
+
e21fe6
+
e21fe6
 class StorageDomain(object):
e21fe6
     def __init__(self, name=None):
e21fe6
         pass
e21fe6
diff --git a/v2v/rhv-upload-createvm.py b/v2v/rhv-upload-createvm.py
e21fe6
index 50bb7e34..8887c52b 100644
e21fe6
--- a/v2v/rhv-upload-createvm.py
e21fe6
+++ b/v2v/rhv-upload-createvm.py
e21fe6
@@ -19,12 +19,54 @@
e21fe6
 import json
e21fe6
 import logging
e21fe6
 import sys
e21fe6
+import time
e21fe6
+import uuid
e21fe6
 
e21fe6
 from urllib.parse import urlparse
e21fe6
 
e21fe6
 import ovirtsdk4 as sdk
e21fe6
 import ovirtsdk4.types as types
e21fe6
 
e21fe6
+
e21fe6
+def debug(s):
e21fe6
+    if params['verbose']:
e21fe6
+        print(s, file=sys.stderr)
e21fe6
+        sys.stderr.flush()
e21fe6
+
e21fe6
+
e21fe6
+def jobs_completed(system_service, correlation_id):
e21fe6
+    jobs_service = system_service.jobs_service()
e21fe6
+
e21fe6
+    try:
e21fe6
+        jobs = jobs_service.list(
e21fe6
+            search="correlation_id=%s" % correlation_id)
e21fe6
+    except sdk.Error as e:
e21fe6
+        debug(
e21fe6
+            "Error searching for jobs with correlation id %s: %s" %
e21fe6
+            (correlation_id, e))
e21fe6
+        # We don't know, assume that jobs did not complete yet.
e21fe6
+        return False
e21fe6
+
e21fe6
+    # STARTED is the only "in progress" status, anything else means the job
e21fe6
+    # has already terminated.
e21fe6
+    if all(job.status != types.JobStatus.STARTED for job in jobs):
e21fe6
+        failed_jobs = [(job.description, str(job.status))
e21fe6
+                       for job in jobs
e21fe6
+                       if job.status != types.JobStatus.FINISHED]
e21fe6
+        if failed_jobs:
e21fe6
+            raise RuntimeError(
e21fe6
+                "Failed to create a VM! Failed jobs: %r" % failed_jobs)
e21fe6
+        return True
e21fe6
+    else:
e21fe6
+        running_jobs = [(job.description, str(job.status)) for job in jobs]
e21fe6
+        debug("Some jobs with correlation id %s are running: %s" %
e21fe6
+              (correlation_id, running_jobs))
e21fe6
+        return False
e21fe6
+
e21fe6
+
e21fe6
+# Seconds to wait for the VM import job to complete in oVirt.
e21fe6
+timeout = 3 * 60
e21fe6
+
e21fe6
 # Parameters are passed in via a JSON doc from the OCaml code.
e21fe6
 # Because this Python code ships embedded inside virt-v2v there
e21fe6
 # is no formal API here.
e21fe6
@@ -67,6 +109,7 @@ system_service = connection.system_service()
e21fe6
 cluster = system_service.clusters_service().cluster_service(params['rhv_cluster_uuid'])
e21fe6
 cluster = cluster.get()
e21fe6
 
e21fe6
+correlation_id = str(uuid.uuid4())
e21fe6
 vms_service = system_service.vms_service()
e21fe6
 vm = vms_service.add(
e21fe6
     types.Vm(
e21fe6
@@ -77,5 +120,17 @@ vm = vms_service.add(
e21fe6
                 data=ovf,
e21fe6
             )
e21fe6
         )
e21fe6
-    )
e21fe6
+    ),
e21fe6
+    query={'correlation_id': correlation_id},
e21fe6
 )
e21fe6
+
e21fe6
+# Wait for the import job to finish.
e21fe6
+endt = time.monotonic() + timeout
e21fe6
+while True:
e21fe6
+    time.sleep(10)
e21fe6
+    if jobs_completed(system_service, correlation_id):
e21fe6
+        break
e21fe6
+    if time.monotonic() > endt:
e21fe6
+        raise RuntimeError(
e21fe6
+            "Timed out waiting for VM creation!"
e21fe6
+            " Jobs still running for correlation id %s" % correlation_id)
e21fe6
-- 
e21fe6
2.31.1
e21fe6