maqet 0.0.1.3__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.
- maqet/__init__.py +50 -6
- maqet/__main__.py +96 -0
- maqet/__version__.py +3 -0
- maqet/api/__init__.py +35 -0
- maqet/api/decorators.py +184 -0
- maqet/api/metadata.py +147 -0
- maqet/api/registry.py +182 -0
- maqet/cli.py +71 -0
- maqet/config/__init__.py +26 -0
- maqet/config/merger.py +237 -0
- maqet/config/parser.py +198 -0
- maqet/config/validators.py +519 -0
- maqet/config_handlers.py +684 -0
- maqet/constants.py +200 -0
- maqet/exceptions.py +226 -0
- maqet/formatters.py +294 -0
- maqet/generators/__init__.py +12 -0
- maqet/generators/base_generator.py +101 -0
- maqet/generators/cli_generator.py +635 -0
- maqet/generators/python_generator.py +247 -0
- maqet/generators/rest_generator.py +58 -0
- maqet/handlers/__init__.py +12 -0
- maqet/handlers/base.py +108 -0
- maqet/handlers/init.py +147 -0
- maqet/handlers/stage.py +196 -0
- maqet/ipc/__init__.py +29 -0
- maqet/ipc/retry.py +265 -0
- maqet/ipc/runner_client.py +285 -0
- maqet/ipc/unix_socket_server.py +239 -0
- maqet/logger.py +160 -55
- maqet/machine.py +884 -0
- maqet/managers/__init__.py +7 -0
- maqet/managers/qmp_manager.py +333 -0
- maqet/managers/snapshot_coordinator.py +327 -0
- maqet/managers/vm_manager.py +683 -0
- maqet/maqet.py +1120 -0
- maqet/os_interactions.py +46 -0
- maqet/process_spawner.py +395 -0
- maqet/qemu_args.py +76 -0
- maqet/qmp/__init__.py +10 -0
- maqet/qmp/commands.py +92 -0
- maqet/qmp/keyboard.py +311 -0
- maqet/qmp/qmp.py +17 -0
- maqet/snapshot.py +473 -0
- maqet/state.py +958 -0
- maqet/storage.py +702 -162
- maqet/validation/__init__.py +9 -0
- maqet/validation/config_validator.py +170 -0
- maqet/vm_runner.py +523 -0
- maqet-0.0.5.dist-info/METADATA +237 -0
- maqet-0.0.5.dist-info/RECORD +55 -0
- {maqet-0.0.1.3.dist-info → maqet-0.0.5.dist-info}/WHEEL +1 -1
- maqet-0.0.5.dist-info/entry_points.txt +2 -0
- maqet-0.0.5.dist-info/licenses/LICENSE +21 -0
- {maqet-0.0.1.3.dist-info → maqet-0.0.5.dist-info}/top_level.txt +0 -1
- maqet/core.py +0 -395
- maqet/functions.py +0 -104
- maqet-0.0.1.3.dist-info/METADATA +0 -104
- maqet-0.0.1.3.dist-info/RECORD +0 -33
- qemu/machine/__init__.py +0 -36
- qemu/machine/console_socket.py +0 -142
- qemu/machine/machine.py +0 -954
- qemu/machine/py.typed +0 -0
- qemu/machine/qtest.py +0 -191
- qemu/qmp/__init__.py +0 -59
- qemu/qmp/error.py +0 -50
- qemu/qmp/events.py +0 -717
- qemu/qmp/legacy.py +0 -319
- qemu/qmp/message.py +0 -209
- qemu/qmp/models.py +0 -146
- qemu/qmp/protocol.py +0 -1057
- qemu/qmp/py.typed +0 -0
- qemu/qmp/qmp_client.py +0 -655
- qemu/qmp/qmp_shell.py +0 -618
- qemu/qmp/qmp_tui.py +0 -655
- qemu/qmp/util.py +0 -219
- qemu/utils/__init__.py +0 -162
- qemu/utils/accel.py +0 -84
- qemu/utils/py.typed +0 -0
- qemu/utils/qemu_ga_client.py +0 -323
- qemu/utils/qom.py +0 -273
- qemu/utils/qom_common.py +0 -175
- qemu/utils/qom_fuse.py +0 -207
maqet/core.py
DELETED
@@ -1,395 +0,0 @@
|
|
1
|
-
import argparse
|
2
|
-
import time
|
3
|
-
from functools import wraps
|
4
|
-
from pathlib import Path
|
5
|
-
from typing import Callable, List
|
6
|
-
|
7
|
-
from benedict import benedict
|
8
|
-
|
9
|
-
from qemu.machine import QEMUMachine
|
10
|
-
|
11
|
-
from .functions import parse_args, split_args
|
12
|
-
from .logger import LOG
|
13
|
-
from .storage import DRIVE_TYPES, Drive
|
14
|
-
|
15
|
-
# To be moved into input.py
|
16
|
-
SPEC_CHAR_KEYS = {
|
17
|
-
"\r": ["ret"],
|
18
|
-
"\n": ["ret"],
|
19
|
-
" ": ["spc"],
|
20
|
-
"=": ["equal"],
|
21
|
-
"+": ["shift", "equal"],
|
22
|
-
"-": ["minus"],
|
23
|
-
"_": ["shift", "minus"],
|
24
|
-
";": ["semicolon"],
|
25
|
-
":": ["shift", "semicolon"],
|
26
|
-
"&": ["shift", "7"],
|
27
|
-
"/": ["slash"],
|
28
|
-
".": ["dot"],
|
29
|
-
">": ["shift", "dot"],
|
30
|
-
"<": ["shift", "comma"],
|
31
|
-
"#": ["shift", "3"],
|
32
|
-
}
|
33
|
-
|
34
|
-
|
35
|
-
class MaqetError(Exception):
|
36
|
-
"""
|
37
|
-
General exception
|
38
|
-
"""
|
39
|
-
|
40
|
-
|
41
|
-
class MachineError(Exception):
|
42
|
-
"""
|
43
|
-
Machine generic error
|
44
|
-
"""
|
45
|
-
|
46
|
-
|
47
|
-
class Machine(QEMUMachine):
|
48
|
-
def __init__(self, *args, **kwargs):
|
49
|
-
"""
|
50
|
-
TODO: make docstring
|
51
|
-
|
52
|
-
Machine objects should be created with **kwargs
|
53
|
-
"""
|
54
|
-
self._config = benedict(kwargs)
|
55
|
-
if 'storage_path' in self._config:
|
56
|
-
self._config.storage_path = Path(self._config.storage_path)
|
57
|
-
if 'binary' not in self._config:
|
58
|
-
raise MachineError("Binary not stated in config")
|
59
|
-
|
60
|
-
self._config.typing_delay = kwargs.get('typing_delay', 0.1)
|
61
|
-
|
62
|
-
binary = self._config['binary']
|
63
|
-
|
64
|
-
qemumachine_args = dict()
|
65
|
-
for key in ['args', 'wrapper', 'name', 'base_temp_dir',
|
66
|
-
'monitor_address', 'drain_console', 'console_log',
|
67
|
-
'log_dir', 'qmp_timer']:
|
68
|
-
if key in self._config:
|
69
|
-
qemumachine_args[key] = self._config[key]
|
70
|
-
|
71
|
-
super().__init__(binary=binary, **qemumachine_args)
|
72
|
-
|
73
|
-
self.storage = dict()
|
74
|
-
if 'storage' in self._config:
|
75
|
-
for name, drive in self._config.storage.items():
|
76
|
-
self.add_drive(name=name, **drive)
|
77
|
-
|
78
|
-
def launch(self):
|
79
|
-
LOG.info(f'VM launch with command: {self.command}')
|
80
|
-
super().launch()
|
81
|
-
|
82
|
-
def add_drive(self, name, **kwargs) -> None:
|
83
|
-
if 'path' not in kwargs:
|
84
|
-
if 'storage_path' in self._config:
|
85
|
-
# Adding default storage path from config
|
86
|
-
kwargs['path'] = self._config.storage_path / name
|
87
|
-
else:
|
88
|
-
raise MachineError(
|
89
|
-
'Cannot add drive: stated neither path nor storage_path')
|
90
|
-
self.storage[name] = Drive(**kwargs)
|
91
|
-
|
92
|
-
# def add_argument(self, argument: str) -> None:
|
93
|
-
# """
|
94
|
-
# Add user argument to VM config
|
95
|
-
# """
|
96
|
-
# self._config['user_arguments'].append(argument)
|
97
|
-
|
98
|
-
def add_args(self, *args: str) -> None:
|
99
|
-
"""
|
100
|
-
Adds to the list of extra arguments to be given to the QEMU binary
|
101
|
-
"""
|
102
|
-
super().add_args(*split_args(args))
|
103
|
-
|
104
|
-
@property
|
105
|
-
def command(self) -> str:
|
106
|
-
"""
|
107
|
-
get full command string that will be executed at start
|
108
|
-
"""
|
109
|
-
return self._binary + ' ' + ' '.join(self._base_args)
|
110
|
-
|
111
|
-
@property
|
112
|
-
def _const_args(self) -> List[str]:
|
113
|
-
if 'arguments' not in self._config:
|
114
|
-
return []
|
115
|
-
return parse_args(self._config.arguments)
|
116
|
-
|
117
|
-
@property
|
118
|
-
def _var_args(self) -> List[str]:
|
119
|
-
# TODO:more reliable method. Maybe add-fd?
|
120
|
-
|
121
|
-
args = []
|
122
|
-
for id, drive in self.storage.items():
|
123
|
-
args += drive()
|
124
|
-
|
125
|
-
return [
|
126
|
-
a_splitted
|
127
|
-
for a in args
|
128
|
-
for a_splitted in a.split()
|
129
|
-
]
|
130
|
-
|
131
|
-
@property
|
132
|
-
def user_args(self):
|
133
|
-
return [
|
134
|
-
a_splitted
|
135
|
-
for a in self._config.user_arguments
|
136
|
-
for a_splitted in a.split()
|
137
|
-
]
|
138
|
-
|
139
|
-
@ property
|
140
|
-
def _base_args(self):
|
141
|
-
args = self._var_args + self._const_args + self.user_args
|
142
|
-
|
143
|
-
if self._qmp_set:
|
144
|
-
if self._sock_pair:
|
145
|
-
moncdev = f"socket,id=mon,fd={self._sock_pair[0].fileno()}"
|
146
|
-
elif isinstance(self._monitor_address, tuple):
|
147
|
-
moncdev = "socket,id=mon,host={},port={}".format(
|
148
|
-
*self._monitor_address
|
149
|
-
)
|
150
|
-
else:
|
151
|
-
moncdev = f"socket,id=mon,path={self._monitor_address}"
|
152
|
-
args.extend(['-chardev', moncdev, '-mon',
|
153
|
-
'chardev=mon,mode=control'])
|
154
|
-
|
155
|
-
if self._machine is not None:
|
156
|
-
args.extend(['-machine', self._machine])
|
157
|
-
for _ in range(self._console_index):
|
158
|
-
args.extend(['-serial', 'null'])
|
159
|
-
if self._console_set:
|
160
|
-
assert self._cons_sock_pair is not None
|
161
|
-
fd = self._cons_sock_pair[0].fileno()
|
162
|
-
chardev = f"socket,id=console,fd={fd}"
|
163
|
-
args.extend(['-chardev', chardev])
|
164
|
-
if self._console_device_type is None:
|
165
|
-
args.extend(['-serial', 'chardev:console'])
|
166
|
-
else:
|
167
|
-
device = '%s,chardev=console' % self._console_device_type
|
168
|
-
args.extend(['-device', device])
|
169
|
-
return args
|
170
|
-
|
171
|
-
def send_keys(self, keys: list[str] = []) -> dict:
|
172
|
-
LOG.debug(f"QMP: keys to input: {keys}")
|
173
|
-
command = "input-send-event"
|
174
|
-
|
175
|
-
arguments = benedict({"events": []})
|
176
|
-
arguments.events = []
|
177
|
-
|
178
|
-
for k in keys:
|
179
|
-
arguments.events.append(benedict(
|
180
|
-
{
|
181
|
-
"type": "key",
|
182
|
-
"data": {"down": True, "key":
|
183
|
-
{"type": "qcode", "data": k}},
|
184
|
-
}
|
185
|
-
))
|
186
|
-
|
187
|
-
r = self.qmp(cmd=command, args_dict=arguments)
|
188
|
-
LOG.debug(f"QMP: {keys}")
|
189
|
-
|
190
|
-
for e in arguments.events:
|
191
|
-
e.data.down = False
|
192
|
-
|
193
|
-
r = self.qmp(cmd=command, args_dict=arguments)
|
194
|
-
LOG.debug(f"QMP: {keys}")
|
195
|
-
return r
|
196
|
-
|
197
|
-
def send_input(self, string: str = "",):
|
198
|
-
"""
|
199
|
-
Send input string to VM by emulating key press events
|
200
|
-
"""
|
201
|
-
chars = list(string)
|
202
|
-
|
203
|
-
for c in chars:
|
204
|
-
keys = []
|
205
|
-
if c in SPEC_CHAR_KEYS.keys(): # Enter
|
206
|
-
keys = SPEC_CHAR_KEYS[c]
|
207
|
-
elif c.isnumeric(): # Numbers
|
208
|
-
keys.append(c)
|
209
|
-
elif c.isalpha(): # Letters
|
210
|
-
if c.isupper():
|
211
|
-
keys.append('shift')
|
212
|
-
keys.append(c.lower())
|
213
|
-
|
214
|
-
LOG.debug(f"Sending keys: {keys}")
|
215
|
-
self.send_keys(keys=keys)
|
216
|
-
time.sleep(self._config.typing_delay)
|
217
|
-
|
218
|
-
def qmp_command(self, command_name: str = None, verbose=False,
|
219
|
-
*args, **kwargs) -> dict:
|
220
|
-
"""
|
221
|
-
Execute custom or pre-cooked qmp command from config.qmp_commands
|
222
|
-
"""
|
223
|
-
# TODO: add kwargs usage for templating cooked commands
|
224
|
-
if command_name is None:
|
225
|
-
command = kwargs
|
226
|
-
else:
|
227
|
-
if 'qmp_commands' not in self._config:
|
228
|
-
raise MachineError('qmp_commands not stated in config')
|
229
|
-
if command_name not in self._config.qmp_commands:
|
230
|
-
raise MachineError('qmp command not stated qmp_commands')
|
231
|
-
command = self._config.qmp_commands[command_name]
|
232
|
-
|
233
|
-
if 'execute' not in command:
|
234
|
-
raise MachineError(
|
235
|
-
'qmp command: execute key and value not found')
|
236
|
-
|
237
|
-
if 'arguments' in command:
|
238
|
-
r = self.qmp(cmd=command.execute, args_dict=command.arguments)
|
239
|
-
else:
|
240
|
-
r = self.qmp(cmd=command.execute)
|
241
|
-
|
242
|
-
if verbose:
|
243
|
-
LOG.debug(f"QMP: {command}\nRETURN: {r}")
|
244
|
-
return r
|
245
|
-
|
246
|
-
def shutdown(self, *args, **kwargs):
|
247
|
-
"""
|
248
|
-
Perform shutdown if machine is running
|
249
|
-
"""
|
250
|
-
if self.is_running():
|
251
|
-
super().shutdown(*args, **kwargs)
|
252
|
-
|
253
|
-
|
254
|
-
class Maqet:
|
255
|
-
"""
|
256
|
-
TODO
|
257
|
-
"""
|
258
|
-
|
259
|
-
def __init__(self, *args, **kwargs) -> None:
|
260
|
-
self.Machine = Machine # For comfortable stage building
|
261
|
-
self._config = benedict(kwargs)
|
262
|
-
|
263
|
-
self._stages = dict()
|
264
|
-
self.storage: dict[str, Drive] = dict()
|
265
|
-
|
266
|
-
def __call__(self, *args, **kwargs) -> None:
|
267
|
-
# Start pipeline
|
268
|
-
|
269
|
-
LOG.info(f"Stages to run: {args}")
|
270
|
-
|
271
|
-
for stage in args:
|
272
|
-
if stage in self._stages:
|
273
|
-
LOG.info(f"Executing stage {stage}")
|
274
|
-
with self.Machine(**self._config.deepcopy()) as vm:
|
275
|
-
self._stages[stage](vm)
|
276
|
-
else:
|
277
|
-
LOG.warning(f"Stage {stage} not found. Skipping")
|
278
|
-
|
279
|
-
@property
|
280
|
-
def command(self) -> str:
|
281
|
-
"""
|
282
|
-
get full command string that will be executed at start
|
283
|
-
"""
|
284
|
-
with self.Machine(**self._config) as vm:
|
285
|
-
return vm.command
|
286
|
-
|
287
|
-
@property
|
288
|
-
def DRIVE_TYPES(self) -> list:
|
289
|
-
"""
|
290
|
-
Get available drive types
|
291
|
-
"""
|
292
|
-
return list(DRIVE_TYPES)
|
293
|
-
|
294
|
-
@property
|
295
|
-
def config(self) -> dict:
|
296
|
-
return self._config
|
297
|
-
|
298
|
-
@config.setter
|
299
|
-
def set_config(self, new_config: dict):
|
300
|
-
self._config = benedict(new_config)
|
301
|
-
|
302
|
-
def merge_config(self, new_config: dict):
|
303
|
-
self._config.merge(new_config)
|
304
|
-
|
305
|
-
def add_argument(self, argument: str) -> None:
|
306
|
-
"""
|
307
|
-
Add user argument to config.
|
308
|
-
It will be added to Machine arguments in every stage
|
309
|
-
"""
|
310
|
-
self._config['user_arguments'].append(argument)
|
311
|
-
|
312
|
-
def stage(self, stage_func: Callable, *args, **kwargs) -> Callable:
|
313
|
-
"""
|
314
|
-
Include function as stage into Maqet pipeline. Does not modify function
|
315
|
-
"""
|
316
|
-
self._stages[stage_func.__name__] = lambda vm: stage_func(vm)
|
317
|
-
|
318
|
-
@ wraps
|
319
|
-
def wrapper(*args, **kwargs):
|
320
|
-
raise MaqetError(
|
321
|
-
"Do not call stage functions by themselves, "
|
322
|
-
f"call {self.__class__}(<stage function name>)"
|
323
|
-
f"or {self.__class__}.cli() "
|
324
|
-
"and enter stages as positional args"
|
325
|
-
)
|
326
|
-
return wrapper
|
327
|
-
|
328
|
-
def cli(self, *args, **kwargs) -> None:
|
329
|
-
"""
|
330
|
-
Run Maqet in Command Line Interface mode
|
331
|
-
"""
|
332
|
-
parser = argparse.ArgumentParser()
|
333
|
-
|
334
|
-
parser.add_argument(
|
335
|
-
"stage",
|
336
|
-
nargs="*",
|
337
|
-
help="stages to run. If not stated - "
|
338
|
-
"start VM with given config and wait for shutdown",
|
339
|
-
)
|
340
|
-
parser.add_argument(
|
341
|
-
"-c", "--config-file",
|
342
|
-
help="Set yaml file as a config",
|
343
|
-
required=False,
|
344
|
-
default=None,
|
345
|
-
type=Path,
|
346
|
-
)
|
347
|
-
parser.add_argument(
|
348
|
-
"-a", "--argument",
|
349
|
-
help="State additional argument for qemu binary",
|
350
|
-
action="append",
|
351
|
-
default=[],
|
352
|
-
# nargs="*",
|
353
|
-
)
|
354
|
-
parser.add_argument(
|
355
|
-
"-s", "--storage-path",
|
356
|
-
help="Set default path for drives",
|
357
|
-
required=False,
|
358
|
-
type=Path,
|
359
|
-
default=None
|
360
|
-
)
|
361
|
-
parser.add_argument(
|
362
|
-
"-v", "--verbose",
|
363
|
-
action='count',
|
364
|
-
help="increase verbose level",
|
365
|
-
default=0
|
366
|
-
)
|
367
|
-
parser.add_argument(
|
368
|
-
"--command",
|
369
|
-
action='store_true',
|
370
|
-
help="Output command with config and exit",
|
371
|
-
default=False
|
372
|
-
)
|
373
|
-
|
374
|
-
cli_args = parser.parse_args()
|
375
|
-
LOG.setLevel(50 - cli_args.verbose *
|
376
|
-
10 if cli_args.verbose < 5 else 10)
|
377
|
-
|
378
|
-
LOG.info(cli_args)
|
379
|
-
|
380
|
-
if cli_args.storage_path is not None:
|
381
|
-
self._config.storage_path = cli_args.storage_path
|
382
|
-
if cli_args.config_file is not None:
|
383
|
-
self.merge_config(
|
384
|
-
benedict.from_yaml(cli_args.config_file.resolve())
|
385
|
-
)
|
386
|
-
if len(cli_args.argument) > 0:
|
387
|
-
for a in cli_args.argument:
|
388
|
-
self.add_argument(a)
|
389
|
-
if cli_args.command:
|
390
|
-
print(self.command)
|
391
|
-
exit(0)
|
392
|
-
|
393
|
-
self(
|
394
|
-
*cli_args.stage,
|
395
|
-
)
|
maqet/functions.py
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import subprocess
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import List
|
5
|
-
|
6
|
-
from benedict import benedict
|
7
|
-
|
8
|
-
from .logger import LOG
|
9
|
-
|
10
|
-
|
11
|
-
def shell_command(command: str, verbose: bool = True) -> benedict:
|
12
|
-
"""
|
13
|
-
Run shell command and return dictionary of stdout, stderr, returncode
|
14
|
-
Return is benedict object, members can be accessed as fields
|
15
|
-
"""
|
16
|
-
command = " ".join(command.split())
|
17
|
-
|
18
|
-
proc = subprocess.Popen(command, shell=True,
|
19
|
-
stdout=subprocess.PIPE,
|
20
|
-
stderr=subprocess.PIPE)
|
21
|
-
out = proc.communicate()
|
22
|
-
|
23
|
-
output = benedict({
|
24
|
-
"stdout": out[0].decode('ascii').strip("\n"),
|
25
|
-
"stderr": out[1].decode('ascii').strip("\n"),
|
26
|
-
"rc": proc.returncode
|
27
|
-
})
|
28
|
-
|
29
|
-
message = f"command `{command}` returned {output}"
|
30
|
-
|
31
|
-
if verbose:
|
32
|
-
level = logging.DEBUG
|
33
|
-
if output.stderr != '':
|
34
|
-
level = logging.WARNING
|
35
|
-
if output.rc != 0:
|
36
|
-
level = logging.ERROR
|
37
|
-
|
38
|
-
LOG.log(level, message)
|
39
|
-
return output
|
40
|
-
|
41
|
-
|
42
|
-
def mount_disk_to_host(path: Path) -> Path:
|
43
|
-
return Path(shell_command(f"""
|
44
|
-
udiskie-mount {str(path)} | grep mounted |\
|
45
|
-
grep -o 'on .*$' | sed 's/on //'
|
46
|
-
""").stdout)
|
47
|
-
|
48
|
-
|
49
|
-
def split_args(args: List):
|
50
|
-
"""
|
51
|
-
Split args by spaces, used to send them into qemu methods
|
52
|
-
"""
|
53
|
-
return [
|
54
|
-
a_splitted
|
55
|
-
for a in args
|
56
|
-
for a_splitted in a.split()
|
57
|
-
]
|
58
|
-
|
59
|
-
|
60
|
-
def parse_options(arg, stack=[], key_only=False):
|
61
|
-
if not isinstance(arg, (list, dict)):
|
62
|
-
if len(stack) > 0:
|
63
|
-
if key_only:
|
64
|
-
return '.'.join(stack) + f'.{arg}'
|
65
|
-
else:
|
66
|
-
return '.'.join(stack) + f'={arg}'
|
67
|
-
else:
|
68
|
-
return arg
|
69
|
-
|
70
|
-
options = []
|
71
|
-
if isinstance(arg, list):
|
72
|
-
for v in arg:
|
73
|
-
if isinstance(arg, (list, dict)):
|
74
|
-
options.append(parse_options(v, stack, key_only=True))
|
75
|
-
else:
|
76
|
-
options.append('.'.join(stack) + f'.{v}')
|
77
|
-
|
78
|
-
elif isinstance(arg, dict):
|
79
|
-
for k, v in arg.items():
|
80
|
-
if isinstance(arg, (list, dict)):
|
81
|
-
options.append(parse_options(v, stack+[k], key_only=False))
|
82
|
-
else:
|
83
|
-
option = '.'.join(stack) + f'={v}'
|
84
|
-
options.append(option)
|
85
|
-
return ','.join(options)
|
86
|
-
|
87
|
-
|
88
|
-
def parse_args(args: list) -> List[str]:
|
89
|
-
final_args = []
|
90
|
-
|
91
|
-
for arg in args:
|
92
|
-
if type(arg) is str:
|
93
|
-
argument = f'-{arg}'
|
94
|
-
else:
|
95
|
-
al = list(arg.items())
|
96
|
-
if len(al) == 1:
|
97
|
-
argument = f"-{al[0][0]} {parse_options(al[0][1])}"
|
98
|
-
else:
|
99
|
-
subarg = arg.copy()
|
100
|
-
del subarg[al[0][0]]
|
101
|
-
argument = f"-{al[0][0]} {al[0][1]},{parse_options(subarg)}"
|
102
|
-
final_args.append(argument)
|
103
|
-
|
104
|
-
return split_args(final_args)
|
maqet-0.0.1.3.dist-info/METADATA
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: maqet
|
3
|
-
Version: 0.0.1.3
|
4
|
-
Project-URL: Repository, https://gitlab.com/m4x0n_24/maqet/-/tree/main
|
5
|
-
Classifier: Programming Language :: Python :: 3.12
|
6
|
-
Requires-Python: >=3.12
|
7
|
-
Description-Content-Type: text/markdown
|
8
|
-
Requires-Dist: python-benedict >=0.33.2
|
9
|
-
|
10
|
-
# MAQET
|
11
|
-
|
12
|
-
**Work in progress.** Documentation and code can be absolute mess.
|
13
|
-
|
14
|
-
If you are willing to help - open issue, MR or do something else
|
15
|
-
to attract maintainer's attention
|
16
|
-
|
17
|
-
## Description
|
18
|
-
|
19
|
-
MAQET stands for M4x0n QEmu Tool.
|
20
|
-
It's based on [QEMU](https://gitlab.com/qemu-project/qemu) python [tooling](https://gitlab.com/qemu-project/qemu/-/tree/master/python?ref_type=heads).
|
21
|
-
|
22
|
-
Main goal of this project is to provide good way to run qemu virtual machine.
|
23
|
-
Author (m4x0n) wants to create virtual environment for repeatable testing
|
24
|
-
of Linux installation and configuration before messing around with real computer,
|
25
|
-
so all features implemented are primarily pursuing this goal.
|
26
|
-
|
27
|
-
### Roadmap
|
28
|
-
|
29
|
-
First usable version:
|
30
|
-
|
31
|
-
- [x] YAML manifest for VM settings
|
32
|
-
- [x] Storage (-drive) management:
|
33
|
-
- [x] RAW and QCOW2 files
|
34
|
-
- [x] Using backing images for QCOW2
|
35
|
-
- [x] Internal snapshots for QCOW2
|
36
|
-
- [x] Stages defined as python functions with decorator (Flask style)
|
37
|
-
- [x] CLI to specify stage, additional arguments and options
|
38
|
-
- [x] QMP commands
|
39
|
-
|
40
|
-
Goals for near future (sorted by priority):
|
41
|
-
|
42
|
-
- [ ] Unified naming of variables (what is config? What is manifest?)
|
43
|
-
- [ ] Code cleanup, adding typing hints, docstrings etc.
|
44
|
-
- [ ] Extensible system of kwargs handling in every class (see in dev notes)
|
45
|
-
- [ ] Unit tests
|
46
|
-
- [ ] Full translation of strings into QMP key inputs
|
47
|
-
|
48
|
-
Advanced goals
|
49
|
-
|
50
|
-
- [ ] Custom QMP command presets
|
51
|
-
- [ ] Advanced storage management
|
52
|
-
- [ ] Runtime snapshots via qmp
|
53
|
-
- [ ] Remote storage types (iscsi)
|
54
|
-
|
55
|
-
## Usage guide
|
56
|
-
|
57
|
-
See [example](https://gitlab.com/m4x0n_24/maqet/-/tree/main/example)
|
58
|
-
Full documentation will be later
|
59
|
-
|
60
|
-
## Development notes
|
61
|
-
|
62
|
-
### Unification of **kwargs handling in extensible way
|
63
|
-
|
64
|
-
Problem: classes have *args and **kwargs arguments. It gives flexibility,
|
65
|
-
but also makes argument handling cumbersome (Ladder of if statements).
|
66
|
-
|
67
|
-
Main class - Maqet, get it's kwargs and uses them as a config,
|
68
|
-
parses and creates Machine class object
|
69
|
-
with relevant arguments, also in form of kwargs.
|
70
|
-
|
71
|
-
Idea is to make it extensible and unified. Like this:
|
72
|
-
|
73
|
-
```yaml
|
74
|
-
binary: qemu-system-x86_64
|
75
|
-
arguments:
|
76
|
-
- foo
|
77
|
-
- bar
|
78
|
-
- baz
|
79
|
-
storage:
|
80
|
-
- hda:
|
81
|
-
size: 5G
|
82
|
-
type: qcow2
|
83
|
-
options:
|
84
|
-
id: sda
|
85
|
-
...
|
86
|
-
some_custom_field:
|
87
|
-
- memory: 8G
|
88
|
-
- cpu: 4
|
89
|
-
- print: I_LIKE_PONIES
|
90
|
-
```
|
91
|
-
|
92
|
-
Binary is easy, just pass it into Machine
|
93
|
-
What about arguments? Also pass into Machie, I made parser that turn them
|
94
|
-
into cli arguments for qemu binary
|
95
|
-
Storage? Call object Storage for every object in the list and pass contents inside
|
96
|
-
Custom field? It will be ignored. If we want to use it?
|
97
|
-
For example:
|
98
|
-
We want to turn `memory: 8G, cpu: 4` into `-m 8G -smp 4`
|
99
|
-
and run `print("I_LIKE_PONIES")` before VM starts.
|
100
|
-
So we need somehow to extend Maqet class. How to do it? Still not sure.
|
101
|
-
|
102
|
-
### QEMU not in PyPI
|
103
|
-
|
104
|
-
Now QEMU tooling for python is used as sparse-checkouted git submodule.
|
maqet-0.0.1.3.dist-info/RECORD
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
maqet/__init__.py,sha256=hH-7x9aitvZKK46YFjdGLvHRSGMl67QLWwYlzikeoRw,96
|
2
|
-
maqet/core.py,sha256=7SjZaVNWsZG7Sp2lw_UcrMKInWHMsprpq9nnnyiEi8s,11841
|
3
|
-
maqet/functions.py,sha256=gZRIeCZpalnK6T9ktDzGHIT8LzniXaEMdzFNVTccXOo,2856
|
4
|
-
maqet/logger.py,sha256=3N9TnInogdYVZ7DrT6Cg6lL8Q-c6AufBDspy9RmLWhk,1821
|
5
|
-
maqet/storage.py,sha256=09atk3-eMf2xH-CGdwrr9D-zHMPXkaLXT1AIjZ6WMxI,6273
|
6
|
-
qemu/machine/__init__.py,sha256=X8_E5OIwoB8zsmqb8mJGOozzSlm5zujaJih6I12BBdg,945
|
7
|
-
qemu/machine/console_socket.py,sha256=uZdScPgbtQXjAtzpBIpXwLuLgMj6MMblaOj4jLjYaEM,5314
|
8
|
-
qemu/machine/machine.py,sha256=wXb2TmPfS9HOC5HoCKhZ0pwcXolqDNi10YmCQs14sCU,32965
|
9
|
-
qemu/machine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
qemu/machine/qtest.py,sha256=ELhoX5pSIL4jeUZjlT5oh5wty4zOTdcfasAjTBxel1Y,5926
|
11
|
-
qemu/qmp/__init__.py,sha256=Gz9qHFm1IPfva9CmBOT1H4MjBygpzXTK-JrHP0ba71E,1545
|
12
|
-
qemu/qmp/error.py,sha256=tCa_0PXVfGfj0-U88WzYnIpSVoo2kCnvyEZWh0hMp0A,1701
|
13
|
-
qemu/qmp/events.py,sha256=ztOo_BbzLJZC3_X0EV4BhY5ibNt0CjnGVWAWEmTTiVk,22653
|
14
|
-
qemu/qmp/legacy.py,sha256=zogqHC05n8RineXAD68iEP6K6RQFMZTlwx9wSpq3zvc,9962
|
15
|
-
qemu/qmp/message.py,sha256=HxYZ5XguixBi1G6xXrKI5NQLekxzdPuOLxeWpz9Qhxw,6355
|
16
|
-
qemu/qmp/models.py,sha256=mz3oDKmHUjro9vFDreooD0PucNFU4tCKJVYxTFfWdN8,4418
|
17
|
-
qemu/qmp/protocol.py,sha256=d-3pInzEUJqI_RlM6IOOyYCOruEEfLm0U96NIjZa1OA,38506
|
18
|
-
qemu/qmp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
qemu/qmp/qmp_client.py,sha256=LqHwh1cFUcgr8kWYBo9Z1XIlrMNypL9_4xeLQ9kRdGk,22584
|
20
|
-
qemu/qmp/qmp_shell.py,sha256=LbI2bIlrGLbKIbLSeGyoxA_Vh4Eb1j3R8fqYxLqrP6c,19897
|
21
|
-
qemu/qmp/qmp_tui.py,sha256=DRq2VhEqG8WkyQ7OdPVKYvJYi502WDpmHQDbISU4Rac,22318
|
22
|
-
qemu/qmp/util.py,sha256=A2ThMPFtt6CpqNGPa4F6T2D4vmhTcyEPGe8vjBV9Bbk,6231
|
23
|
-
qemu/utils/__init__.py,sha256=hC2_82OPDhVTKE6UUG_bsuBVDkqjwD7LArxBxuzu8BY,5646
|
24
|
-
qemu/utils/accel.py,sha256=jOuuDLXyJMjZX9Uae-XnhNdYFnrXMlaGaUlxyCNntwU,2348
|
25
|
-
qemu/utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
-
qemu/utils/qemu_ga_client.py,sha256=ycQ6lOh7Q0DEwvNq_LyE0Y4DRZfimq8kjhNlyn3K0Zk,9484
|
27
|
-
qemu/utils/qom.py,sha256=sC2wXMZ_dH4E5HnYinjCMpagx_I-9NJ5oJi0rEiqqQQ,7564
|
28
|
-
qemu/utils/qom_common.py,sha256=UQitmqLNGYfd3DoMaLNxRprgJeaEs6hX-8mrhGQ2mkc,4991
|
29
|
-
qemu/utils/qom_fuse.py,sha256=uDbTKVWWej1Ey1Jsnw2p9sjmuLC_2vBBtBZpPG40I-k,5966
|
30
|
-
maqet-0.0.1.3.dist-info/METADATA,sha256=lkT0rMHt-c6_DXT-HjQPZFULSEHIzceQqsBMSw24Oo4,3154
|
31
|
-
maqet-0.0.1.3.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
32
|
-
maqet-0.0.1.3.dist-info/top_level.txt,sha256=9J0eHJ7TIdifxVkOxhgWVSyxkKRQhuFcbqUzIjhmgWg,11
|
33
|
-
maqet-0.0.1.3.dist-info/RECORD,,
|
qemu/machine/__init__.py
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
QEMU development and testing library.
|
3
|
-
|
4
|
-
This library provides a few high-level classes for driving QEMU from a
|
5
|
-
test suite, not intended for production use.
|
6
|
-
|
7
|
-
| QEMUQtestProtocol: send/receive qtest messages.
|
8
|
-
| QEMUMachine: Configure and Boot a QEMU VM
|
9
|
-
| +-- QEMUQtestMachine: VM class, with a qtest socket.
|
10
|
-
|
11
|
-
"""
|
12
|
-
|
13
|
-
# Copyright (C) 2020-2021 John Snow for Red Hat Inc.
|
14
|
-
# Copyright (C) 2015-2016 Red Hat Inc.
|
15
|
-
# Copyright (C) 2012 IBM Corp.
|
16
|
-
#
|
17
|
-
# Authors:
|
18
|
-
# John Snow <jsnow@redhat.com>
|
19
|
-
# Fam Zheng <fam@euphon.net>
|
20
|
-
#
|
21
|
-
# This work is licensed under the terms of the GNU GPL, version 2. See
|
22
|
-
# the COPYING file in the top-level directory.
|
23
|
-
#
|
24
|
-
|
25
|
-
# pylint: disable=import-error
|
26
|
-
# see: https://github.com/PyCQA/pylint/issues/3624
|
27
|
-
# see: https://github.com/PyCQA/pylint/issues/3651
|
28
|
-
from .machine import QEMUMachine
|
29
|
-
from .qtest import QEMUQtestMachine, QEMUQtestProtocol
|
30
|
-
|
31
|
-
|
32
|
-
__all__ = (
|
33
|
-
'QEMUMachine',
|
34
|
-
'QEMUQtestProtocol',
|
35
|
-
'QEMUQtestMachine',
|
36
|
-
)
|