1506 Commits

Author SHA1 Message Date
Albert Esteve
44ee575eb1 Release 2.4.6-1
Some checks failed
CI / test (centos-8, py36) (push) Failing after 1s
CI / test (centos-8, py38) (push) Failing after 2s
CI / test (centos-9, py39) (push) Failing after 1s
CI / test (fedora-35, py310) (push) Failing after 1s
CI / test (fedora-36, py310) (push) Failing after 2s
CI / rpm (centos-8) (push) Failing after 1s
CI / rpm (centos-9) (push) Failing after 1s
CI / rpm (fedora-35) (push) Failing after 1s
CI / rpm (fedora-36) (push) Failing after 1s
Signed-off-by: Albert Esteve <aesteve@redhat.com>
v2.4.6
2022-08-03 14:55:31 +02:00
Albert Esteve
5d2c3829b7 ovirt-img: upload consider sparse to measure
Measure is only required for qcow2 sparse images
to block storage. Currently we only consider the
format, but we can also consider sparse
attribute before measuring the image to avoid
unnecessary measures.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-03 14:14:02 +03:00
Nir Soffer
0f5f5da78a client: Optimize upload to new image
When we upload to a new disk using qcow2 format or new file we know that
the disk is empty and the entire disk contents is read as zeros by the
guest. In this case we can skip zero extents on the source image instead
of zeroing them on the destination image.

Zeroing on the destination image is usually fast, but when uploading big
images it can slow down the upload a lot. When uploading to qcow2 image,
zeroing creates suboptimal image that will be slower to read and copy
later.

Example upload of empty 8 TiB image:

Before:

    ./ovirt-img upload-disk -c engine --storage-domain fc-01 empty-8t.qcow2
    [ 100% ] 8.00 TiB, 222.82 s, 36.77 GiB/s | upload completed

After:

    ./ovirt-img upload-disk -c engine --storage-domain fc-01 empty-8t.qcow2
    [ 100% ] 8.00 TiB, 11.51 s, 711.88 GiB/s | upload completed

Example upload of 8 TiB Fedora 35 image:

Before:

    $ ./ovirt-img upload-disk -c engine --storage-domain fc-01 fedora-35-8t.qcow2
    [ 100% ] 8.00 TiB, 317.88 s, 25.77 GiB/s | upload completed

After:

    $ ./ovirt-img upload-disk -c engine --storage-domain fc-01 fedora-35-8t.qcow2
    [ 100% ] 8.00 TiB, 109.13 s, 75.07 GiB/s | upload completed

Fixes: #76
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-08-01 16:46:37 +02:00
Nir Soffer
8cdb4cd540 tests: Update comment about comparing images
We know why we cannot compare allocation for raw and qcow2 images.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-08-01 16:46:37 +02:00
Nir Soffer
10144cbcc1 client: Improve whitespace for better readability
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-08-01 16:46:37 +02:00
Nir Soffer
84e7b9c7cb client: Move sparse selection to is_iso block
We need the sparse property to select if the disk is zero or not.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-08-01 16:46:37 +02:00
Albert Esteve
c77a1ba4d1 ovirt-img: add name option for disk
Add '--name' option to the 'upload-disk' command
to allow users to define a custom alias name
for the new uploaded disk. If ommited, default
behaviour remains, taking the source image filename
as an alias.

usage: ovirt-img upload-disk [-h] [-c CONFIG] [--engine-url ENGINE_URL]
                             [--username USERNAME]
                             [--password-file PASSWORD_FILE] [--cafile CAFILE]
                             [--log-file LOG_FILE] [--log-level LOG_LEVEL]
                             [--max-workers MAX_WORKERS]
                             [--buffer-size BUFFER_SIZE] -s STORAGE_DOMAIN
                             [-f {raw,qcow2}] [--preallocated]
                             [--disk-id DISK_ID] [--name NAME]
                             filename
....
  --name NAME           Alias name for the new disk. If not specified, name
                        will correspond with the image filename.

Closes: #121
Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-01 17:20:51 +03:00
Albert Esteve
a7b926646f ovirt-img: upload-disk
Add upload-disk sub-command for upload disks.

