clonebox 1.1.3__py3-none-any.whl → 1.1.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.
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """Data models for snapshot management."""
3
+
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime, timedelta
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Optional
9
+
10
+
11
+ class SnapshotType(Enum):
12
+ """Type of snapshot."""
13
+
14
+ DISK_ONLY = "disk" # Only disk state (offline)
15
+ FULL = "full" # Disk + memory + device state (online)
16
+ EXTERNAL = "external" # External snapshot file
17
+
18
+
19
+ class SnapshotState(Enum):
20
+ """State of snapshot operation."""
21
+
22
+ CREATING = "creating"
23
+ READY = "ready"
24
+ REVERTING = "reverting"
25
+ DELETING = "deleting"
26
+ FAILED = "failed"
27
+
28
+
29
+ @dataclass
30
+ class Snapshot:
31
+ """Represents a VM snapshot."""
32
+
33
+ name: str
34
+ vm_name: str
35
+ snapshot_type: SnapshotType
36
+ state: SnapshotState
37
+ created_at: datetime
38
+
39
+ description: Optional[str] = None
40
+
41
+ # Snapshot hierarchy
42
+ parent_name: Optional[str] = None
43
+ children: List[str] = field(default_factory=list)
44
+
45
+ # Storage info
46
+ disk_path: Optional[Path] = None
47
+ memory_path: Optional[Path] = None
48
+ size_bytes: int = 0
49
+
50
+ # Metadata
51
+ metadata: Dict[str, Any] = field(default_factory=dict)
52
+ tags: List[str] = field(default_factory=list)
53
+
54
+ # Auto-snapshot info
55
+ auto_created: bool = False
56
+ auto_policy: Optional[str] = None
57
+ expires_at: Optional[datetime] = None
58
+
59
+ def to_dict(self) -> Dict[str, Any]:
60
+ """Convert to dictionary for serialization."""
61
+ return {
62
+ "name": self.name,
63
+ "vm_name": self.vm_name,
64
+ "type": self.snapshot_type.value,
65
+ "state": self.state.value,
66
+ "created_at": self.created_at.isoformat(),
67
+ "description": self.description,
68
+ "parent_name": self.parent_name,
69
+ "children": self.children,
70
+ "disk_path": str(self.disk_path) if self.disk_path else None,
71
+ "memory_path": str(self.memory_path) if self.memory_path else None,
72
+ "size_bytes": self.size_bytes,
73
+ "metadata": self.metadata,
74
+ "tags": self.tags,
75
+ "auto_created": self.auto_created,
76
+ "auto_policy": self.auto_policy,
77
+ "expires_at": self.expires_at.isoformat() if self.expires_at else None,
78
+ }
79
+
80
+ @classmethod
81
+ def from_dict(cls, data: Dict[str, Any]) -> "Snapshot":
82
+ """Create from dictionary."""
83
+ return cls(
84
+ name=data["name"],
85
+ vm_name=data["vm_name"],
86
+ snapshot_type=SnapshotType(data["type"]),
87
+ state=SnapshotState(data["state"]),
88
+ created_at=datetime.fromisoformat(data["created_at"]),
89
+ description=data.get("description"),
90
+ parent_name=data.get("parent_name"),
91
+ children=data.get("children", []),
92
+ disk_path=Path(data["disk_path"]) if data.get("disk_path") else None,
93
+ memory_path=Path(data["memory_path"]) if data.get("memory_path") else None,
94
+ size_bytes=data.get("size_bytes", 0),
95
+ metadata=data.get("metadata", {}),
96
+ tags=data.get("tags", []),
97
+ auto_created=data.get("auto_created", False),
98
+ auto_policy=data.get("auto_policy"),
99
+ expires_at=(
100
+ datetime.fromisoformat(data["expires_at"]) if data.get("expires_at") else None
101
+ ),
102
+ )
103
+
104
+ @property
105
+ def is_expired(self) -> bool:
106
+ """Check if snapshot has expired."""
107
+ if self.expires_at is None:
108
+ return False
109
+ return datetime.now() > self.expires_at
110
+
111
+ @property
112
+ def age(self) -> timedelta:
113
+ """Get snapshot age."""
114
+ return datetime.now() - self.created_at
115
+
116
+
117
+ @dataclass
118
+ class SnapshotPolicy:
119
+ """Policy for automatic snapshots."""
120
+
121
+ name: str
122
+ enabled: bool = True
123
+
124
+ # Retention settings
125
+ max_snapshots: int = 10
126
+ max_age_days: int = 30
127
+ min_snapshots: int = 1 # Keep at least N snapshots
128
+
129
+ # Auto-snapshot triggers
130
+ before_operations: List[str] = field(
131
+ default_factory=lambda: ["upgrade", "resize", "config-change"]
132
+ )
133
+ scheduled_interval_hours: Optional[int] = None # e.g., 24 for daily
134
+
135
+ # Naming
136
+ name_prefix: str = "auto-"
137
+ include_timestamp: bool = True
138
+
139
+ # Cleanup
140
+ auto_cleanup: bool = True
141
+ cleanup_on_success: bool = False # Remove pre-operation snapshot if op succeeds
142
+
143
+ def to_dict(self) -> Dict[str, Any]:
144
+ """Convert to dictionary."""
145
+ return {
146
+ "name": self.name,
147
+ "enabled": self.enabled,
148
+ "max_snapshots": self.max_snapshots,
149
+ "max_age_days": self.max_age_days,
150
+ "min_snapshots": self.min_snapshots,
151
+ "before_operations": self.before_operations,
152
+ "scheduled_interval_hours": self.scheduled_interval_hours,
153
+ "name_prefix": self.name_prefix,
154
+ "include_timestamp": self.include_timestamp,
155
+ "auto_cleanup": self.auto_cleanup,
156
+ "cleanup_on_success": self.cleanup_on_success,
157
+ }
158
+
159
+ @classmethod
160
+ def from_dict(cls, data: Dict[str, Any]) -> "SnapshotPolicy":
161
+ """Create from dictionary."""
162
+ return cls(
163
+ name=data["name"],
164
+ enabled=data.get("enabled", True),
165
+ max_snapshots=data.get("max_snapshots", 10),
166
+ max_age_days=data.get("max_age_days", 30),
167
+ min_snapshots=data.get("min_snapshots", 1),
168
+ before_operations=data.get("before_operations", ["upgrade", "resize", "config-change"]),
169
+ scheduled_interval_hours=data.get("scheduled_interval_hours"),
170
+ name_prefix=data.get("name_prefix", "auto-"),
171
+ include_timestamp=data.get("include_timestamp", True),
172
+ auto_cleanup=data.get("auto_cleanup", True),
173
+ cleanup_on_success=data.get("cleanup_on_success", False),
174
+ )
175
+
176
+ def generate_snapshot_name(self, operation: Optional[str] = None) -> str:
177
+ """Generate snapshot name based on policy."""
178
+ parts = [self.name_prefix]
179
+ if operation:
180
+ parts.append(operation)
181
+ if self.include_timestamp:
182
+ parts.append(datetime.now().strftime("%Y%m%d-%H%M%S"))
183
+ return "-".join(parts)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 1.1.3
3
+ Version: 1.1.5
4
4
  Summary: Clone your workstation environment to an isolated VM with selective apps, paths and services
