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

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