$ ./ovirt-img upload-disk -h
usage: ovirt-img upload-disk [-h] [-c CONFIG] [--engine-url ENGINE_URL]
                             [--username USERNAME]
                             [--password-file PASSWORD_FILE] [--cafile CAFILE]
                             [--log-file LOG_FILE] [--log-level LOG_LEVEL]
                             [--max-workers MAX_WORKERS]
                             [--buffer-size BUFFER_SIZE] -s STORAGE_DOMAIN
                             [-f {raw,qcow2}] [--preallocated] [--disk-id ID]
                             filename

positional arguments:
  filename              Path to image to upload. Supported formats: raw,
                        qcow2, iso.

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG, --config CONFIG
                        If set, read specified section from the configuration
                        file.
  --engine-url ENGINE_URL
                        ovirt-engine URL. If not set, read from the specified
                        config section (required).
  --username USERNAME   ovirt-engine username. If not set, read from the
                        specified config section (required).
  --password-file PASSWORD_FILE
                        Read ovirt-engine password from file. If not set, read
                        password from the specified config section, or prompt
                        the user for the password.
  --cafile CAFILE       Path to ovirt-engine CA certificate. If not set, read
                        from the specified config section
  --log-file LOG_FILE   Log to file instead of stderr.
  --log-level LOG_LEVEL
                        Log level (choices: {debug, info, warning, error},
                        default: warning).
  --max-workers MAX_WORKERS
                        Maximum number of workers (range: 1-8, default: 4).
  --buffer-size BUFFER_SIZE
                        Buffer size per worker (range: 64k-16m, default: 4m).
  -s STORAGE_DOMAIN, --storage-domain STORAGE_DOMAIN
                        Name of the storage domain.
  -f {raw,qcow2}, --format {raw,qcow2}
                        Upload image format (default qcow2 for data disks and
                        raw for iso disks).
  --preallocated        Create preallocated disk. Required when using raw
                        format on block based storage domain (iSCSI, FC). ISO
                        images are always uploaded to preallocated disk.
  --disk-id ID          A UUID for the new disk. If not specified oVirt will
                        create a new UUID.

$ ./ovirt-img upload-disk --config engine --storage-domain iscsi-sd disk1.qcow2
[ ----  ] 0 bytes, 0.00 s, 0 bytes/s | inspecting image
[ ----  ] 0 bytes, 0.01 s, 0 bytes/s | creating disk
[ ----  ] 0 bytes, 6.39 s, 0 bytes/s | creating transfer
[  38%  ] 2.29 GiB, 15.99 s, 146.59 MiB/s | uploading image
[ 100%  ] 6.00 GiB, 22.88 s, 268.56 MiB/s | finalizing transfer
[ 100%  ] 6.00 GiB, 33.02 s, 186.07 MiB/s | upload completed

Possible combinations:
Content | Storage | Backup | Format | Allocation   | Allowed
------- | ------- | ------ | ------ | ------------ | -------
data    | block   | yes    | qcow   | sparse       | ✓
data    | block   | yes    | qcow   | preallocated | ✓
data    | block   | no     | raw    | sparse       | x
data    | block   | no     | raw    | preallocated | ✓
data    | file    | yes    | qcow   | sparse       | ✓
data    | file    | yes    | qcow   | preallocated | ✓
data    | file    | no     | raw    | sparse       | ✓
data    | file    | no     | raw    | sparse       | ✓
iso     | block   | no     | raw    | preallocated | ✓
iso     | file    | no     | raw    | preallocated | ✓

Note: Source allocation policies omitted as they make no difference.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-01 15:23:46 +03:00
Albert Esteve
e5c5efed55 ovirt-img: add arg for subcommand transfer options
Add an additional argument to the options.add_sub_command
function to optionally (True by default) add common
transfer-related options to the command.

Currently these options are "--max-workers" and
"--buffer-size".

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-01 15:23:46 +03:00
Albert Esteve
2aad7979d4 ovirt-img: add uuid options type for validation
Add UUID type for UUID normalization and parameter
validation. Use it to verify disk_id in download-disk.

