qemu-e2k/python/qemu/utils/qom_common.py
Vladimir Sementsov-Ogievskiy 684750ab4f python/qemu: rename command() to cmd()
Use a shorter name. We are going to move in iotests from qmp() to
command() where possible. But command() is longer than qmp() and don't
look better. Let's rename.

You can simply grep for '\.command(' and for 'def command(' to check
that everything is updated (command() in tests/docker/docker.py is
unrelated).

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-id: 20231006154125.1068348-6-vsementsov@yandex-team.ru
[vsementsov: also update three occurrences in
   tests/avocado/machine_aspeed.py and keep r-b]
Signed-off-by: John Snow <jsnow@redhat.com>
2023-10-12 14:21:43 -04:00

176 lines
4.9 KiB
Python

"""
QOM Command abstractions.
"""
##
# Copyright John Snow 2020, for Red Hat, Inc.
# Copyright IBM, Corp. 2011
#
# Authors:
# John Snow <jsnow@redhat.com>
# Anthony Liguori <aliguori@amazon.com>
#
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
#
# Based on ./scripts/qmp/qom-[set|get|tree|list]
##
import argparse
import os
import sys
from typing import (
Any,
Dict,
List,
Optional,
Type,
TypeVar,
)
from qemu.qmp import QMPError
from qemu.qmp.legacy import QEMUMonitorProtocol
class ObjectPropertyInfo:
"""
Represents the return type from e.g. qom-list.
"""
def __init__(self, name: str, type_: str,
description: Optional[str] = None,
default_value: Optional[object] = None):
self.name = name
self.type = type_
self.description = description
self.default_value = default_value
@classmethod
def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
"""
Build an ObjectPropertyInfo from a Dict with an unknown shape.
"""
assert value.keys() >= {'name', 'type'}
assert value.keys() <= {'name', 'type', 'description', 'default-value'}
return cls(value['name'], value['type'],
value.get('description'),
value.get('default-value'))
@property
def child(self) -> bool:
"""Is this property a child property?"""
return self.type.startswith('child<')
@property
def link(self) -> bool:
"""Is this property a link property?"""
return self.type.startswith('link<')
CommandT = TypeVar('CommandT', bound='QOMCommand')
class QOMCommand:
"""
Represents a QOM sub-command.
:param args: Parsed arguments, as returned from parser.parse_args.
"""
name: str
help: str
def __init__(self, args: argparse.Namespace):
if args.socket is None:
raise QMPError("No QMP socket path or address given")
self.qmp = QEMUMonitorProtocol(
QEMUMonitorProtocol.parse_address(args.socket)
)
self.qmp.connect()
@classmethod
def register(cls, subparsers: Any) -> None:
"""
Register this command with the argument parser.
:param subparsers: argparse subparsers object, from "add_subparsers".
"""
subparser = subparsers.add_parser(cls.name, help=cls.help,
description=cls.help)
cls.configure_parser(subparser)
@classmethod
def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
"""
Configure a parser with this command's arguments.
:param parser: argparse parser or subparser object.
"""
default_path = os.environ.get('QMP_SOCKET')
parser.add_argument(
'--socket', '-s',
dest='socket',
action='store',
help='QMP socket path or address (addr:port).'
' May also be set via QMP_SOCKET environment variable.',
default=default_path
)
parser.set_defaults(cmd_class=cls)
@classmethod
def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
"""
Add the <path>.<proptery> positional argument to this command.
:param parser: The parser to add the argument to.
"""
parser.add_argument(
'path_prop',
metavar='<path>.<property>',
action='store',
help="QOM path and property, separated by a period '.'"
)
def run(self) -> int:
"""
Run this command.
:return: 0 on success, 1 otherwise.
"""
raise NotImplementedError
def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
"""
:return: a strongly typed list from the 'qom-list' command.
"""
rsp = self.qmp.cmd('qom-list', path=path)
# qom-list returns List[ObjectPropertyInfo]
assert isinstance(rsp, list)
return [ObjectPropertyInfo.make(x) for x in rsp]
@classmethod
def command_runner(
cls: Type[CommandT],
args: argparse.Namespace
) -> int:
"""
Run a fully-parsed subcommand, with error-handling for the CLI.
:return: The return code from `run()`.
"""
try:
cmd = cls(args)
return cmd.run()
except QMPError as err:
print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
return -1
@classmethod
def entry_point(cls) -> int:
"""
Build this command's parser, parse arguments, and run the command.
:return: `run`'s return code.
"""
parser = argparse.ArgumentParser(description=cls.help)
cls.configure_parser(parser)
args = parser.parse_args()
return cls.command_runner(args)