Skip to content

Commit 01be59f

Browse files
committed
ovirt-img: support json output
Add json as an output option, to print the progress in jsonlines[1] format. A consistent machine readable format, that can be later parsed. $ ovirt-img download-disk -c engine --output json <disk_id> download.raw {"transferred": 0, elapsed: 0.0, "description": "setting up"} ... {"transferred": 249561088, "size": 261095424, elapsed: 1.234567, "description": "downloading image"} {"transferred": 256901120, "size": 261095424, elapsed: 1.345678, "description": "downloading image"} {"transferred": 261095424, "size": 261095424, elapsed: 1.456789, "description": "finalizing transfer"} ... [1] https://jsonlines.org/ Fixes: oVirt#154 Signed-off-by: Albert Esteve <aesteve@redhat.com>
1 parent 0620ae4 commit 01be59f

File tree

4 files changed

+90
-2
lines changed

4 files changed

+90
-2
lines changed

ovirt_imageio/client/_options.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __repr__(self):
4646

4747

4848
log_level = Choices("log_level", ("debug", "info", "warning", "error"))
49-
output_format = Choices("output_format", ("text",))
49+
output_format = Choices("output_format", ("human", "json"))
5050

5151

5252
def bool_string(value):

ovirt_imageio/client/_ui.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-FileCopyrightText: Red Hat, Inc.
22
# SPDX-License-Identifier: GPL-2.0-or-later
33

4+
import json
45
import sys
56
import threading
67
import time
@@ -9,6 +10,7 @@
910

1011

1112
FORMAT_TEXT = 'text'
13+
FORMAT_JSON = 'json'
1214

1315
DEFAULT_WIDTH = 79
1416

@@ -52,8 +54,24 @@ def draw(self, elapsed, size, value, transferred, phase=None, last=False):
5254
self._write_output(line, end)
5355

5456

57+
class JsonFormat(OutputFormat):
58+
59+
def draw(self, elapsed, size, value, transferred, phase=None, last=False):
60+
line_dict = {
61+
'transferred': transferred,
62+
'elapsed': elapsed,
63+
'description': phase or "",
64+
}
65+
if size is not None:
66+
line_dict["size"] = size
67+
68+
line = json.dumps(line_dict)
69+
self._write_output(line)
70+
71+
5572
FORMATTER = {
5673
FORMAT_TEXT: TextFormat,
74+
FORMAT_JSON: JsonFormat,
5775
}
5876

5977

test/client_options_test.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ def test_transfer_options_disabled(config):
344344
assert not hasattr(args, 'buffer_size')
345345

346346

347-
@pytest.mark.parametrize("output", [
347+
@pytest.mark.parametrize("format", [
348348
"text",
349+
"json",
349350
])
350351
def test_output_option(config, output):
351352
parser = _options.Parser()

test/client_ui_test.py

+69
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: GPL-2.0-or-later
33

44
import pytest
5+
import json
56

67
from ovirt_imageio import client
78
from ovirt_imageio._internal.units import MiB, GiB
@@ -84,6 +85,74 @@ def test_draw():
8485
assert f.last == line.ljust(79) + "\n"
8586

8687

88+
def test_json_format():
89+
fake_time = FakeTime()
90+
f = FakeFile()
91+
92+
# Size is unknown at this point.
93+
pb = client.ProgressBar(
94+
phase="setting up", output=f, format="json", now=fake_time)
95+
line_data = {
96+
"transferred": 0,
97+
"elapsed": 0.0,
98+
"description": "setting up",
99+
}
100+
line = f'{json.dumps(line_data)}'
101+
assert f.last == line + "\n"
102+
103+
# Size was updated, but no bytes were transferred yet.
104+
fake_time.now += 0.1
105+
pb.size = 3 * GiB
106+
line_data = {
107+
"transferred": 0,
108+
"elapsed": fake_time.now,
109+
"description": "setting up",
110+
"size": 3 * GiB,
111+
}
112+
line = f'{json.dumps(line_data)}'
113+
assert f.last == line + "\n"
114+
115+
# Phase was updated.
116+
fake_time.now += 0.2
117+
pb.phase = "downloading image"
118+
line_data = {
119+
"transferred": 0,
120+
"elapsed": fake_time.now,
121+
"description": "downloading image",
122+
"size": 3 * GiB,
123+
}
124+
line = f'{json.dumps(line_data)}'
125+
assert f.last == line + "\n"
126+
127+
# All data transferred.
128+
fake_time.now += 2.0
129+
pb.update(3 * GiB)
130+
line_data = {
131+
"transferred": 3 * GiB,
132+
"elapsed": fake_time.now,
133+
"description": "downloading image",
134+
"size": 3 * GiB,
135+
}
136+
line = f'{json.dumps(line_data)}'
137+
assert f.last == line + "\n"
138+
139+
# Cleaning up after download.
140+
pb.phase = "cleaning up"
141+
line_data = {
142+
"transferred": 3 * GiB,
143+
"elapsed": fake_time.now,
144+
"description": "cleaning up",
145+
"size": 3 * GiB,
146+
}
147+
line = f'{json.dumps(line_data)}'
148+
assert f.last == line + "\n"
149+
150+
# Closing prints the final line.
151+
pb.close()
152+
line = f'{json.dumps(line_data)}'
153+
assert f.last == line + "\n"
154+
155+
87156
def test_with_size():
88157
fake_time = FakeTime()
89158
f = FakeFile()

0 commit comments

Comments
 (0)