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

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