5
5
  Author: CloneBox Team
6
6
  License: Apache-2.0
@@ -32,6 +32,7 @@ Requires-Dist: psutil>=5.9.0
32
32
  Requires-Dist: pyyaml>=6.0
33
33
  Requires-Dist: pydantic>=2.0.0
34
34
  Requires-Dist: python-dotenv>=1.0.0
35
+ Requires-Dist: cryptography>=42.0.0
35
36
  Provides-Extra: dev
36
37
  Requires-Dist: pytest>=7.0.0; extra == "dev"
37
38
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -114,6 +115,8 @@ CloneBox excels in scenarios where developers need:
114
115
  | 🎛️ Profiles System (`ml-dev`, `web-stack`) | ✅ Stable |
115
116
  | 🔍 Auto-detection (services, apps, paths) | ✅ Stable |
116
117
  | 🔒 P2P Secure Transfer (AES-256) | ✅ **NEW** |
118
+ | 📸 Snapshot Management | ✅ **NEW** |
119
+ | 🏥 Health Check System | ✅ **NEW** |
117
120
  | 🧪 95%+ Test Coverage | ✅ |
118
121
 
119
122
  ### P2P Secure VM Sharing
@@ -141,9 +144,55 @@ clonebox sync-key user@hostB # Sync encryption key
141
144
  clonebox list-remote user@hostB # List remote VMs
