clonebox 1.1.13__py3-none-any.whl → 1.1.14__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.
- clonebox/audit.py +448 -0
- clonebox/cli.py +398 -5
- clonebox/cloner.py +40 -12
- clonebox/orchestrator.py +568 -0
- clonebox/plugins/__init__.py +24 -0
- clonebox/plugins/base.py +319 -0
- clonebox/plugins/manager.py +438 -0
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/METADATA +1 -1
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/RECORD +13 -8
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/WHEEL +0 -0
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/entry_points.txt +0 -0
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/licenses/LICENSE +0 -0
- {clonebox-1.1.13.dist-info → clonebox-1.1.14.dist-info}/top_level.txt +0 -0
clonebox/plugins/base.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes for CloneBox plugins.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Optional, Dict, Any, List, Callable, Type
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PluginHook(Enum):
|
|
11
|
+
"""Available plugin hooks."""
|
|
12
|
+
# VM Lifecycle
|
|
13
|
+
PRE_VM_CREATE = "pre_vm_create"
|
|
14
|
+
POST_VM_CREATE = "post_vm_create"
|
|
15
|
+
PRE_VM_START = "pre_vm_start"
|
|
16
|
+
POST_VM_START = "post_vm_start"
|
|
17
|
+
PRE_VM_STOP = "pre_vm_stop"
|
|
18
|
+
POST_VM_STOP = "post_vm_stop"
|
|
19
|
+
PRE_VM_DELETE = "pre_vm_delete"
|
|
20
|
+
POST_VM_DELETE = "post_vm_delete"
|
|
21
|
+
|
|
22
|
+
# Snapshot
|
|
23
|
+
PRE_SNAPSHOT_CREATE = "pre_snapshot_create"
|
|
24
|
+
POST_SNAPSHOT_CREATE = "post_snapshot_create"
|
|
25
|
+
PRE_SNAPSHOT_RESTORE = "pre_snapshot_restore"
|
|
26
|
+
POST_SNAPSHOT_RESTORE = "post_snapshot_restore"
|
|
27
|
+
|
|
28
|
+
# Health
|
|
29
|
+
PRE_HEALTH_CHECK = "pre_health_check"
|
|
30
|
+
POST_HEALTH_CHECK = "post_health_check"
|
|
31
|
+
ON_HEALTH_FAILURE = "on_health_failure"
|
|
32
|
+
|
|
33
|
+
# Config
|
|
34
|
+
PRE_CONFIG_LOAD = "pre_config_load"
|
|
35
|
+
POST_CONFIG_LOAD = "post_config_load"
|
|
36
|
+
CONFIG_VALIDATE = "config_validate"
|
|
37
|
+
|
|
38
|
+
# Cloud-init
|
|
39
|
+
CLOUD_INIT_CUSTOMIZE = "cloud_init_customize"
|
|
40
|
+
|
|
41
|
+
# Export/Import
|
|
42
|
+
PRE_EXPORT = "pre_export"
|
|
43
|
+
POST_EXPORT = "post_export"
|
|
44
|
+
PRE_IMPORT = "pre_import"
|
|
45
|
+
POST_IMPORT = "post_import"
|
|
46
|
+
|
|
47
|
+
# CLI
|
|
48
|
+
CLI_COMMAND_REGISTER = "cli_command_register"
|
|
49
|
+
|
|
50
|
+
# System
|
|
51
|
+
ON_ERROR = "on_error"
|
|
52
|
+
ON_STARTUP = "on_startup"
|
|
53
|
+
ON_SHUTDOWN = "on_shutdown"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class PluginMetadata:
|
|
58
|
+
"""Metadata about a plugin."""
|
|
59
|
+
name: str
|
|
60
|
+
version: str
|
|
61
|
+
description: str = ""
|
|
62
|
+
author: str = ""
|
|
63
|
+
url: str = ""
|
|
64
|
+
dependencies: List[str] = field(default_factory=list)
|
|
65
|
+
hooks: List[PluginHook] = field(default_factory=list)
|
|
66
|
+
config_schema: Optional[Dict[str, Any]] = None
|
|
67
|
+
|
|
68
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
69
|
+
"""Convert to dictionary."""
|
|
70
|
+
return {
|
|
71
|
+
"name": self.name,
|
|
72
|
+
"version": self.version,
|
|
73
|
+
"description": self.description,
|
|
74
|
+
"author": self.author,
|
|
75
|
+
"url": self.url,
|
|
76
|
+
"dependencies": self.dependencies,
|
|
77
|
+
"hooks": [h.value for h in self.hooks],
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class PluginContext:
|
|
83
|
+
"""Context passed to plugin hooks."""
|
|
84
|
+
hook: PluginHook
|
|
85
|
+
vm_name: Optional[str] = None
|
|
86
|
+
config: Optional[Dict[str, Any]] = None
|
|
87
|
+
cloner: Optional[Any] = None
|
|
88
|
+
console: Optional[Any] = None
|
|
89
|
+
user_session: bool = False
|
|
90
|
+
extra: Dict[str, Any] = field(default_factory=dict)
|
|
91
|
+
|
|
92
|
+
# For modification by plugins
|
|
93
|
+
should_continue: bool = True
|
|
94
|
+
modified_config: Optional[Dict[str, Any]] = None
|
|
95
|
+
errors: List[str] = field(default_factory=list)
|
|
96
|
+
warnings: List[str] = field(default_factory=list)
|
|
97
|
+
|
|
98
|
+
def add_error(self, message: str) -> None:
|
|
99
|
+
"""Add an error message."""
|
|
100
|
+
self.errors.append(message)
|
|
101
|
+
|
|
102
|
+
def add_warning(self, message: str) -> None:
|
|
103
|
+
"""Add a warning message."""
|
|
104
|
+
self.warnings.append(message)
|
|
105
|
+
|
|
106
|
+
def cancel(self, reason: str = "") -> None:
|
|
107
|
+
"""Cancel the operation."""
|
|
108
|
+
self.should_continue = False
|
|
109
|
+
if reason:
|
|
110
|
+
self.add_error(reason)
|
|
111
|
+
|
|
112
|
+
def add_detail(self, key: str, value: Any) -> None:
|
|
113
|
+
"""Add a detail to the context's extra dict."""
|
|
114
|
+
self.extra[key] = value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Plugin(ABC):
|
|
118
|
+
"""
|
|
119
|
+
Base class for CloneBox plugins.
|
|
120
|
+
|
|
121
|
+
Plugins can hook into various points of CloneBox's lifecycle to:
|
|
122
|
+
- Modify configuration before VM creation
|
|
123
|
+
- Add custom cloud-init scripts
|
|
124
|
+
- Perform actions after VM operations
|
|
125
|
+
- Add custom CLI commands
|
|
126
|
+
- Integrate with external systems
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
class MyPlugin(Plugin):
|
|
130
|
+
@property
|
|
131
|
+
def metadata(self) -> PluginMetadata:
|
|
132
|
+
return PluginMetadata(
|
|
133
|
+
name="my-plugin",
|
|
134
|
+
version="1.0.0",
|
|
135
|
+
description="My custom plugin",
|
|
136
|
+
hooks=[PluginHook.POST_VM_CREATE],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def on_post_vm_create(self, ctx: PluginContext) -> None:
|
|
140
|
+
# Do something after VM is created
|
|
141
|
+
print(f"VM {ctx.vm_name} created!")
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def metadata(self) -> PluginMetadata:
|
|
147
|
+
"""Return plugin metadata."""
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
def initialize(self, config: Dict[str, Any]) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Initialize the plugin with configuration.
|
|
153
|
+
Called when plugin is loaded.
|
|
154
|
+
"""
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
def shutdown(self) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Cleanup when plugin is unloaded.
|
|
160
|
+
"""
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
# VM Lifecycle hooks
|
|
164
|
+
def on_pre_vm_create(self, ctx: PluginContext) -> None:
|
|
165
|
+
"""Called before VM creation."""
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
def on_post_vm_create(self, ctx: PluginContext) -> None:
|
|
169
|
+
"""Called after VM creation."""
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
def on_pre_vm_start(self, ctx: PluginContext) -> None:
|
|
173
|
+
"""Called before VM start."""
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
def on_post_vm_start(self, ctx: PluginContext) -> None:
|
|
177
|
+
"""Called after VM start."""
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
def on_pre_vm_stop(self, ctx: PluginContext) -> None:
|
|
181
|
+
"""Called before VM stop."""
|
|
182
|
+
pass
|
|
183
|
+
|
|
184
|
+
def on_post_vm_stop(self, ctx: PluginContext) -> None:
|
|
185
|
+
"""Called after VM stop."""
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
def on_pre_vm_delete(self, ctx: PluginContext) -> None:
|
|
189
|
+
"""Called before VM deletion."""
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
def on_post_vm_delete(self, ctx: PluginContext) -> None:
|
|
193
|
+
"""Called after VM deletion."""
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
# Snapshot hooks
|
|
197
|
+
def on_pre_snapshot_create(self, ctx: PluginContext) -> None:
|
|
198
|
+
"""Called before snapshot creation."""
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
def on_post_snapshot_create(self, ctx: PluginContext) -> None:
|
|
202
|
+
"""Called after snapshot creation."""
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
def on_pre_snapshot_restore(self, ctx: PluginContext) -> None:
|
|
206
|
+
"""Called before snapshot restore."""
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
def on_post_snapshot_restore(self, ctx: PluginContext) -> None:
|
|
210
|
+
"""Called after snapshot restore."""
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
# Health hooks
|
|
214
|
+
def on_pre_health_check(self, ctx: PluginContext) -> None:
|
|
215
|
+
"""Called before health check."""
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
def on_post_health_check(self, ctx: PluginContext) -> None:
|
|
219
|
+
"""Called after health check."""
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
def on_health_failure(self, ctx: PluginContext) -> None:
|
|
223
|
+
"""Called when health check fails."""
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
# Config hooks
|
|
227
|
+
def on_pre_config_load(self, ctx: PluginContext) -> None:
|
|
228
|
+
"""Called before config is loaded."""
|
|
229
|
+
pass
|
|
230
|
+
|
|
231
|
+
def on_post_config_load(self, ctx: PluginContext) -> None:
|
|
232
|
+
"""Called after config is loaded. Can modify ctx.modified_config."""
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
def on_config_validate(self, ctx: PluginContext) -> None:
|
|
236
|
+
"""Called to validate config. Add errors to ctx.errors if invalid."""
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
# Cloud-init hooks
|
|
240
|
+
def on_cloud_init_customize(self, ctx: PluginContext) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Called to customize cloud-init.
|
|
243
|
+
Modify ctx.extra['cloud_init'] to add custom cloud-init content.
|
|
244
|
+
"""
|
|
245
|
+
pass
|
|
246
|
+
|
|
247
|
+
# Export/Import hooks
|
|
248
|
+
def on_pre_export(self, ctx: PluginContext) -> None:
|
|
249
|
+
"""Called before VM export."""
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
def on_post_export(self, ctx: PluginContext) -> None:
|
|
253
|
+
"""Called after VM export."""
|
|
254
|
+
pass
|
|
255
|
+
|
|
256
|
+
def on_pre_import(self, ctx: PluginContext) -> None:
|
|
257
|
+
"""Called before VM import."""
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
def on_post_import(self, ctx: PluginContext) -> None:
|
|
261
|
+
"""Called after VM import."""
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
# CLI hooks
|
|
265
|
+
def on_cli_command_register(self, ctx: PluginContext) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Called to register custom CLI commands.
|
|
268
|
+
Add commands to ctx.extra['commands'].
|
|
269
|
+
"""
|
|
270
|
+
pass
|
|
271
|
+
|
|
272
|
+
# System hooks
|
|
273
|
+
def on_error(self, ctx: PluginContext) -> None:
|
|
274
|
+
"""Called when an error occurs."""
|
|
275
|
+
pass
|
|
276
|
+
|
|
277
|
+
def on_startup(self, ctx: PluginContext) -> None:
|
|
278
|
+
"""Called on CloneBox startup."""
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
def on_shutdown(self, ctx: PluginContext) -> None:
|
|
282
|
+
"""Called on CloneBox shutdown."""
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
def handle_hook(self, hook: PluginHook, ctx: PluginContext) -> None:
|
|
286
|
+
"""Dispatch hook to appropriate handler method."""
|
|
287
|
+
handler_map: Dict[PluginHook, Callable[[PluginContext], None]] = {
|
|
288
|
+
PluginHook.PRE_VM_CREATE: self.on_pre_vm_create,
|
|
289
|
+
PluginHook.POST_VM_CREATE: self.on_post_vm_create,
|
|
290
|
+
PluginHook.PRE_VM_START: self.on_pre_vm_start,
|
|
291
|
+
PluginHook.POST_VM_START: self.on_post_vm_start,
|
|
292
|
+
PluginHook.PRE_VM_STOP: self.on_pre_vm_stop,
|
|
293
|
+
PluginHook.POST_VM_STOP: self.on_post_vm_stop,
|
|
294
|
+
PluginHook.PRE_VM_DELETE: self.on_pre_vm_delete,
|
|
295
|
+
PluginHook.POST_VM_DELETE: self.on_post_vm_delete,
|
|
296
|
+
PluginHook.PRE_SNAPSHOT_CREATE: self.on_pre_snapshot_create,
|
|
297
|
+
PluginHook.POST_SNAPSHOT_CREATE: self.on_post_snapshot_create,
|
|
298
|
+
PluginHook.PRE_SNAPSHOT_RESTORE: self.on_pre_snapshot_restore,
|
|
299
|
+
PluginHook.POST_SNAPSHOT_RESTORE: self.on_post_snapshot_restore,
|
|
300
|
+
PluginHook.PRE_HEALTH_CHECK: self.on_pre_health_check,
|
|
301
|
+
PluginHook.POST_HEALTH_CHECK: self.on_post_health_check,
|
|
302
|
+
PluginHook.ON_HEALTH_FAILURE: self.on_health_failure,
|
|
303
|
+
PluginHook.PRE_CONFIG_LOAD: self.on_pre_config_load,
|
|
304
|
+
PluginHook.POST_CONFIG_LOAD: self.on_post_config_load,
|
|
305
|
+
PluginHook.CONFIG_VALIDATE: self.on_config_validate,
|
|
306
|
+
PluginHook.CLOUD_INIT_CUSTOMIZE: self.on_cloud_init_customize,
|
|
307
|
+
PluginHook.PRE_EXPORT: self.on_pre_export,
|
|
308
|
+
PluginHook.POST_EXPORT: self.on_post_export,
|
|
309
|
+
PluginHook.PRE_IMPORT: self.on_pre_import,
|
|
310
|
+
PluginHook.POST_IMPORT: self.on_post_import,
|
|
311
|
+
PluginHook.CLI_COMMAND_REGISTER: self.on_cli_command_register,
|
|
312
|
+
PluginHook.ON_ERROR: self.on_error,
|
|
313
|
+
PluginHook.ON_STARTUP: self.on_startup,
|
|
314
|
+
PluginHook.ON_SHUTDOWN: self.on_shutdown,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
handler = handler_map.get(hook)
|
|
318
|
+
if handler:
|
|
319
|
+
handler(ctx)
|