maqet 0.0.1.4__py3-none-any.whl → 0.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. maqet/__init__.py +50 -6
  2. maqet/__main__.py +96 -0
  3. maqet/__version__.py +3 -0
  4. maqet/api/__init__.py +35 -0
  5. maqet/api/decorators.py +184 -0
  6. maqet/api/metadata.py +147 -0
  7. maqet/api/registry.py +182 -0
  8. maqet/cli.py +71 -0
  9. maqet/config/__init__.py +26 -0
  10. maqet/config/merger.py +237 -0
  11. maqet/config/parser.py +198 -0
  12. maqet/config/validators.py +519 -0
  13. maqet/config_handlers.py +684 -0
  14. maqet/constants.py +200 -0
  15. maqet/exceptions.py +226 -0
  16. maqet/formatters.py +294 -0
  17. maqet/generators/__init__.py +12 -0
  18. maqet/generators/base_generator.py +101 -0
  19. maqet/generators/cli_generator.py +635 -0
  20. maqet/generators/python_generator.py +247 -0
  21. maqet/generators/rest_generator.py +58 -0
  22. maqet/handlers/__init__.py +12 -0
  23. maqet/handlers/base.py +108 -0
  24. maqet/handlers/init.py +147 -0
  25. maqet/handlers/stage.py +196 -0
  26. maqet/ipc/__init__.py +29 -0
  27. maqet/ipc/retry.py +265 -0
  28. maqet/ipc/runner_client.py +285 -0
  29. maqet/ipc/unix_socket_server.py +239 -0
  30. maqet/logger.py +160 -55
  31. maqet/machine.py +884 -0
  32. maqet/managers/__init__.py +7 -0
  33. maqet/managers/qmp_manager.py +333 -0
  34. maqet/managers/snapshot_coordinator.py +327 -0
  35. maqet/managers/vm_manager.py +683 -0
  36. maqet/maqet.py +1120 -0
  37. maqet/os_interactions.py +46 -0
  38. maqet/process_spawner.py +395 -0
  39. maqet/qemu_args.py +76 -0
  40. maqet/qmp/__init__.py +10 -0
  41. maqet/qmp/commands.py +92 -0
  42. maqet/qmp/keyboard.py +311 -0
  43. maqet/qmp/qmp.py +17 -0
  44. maqet/snapshot.py +473 -0
  45. maqet/state.py +958 -0
  46. maqet/storage.py +702 -162
  47. maqet/validation/__init__.py +9 -0
  48. maqet/validation/config_validator.py +170 -0
  49. maqet/vm_runner.py +523 -0
  50. maqet-0.0.5.dist-info/METADATA +237 -0
  51. maqet-0.0.5.dist-info/RECORD +55 -0
  52. {maqet-0.0.1.4.dist-info → maqet-0.0.5.dist-info}/WHEEL +1 -1
  53. maqet-0.0.5.dist-info/entry_points.txt +2 -0
  54. maqet-0.0.5.dist-info/licenses/LICENSE +21 -0
  55. {maqet-0.0.1.4.dist-info → maqet-0.0.5.dist-info}/top_level.txt +0 -1
  56. maqet/core.py +0 -411
  57. maqet/functions.py +0 -104
  58. maqet-0.0.1.4.dist-info/METADATA +0 -6
  59. maqet-0.0.1.4.dist-info/RECORD +0 -33
  60. qemu/machine/__init__.py +0 -36
  61. qemu/machine/console_socket.py +0 -142
  62. qemu/machine/machine.py +0 -954
  63. qemu/machine/py.typed +0 -0
  64. qemu/machine/qtest.py +0 -191
  65. qemu/qmp/__init__.py +0 -59
  66. qemu/qmp/error.py +0 -50
  67. qemu/qmp/events.py +0 -717
  68. qemu/qmp/legacy.py +0 -319
  69. qemu/qmp/message.py +0 -209
  70. qemu/qmp/models.py +0 -146
  71. qemu/qmp/protocol.py +0 -1057
  72. qemu/qmp/py.typed +0 -0
  73. qemu/qmp/qmp_client.py +0 -655
  74. qemu/qmp/qmp_shell.py +0 -618
  75. qemu/qmp/qmp_tui.py +0 -655
  76. qemu/qmp/util.py +0 -219
  77. qemu/utils/__init__.py +0 -162
  78. qemu/utils/accel.py +0 -84
  79. qemu/utils/py.typed +0 -0
  80. qemu/utils/qemu_ga_client.py +0 -323
  81. qemu/utils/qom.py +0 -273
  82. qemu/utils/qom_common.py +0 -175
  83. qemu/utils/qom_fuse.py +0 -207
