|
|
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 |
|