Source code for qemu.qmp.qom_common

"""
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 . import QEMUMonitorProtocol, QMPError


# The following is needed only for a type alias.
Subparsers = argparse._SubParsersAction  # pylint: disable=protected-access


[docs]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
[docs] @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')
[docs]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()
[docs] @classmethod def register(cls, subparsers: Subparsers) -> 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)
[docs] @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)
[docs] @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 '.'" )
[docs] def run(self) -> int: """ Run this command. :return: 0 on success, 1 otherwise. """ raise NotImplementedError
[docs] def qom_list(self, path: str) -> List[ObjectPropertyInfo]: """ :return: a strongly typed list from the 'qom-list' command. """ rsp = self.qmp.command('qom-list', path=path) # qom-list returns List[ObjectPropertyInfo] assert isinstance(rsp, list) return [ObjectPropertyInfo.make(x) for x in rsp]
[docs] @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
[docs] @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)