<div dir="ltr"><div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Thu, Sep 20, 2018 at 11:51 AM Richard W.M. Jones <<a href="mailto:rjones@redhat.com">rjones@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Previously this output method was almost completely untested.<br>
<br>
This commit adds a fake ovirtsdk4 module so we can test the<br>
-o rhv-upload method fairly completely without needing an actual<br>
oVirt instance around.<br>
---<br>
 v2v/Makefile.am                               |   4 +<br>
 .../ovirtsdk4/__init__.py                     | 146 ++++++++++++++++++<br>
 .../ovirtsdk4/types.py                        | 125 +++++++++++++++<br>
 v2v/test-v2v-o-rhv-upload.sh                  |  51 ++++++<br>
 4 files changed, 326 insertions(+)<br>
<br>
diff --git a/v2v/Makefile.am b/v2v/Makefile.am<br>
index 14183572b..aab356637 100644<br>
--- a/v2v/Makefile.am<br>
+++ b/v2v/Makefile.am<br>
@@ -382,6 +382,7 @@ TESTS += \<br>
        test-v2v-o-openstack.sh \<br>
        test-v2v-o-qemu.sh \<br>
        test-v2v-o-rhv.sh \<br>
+       test-v2v-o-rhv-upload.sh \<br>
        test-v2v-o-vdsm-options.sh \<br>
        test-v2v-oa-option.sh \<br>
        test-v2v-of-option.sh \<br>
@@ -539,6 +540,9 @@ EXTRA_DIST += \<br>
        test-v2v-o-qemu.sh \<br>
        test-v2v-o-rhv.ovf.expected \<br>
        test-v2v-o-rhv.sh \<br>
+       test-v2v-o-rhv-upload.sh \<br>
+       test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py \<br>
+       test-v2v-o-rhv-upload-module/ovirtsdk4/types.py \<br>
        test-v2v-o-rhv-upload-oo-query.sh \<br>
        test-v2v-o-vdsm-oo-query.sh \<br>
        test-v2v-o-vdsm-options.ovf.expected \<br>