142
145
  ```
143
146
 
147
+ ### Snapshot Management
148
+
149
+ Save and restore VM states:
150
+
151
+ ```bash
152
+ # Create snapshot before risky operation
153
+ clonebox snapshot create my-vm --name "before-upgrade" --user
154
+
155
+ # List all snapshots
156
+ clonebox snapshot list my-vm --user
157
+
158
+ # Restore to previous state
159
+ clonebox snapshot restore my-vm --name "before-upgrade" --user
160
+
161
+ # Delete old snapshot
162
+ clonebox snapshot delete my-vm --name "before-upgrade" --user
163
+ ```
164
+
165
+ ### Health Checks
166
+
167
+ Configure health probes in `.clonebox.yaml`:
168
+
169
+ ```yaml
170
+ health_checks:
171
+ - name: nginx
172
+ type: http
173
+ url: http://localhost:80/health
174
+ expected_status: 200
175
+
176
+ - name: postgres
177
+ type: tcp
178
+ host: localhost
179
+ port: 5432
180
+
181
+ - name: redis
182
+ type: command
183
+ exec: "redis-cli ping"
184
+ expected_output: "PONG"
185
+ ```
186
+
187
+ Run health checks:
188
+
189
+ ```bash
190
+ clonebox health my-vm --user
191
+ ```
192
+
144
193
  ### Roadmap
145
194
 
146
- - **v1.2.0**: `clonebox exec` command, VM snapshots, snapshot restore
195
+ - **v1.2.0**: Resource limits, progress bars, secrets isolation
147
196
  - **v1.3.0**: Multi-VM orchestration, cluster mode
148
197
  - **v2.0.0**: Cloud provider support (AWS, GCP, Azure), Windows WSL2 support
149
198
 
@@ -0,0 +1,42 @@
1
+ clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
+ clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
+ clonebox/cli.py,sha256=m2WBz1SDTgpeDvX-dc93bDp4CKp7AcEI17vXWx3D4os,136109
4
+ clonebox/cloner.py,sha256=aH7BwmLgPITHzyHn_n_AKqtEADDm_aUXiRG-nB2rxMQ,90063
5
+ clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
6
+ clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
7
+ clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
8
+ clonebox/di.py,sha256=feFMXP5ff-7gwrIqgnoCpk1ivaiZA_lv2wcpkCSKiew,5648
9
+ clonebox/exporter.py,sha256=WIzVvmA0z_jjrpyXxvnXoLp9oaW6fKS7k0PGwzx_PIM,5629
10
+ clonebox/importer.py,sha256=Q9Uk1IOA41mgGhU4ynW2k-h9GEoGxRKI3c9wWE4uxcA,7097
11
+ clonebox/logging.py,sha256=jD_WgkHmt_h99EmX82QWK7D0OKWyxqcLwgT4UDQFp2g,3675
12
+ clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
13
+ clonebox/monitor.py,sha256=zlIarNf8w_i34XI8hZGxxrg5PVZK_Yxm6FQnkhLavRI,9181
14
+ clonebox/p2p.py,sha256=6o0JnscKqF9-BftQhW5fF1W6YY1wXshY9LEklNcHGJc,5913
15
+ clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
16
+ clonebox/resource_monitor.py,sha256=lDR9KyPbVtImeeOkOBPPVP-5yCgoL5hsVFPZ_UqsY0w,5286
17
+ clonebox/resources.py,sha256=IkuM4OdSDV4qhyc0eIynwbAHBTv0aVSxxW-gghsnCAs,6815
18
+ clonebox/rollback.py,sha256=hpwO-8Ehe1pW0wHuZvJkC_qxZ6yEo9otCJRhGIUArCo,5711
19
+ clonebox/secrets.py,sha256=QtGMIjhTKq14qQRGy-pX0TU3vKLiZt7CMloAlF2GmzQ,10515
20
+ clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
21
+ clonebox/backends/libvirt_backend.py,sha256=sIHFIvFO1hIOXEFR_foSkOGBgIzaJVQs-njOU8GdafA,7170
22
+ clonebox/backends/qemu_disk.py,sha256=YsGjYX5sbEf35Y4yjTpNkZat73a4RGBxY-KTVzJhqIs,1687
23
+ clonebox/backends/subprocess_runner.py,sha256=c-IyaMxM1cmUu64h654oAvulm83K5Mu-VQxXJ_0BOds,1506
24
+ clonebox/health/__init__.py,sha256=aKJJPQwJLnoCY728QuKUxYx1TZyooGEnyUVOegZ58Ok,422
25
+ clonebox/health/manager.py,sha256=6nn0a8QtxeEuuafDbn5ZBqHQdaJ2qg7yTstyAGPJWP0,9987
26
+ clonebox/health/models.py,sha256=sPumwj8S-88KgzSGw1Kq9bBbPVRd2RR0R87Z8hKJ_28,6001
27
+ clonebox/health/probes.py,sha256=CkiGcayqRRysqaBJst-YpSrvUzMdwsqD4TiQSluLt3Y,11305
28
+ clonebox/interfaces/disk.py,sha256=F7Xzj2dq5UTZ2KGCuThDM8bwTps6chFbquOUmfLREjI,985
29
+ clonebox/interfaces/hypervisor.py,sha256=8ms4kZLA-5Ba1e_n68mCucwP_K9mufbmTBlo7XzURn4,1991
30
+ clonebox/interfaces/network.py,sha256=YPIquxEB7sZHczbpuopcZpffTjWYI6cKmAu3wAEFllk,853
31
+ clonebox/interfaces/process.py,sha256=njvAIZw_TCjw01KpyVQKIDoRvhTwl0FfVGbQ6mxTROk,1024
32
+ clonebox/snapshots/__init__.py,sha256=ndlrIavPAiA8z4Ep3-D_EPhOcjNKYFnP3rIpEKaGdb8,273
33
+ clonebox/snapshots/manager.py,sha256=hGzM8V6ZJPXjTqj47c4Kr8idlE-c1Q3gPUvuw1HvS1A,11393
34
+ clonebox/snapshots/models.py,sha256=sRnn3OZE8JG9FZJlRuA3ihO-JXoPCQ3nD3SQytflAao,6206
35
+ clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
36
+ clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
37
+ clonebox-1.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
+ clonebox-1.1.5.dist-info/METADATA,sha256=0sp9wPpm969pIM4iwq3m22B3uxh0vC694mJSIRjrUoc,48882
39
+ clonebox-1.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
40
+ clonebox-1.1.5.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
41
+ clonebox-1.1.5.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
42
+ clonebox-1.1.5.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
- clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
- clonebox/cli.py,sha256=iPc2DWEDZaqBMXRJqpWKubmka9ZuJbAvs6mkMMEHAlw,122910
4
- clonebox/cloner.py,sha256=2YQO4SHCv0xOsU1hL9IqdgmxxJN-2j75X9pe-LpTpJE,82696
5
- clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
6
- clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
7
- clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
8
- clonebox/exporter.py,sha256=WIzVvmA0z_jjrpyXxvnXoLp9oaW6fKS7k0PGwzx_PIM,5629
9
- clonebox/importer.py,sha256=Q9Uk1IOA41mgGhU4ynW2k-h9GEoGxRKI3c9wWE4uxcA,7097
10
- clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
11
- clonebox/p2p.py,sha256=LPQQ7wNO84yDnpVrGkaRU-FDUzqmC4URdZXVeHsNOew,5889
12
- clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
13
- clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
14
- clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
15
- clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
16
- clonebox-1.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
17
- clonebox-1.1.3.dist-info/METADATA,sha256=16ilGCqzzvMBvEXxLZ-JlTyNxaI0u6ws74zM121Sw5o,47947
18
- clonebox-1.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
19
- clonebox-1.1.3.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
20
- clonebox-1.1.3.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
21
- clonebox-1.1.3.dist-info/RECORD,,