Output:
ovirt-img download-disk: error: argument disk_id:
    invalid UUID value: 'invalid-uuid-argument'

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-01 15:23:46 +03:00
Albert Esteve
69d286d0a0 ovirt-img: new add_disk
Add a new "add_disk" function to the _ovirt
module for the ovirt-img tool. Add disk allows
to create a new disk in the engine through an
open connection.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-08-01 15:23:46 +03:00
Nir Soffer
ccb022cc90 ovirt-img: Terminate gracefully on signals
Register signals handlers before running the command, and handle
TerminatedBySignal exception gracefully.

When terminated by signal, log an error instead of a traceback, since
this is expected condition. Since termination by SIGINT is typically
initiated by a user log info level message.

For any other error, log a traceback to make it easy to report useful
bug reports.

An example run when ovirt-img is interrupted:

    $ ./ovirt-img download-disk -c engine adb9b2c7 dl.qcow2
    [  49% ] 3.00 GiB, 4.68 s, 656.51 MiB/s | command failed

An example run when ovirt-img is interrupted with more verbose logging:

    $ ./ovirt-img download-disk -c engine adb9b2c7 dl.qcow2 --log-level info
    ...
    [  70% ] 4.25 GiB, 3.62 s, 1.17 GiB/s | command failed
    2022-07-28 18:08:16,562 INFO    (MainThread) [tool] Exiting: Terminated by signal 2

An example run when ovirt-img is terminated::

    $ ./ovirt-img download-disk -c engine 07b53997 dl.qcow2
    [  26% ] 13.25 GiB, 26.23 s, 517.27 MiB/s | command failed
    2022-07-28 18:15:53,518 ERROR   (MainThread) [tool] Exiting: Terminated by signal 15