diff --git a/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py b/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py<br>
new file mode 100644<br>
index 000000000..2ddb950ea<br>
--- /dev/null<br>
+++ b/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/__init__.py<br>
@@ -0,0 +1,146 @@<br>
+# -*- python -*-<br>
+# Copyright (C) 2018 Red Hat Inc.<br>
+#<br>
+# This program is free software; you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation; either version 2 of the License, or<br>
+# (at your option) any later version.<br>
+#<br>
+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU General Public License along<br>
+# with this program; if not, write to the Free Software Foundation, Inc.,<br>
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.<br>
+<br>
+# Fake ovirtsdk4 module used as a test harness.<br>
+# See v2v/test-v2v-o-rhv-upload.sh<br>
+<br>
+class Error(Exception):<br>
+    pass<br>
+class NotFoundError(Error):<br>
+    pass<br>
+<br>
+class Connection(object):<br>
+    def __init__(<br>
+            self,<br>
+            url = None,<br>
+            username = None,<br>
+            password = None,<br>
+            ca_file = None,<br>
+            log = None,<br>
+            insecure = False,<br>
+    ):<br>
+        pass<br>
+<br>
+    def system_service(self):<br>
+        return SystemService()<br>
+<br>
+class SystemService(object):<br>
+    def data_centers_service(self):<br>
+        return DataCentersService()<br>
+<br>
+    def disks_service(self):<br>
+        return DisksService()<br>
+<br>
+    def image_transfers_service(self):<br>
+        return ImageTransfersService()<br>
+<br>
+    def storage_domains_service(self):<br>
+        return StorageDomainsService()<br>
+<br>
+    def vms_service(self):<br>
+        return VmsService()<br>
+<br>
+class DataCentersService(object):<br>
+    def list(self, search=None, case_sensitive=False):<br>
+        return []<br>
+<br>
+class DiskService(object):<br>
+    def __init__(self, disk_id):<br>
+        self._disk_id = disk_id<br>
+<br>
+    def get(self):<br>
+        return types.Disk()<br>
+<br>
+    def remove(self):<br>
+        pass<br>
+<br>
+class DisksService(object):<br>
+    def add(self, disk=None):<br>
+        return disk<br>
+<br>
+    def disk_service(self, disk_id):<br>
+        return DiskService(disk_id)<br>
+<br>
+class ImageTransferService(object):<br>
+    def __init__(self):<br>
+        self._finalized = False<br>
+<br>
+    def get(self):<br>
+        if self._finalized:<br>
+            raise NotFoundError<br>
+        else:<br>
+            return types.ImageTransfer()<br>
+<br>
+    def finalize(self):<br>
+        self._finalized = True<br>
+<br>
+class ImageTransfersService(object):<br>
+    def add(self, transfer):<br>
+        return transfer<br>
+<br>
+    def image_transfer_service(self, id):<br>
+        return ImageTransferService()<br>
+<br>
+class StorageDomain(object):<br>
+    id = "ba87af68-b630-4211-a73a-694c1a689405"<br>
+<br>
+class StorageDomainsService(object):<br>
+    def list(self, search=None):<br>
+        return [ StorageDomain() ]<br>
+<br>
+class VmsService(object):<br>
+    def add(self, vm):<br>
+        return vm<br>
+<br>
+    def list(self, search=None):<br>
+        return []<br>
+<br>
+# Create a background thread running a web server which is<br>
+# simulating the imageio server.<br></blockquote><div><br></div><div>This functionality should be separated from the fake SDK module, since it is</div><div>not part of the SDK, and may be replaced by real imageio server later.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+from http.server import HTTPServer, BaseHTTPRequestHandler<br>
+import random<br>
+import threading<br>
+<br>
+# Choose a random port number in range [50000,59999]<br>
+imageio_port = random.randint(50000,60000)<br>
+<br>
+class RequestHandler(BaseHTTPRequestHandler):<br></blockquote><div><br></div><div>This request handler is using HTTP/1.0, and will close the connection after</div><div>every request. This is not a good implementation of the imageio server, and also</div><div>hides bugs in this code.</div><div><br></div><div>Should be fixed by adding:</div><div><br></div><div>    protocol_version = "HTTP/1.1"</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    def do_OPTIONS(self): </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+        self.send_response(200)<br>
+        self.send_header("Content-type", "application/json; charset=UTF-8")<br>
+        self.end_headers()<br>
+        # Advertize only zero support.<br>
+        self.wfile.write(b'''{ "features": [ "zero" ] }''')<br>
+<br>
+    # eg. zero request.  Just ignore it.<br>
+    def do_PATCH(self):<br></blockquote><div><br></div><div>This must read content-length bytes from self.rfile.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+        self.send_response(200)<br>
+        self.end_headers()<br>
+<br>
+    # Flush request.  Ignore it.<br>
+    def do_PUT(self):<br></blockquote><div><br></div><div>This must read content-length bytes from self.rfile.</div><div><br></div><div>Both bugs are hidden by the fact that this server close the connection at the end</div><div>of the request.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+        self.send_response(200)<br>
+        self.end_headers()<br>
+<br>
+def server():<br>
+    server_address = ("", imageio_port)<br>
+    httpd = HTTPServer(server_address, RequestHandler)<br>
+    httpd.serve_forever()<br></blockquote><div><br></div><div>Using HTTP instead of HTTPS is not a good idea. We are not testing the same code</div><div>on the client side.</div><div><br></div><div>It is easy to run HTTPS server using pre-created certificate, if we disable certificate</div><div>verification on the client side (e.g. --insecure).</div><div><br></div><div>Nir</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+thread = threading.Thread(target = server, args = [], daemon = True)<br>
+thread.start()<br>
diff --git a/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py b/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py<br>
new file mode 100644<br>
index 000000000..9b3f557ee<br>
--- /dev/null<br>
+++ b/v2v/test-v2v-o-rhv-upload-module/ovirtsdk4/types.py<br>
@@ -0,0 +1,125 @@<br>
+# -*- python -*-<br>
+# Copyright (C) 2018 Red Hat Inc.<br>
+#<br>
+# This program is free software; you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation; either version 2 of the License, or<br>
+# (at your option) any later version.<br>
+#<br>
+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU General Public License along<br>
+# with this program; if not, write to the Free Software Foundation, Inc.,<br>
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.<br>
+<br>
+# Fake ovirtsdk4 module used as a test harness.<br>
+# See v2v/test-v2v-o-rhv-upload.sh<br>
+<br>
+from enum import Enum<br>
+from ovirtsdk4 import imageio_port<br>
+<br>
+class Cluster(object):<br>
+    def __init__(self, name):<br>
+        pass<br>
+<br>
+class Configuration(object):<br>
+    def __init__(self, type=None, data=None):<br>
+        pass<br>
+class ConfigurationType(Enum):<br>
+    OVA = 'ova'<br>
+    OVF = 'ovf'<br>
+<br>
+    def __init__(self, image):<br>
+        self._image = image<br>
+<br>
+    def __str__(self):<br>
+        return self._image<br>
+<br>
+class DiskFormat(Enum):<br>
+    COW = "cow"<br>
+    RAW = "raw"<br>
+<br>
+    def __init__(self, image):<br>
+        self._image = image<br>
+<br>
+    def __str__(self):<br>
+        return self._image<br>
+<br>
+class DiskStatus(Enum):<br>
+    ILLEGAL = "illegal"<br>
+    LOCKED = "locked"<br>
+    OK = "ok"<br>
+<br>
+    def __init__(self, image):<br>
+        self._image = image<br>
+<br>
+    def __str__(self):<br>
+        return self._image<br>
+<br>
+class Disk(object):<br>
+    def __init__(<br>
+            self,<br>
+            id = None,<br>
+            name = None,<br>
+            description = None,<br>
+            format = None,<br>
+            initial_size = None,<br>
+            provisioned_size = None,<br>
+            sparse = False,<br>
+            storage_domains = None<br>
+    ):<br>
+        pass<br>
+<br>
+    id = 123<br>
+    status = DiskStatus.OK<br>
+<br>
+class ImageTransferPhase(Enum):<br>
+    CANCELLED = 'cancelled'<br>
+    FINALIZING_FAILURE = 'finalizing_failure'<br>
+    FINALIZING_SUCCESS = 'finalizing_success'<br>
+    FINISHED_FAILURE = 'finished_failure'<br>
+    FINISHED_SUCCESS = 'finished_success'<br>
+    INITIALIZING = 'initializing'<br>
+    PAUSED_SYSTEM = 'paused_system'<br>
+    PAUSED_USER = 'paused_user'<br>
+    RESUMING = 'resuming'<br>
+    TRANSFERRING = 'transferring'<br>
+    UNKNOWN = 'unknown'<br>
+<br>
+    def __init__(self, image):<br>
+        self._image = image<br>
+<br>
+    def __str__(self):<br>
+        return self._image<br>
+<br>
+class ImageTransfer(object):<br>
+    def __init__(<br>
+            self,<br>
+            disk = None,<br>
+            host = None,<br>
+            inactivity_timeout = None,<br>
+    ):<br>
+        pass<br>
+<br>
+    id = 456<br>
+    phase = ImageTransferPhase.TRANSFERRING<br>
+    transfer_url = "<a href="http://localhost" rel="noreferrer" target="_blank">http://localhost</a>:" + str(imageio_port) + "/"<br>
+<br>
+class Initialization(object):<br>
+    def __init__(self, configuration):<br>
+        pass<br>
+<br>
+class StorageDomain(object):<br>
+    def __init__(self, name = None):<br>
+        pass<br>
+<br>
+class Vm(object):<br>
+    def __init__(<br>
+            self,<br>
+            cluster = None,<br>
+            initialization = None<br>
+    ):<br>
+        pass<br>
diff --git a/v2v/test-v2v-o-rhv-upload.sh b/v2v/test-v2v-o-rhv-upload.sh<br>
new file mode 100755<br>
index 000000000..8bda7cc0b<br>
--- /dev/null<br>
+++ b/v2v/test-v2v-o-rhv-upload.sh<br>
@@ -0,0 +1,51 @@<br>
+#!/bin/bash -<br>
+# libguestfs virt-v2v test script<br>
+# Copyright (C) 2018 Red Hat Inc.<br>
+#<br>
+# This program is free software; you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation; either version 2 of the License, or<br>
+# (at your option) any later version.<br>
+#<br>
+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU General Public License<br>
+# along with this program; if not, write to the Free Software<br>
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.<br>
+<br>
+# Test -o rhv-upload.<br>
+#<br>
+# These uses a test harness (see<br>
+# v2v/test-v2v-o-rhv-upload-module/ovirtsdk4) to fake responses from<br>
+# oVirt.<br>
+<br>
+set -e<br>
+set -x<br>
+<br>
+$TEST_FUNCTIONS<br>
+skip_if_skipped<br>
+skip_if_backend uml<br>
+skip_unless_phony_guest windows.img<br>
+<br>
+libvirt_uri="test://$abs_top_builddir/test-data/phony-guests/guests.xml"<br>
+f=$top_builddir/test-data/phony-guests/windows.img<br>
+<br>
+export VIRT_TOOLS_DATA_DIR="$top_srcdir/test-data/fake-virt-tools"<br>
+export VIRTIO_WIN="$top_srcdir/test-data/fake-virtio-win"<br>
+export PYTHONPATH=$srcdir/test-v2v-o-rhv-upload-module:$PYTHONPATH<br>
+<br>
+# Run virt-v2v -o rhv-upload.<br>
+#<br>
+# The fake ovirtsdk4 module doesn't care about most of the options<br>
+# like -oc, -oo rhv-cafile, -op etc.  Any values may be used.<br>
+$VG virt-v2v --debug-gc -v -x \<br>
+    -i libvirt -ic "$libvirt_uri" windows \<br>
+    -o rhv-upload \<br>
+    -oc <a href="https://example.com/ovirt-engine/api" rel="noreferrer" target="_blank">https://example.com/ovirt-engine/api</a> \<br>
+    -oo rhv-cafile=/dev/null \<br>
+    -oo rhv-direct \<br>
+    -op /dev/null \<br>
+    -os .<br>
-- <br>
2.19.0.rc0<br>
<br>
_______________________________________________<br>
Libguestfs mailing list<br>
<a href="mailto:Libguestfs@redhat.com" target="_blank">Libguestfs@redhat.com</a><br>
<a href="https://www.redhat.com/mailman/listinfo/libguestfs" rel="noreferrer" target="_blank">https://www.redhat.com/mailman/listinfo/libguestfs</a><br>
</blockquote></div></div></div>