qom.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. """
  2. QEMU Object Model testing tools.
  3. usage: qom [-h] {set,get,list,tree,fuse} ...
  4. Query and manipulate QOM data
  5. optional arguments:
  6. -h, --help show this help message and exit
  7. QOM commands:
  8. {set,get,list,tree,fuse}
  9. set Set a QOM property value
  10. get Get a QOM property value
  11. list List QOM properties at a given path
  12. tree Show QOM tree from a given path
  13. fuse Mount a QOM tree as a FUSE filesystem
  14. """
  15. ##
  16. # Copyright John Snow 2020, for Red Hat, Inc.
  17. # Copyright IBM, Corp. 2011
  18. #
  19. # Authors:
  20. # John Snow <jsnow@redhat.com>
  21. # Anthony Liguori <aliguori@amazon.com>
  22. #
  23. # This work is licensed under the terms of the GNU GPL, version 2 or later.
  24. # See the COPYING file in the top-level directory.
  25. #
  26. # Based on ./scripts/qmp/qom-[set|get|tree|list]
  27. ##
  28. import argparse
  29. from qemu.qmp import ExecuteError
  30. from .qom_common import QOMCommand
  31. try:
  32. from .qom_fuse import QOMFuse
  33. except ModuleNotFoundError as _err:
  34. if _err.name != 'fuse':
  35. raise
  36. else:
  37. assert issubclass(QOMFuse, QOMCommand)
  38. class QOMSet(QOMCommand):
  39. """
  40. QOM Command - Set a property to a given value.
  41. usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value>
  42. Set a QOM property value
  43. positional arguments:
  44. <path>.<property> QOM path and property, separated by a period '.'
  45. <value> new QOM property value
  46. optional arguments:
  47. -h, --help show this help message and exit
  48. --socket SOCKET, -s SOCKET
  49. QMP socket path or address (addr:port). May also be
  50. set via QMP_SOCKET environment variable.
  51. """
  52. name = 'set'
  53. help = 'Set a QOM property value'
  54. @classmethod
  55. def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
  56. super().configure_parser(parser)
  57. cls.add_path_prop_arg(parser)
  58. parser.add_argument(
  59. 'value',
  60. metavar='<value>',
  61. action='store',
  62. help='new QOM property value'
  63. )
  64. def __init__(self, args: argparse.Namespace):
  65. super().__init__(args)
  66. self.path, self.prop = args.path_prop.rsplit('.', 1)
  67. self.value = args.value
  68. def run(self) -> int:
  69. rsp = self.qmp.cmd(
  70. 'qom-set',
  71. path=self.path,
  72. property=self.prop,
  73. value=self.value
  74. )
  75. print(rsp)
  76. return 0
  77. class QOMGet(QOMCommand):
  78. """
  79. QOM Command - Get a property's current value.
  80. usage: qom-get [-h] [--socket SOCKET] <path>.<property>
  81. Get a QOM property value
  82. positional arguments:
  83. <path>.<property> QOM path and property, separated by a period '.'
  84. optional arguments:
  85. -h, --help show this help message and exit
  86. --socket SOCKET, -s SOCKET
  87. QMP socket path or address (addr:port). May also be
  88. set via QMP_SOCKET environment variable.
  89. """
  90. name = 'get'
  91. help = 'Get a QOM property value'
  92. @classmethod
  93. def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
  94. super().configure_parser(parser)
  95. cls.add_path_prop_arg(parser)
  96. def __init__(self, args: argparse.Namespace):
  97. super().__init__(args)
  98. try:
  99. tmp = args.path_prop.rsplit('.', 1)
  100. except ValueError as err:
  101. raise ValueError('Invalid format for <path>.<property>') from err
  102. self.path = tmp[0]
  103. self.prop = tmp[1]
  104. def run(self) -> int:
  105. rsp = self.qmp.cmd(
  106. 'qom-get',
  107. path=self.path,
  108. property=self.prop
  109. )
  110. if isinstance(rsp, dict):
  111. for key, value in rsp.items():
  112. print(f"{key}: {value}")
  113. else:
  114. print(rsp)
  115. return 0
  116. class QOMList(QOMCommand):
  117. """
  118. QOM Command - List the properties at a given path.
  119. usage: qom-list [-h] [--socket SOCKET] <path>
  120. List QOM properties at a given path
  121. positional arguments:
  122. <path> QOM path
  123. optional arguments:
  124. -h, --help show this help message and exit
  125. --socket SOCKET, -s SOCKET
  126. QMP socket path or address (addr:port). May also be
  127. set via QMP_SOCKET environment variable.
  128. """
  129. name = 'list'
  130. help = 'List QOM properties at a given path'
  131. @classmethod
  132. def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
  133. super().configure_parser(parser)
  134. parser.add_argument(
  135. 'path',
  136. metavar='<path>',
  137. action='store',
  138. help='QOM path',
  139. )
  140. def __init__(self, args: argparse.Namespace):
  141. super().__init__(args)
  142. self.path = args.path
  143. def run(self) -> int:
  144. rsp = self.qom_list(self.path)
  145. for item in rsp:
  146. if item.child:
  147. print(f"{item.name}/")
  148. elif item.link:
  149. print(f"@{item.name}/")
  150. else:
  151. print(item.name)
  152. return 0
  153. class QOMTree(QOMCommand):
  154. """
  155. QOM Command - Show the full tree below a given path.
  156. usage: qom-tree [-h] [--socket SOCKET] [<path>]
  157. Show QOM tree from a given path
  158. positional arguments:
  159. <path> QOM path
  160. optional arguments:
  161. -h, --help show this help message and exit
  162. --socket SOCKET, -s SOCKET
  163. QMP socket path or address (addr:port). May also be
  164. set via QMP_SOCKET environment variable.
  165. """
  166. name = 'tree'
  167. help = 'Show QOM tree from a given path'
  168. @classmethod
  169. def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
  170. super().configure_parser(parser)
  171. parser.add_argument(
  172. 'path',
  173. metavar='<path>',
  174. action='store',
  175. help='QOM path',
  176. nargs='?',
  177. default='/'
  178. )
  179. def __init__(self, args: argparse.Namespace):
  180. super().__init__(args)
  181. self.path = args.path
  182. def _list_node(self, path: str) -> None:
  183. print(path)
  184. items = self.qom_list(path)
  185. for item in items:
  186. if item.child:
  187. continue
  188. try:
  189. rsp = self.qmp.cmd('qom-get', path=path,
  190. property=item.name)
  191. print(f" {item.name}: {rsp} ({item.type})")
  192. except ExecuteError as err:
  193. print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
  194. print('')
  195. for item in items:
  196. if not item.child:
  197. continue
  198. if path == '/':
  199. path = ''
  200. self._list_node(f"{path}/{item.name}")
  201. def run(self) -> int:
  202. self._list_node(self.path)
  203. return 0
  204. def main() -> int:
  205. """QOM script main entry point."""
  206. parser = argparse.ArgumentParser(
  207. description='Query and manipulate QOM data'
  208. )
  209. subparsers = parser.add_subparsers(
  210. title='QOM commands',
  211. dest='command'
  212. )
  213. for command in QOMCommand.__subclasses__():
  214. command.register(subparsers)
  215. args = parser.parse_args()
  216. if args.command is None:
  217. parser.error('Command not specified.')
  218. return 1
  219. cmd_class = args.cmd_class
  220. assert isinstance(cmd_class, type(QOMCommand))
  221. return cmd_class.command_runner(args)