An example run when qemu-nbd is terminated:

    $ ./ovirt-img download-disk -c engine 07b53997 dl.qcow2
    [  17% ] 8.75 GiB, 16.74 s, 535.39 MiB/s | command failed
    2022-07-28 18:20:23,721 ERROR   (MainThread) [tool] Command failed
    Traceback (most recent call last):
      File "ovirt-imageio/ovirt_imageio/client/_tool.py", line 38, in main
        args.command(args)
      File "ovirt-imageio/ovirt_imageio/client/_download.py", line 73, in download_disk
        _api.download(
      File "ovirt-imageio/ovirt_imageio/client/_api.py", line 179, in download
        _io.copy(
      File "ovirt-imageio/ovirt_imageio/client/_io.py", line 46, in copy
        with Executor(name=name) as executor:
      File "ovirt-imageio/ovirt_imageio/client/_io.py", line 198, in __exit__
        self.stop()
      File "ovirt-imageio/ovirt_imageio/client/_io.py", line 175, in stop
        raise self._errors[0]
      File "ovirt-imageio/ovirt_imageio/client/_io.py", line 248, in _run
        handler.copy(req)
      File "ovirt-imageio/ovirt_imageio/client/_io.py", line 292, in copy
        self._src.write_to(self._dst, req.length, self._buf)
      File "ovirt-imageio/ovirt_imageio/_internal/backends/http.py", line 220, in write_to
        writer.write(view[:n])
      File "ovirt-imageio/ovirt_imageio/_internal/backends/nbd.py", line 119, in write
        self._client.write(self._position, buf)
      File "ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 445, in write
        self._recv_reply(cmd)
      File "ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 959, in _recv_reply
        magic = self._recv_fmt("!I")[0]
      File "ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 1221, in _recv_fmt
        data = self._recv(s.size)
      File "ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 1231, in _recv
        self._recv_into(buf)
      File "ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 1241, in _recv_into
        raise ProtocolError(
    ovirt_imageio._internal.nbd.ProtocolError: Server closed the connection, read 0 bytes, expected 4 bytes

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Nir Soffer
099b142fdb api: Block SIGINT when running qemu-ndb
If the application is handling signals, run qemu-nbd with SIGINT
blocked. This will allow clean termination when interrupting the
ovirt-img tool.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Nir Soffer
96b387bdfc qemu_nbd: Support blocking signals
Add block_signals argument to run qemu-nbd with blocked signals. This is
useful for clean termination when qemu-nbd is managed by a client that
handle signals.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Nir Soffer
4981296dd1 client: Infrastructure for signal handling
Add an _app module for managing application global state. The module
registers signal handlers for SIGINT and SIGTERM. When signal is
received, it saves the first signal.

The worker copy loop checks now if the application was terminated, and
raises _app.TerminatedBySignal. This error is propagated to main() so
the tool can handle termination by signals gracefully.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Nir Soffer
286561310b io: Move the io module to the client
The io module is used only on the client side, and we don't have plans
to use it on the server side. On the client side we can teach io.copy()
to abort cleanly when the application is terminated, which will simplify
signal handling.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Nir Soffer
03d18c9759 io: Suppress worker exceptions
We raise the first error in the thread calling io.copy(), so the caller
can log a detailed exception if needed. However we also logged a
traceback for every failed worker, which log the same error twice, and
makes termination by signal a big mess.

Replace the exception log with debug log for cleaner termination.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-29 08:33:27 +02:00
Albert Esteve
d409e771e8 Revert "io: Suppress worker exceptions"
This reverts commit bf73f3ae88f40195cdd897e2badcedfa42a326ec.
2022-07-28 17:25:20 +02:00
Albert Esteve
11718094bb Revert "io: Move the io module to the client"
This reverts commit f3db5878461409d21dc0c502440366627c508921.
2022-07-28 17:25:20 +02:00
Albert Esteve
e4d03f5bb6 Revert "ovirt-img: Terminate gracefully on signals"
This reverts commit e1c73466c91466f915b788c97f81f8e97d2ff619.
2022-07-28 17:25:20 +02:00
Nir Soffer
e1c73466c9 ovirt-img: Terminate gracefully on signals
When receiving a termination signal (SIGINT, SIGTERM), send the signal
to the entire process group. This will terminate qemu-nbd and trigger
normal cleanup flow.

When handling command errors, suppress errors if the tool was terminated
by signal for cleaner termination.

Example interrupted run:

    $ ./ovirt-img download-disk -c engine adb9b2c7-32a9-4e87-894c-710de7165086 dl.qcow2
    [  87% ] 5.27 GiB, 4.84 s, 1.09 GiB/s | command failed
    2022-07-28 12:49:15,731 ERROR   (MainThread) [tool] Terminated by signal 2

Example run when qemu-nbd is killed during transfer:

    $ ./ovirt-img download-disk -c engine 1860d15a-e407-4b52-9b72-bf7555bef48f dl.qcow2
    [  85% ] 8.50 GiB, 16.33 s, 533.12 MiB/s | command failed
    2022-07-28 12:53:06,143 ERROR   (MainThread) [tool] Command failed
    Traceback (most recent call last):
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/client/_tool.py", line 41, in main
        args.command(args)
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/client/_download.py", line 73, in download_disk
        _api.download(
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/client/_api.py", line 176, in download
        io.copy(
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/io.py", line 44, in copy
        with Executor(name=name) as executor:
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/io.py", line 196, in __exit__
        self.stop()
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/io.py", line 173, in stop
        raise self._errors[0]
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/io.py", line 245, in _run
        handler.copy(req)
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/io.py", line 289, in copy
        self._src.write_to(self._dst, req.length, self._buf)
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/backends/http.py", line 220, in write_to
        writer.write(view[:n])
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/backends/nbd.py", line 119, in write
        self._client.write(self._position, buf)
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 444, in write
        self._send(data)
      File "/home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/nbd.py", line 1227, in _send
        self._sock.sendall(data)
    BrokenPipeError: [Errno 32] Broken pipe

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 15:15:40 +02:00
Nir Soffer
f3db587846 io: Move the io module to the client
The io module is used only on the client side, and we don't have plans
to use it on the server side. On the client side we can teach io.copy()
to abort cleanly when the application is terminated, which will simplify
signal handling.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 15:15:40 +02:00
Nir Soffer
bf73f3ae88 io: Suppress worker exceptions
We raise the first error in the thread calling io.copy(), so the caller
can log a detailed exception if needed. However we also logged a
traceback for every failed worker, which log the same error twice, and
makes termination by signal a big mess.

Replace the exception log with debug log for cleaner termination.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 15:15:40 +02:00
Nir Soffer
78991c53d0 ovirt-img: Add missing tests
Test that parsing options without arguments is same as --help.

Fixes: 744799e00467 (ovirt-img: Fix running without arguments)
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 12:19:34 +03:00
Albert Esteve
df280abacf ovirt-img: fix logging config
In ovit-img log_file defaults to /dev/stderr fd,
and is passed to logging configurator through
filename argument.

However, this is not correct, filename only is
used for files. File descriptions shall go
as stream parameters.

Otherwise the tool breaks with
'OSError: [Errno 29] Illegal seek'
in an internal logging module.
Reproduced with Python 3.6 in CentOS Stream 8.

To solve it, default log_file to None instead, so
that logging configuration does not use a filename
and falls back to the stream argument, which defaults
to stderr.

Fixes: a2bbe122cc85d08dd5ab60e6d2438b54b1f11e6b
Fixes: #113
Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-07-28 12:04:15 +03:00
Nir Soffer
5d05800482 ovirt-img: Show download disk phases
During ovirt-img download-disk we show now the progress phase. Here is
an example run showing the different phases:

    $ ./ovirt-img download-disk -c engine adb9b2c7-32a9-4e87-894c-710de7165086 dl.qcow2 | tr "\r" "\n"
    [ ---- ] 0 bytes, 0.00 s, 0 bytes/s | creating transfer
    [ ---- ] 0 bytes, 0.65 s, 0 bytes/s | downloading image
    [   0% ] 0 bytes, 0.71 s, 0 bytes/s | downloading image
    [   0% ] 960.00 KiB, 0.71 s, 1.32 MiB/s | downloading image
    [   2% ] 159.12 MiB, 0.71 s, 224.59 MiB/s | downloading image
    [   6% ] 415.00 MiB, 0.71 s, 585.68 MiB/s | downloading image
    [  10% ] 663.38 MiB, 0.71 s, 936.15 MiB/s | downloading image
    [  14% ] 914.75 MiB, 0.71 s, 1.26 GiB/s | downloading image
    [  15% ] 932.06 MiB, 0.71 s, 1.28 GiB/s | downloading image
    [  30% ] 1.81 GiB, 0.72 s, 2.51 GiB/s | downloading image
    [  31% ] 1.87 GiB, 0.83 s, 2.24 GiB/s | downloading image
    [  44% ] 2.68 GiB, 0.86 s, 3.12 GiB/s | downloading image
    [  45% ] 2.74 GiB, 1.01 s, 2.72 GiB/s | downloading image
    [  46% ] 2.81 GiB, 1.14 s, 2.47 GiB/s | downloading image
    [  64% ] 3.88 GiB, 1.16 s, 3.34 GiB/s | downloading image
    [  66% ] 3.96 GiB, 1.25 s, 3.17 GiB/s | downloading image
    [  67% ] 4.03 GiB, 1.26 s, 3.19 GiB/s | downloading image
    [  68% ] 4.11 GiB, 1.30 s, 3.17 GiB/s | downloading image
    [  83% ] 5.03 GiB, 1.35 s, 3.74 GiB/s | downloading image
    [  86% ] 5.16 GiB, 1.35 s, 3.82 GiB/s | downloading image
    [  87% ] 5.25 GiB, 1.58 s, 3.32 GiB/s | downloading image
    [  88% ] 5.29 GiB, 1.64 s, 3.24 GiB/s | downloading image
    [  89% ] 5.37 GiB, 1.79 s, 3.00 GiB/s | downloading image
    [  91% ] 5.50 GiB, 1.95 s, 2.82 GiB/s | downloading image
    [  92% ] 5.57 GiB, 1.97 s, 2.83 GiB/s | downloading image
    [  93% ] 5.62 GiB, 1.97 s, 2.85 GiB/s | downloading image
    [  94% ] 5.69 GiB, 2.07 s, 2.74 GiB/s | downloading image
    [  95% ] 5.70 GiB, 2.12 s, 2.70 GiB/s | downloading image
    [  96% ] 5.80 GiB, 2.39 s, 2.42 GiB/s | downloading image
    [  98% ] 5.91 GiB, 2.42 s, 2.45 GiB/s | downloading image
    [  99% ] 5.97 GiB, 2.45 s, 2.44 GiB/s | downloading image
    [ 100% ] 6.00 GiB, 2.45 s, 2.45 GiB/s | downloading image
    [ 100% ] 6.00 GiB, 2.48 s, 2.42 GiB/s | finalizing transfer
    [ 100% ] 6.00 GiB, 4.56 s, 1.32 GiB/s | download completed
    [ 100% ] 6.00 GiB, 4.56 s, 1.32 GiB/s | download completed

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 11:55:32 +03:00
Nir Soffer
06c2c7a338 ovirt-img: Read password in the parser
Reading the password in _ovirt.connect() does not work with creating a
progress bar before connecting. Change the parser to read the password
before returning the parsed arguments. Callers can consume the password
from args.password.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 11:55:32 +03:00
Nir Soffer
776698892d client: Add ProgressBar error_phase
When running an operation with a progress bar as context manager, the
code run in the context may fail. In this case we wan to display that
the operation failed in the progress.

Add error_phase argument defaulting to "command failed" to make this
easy.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 10:45:30 +03:00
Nir Soffer
1de1964240 client: Show progress phase
Transferring an image include several phases that may take several
seconds, and we want to show what the tool is doing. When we finish, we
want to show that the operation was completed. If we failed, we want to
show a short failure message even if we use log errors to file.

Add an optional phase argument to ProgressBar. If the phase is set, it
is displayed at the end:

    [ ---- ] 0 bytes, 0.00 s, 0 bytes/s | setting up

The caller can change the phase by setting a new value:

    pb.phase = "downloading image"

This will redraw the progress bar:

    [   0% ] 0 bytes, 0.00 s, 0 bytes/s | downloading image

This change only adds the infrastructure, no change yet in the actual
commands.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 10:45:30 +03:00
Nir Soffer
c0b6250547 client: Redraw progress when changing size
With current code we update the progress very quickly after getting the
size, so it it does not matter. But in future versions we will get the
size from OPTIONS response, and when downloading big images this can
update the progress few seconds before the first byte is transferred.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 10:45:30 +03:00
Nir Soffer
079a2455f6 client: Spelling in ui test
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 10:45:30 +03:00
Nir Soffer
56ef8eb80f client: Make ui tests easier to work with
Make it easier to assert about the progress line by separating the
progress text and the assert about the text width and line terminator.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-28 10:45:30 +03:00
Nir Soffer
9db92cfe7e ovirt-img: Add logging options
The ovirt-img tool support now optional logging options:

  --log-file LOG_FILE   Log file name (default: /dev/stderr).
  --log-level LOG_LEVEL
                        Log level (choices: {debug, info, warning, error},
                        default: warning).

Users can change the default log file and level using the configuration
file:

    [engine]
    log_file = /home/user/.ovirt-img/engine.log
    log_level = info

Like other options, command lines options override values from the
config file.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-27 13:14:11 +02:00
Nir Soffer
487483901e ovirt-img: More general options parsing
Add infrastructure to support mixing command line arguments and config
file options.

Parser options are defined now using list of Option named tuple. We use
the list to add arguments to the arguments parser, read and merge config
file options, set defaults, and check required arguments.

We support now:
- command line only arguments (e.g. --config, --password-file)
- config only arguments (e.g. password)
- default values if the command line argument and the config file do not
  specify a value.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-27 13:14:11 +02:00
Nir Soffer
744799e004 ovirt-img: Fix running without arguments
When running without arguments we fail after parsing the arguments
because `config` is not set, and required arguments are missing. Fix by
detecting the case when no command was specified and failing with useful
help (like ovirt-img --help). This is a common behavior for tool that
has sub commands (e.g git).

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-27 13:14:11 +02:00
Nir Soffer
8719052d8f ovirt-img: Package as part of ovirt-imageio-client
The ovirt-imageio-client package installs now the ovirt-img tool, and
requires python3-ovirt-engine-sdk4.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-27 08:53:08 +02:00
Nir Soffer
549d8bb68e client: Use units constants in ui tests
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
6ace659faa client: Make conditional expression more clear
Using parenthesis is not required but make the expression more clear.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
676498cf72 client: Modernize ProgressBar._draw
- Use fstring for formatting the progress line
- Use conditional expression

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
d31ad920e6 client: Ensure closed progress is not updated
Add a closed flag, ensuring that progress is not updated after closing.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
1fe9586b95 client: Remove wrong check
The progress is always drawn in __init__, so we always want to draw when
closing.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
101819e6c5 client: Make private attributes private
Change ProgressBar private attributes to private names. The only public
attribute is the size which is set only after connecting to the source
and getting the source size.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
d0346718ae client: Spelling fixes
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:09:49 +02:00
Nir Soffer
ed29f76ea6 client: Less noisy progress
Previously we updated the progress up to 10 times per second, and we
displayed fractional percent values (e.g. 12.57%). This is too noisy and
creates too many uninteresting updates. It also calls
time.monotonic_time() on every update to check if it is time to update
which is waste of resources for very little benefit.

Change the progress to show integer values (12%) and update the progress
only when the progress value changes. With this change we update the
progress up to 100 times during a transfer.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 13:56:23 +03:00
Nir Soffer
1e68d9e420 ovirt-img: Add configuration file
Add -c,--config option specifying the config section in the ovirt-img
configuration file.

Example file:

    $ cat ~/.config/ovirt-img.conf
    [engine]
    engine_url = https://my.engine
    username = admin@internal
    password = password
    cafile = /home/user/certs/my.engine.pem

The configuration file can contain multiple sections. This can be useful
for people managing multiple oVirt environments.

Using a configuration file downloading is much simpler:

    $ ./ovirt-img download-disk -c engine adb9b2c7-32a9-4e87-894c-710de7165086 disk.qcow2

If the same options are specified both in the command line and the
config file, command line options win.

The password option is special case; if --password-file is specified, we
read the password from the password file even if password is specified
in the config file.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00
Nir Soffer
3858fbca43 tool: Add --max-workers and --buffer-size options
These options are useful mostly for testing performance and choosing
good defaults. They may be useful for users if the defaults are not
optimal for their environment.

Example usage:

    ./ovirt-img download-disk --buffer-size 256k --max-workers 2 ...

Using the size validator we can show online help using human sizes:

    $ ./ovirt-img download-disk -h
    ...
      --max-workers MAX_WORKERS
                            Maximum number of workers (range: 1-8, default: 4).
      --buffer-size BUFFER_SIZE
                            Buffer size per worker (range: 64k-16m, default: 4m).

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00
Nir Soffer
9c968a81dd client: Export also MAX_WORKERS
Export also MAX_WORKERS to help users choose a good default value,
similar to BUFFER_SIZE. Users using the client defaults will be updated
automatically when the client changes the default value.

Both names are also exported now from the _api module for internal
usage.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00
Nir Soffer
b10378d4a2 tool: Add size validator
The Size validator accept human-size like "1m" and convert it an
integer.

    >>> size = _options.Size()
    >>> size("2g")
    2147483648

The validator supports minimum and maximum values to ensure
that user input in in the valid range:

    >>> size = Size(minimum=4*KiB, maximum=1*MiB)
    >>> size("42")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "ovirt_imageio/client/_options.py", line 89, in __call__
        raise ValueError(f"Size {s!r} < {self.minimum}")
    ValueError: Size '42' < 4k
    >>> size("2m")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "ovirt_imageio/client/_options.py", line 92, in __call__
        raise ValueError(f"Size {s!r} > {self.maximum}")
    ValueError: Size '2m' > 1m

Finally the validator limits are printed as size value. This makes it
easy to show the minimum, maximum and default value in online help:

    >>> size = Size(minimum=4*KiB, default=256*KiB, maximum=1*MiB)
    >>> f"range: {size.minimum}-{size.maximum} default: {size.default}"
    'range: 4k-1m default: 256k'

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00
Nir Soffer
c96dc8eaa8 Start the ovirt-img tool
ovirt-img is a tool for transferring disk images, replacing the oVirt
python SDK examples.

This is a minimal version implementing only download disk. More commands
will be added later.

The tool can be used without a configuration file; this will be handy
when running the tool in a container.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00
Nir Soffer
6f8ff16357 Add ovirt engine python sdk
The tool package depends on ovirt engine python sdk. This import is
problematic since the python sdk is not packaged for Fedora, but we can
consume it via pip.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2022-07-25 12:56:28 +03:00