2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

python: Add option for JSON output to unixctl classes and appctl.py.

This patch introduces support for different output formats to Python
Unixctl* classes and appctl.py, similar to what the previous commit did
for ovs-appctl.
In particular, tests/appctl.py gains a global option '-f,--format'
which allows users to request JSON instead of plain-text for humans.

Reported-at: https://bugzilla.redhat.com/1824861
Signed-off-by: Jakob Meng <code@jakobmeng.de>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Jakob Meng
2024-07-09 09:14:18 +02:00
committed by Ilya Maximets
parent 939a5cea5b
commit 97a1bce6aa
6 changed files with 98 additions and 16 deletions

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import enum
import sys
import ovs.util
@@ -19,6 +20,13 @@ import ovs.util
commands = {}
@enum.unique
# FIXME: Use @enum.verify(enum.NAMED_FLAGS) from Python 3.11 when available.
class UnixctlOutputFormat(enum.IntFlag):
TEXT = 1 << 0
JSON = 1 << 1
class _UnixctlCommand(object):
def __init__(self, usage, min_args, max_args, callback, aux):
self.usage = usage

View File

@@ -14,6 +14,7 @@
import os
import ovs.json
import ovs.jsonrpc
import ovs.stream
import ovs.util
@@ -41,10 +42,10 @@ class UnixctlClient(object):
return error, None, None
if reply.error is not None:
return 0, str(reply.error), None
return 0, reply.error, None
else:
assert reply.result is not None
return 0, None, str(reply.result)
return 0, None, reply.result
def close(self):
self._conn.close()

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import copy
import errno
import os
@@ -35,6 +36,7 @@ class UnixctlConnection(object):
assert isinstance(rpc, ovs.jsonrpc.Connection)
self._rpc = rpc
self._request_id = None
self._fmt = ovs.unixctl.UnixctlOutputFormat.TEXT
def run(self):
self._rpc.run()
@@ -63,10 +65,29 @@ class UnixctlConnection(object):
return error
def reply(self, body):
self._reply_impl(True, body)
assert body is None or isinstance(body, str)
if body is None:
body = ""
if self._fmt == ovs.unixctl.UnixctlOutputFormat.JSON:
body = {
"reply-format": "plain",
"reply": body
}
return self._reply_impl_json(True, body)
def reply_json(self, body):
self._reply_impl_json(True, body)
def reply_error(self, body):
self._reply_impl(False, body)
assert body is None or isinstance(body, str)
if body is None:
body = ""
return self._reply_impl_json(False, body)
# Called only by unixctl classes.
def _close(self):
@@ -78,15 +99,11 @@ class UnixctlConnection(object):
if not self._rpc.get_backlog():
self._rpc.recv_wait(poller)
def _reply_impl(self, success, body):
def _reply_impl_json(self, success, body):
assert isinstance(success, bool)
assert body is None or isinstance(body, str)
assert self._request_id is not None
if body is None:
body = ""
if success:
reply = Message.create_reply(body, self._request_id)
else:
@@ -133,6 +150,25 @@ def _unixctl_version(conn, unused_argv, version):
conn.reply(version)
def _unixctl_set_options(conn, argv, unused_aux):
assert isinstance(conn, UnixctlConnection)
parser = argparse.ArgumentParser()
parser.add_argument("--format", default="text",
choices=[fmt.name.lower()
for fmt in ovs.unixctl.UnixctlOutputFormat],
type=str.lower)
try:
args = parser.parse_args(args=argv)
except argparse.ArgumentError as e:
conn.reply_error(str(e))
return
conn._fmt = ovs.unixctl.UnixctlOutputFormat[args.format.upper()]
conn.reply(None)
class UnixctlServer(object):
def __init__(self, listener):
assert isinstance(listener, ovs.stream.PassiveStream)
@@ -207,4 +243,7 @@ class UnixctlServer(object):
ovs.unixctl.command_register("version", "", 0, 0, _unixctl_version,
version)
ovs.unixctl.command_register("set-options", "[--format text|json]", 1,
2, _unixctl_set_options, None)
return 0, UnixctlServer(listener)