@@ -1,323 +0,0 @@
1
- """
2
- QEMU Guest Agent Client
3
-
4
- Usage:
5
-
6
- Start QEMU with:
7
-
8
- # qemu [...] -chardev socket,path=/tmp/qga.sock,server=on,wait=off,id=qga0 \
9
- -device virtio-serial \
10
- -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
11
-
12
- Run the script:
13
-
14
- $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
15
-
16
- or
17
-
18
- $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
19
- $ qemu-ga-client <command> [args...]
20
-
21
- For example:
22
-
23
- $ qemu-ga-client cat /etc/resolv.conf
24
- # Generated by NetworkManager
25
- nameserver 10.0.2.3
26
- $ qemu-ga-client fsfreeze status
27
- thawed
28
- $ qemu-ga-client fsfreeze freeze
29
- 2 filesystems frozen
30
-
31
- See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
32
- """
33
-
34
- # Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
35
- #
36
- # This work is licensed under the terms of the GNU GPL, version 2. See
37
- # the COPYING file in the top-level directory.
38
-
39
- import argparse
40
- import asyncio
41
- import base64
42
- import os
43
- import random
44
- import sys
45
- from typing import (
46
- Any,
47
- Callable,
48
- Dict,
49
- Optional,
50
- Sequence,
51
- )
52
-
53
- from qemu.qmp import ConnectError, SocketAddrT
54
- from qemu.qmp.legacy import QEMUMonitorProtocol
55
-
56
-
57
- # This script has not seen many patches or careful attention in quite
58
- # some time. If you would like to improve it, please review the design
59
- # carefully and add docstrings at that point in time. Until then:
60
-
61
- # pylint: disable=missing-docstring
62
-
63
-
64
- class QemuGuestAgent(QEMUMonitorProtocol):
65
- def __getattr__(self, name: str) -> Callable[..., Any]:
66
- def wrapper(**kwds: object) -> object:
67
- return self.cmd('guest-' + name.replace('_', '-'), **kwds)
68
- return wrapper
69
-
70
-
71
- class QemuGuestAgentClient:
72
- def __init__(self, address: SocketAddrT):
73
- self.qga = QemuGuestAgent(address)
74
- self.qga.connect(negotiate=False)
75
-
76
- def sync(self, timeout: Optional[float] = 3) -> None:
77
- # Avoid being blocked forever
78
- if not self.ping(timeout):
79
- raise EnvironmentError('Agent seems not alive')
80
- uid = random.randint(0, (1 << 32) - 1)
81
- while True:
82
- ret = self.qga.sync(id=uid)
83
- if isinstance(ret, int) and int(ret) == uid:
84
- break
85
-
86
- def __file_read_all(self, handle: int) -> bytes:
87
- eof = False
88
- data = b''
89
- while not eof:
90
- ret = self.qga.file_read(handle=handle, count=1024)
91
- _data = base64.b64decode(ret['buf-b64'])
92
- data += _data
93
- eof = ret['eof']
94
- return data
95
-
96
- def read(self, path: str) -> bytes:
97
- handle = self.qga.file_open(path=path)
98
- try:
99
- data = self.__file_read_all(handle)
100
- finally:
101
- self.qga.file_close(handle=handle)
102
- return data
103
-
104
- def info(self) -> str:
105
- info = self.qga.info()
106
-
107
- msgs = []
108
- msgs.append('version: ' + info['version'])
109
- msgs.append('supported_commands:')
110
- enabled = [c['name'] for c in info['supported_commands']
111
- if c['enabled']]
112
- msgs.append('\tenabled: ' + ', '.join(enabled))
113
- disabled = [c['name'] for c in info['supported_commands']
114
- if not c['enabled']]
115
- msgs.append('\tdisabled: ' + ', '.join(disabled))
116
-
117
- return '\n'.join(msgs)
118
-
119
- @classmethod
120
- def __gen_ipv4_netmask(cls, prefixlen: int) -> str:
121
- mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
122
- return '.'.join([str(mask >> 24),
123
- str((mask >> 16) & 0xff),
124
- str((mask >> 8) & 0xff),
125
- str(mask & 0xff)])
126
-
127
- def ifconfig(self) -> str:
128
- nifs = self.qga.network_get_interfaces()
129
-
130
- msgs = []
131
- for nif in nifs:
132
- msgs.append(nif['name'] + ':')
133
- if 'ip-addresses' in nif:
134
- for ipaddr in nif['ip-addresses']:
135
- if ipaddr['ip-address-type'] == 'ipv4':
136
- addr = ipaddr['ip-address']
137
- mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
138
- msgs.append(f"\tinet {addr} netmask {mask}")
139
- elif ipaddr['ip-address-type'] == 'ipv6':
140
- addr = ipaddr['ip-address']
141
- prefix = ipaddr['prefix']
142
- msgs.append(f"\tinet6 {addr} prefixlen {prefix}")
143
- if nif['hardware-address'] != '00:00:00:00:00:00':
144
- msgs.append("\tether " + nif['hardware-address'])
145
-
146
- return '\n'.join(msgs)
147
-
148
- def ping(self, timeout: Optional[float]) -> bool:
149
- self.qga.settimeout(timeout)
150
- try:
151
- self.qga.ping()
152
- except asyncio.TimeoutError:
153
- return False
154
- return True
155
-
156
- def fsfreeze(self, cmd: str) -> object:
157
- if cmd not in ['status', 'freeze', 'thaw']:
158
- raise ValueError('Invalid command: ' + cmd)
159
- # Can be int (freeze, thaw) or GuestFsfreezeStatus (status)
160
- return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
161
-
162
- def fstrim(self, minimum: int) -> Dict[str, object]:
163
- # returns GuestFilesystemTrimResponse
164
- ret = getattr(self.qga, 'fstrim')(minimum=minimum)
165
- assert isinstance(ret, dict)
166
- return ret
167
-
168
- def suspend(self, mode: str) -> None:
169
- if mode not in ['disk', 'ram', 'hybrid']:
170
- raise ValueError('Invalid mode: ' + mode)
171
-
172
- try:
173
- getattr(self.qga, 'suspend' + '_' + mode)()
174
- # On error exception will raise
175
- except asyncio.TimeoutError:
176
- # On success command will timed out
177
- pass
178
-
179
- def shutdown(self, mode: str = 'powerdown') -> None:
180
- if mode not in ['powerdown', 'halt', 'reboot']:
181
- raise ValueError('Invalid mode: ' + mode)
182
-
183
- try:
184
- self.qga.shutdown(mode=mode)
185
- except asyncio.TimeoutError:
186
- pass
187
-
188
-
189
- def _cmd_cat(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
190
- if len(args) != 1:
191
- print('Invalid argument')
192
- print('Usage: cat <file>')
193
- sys.exit(1)
194
- print(client.read(args[0]))
195
-
196
-
197
- def _cmd_fsfreeze(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
198
- usage = 'Usage: fsfreeze status|freeze|thaw'
199
- if len(args) != 1:
200
- print('Invalid argument')
201
- print(usage)
202
- sys.exit(1)
203
- if args[0] not in ['status', 'freeze', 'thaw']:
204
- print('Invalid command: ' + args[0])
205
- print(usage)
206
- sys.exit(1)
207
- cmd = args[0]
208
- ret = client.fsfreeze(cmd)
209
- if cmd == 'status':
210
- print(ret)
211
- return
212
-
213
- assert isinstance(ret, int)
214
- verb = 'frozen' if cmd == 'freeze' else 'thawed'
215
- print(f"{ret:d} filesystems {verb}")
216
-
217
-
218
- def _cmd_fstrim(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
219
- if len(args) == 0:
220
- minimum = 0
221
- else:
222
- minimum = int(args[0])
223
- print(client.fstrim(minimum))
224
-
225
-
226
- def _cmd_ifconfig(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
227
- assert not args
228
- print(client.ifconfig())
229
-
230
-
231
- def _cmd_info(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
232
- assert not args
233
- print(client.info())
234
-
235
-
236
- def _cmd_ping(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
237
- timeout = 3.0 if len(args) == 0 else float(args[0])
238
- alive = client.ping(timeout)
239
- if not alive:
240
- print("Not responded in %s sec" % args[0])
241
- sys.exit(1)
242
-
243
-
244
- def _cmd_suspend(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
245
- usage = 'Usage: suspend disk|ram|hybrid'
246
- if len(args) != 1:
247
- print('Less argument')
248
- print(usage)
249
- sys.exit(1)
250
- if args[0] not in ['disk', 'ram', 'hybrid']:
251
- print('Invalid command: ' + args[0])
252
- print(usage)
253
- sys.exit(1)
254
- client.suspend(args[0])
255
-
256
-
257
- def _cmd_shutdown(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
258
- assert not args
259
- client.shutdown()
260
-
261
-
262
- _cmd_powerdown = _cmd_shutdown
263
-
264
-
265
- def _cmd_halt(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
266
- assert not args
267
- client.shutdown('halt')
268
-
269
-
270
- def _cmd_reboot(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
271
- assert not args
272
- client.shutdown('reboot')
273
-
274
-
275
- commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
276
-
277
-
278
- def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
279
- if not os.path.exists(address):
280
- print(f"'{address}' not found. (Is QEMU running?)")
281
- sys.exit(1)
282
-
283
- if cmd not in commands:
284
- print('Invalid command: ' + cmd)
285
- print('Available commands: ' + ', '.join(commands))
286
- sys.exit(1)
287
-
288
- try:
289
- client = QemuGuestAgentClient(address)
290
- except ConnectError as err:
291
- print(err)
292
- if isinstance(err.exc, ConnectionError):
293
- print('(Is QEMU running?)')
294
- sys.exit(1)
295
-
296
- if cmd == 'fsfreeze' and args[0] == 'freeze':
297
- client.sync(60)
298
- elif cmd != 'ping':
299
- client.sync()
300
-
301
- globals()['_cmd_' + cmd](client, args)
302
-
303
-
304
- def main() -> None:
305
- address = os.environ.get('QGA_CLIENT_ADDRESS')
306
-
307
- parser = argparse.ArgumentParser()
308
- parser.add_argument('--address', action='store',
309
- default=address,
310
- help='Specify a ip:port pair or a unix socket path')
311
- parser.add_argument('command', choices=commands)
312
- parser.add_argument('args', nargs='*')
313
-
314
- args = parser.parse_args()
315
- if args.address is None:
316
- parser.error('address is not specified')
317
- sys.exit(1)
318
-
319
- send_command(args.address, args.command, args.args)
320
-
321
-
322
- if __name__ == '__main__':
323
- main()
qemu/utils/qom.py DELETED
@@ -1,273 +0,0 @@
1
- """
2
- QEMU Object Model testing tools.
3
-
4
- usage: qom [-h] {set,get,list,tree,fuse} ...
5
-
6
- Query and manipulate QOM data
7
-
8
- optional arguments:
9
- -h, --help show this help message and exit
10
-
11
- QOM commands:
12
- {set,get,list,tree,fuse}
13
- set Set a QOM property value
14
- get Get a QOM property value
15
- list List QOM properties at a given path
16
- tree Show QOM tree from a given path
17
- fuse Mount a QOM tree as a FUSE filesystem
18
- """
19
- ##
20
- # Copyright John Snow 2020, for Red Hat, Inc.
21
- # Copyright IBM, Corp. 2011
22
- #
23
- # Authors:
24
- # John Snow <jsnow@redhat.com>
25
- # Anthony Liguori <aliguori@amazon.com>
26
- #
27
- # This work is licensed under the terms of the GNU GPL, version 2 or later.
28
- # See the COPYING file in the top-level directory.
29
- #
30
- # Based on ./scripts/qmp/qom-[set|get|tree|list]
31
- ##
32
-
33
- import argparse
34
-
35
- from qemu.qmp import ExecuteError
36
-
37
- from .qom_common import QOMCommand
38
-
39
-
40
- try:
41
- from .qom_fuse import QOMFuse
42
- except ModuleNotFoundError as _err:
43
- if _err.name != 'fuse':
44
- raise
45
- else:
46
- assert issubclass(QOMFuse, QOMCommand)
47
-
48
-
49
- class QOMSet(QOMCommand):
50
- """
51
- QOM Command - Set a property to a given value.
52
-
53
- usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value>
54
-
55
- Set a QOM property value
56
-
57
- positional arguments:
58
- <path>.<property> QOM path and property, separated by a period '.'
59
- <value> new QOM property value
60
-
61
- optional arguments:
62
- -h, --help show this help message and exit
63
- --socket SOCKET, -s SOCKET
64
- QMP socket path or address (addr:port). May also be
65
- set via QMP_SOCKET environment variable.
66
- """
67
- name = 'set'
68
- help = 'Set a QOM property value'
69
-
70
- @classmethod
71
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
72
- super().configure_parser(parser)
73
- cls.add_path_prop_arg(parser)
74
- parser.add_argument(
75
- 'value',
76
- metavar='<value>',
77
- action='store',
78
- help='new QOM property value'
79
- )
80
-
81
- def __init__(self, args: argparse.Namespace):
82
- super().__init__(args)
83
- self.path, self.prop = args.path_prop.rsplit('.', 1)
84
- self.value = args.value
85
-
86
- def run(self) -> int:
87
- rsp = self.qmp.cmd(
88
- 'qom-set',
89
- path=self.path,
90
- property=self.prop,
91
- value=self.value
92
- )
93
- print(rsp)
94
- return 0
95
-
96
-
97
- class QOMGet(QOMCommand):
98
- """
99
- QOM Command - Get a property's current value.
100
-
101
- usage: qom-get [-h] [--socket SOCKET] <path>.<property>
102
-
103
- Get a QOM property value
104
-
105
- positional arguments:
106
- <path>.<property> QOM path and property, separated by a period '.'
107
-
108
- optional arguments:
109
- -h, --help show this help message and exit
110
- --socket SOCKET, -s SOCKET
111
- QMP socket path or address (addr:port). May also be
112
- set via QMP_SOCKET environment variable.
113
- """
114
- name = 'get'
115
- help = 'Get a QOM property value'
116
-
117
- @classmethod
118
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
119
- super().configure_parser(parser)
120
- cls.add_path_prop_arg(parser)
121
-
122
- def __init__(self, args: argparse.Namespace):
123
- super().__init__(args)
124
- try:
125
- tmp = args.path_prop.rsplit('.', 1)
126
- except ValueError as err:
127
- raise ValueError('Invalid format for <path>.<property>') from err
128
- self.path = tmp[0]
129
- self.prop = tmp[1]
130
-
131
- def run(self) -> int:
132
- rsp = self.qmp.cmd(
133
- 'qom-get',
134
- path=self.path,
135
- property=self.prop
136
- )
137
- if isinstance(rsp, dict):
138
- for key, value in rsp.items():
139
- print(f"{key}: {value}")
140
- else:
141
- print(rsp)
142
- return 0
143
-
144
-
145
- class QOMList(QOMCommand):
146
- """
147
- QOM Command - List the properties at a given path.
148
-
149
- usage: qom-list [-h] [--socket SOCKET] <path>
150
-
151
- List QOM properties at a given path
152
-
153
- positional arguments:
154
- <path> QOM path
155
-
156
- optional arguments:
157
- -h, --help show this help message and exit
158
- --socket SOCKET, -s SOCKET
159
- QMP socket path or address (addr:port). May also be
160
- set via QMP_SOCKET environment variable.
161
- """
162
- name = 'list'
163
- help = 'List QOM properties at a given path'
164
-
165
- @classmethod
166
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
167
- super().configure_parser(parser)
168
- parser.add_argument(
169
- 'path',
170
- metavar='<path>',
171
- action='store',
172
- help='QOM path',
173
- )
174
-
175
- def __init__(self, args: argparse.Namespace):
176
- super().__init__(args)
177
- self.path = args.path
178
-
179
- def run(self) -> int:
180
- rsp = self.qom_list(self.path)
181
- for item in rsp:
182
- if item.child:
183
- print(f"{item.name}/")
184
- elif item.link:
185
- print(f"@{item.name}/")
186
- else:
187
- print(item.name)
188
- return 0
189
-
190
-
191
- class QOMTree(QOMCommand):
192
- """
193
- QOM Command - Show the full tree below a given path.
194
-
195
- usage: qom-tree [-h] [--socket SOCKET] [<path>]
196
-
197
- Show QOM tree from a given path
198
-
199
- positional arguments:
200
- <path> QOM path
201
-
202
- optional arguments:
203
- -h, --help show this help message and exit
204
- --socket SOCKET, -s SOCKET
205
- QMP socket path or address (addr:port). May also be
206
- set via QMP_SOCKET environment variable.
207
- """
208
- name = 'tree'
209
- help = 'Show QOM tree from a given path'
210
-
211
- @classmethod
212
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
213
- super().configure_parser(parser)
214
- parser.add_argument(
215
- 'path',
216
- metavar='<path>',
217
- action='store',
218
- help='QOM path',
219
- nargs='?',
220
- default='/'
221
- )
222
-
223
- def __init__(self, args: argparse.Namespace):
224
- super().__init__(args)
225
- self.path = args.path
226
-
227
- def _list_node(self, path: str) -> None:
228
- print(path)
229
- items = self.qom_list(path)
230
- for item in items:
231
- if item.child:
232
- continue
233
- try:
234
- rsp = self.qmp.cmd('qom-get', path=path,
235
- property=item.name)
236
- print(f" {item.name}: {rsp} ({item.type})")
237
- except ExecuteError as err:
238
- print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
239
- print('')
240
- for item in items:
241
- if not item.child:
242
- continue
243
- if path == '/':
244
- path = ''
245
- self._list_node(f"{path}/{item.name}")
246
-
247
- def run(self) -> int:
248
- self._list_node(self.path)
249
- return 0
250
-
251
-
252
- def main() -> int:
253
- """QOM script main entry point."""
254
- parser = argparse.ArgumentParser(
255
- description='Query and manipulate QOM data'
256
- )
257
- subparsers = parser.add_subparsers(
258
- title='QOM commands',
259
- dest='command'
260
- )
261
-
262
- for command in QOMCommand.__subclasses__():
263
- command.register(subparsers)
264
-
265
- args = parser.parse_args()
266
-
267
- if args.command is None:
268
- parser.error('Command not specified.')
269
- return 1
270
-
271
- cmd_class = args.cmd_class
272
- assert isinstance(cmd_class, type(QOMCommand))
273
- return cmd_class.command_runner(args)