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
maqet/config/parser.py ADDED
@@ -0,0 +1,198 @@
1
+ """
2
+ Configuration Parser
3
+
4
+ Parses YAML configuration files for VM settings only.
5
+ Does NOT execute API commands - configs are purely for VM configuration.
6
+ Supports multiple config file deep-merging for flexible VM configuration.
7
+ """
8
+
9
+ import os
10
+ import stat
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any, Dict
13
+
14
+ if TYPE_CHECKING:
15
+ from ..maqet import Maqet
16
+
17
+ from .merger import ConfigError
18
+
19
+
20
+ class ConfigParser:
21
+ """
22
+ Parses and validates VM configuration files.
23
+
24
+ This parser handles YAML configuration files for VM settings ONLY.
25
+ It does NOT execute API commands - those should be handled separately
26
+ by calling methods on the Maqet instance.
27
+
28
+ The parser validates configuration structure and types but does not
29
+ start VMs or execute commands.
30
+
31
+ NOTE: Configuration parsing flow:
32
+ 1. ConfigParser.parse_config() - Loads and validates YAML
33
+ 2. Machine._configure_machine() - Converts config to QEMU args
34
+ 3. ConfigHandlers (config_handlers.py) - Handler-based config processing
35
+ 4. StorageManager.get_qemu_args() - Storage-specific QEMU args
36
+
37
+ The old qemu_args.py approach has been replaced with the extensible
38
+ handler-based system for better maintainability and test coverage.
39
+ """
40
+
41
+ def __init__(self, maqet_instance: "Maqet"):
42
+ """
43
+ Initialize config parser.
44
+
45
+ Args:
46
+ maqet_instance: Maqet instance for validation context
47
+ """
48
+ self.maqet = maqet_instance
49
+
50
+ def parse_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
51
+ """
52
+ Parse configuration dictionary.
53
+
54
+ Args:
55
+ config_data: Configuration dictionary
56
+
57
+ Returns:
58
+ Normalized configuration data
59
+ """
60
+ if not isinstance(config_data, dict):
61
+ raise ConfigError("Configuration must be a dictionary")
62
+
63
+ return config_data
64
+
65
+ def validate_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
66
+ """
67
+ Validate VM configuration data.
68
+
69
+ This method validates VM configuration settings without executing
70
+ any API commands. Configuration files are purely for VM settings.
71
+
72
+ Args:
73
+ config_data: Configuration dictionary
74
+
75
+ Returns:
76
+ Validated configuration data
77
+
78
+ Example:
79
+ config = {
80
+ 'binary': '/usr/bin/qemu-system-x86_64',
81
+ 'memory': '4G',
82
+ 'cpu': 2,
83
+ 'storage': [
84
+ {'name': 'hdd', 'size': '20G', 'type': 'qcow2'}
85
+ ]
86
+ }
87
+ validated = parser.validate_config(config)
88
+ """
89
+
90
+ # Use dynamic validation system with decorators
91
+ from .validators import ConfigValidationError, validate_config_data
92
+
93
+ # File integrity validation implemented in _validate_config_file_security():
94
+ # - Size limit check (10MB) prevents DoS with huge YAML
95
+ # - World-writable permission warning for security
96
+ # - File ownership validation (warns if not owned by current user)
97
+ # - yaml.safe_load used in merger.py prevents code execution
98
+ # NOTE: Checksum/signature validation not implemented (would require key management)
99
+ try:
100
+ # Unknown keys are silently ignored - no validation needed
101
+ # This allows forward compatibility and flexibility in config files
102
+
103
+ # Early storage validation to fail fast before VM creation
104
+ if "storage" in config_data:
105
+ from ..storage import validate_storage_config
106
+
107
+ try:
108
+ validate_storage_config(config_data["storage"])
109
+ except ValueError as e:
110
+ raise ConfigError(f"Storage configuration error: {e}")
111
+
112
+ # Use dynamic validator system
113
+ return validate_config_data(config_data)
114
+
115
+ except ConfigValidationError as e:
116
+ raise ConfigError(str(e))
117
+
118
+ def _validate_config_file_security(self, config_path: str) -> None:
119
+ """
120
+ Validate configuration file security before loading.
121
+
122
+ Args:
123
+ config_path: Path to YAML configuration file
124
+
125
+ Raises:
126
+ ConfigError: If file has security issues
127
+ """
128
+
129
+ # Using pathlib for modern path handling
130
+ # TODO(m4x0n, 2025-10-10): Standardize all path operations to use
131
+ # pathlib throughout codebase. See CODE_ISSUES_REPORT.md #4.
132
+ path = Path(config_path)
133
+
134
+ # Check if file exists first
135
+ if not path.exists():
136
+ # Let load_and_merge_files handle the FileNotFoundError
137
+ return
138
+
139
+ # Check file size to prevent DoS with huge YAML files
140
+ max_size = 10 * 1024 * 1024 # 10MB limit
141
+ file_stat = path.stat()
142
+ file_size = file_stat.st_size
143
+ if file_size > max_size:
144
+ raise ConfigError(
145
+ f"Configuration file too large ({file_size} bytes, max {
146
+ max_size}). "
147
+ f"This may indicate a malicious or corrupted file."
148
+ )
149
+
150
+ # Check file permissions - warn if world-writable
151
+ mode = file_stat.st_mode
152
+ # FIXME(m4x0n, 2025-10-10): Missing import. Issue #1 in
153
+ # CODE_ISSUES_REPORT.md - needs `import stat` at module level.
154
+ if mode & stat.S_IWOTH:
155
+ from ..logger import LOG
156
+
157
+ LOG.warning(
158
+ f"Configuration file {config_path} is world-writable. "
159
+ f"This is a security risk as anyone can modify VM settings."
160
+ )
161
+
162
+ # Check if file is owned by current user (Unix only)
163
+ # FIXME(m4x0n, 2025-10-10): Missing import. Issue #2 in
164
+ # CODE_ISSUES_REPORT.md - needs `import os` at module level.
165
+ if hasattr(os, "getuid"):
166
+ current_uid = os.getuid()
167
+ if file_stat.st_uid != current_uid:
168
+ from ..logger import LOG
169
+
170
+ LOG.warning(
171
+ f"Configuration file {
172
+ config_path} is not owned by current user. "
173
+ f"Verify the file source before using."
174
+ )
175
+
176
+ def load_and_validate(self, config_path: str) -> Dict[str, Any]:
177
+ """
178
+ Load configuration file and validate it.
179
+
180
+ Args:
181
+ config_path: Path to YAML configuration file
182
+
183
+ Returns:
184
+ Validated configuration data
185
+
186
+ Raises:
187
+ ConfigError: If file cannot be loaded or validation fails
188
+ """
189
+ from .merger import ConfigMerger
190
+
191
+ # Validate file security before loading
192
+ self._validate_config_file_security(config_path)
193
+
194
+ # Load single config file (uses yaml.safe_load for security)
195
+ config_data = ConfigMerger.load_and_merge_files(config_path)
196
+
197
+ # Validate the loaded config
198
+ return self.validate_config(config_data)