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
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: maqet
3
+ Version: 0.0.5
4
+ Summary: M4x0n's QEMU Tool - A Python-based QEMU automation framework for VM running
5
+ Author: Max Rogol
6
+ Maintainer: Max Rogol
7
+ License-Expression: MIT
8
+ Project-URL: Repository, https://github.com/m4x0n/maqet
9
+ Project-URL: Documentation, https://github.com/m4x0n/maqet/blob/main/README.md
10
+ Project-URL: Issues, https://github.com/m4x0n/maqet/issues
11
+ Keywords: qemu,virtualization,vm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.12
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: python-benedict>=0.33.2
26
+ Requires-Dist: PyYAML>=6.0.2
27
+ Provides-Extra: qemu
28
+ Requires-Dist: qemu.qmp>=3.0.0; extra == "qemu"
29
+ Provides-Extra: tables
30
+ Requires-Dist: tabulate>=0.9.0; extra == "tables"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=6.0; extra == "dev"
33
+ Requires-Dist: pytest-cov; extra == "dev"
34
+ Requires-Dist: pytest-testmon; extra == "dev"
35
+ Requires-Dist: black; extra == "dev"
36
+ Requires-Dist: flake8; extra == "dev"
37
+ Requires-Dist: mypy; extra == "dev"
38
+ Requires-Dist: isort; extra == "dev"
39
+ Requires-Dist: pre-commit; extra == "dev"
40
+ Requires-Dist: tabulate>=0.9.0; extra == "dev"
41
+ Dynamic: license-file
42
+
43
+ # MAQET
44
+
45
+ **MAQET** (M4x0n's QEMU Tool) is a VM management system that implements unified API generation. Methods decorated with `@api_method` automatically become CLI commands, Python API methods, and configuration-driven calls.
46
+
47
+ ## Quick Start
48
+
49
+ ### Installation
50
+
51
+ ```bash
52
+ pip install maqet
53
+ ```
54
+
55
+ **Optional Dependencies:**
56
+
57
+ - `psutil` - Enhanced process management and validation (recommended)
58
+
59
+ ```bash
60
+ pip install psutil
61
+ ```
62
+
63
+ Without psutil, basic PID tracking still works but ownership validation is skipped.
64
+
65
+ ### Core Concept
66
+
67
+ Write once, use everywhere. A single method becomes a CLI command, Python API, and configuration option:
68
+
69
+ ```python
70
+ @api_method(cli_name="start", description="Start a virtual machine", category="vm")
71
+ def start(self, vm_id: str, detach: bool = False):
72
+ """Start a virtual machine."""
73
+ # Single implementation
74
+ ```
75
+
76
+ This automatically creates:
77
+
78
+ - **CLI**: `maqet start myvm --detach`
79
+ - **Python API**: `maqet.start("myvm", detach=True)`
80
+ - **Config**: VM settings only (no commands in YAML)
81
+
82
+ ## Usage
83
+
84
+ ### Command Line Interface
85
+
86
+ ```bash
87
+ # Create a VM
88
+ maqet add --config config.yaml --name myvm
89
+
90
+ # Start VM
91
+ maqet start myvm
92
+
93
+ # List all VMs
94
+ maqet ls
95
+
96
+ # Check VM status
97
+ maqet status myvm
98
+
99
+ # Execute QMP command
100
+ maqet qmp myvm system_powerdown
101
+
102
+ # Remove VM
103
+ maqet rm myvm --force
104
+ ```
105
+
106
+ ### Python API
107
+
108
+ ```python
109
+ from maqet import Maqet
110
+
111
+ maqet = Maqet()
112
+
113
+ # Create and start VM
114
+ vm_id = maqet.add(name='myvm', memory='4G', cpu=2)
115
+ maqet.start(vm_id)
116
+
117
+ # Manage VM
118
+ status = maqet.status(vm_id)
119
+ maqet.qmp(vm_id, 'system_powerdown')
120
+ maqet.rm(vm_id, force=True)
121
+ ```
122
+
123
+ ### Configuration Files
124
+
125
+ ```yaml
126
+ # config.yaml - VM configuration only
127
+ name: myvm
128
+ binary: /usr/bin/qemu-system-x86_64
129
+ memory: 4G
130
+ cpu: 2
131
+ storage:
132
+ - name: hdd
133
+ size: 20G
134
+ type: qcow2
135
+ interface: virtio
136
+ ```
137
+
138
+ ```bash
139
+ # Use configuration file
140
+ maqet add --config config.yaml
141
+ maqet start myvm --detach
142
+ ```
143
+
144
+ **Configuration Features:**
145
+
146
+ - Deep-merge multiple config files
147
+ - Lists are concatenated (storage, network)
148
+ - Command-line args override config values
149
+ - Full QEMU argument support
150
+
151
+ See [Configuration Guide](docs/user-guide/configuration.md) for details.
152
+
153
+ ## Core Commands
154
+
155
+ | Command | Description | Example |
156
+ |---------|-------------|---------|
157
+ | `add` | Create new VM | `maqet add --config config.yaml --name myvm` |
158
+ | `start` | Start VM | `maqet start myvm` |
159
+ | `stop` | Stop VM | `maqet stop myvm --force` |
160
+ | `rm` | Remove VM | `maqet rm myvm --force` |
161
+ | `ls` | List VMs | `maqet ls --status running` |
162
+ | `status` | Show VM status | `maqet status myvm` |
163
+ | `apply` | Apply configuration | `maqet apply myvm --memory 8G` |
164
+ | `snapshot` | Manage snapshots | `maqet snapshot myvm create hdd snap1` |
165
+
166
+ ### QMP Commands
167
+
168
+ | Command | Description | Example |
169
+ |---------|-------------|---------|
170
+ | `qmp keys` | Send key combination | `maqet qmp keys myvm ctrl alt f2` |
171
+ | `qmp type` | Type text to VM | `maqet qmp type myvm "hello world"` |
172
+ | `qmp screendump` | Take screenshot | `maqet qmp screendump myvm screenshot.ppm` |
173
+ | `qmp pause` | Pause VM | `maqet qmp pause myvm` |
174
+ | `qmp resume` | Resume VM | `maqet qmp resume myvm` |
175
+ | `qmp device-add` | Hot-plug device | `maqet qmp device-add myvm usb-storage` |
176
+ | `qmp device-del` | Hot-unplug device | `maqet qmp device-del myvm usb1` |
177
+
178
+ ### Global Options
179
+
180
+ | Option | Description |
181
+ |--------|-------------|
182
+ | `-v, --verbose` | Increase verbosity (-v, -vv, -vvv) |
183
+ | `-q, --quiet` | Suppress non-error output |
184
+ | `--data-dir` | Override data directory |
185
+ | `--log-file` | Enable file logging |
186
+
187
+ ## Documentation
188
+
189
+ ### Full Documentation
190
+
191
+ - **[Documentation Index](docs/README.md)** - Complete documentation portal
192
+ - **[Architecture](docs/architecture/)** - Internal architecture and design
193
+ - **[Development](docs/development/)** - Contributing and development guides
194
+ - **[Deployment](docs/deployment/)** - Production deployment
195
+ - **[Reference](docs/reference/)** - Technical references
196
+
197
+ ### Architecture
198
+
199
+ - **Unified API System** - Single methods generate CLI, Python API, and config
200
+ - **State Management** - SQLite backend with XDG compliance
201
+ - **QEMU Integration** - Full QMP protocol support
202
+ - **Storage System** - QCOW2, Raw, VirtFS support with snapshots
203
+
204
+ See [QEMU Internal Architecture](docs/architecture/QEMU_INTERNAL_ARCHITECTURE.md) for details.
205
+
206
+ ### Development
207
+
208
+ ```bash
209
+ # Run tests
210
+ python tests/run_tests.py
211
+
212
+ # Run pre-commit checks
213
+ pre-commit run --all-files
214
+ ```
215
+
216
+ See [Testing Guide](docs/development/TESTING.md) for details on running tests and contributing.
217
+
218
+ ### Roadmap
219
+
220
+ See [Roadmap](docs/development/ROADMAP.md) and [Future Features](docs/development/FUTURE_FEATURES.md) for planned improvements.
221
+
222
+ ## Features
223
+
224
+ - **Write Once, Use Everywhere** - Single method for CLI, API, and config
225
+ - **XDG Compliant** - Follows Linux directory standards
226
+ - **Production Ready** - Security hardened, tested, robust error handling
227
+ - **Full QMP Support** - Complete QEMU Machine Protocol integration
228
+ - **Snapshot Management** - Create, load, list, and delete snapshots
229
+ - **Hot-plug Support** - Add/remove devices while VM is running
230
+
231
+ ## Contributing
232
+
233
+ Contributions welcome! See [Development Guide](docs/development/) for contributing guidelines.
234
+
235
+ ## License
236
+
237
+ MIT License - see [LICENSE](LICENSE) file for details.
@@ -0,0 +1,55 @@
1
+ maqet/__init__.py,sha256=9D0pu897AZ7QRXp_9m9VGqZPChrIc0J74SD7-6_wWQE,1635
2
+ maqet/__main__.py,sha256=lZPzTVQaTF1Wd7HatKbLxQVbor8RaQaJObWaCY97F9c,2573
3
+ maqet/__version__.py,sha256=z85BMRCM_1zI8rVf8B1JAWN9Pp4eDYrp2cabRrxeJWo,60
4
+ maqet/cli.py,sha256=saZioU6IYiC_meYh1_XGnBQuS9uXSznEqqczSBT5VhA,1891
5
+ maqet/config_handlers.py,sha256=qfsxMO8SbrgPiMkPU5rtNIXQhpb0Vcfw2tKkda6UPs0,28249
6
+ maqet/constants.py,sha256=AxaBzSByXdyvd1hkmY1DyAzfuUFJayLZ8JFNLdmwUFc,5250
7
+ maqet/exceptions.py,sha256=G3DtdaX0myyF3t1K4to4bTAviE2iNcXGftJ14BWucko,5407
8
+ maqet/formatters.py,sha256=pyyXztPg-VSplIfxK81g4a_-7rCyhILFGPBv_gaVt78,9001
9
+ maqet/logger.py,sha256=EgQl48uv2qxRbQfzPOYCvpGEukNYd9Q5eIis7FrVc6w,5417
10
+ maqet/machine.py,sha256=IoUgKEb15n43T3NmwRcwe3OtDoOaMFaR_emJpBCVCtc,32322
11
+ maqet/maqet.py,sha256=6I_hyvDjermX8o-Zao4c4R4R_Bfl-Kjf5kbfKFaO6F8,36638
12
+ maqet/os_interactions.py,sha256=nLLnnuWVUh_41QiG0EPhlOydDTE44RQMLhYAqreV6AQ,1212
13
+ maqet/process_spawner.py,sha256=TOAH0QZBW-CZjY4Nvt5oSWXxgY4iMwY0yIFt3gPXy6Q,12833
14
+ maqet/qemu_args.py,sha256=7J0-qvUJPLAVbjKR1m8vH2o7ay9yvgxoRZwNA-5mm18,2359
15
+ maqet/snapshot.py,sha256=Hlz8uMJVuoQhGl8CCl27Emux3E9yHKMfwXtVUrXy4GY,16269
16
+ maqet/state.py,sha256=bxWJ_rv6LYJjObfX5UCHY32hi2QRDoirtiT_L6_k8zc,33634
17
+ maqet/storage.py,sha256=eTM7GoV-zmpDq0ThK_cThcIwcMJSmRMasRwyZ9YxrLM,25804
18
+ maqet/vm_runner.py,sha256=yVNmvQVAnVX12W8a-g60E25ix1FRBJxpPEdZJWyTKyY,17985
19
+ maqet/api/__init__.py,sha256=VgP-LeRWxeOdZmESO1ugMz_fuXG8o-iJb5aQ4xtbMmA,1234
20
+ maqet/api/decorators.py,sha256=bd72qw-2rqRrsqlEK0s9pFCzcRxY57qAJs-oMs_aRog,6072
21
+ maqet/api/metadata.py,sha256=vGp-V7CI9b8PIr99azfBrwoV1TK9rpaj9ItFLWs8ZRo,4786
22
+ maqet/api/registry.py,sha256=pQhSAZIz5FLwyu5rxYGDijoBwaWrDdIEtjO1uiKl55I,5962
23
+ maqet/config/__init__.py,sha256=7QrAGSLnbhfdISNk1IM3yod8Ug7y6O1iZnT--pdeYgU,534
24
+ maqet/config/merger.py,sha256=-ClcGMH5ZeWeElu_gNEDyVdBCpxWjke_FjUyiLqEf3s,8397
25
+ maqet/config/parser.py,sha256=hQJ-1uOUILuafe38zzmcedXkSrmHuHn8GdKg4XMv_FU,6850
26
+ maqet/config/validators.py,sha256=TEzraqWZ7sfXqiev0v5euVwAXEW7HUj8jIx9QCg5ofg,16497
27
+ maqet/generators/__init__.py,sha256=xSBx-yp5Q1FfxgXA_-tMP2hLwrAL9kTJivnFDqLA31U,310
28
+ maqet/generators/base_generator.py,sha256=Hd9ZOYekeDXsEYIkVhyGszJBWgtX4pIQtMkrRshvld4,2892
29
+ maqet/generators/cli_generator.py,sha256=V4t_WPxZJGPv-A74NbSNaj9Zfp6vD_1fLAjh1R5TkS4,23080
30
+ maqet/generators/python_generator.py,sha256=udgCoDczMZdAZYuB5VfzeZ51yGNhZMN2Eoya10s9dTQ,7725
31
+ maqet/generators/rest_generator.py,sha256=ERNEjcWt0kKyw2cVYgFaxiOA_hX4nhivFJ-iMg404nc,1800
32
+ maqet/handlers/__init__.py,sha256=mECVYMkGBnJgNsAjZHwk56sfJkUSklfnsa-EV7I9DdM,281
33
+ maqet/handlers/base.py,sha256=3rT4w6I5H4rt7Fr3o709whGh_UZEcqBP0BG7qCODhfY,3491
34
+ maqet/handlers/init.py,sha256=dkfizI37cJsZ7GMNnV0nrFBinunOBR-eQs-RMjz68HI,4671
35
+ maqet/handlers/stage.py,sha256=6sRQ72sR6MPnQdUTfl75vrAKiLCbQIKAycReT8TG9IA,5557
36
+ maqet/ipc/__init__.py,sha256=h7Z0abJvi8QbZ4We5Z9zNpwIh0V4ve7d-9YDVbNIrRA,704
37
+ maqet/ipc/retry.py,sha256=2-GI5ScGTgQ8iDlrWXGeuOAEKzi9kH4aaG6WobUNg84,8968
38
+ maqet/ipc/runner_client.py,sha256=yifYgiNLKdwFShxswFZGb3vaBpFswLPVsJWiKulAldg,9333
39
+ maqet/ipc/unix_socket_server.py,sha256=uz020s88JM6bmUzsRDvc3wfhtUwr0oRVkPJch3CDKRU,7945
40
+ maqet/managers/__init__.py,sha256=l2DCi2RH-5MgHxryQMfZ945YU8vI0Gc2vV1TkRMsef4,231
41
+ maqet/managers/qmp_manager.py,sha256=NXUn5iB7sXH0UXocc5Z8VbUtIFuByKIWdkfIw4uabDI,10382
42
+ maqet/managers/snapshot_coordinator.py,sha256=iVYVJSQa7tqtfN00ElZ8tacYYF_T55SrqM0FYzni-q0,11007
43
+ maqet/managers/vm_manager.py,sha256=_6oIbnMO_3RAcB2KgPlWXik6pk-yew_ZKvUvWWKXJCc,24171
44
+ maqet/qmp/__init__.py,sha256=nd0aNCjKd9YIgRmmS7Ltb6X2qn2o56kTq9UgzeRr_Y0,187
45
+ maqet/qmp/commands.py,sha256=g5tHWK1XYQukCpiGkrHQJKmzJ99A7VC5Cmz1Tp29mxw,2238
46
+ maqet/qmp/keyboard.py,sha256=gBsnaloceKp-T_XzaBCqu2uY0xcbtcvAab6MNE7V080,5521
47
+ maqet/qmp/qmp.py,sha256=ABgV_Qpce4C2RMxpU5sbAbNRFdpNsz3LAAh9A-eLE9U,637
48
+ maqet/validation/__init__.py,sha256=hdkYdm72DsKO0OJikHo_NIak_qz4PzP3rQfTekhXrKM,219
49
+ maqet/validation/config_validator.py,sha256=ZZm-NRnFLCTTb6UAqnu2BRYWffw0oZbh3HI56Gi0RLc,5728
50
+ maqet-0.0.5.dist-info/licenses/LICENSE,sha256=rOgOi9uqKq5-h53e_v1CuY7rQpxL8heKLrweLmnC5rI,1066
51
+ maqet-0.0.5.dist-info/METADATA,sha256=oMfMTxgpJ8iMj9F7Sw7ygk-FQ3diakC9Uph6cQg1aAk,6874
52
+ maqet-0.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ maqet-0.0.5.dist-info/entry_points.txt,sha256=CDsejhGOi9jDKmlxaIiDUSj208af6l0BmDQWboEdB4M,46
54
+ maqet-0.0.5.dist-info/top_level.txt,sha256=qfP8Lh5LI3qwQ5XoATTR8UIYECuLUJRhB1sWjgqEH5I,6
55
+ maqet-0.0.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ maqet = maqet.__main__:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Max Rogol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.