hysn-firecracker-python 1.0.3.post0__tar.gz → 1.0.4.post0__tar.gz
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.
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/.github/workflows/pypi-release.yml +1 -0
- {hysn_firecracker_python-1.0.3.post0/hysn_firecracker_python.egg-info → hysn_firecracker_python-1.0.4.post0}/PKG-INFO +5 -1
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/README.md +2 -1
- hysn_firecracker_python-1.0.4.post0/docs/cleanup.md +569 -0
- hysn_firecracker_python-1.0.4.post0/examples/create_vm_with_filesystem_format.py +95 -0
- hysn_firecracker_python-1.0.4.post0/firecracker/_version.py +24 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/config.py +3 -2
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/microvm.py +77 -21
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/network.py +95 -4
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/vmm.py +51 -3
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0/hysn_firecracker_python.egg-info}/PKG-INFO +5 -1
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/hysn_firecracker_python.egg-info/SOURCES.txt +4 -0
- hysn_firecracker_python-1.0.4.post0/hysn_firecracker_python.egg-info/requires.txt +8 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/pyproject.toml +5 -2
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/README.md +25 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/conftest.py +22 -6
- hysn_firecracker_python-1.0.4.post0/tests/test_cleanup.py +350 -0
- hysn_firecracker_python-1.0.4.post0/tests/test_rootfs_format.py +124 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/uv.lock +466 -4
- hysn_firecracker_python-1.0.3.post0/firecracker/_version.py +0 -34
- hysn_firecracker_python-1.0.3.post0/hysn_firecracker_python.egg-info/requires.txt +0 -4
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/.gitignore +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/AGENTS.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/Dockerfile.test +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/FIRECRACKER_SETUP.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/LICENSE +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/Makefile +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/QUICKSTART.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/README.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/TODO.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/assets/img/firecracker.png +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/assets/rootfs/setup-firecracker-official.sh +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/cleanup_firecracker.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/debian/compat +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/debian/control +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/debian/copyright +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/debian/rules +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docker-compose.test.yml +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/api-reference.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/configuration.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/examples.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/getting-started.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/index.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/network.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/docs/testing-in-docker.md +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/Dockerfile +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/configure_vm_network.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/create_vm.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/id_rsa +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/id_rsa.pub +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/init +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/load_snapshot.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/sample.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/examples/setup.sh +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/__init__.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/api.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/exceptions.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/logger.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/process.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/scripts.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/firecracker/utils.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/hysn_firecracker_python.egg-info/dependency_links.txt +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/hysn_firecracker_python.egg-info/entry_points.txt +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/hysn_firecracker_python.egg-info/top_level.txt +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/requirements.txt +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/scripts/cleanup_resources.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/scripts/run-tests-docker.sh +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/setup.cfg +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/setup.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_api_client.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_docker_integration.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_error_handling.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_logger.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_microvm.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_microvm_error_paths.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_microvm_initialization.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_network_error_paths.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_networking.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_port_forward.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_process_manager.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_scripts.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_snapshots.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_utils.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_vm_configuration.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/tests/test_vmm_manager.py +0 -0
- {hysn_firecracker_python-1.0.3.post0 → hysn_firecracker_python-1.0.4.post0}/verify-setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hysn-firecracker-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4.post0
|
|
4
4
|
Summary: A Python client library to interact with Firecracker microVMs
|
|
5
5
|
Author: Muhammad Yuga Nugraha
|
|
6
6
|
License: MIT License
|
|
@@ -35,6 +35,10 @@ Requires-Python: >=3.8
|
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
License-File: LICENSE
|
|
37
37
|
Requires-Dist: docker==7.1.0
|
|
38
|
+
Requires-Dist: requests==2.32.3
|
|
39
|
+
Requires-Dist: requests-unixsocket==0.4.1
|
|
40
|
+
Requires-Dist: tenacity==9.0.0
|
|
41
|
+
Requires-Dist: psutil==7.0.0
|
|
38
42
|
Requires-Dist: pyroute2==0.8.1
|
|
39
43
|
Requires-Dist: paramiko==3.5.1
|
|
40
44
|
Requires-Dist: faker==37.9.0
|
|
@@ -8,6 +8,7 @@ Welcome to the Firecracker Python SDK documentation! This documentation will hel
|
|
|
8
8
|
- [API Reference](api-reference.md) - Detailed information about the SDK's API
|
|
9
9
|
- [Configuration](configuration.md) - Configuration options for microVMs
|
|
10
10
|
- [Network](network.md) - Networking capabilities and examples
|
|
11
|
+
- [Cleanup](cleanup.md) - Resource cleanup and orphaned resource management
|
|
11
12
|
- [Examples](examples.md) - Practical examples for using the SDK
|
|
12
13
|
|
|
13
14
|
## What is Firecracker?
|
|
@@ -70,4 +71,4 @@ python3 -m venv venv
|
|
|
70
71
|
source venv/bin/activate
|
|
71
72
|
pip install -r requirements.txt
|
|
72
73
|
pip install -e .
|
|
73
|
-
```
|
|
74
|
+
```
|
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# Firecracker Python Resource Cleanup Guide
|
|
2
|
+
|
|
3
|
+
This document describes the cleanup system for Firecracker microVM resources, including improvements for handling orphaned resources and resilient cleanup behavior.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Firecracker Python SDK provides automatic resource cleanup when deleting microVMs. The cleanup system is designed to:
|
|
8
|
+
|
|
9
|
+
1. **Clean all resources** associated with a VM when it's deleted
|
|
10
|
+
2. **Detect and clean orphaned resources** from failed VMs
|
|
11
|
+
3. **Continue cleanup** even if individual steps fail (resilient cleanup)
|
|
12
|
+
|
|
13
|
+
## Resource Types Cleaned
|
|
14
|
+
|
|
15
|
+
When a microVM is deleted, the following resources are automatically cleaned:
|
|
16
|
+
|
|
17
|
+
### Network Resources
|
|
18
|
+
|
|
19
|
+
- **TAP devices**: Virtual network interfaces (e.g., `tap_vm123`)
|
|
20
|
+
- **NAT rules**: Network address translation rules for internet access
|
|
21
|
+
- **Port forwarding rules**: Rules for forwarding host ports to VM ports
|
|
22
|
+
- **Masquerade rules**: Network masquerade for outbound traffic
|
|
23
|
+
|
|
24
|
+
### Process Resources
|
|
25
|
+
|
|
26
|
+
- **Firecracker processes**: The VMM processes running the microVMs
|
|
27
|
+
|
|
28
|
+
### File System Resources
|
|
29
|
+
|
|
30
|
+
- **VM directories**: Configuration and runtime files in `/var/lib/firecracker/{vm_id}/`
|
|
31
|
+
|
|
32
|
+
## Orphaned Resource Detection
|
|
33
|
+
|
|
34
|
+
### What are Orphaned Resources?
|
|
35
|
+
|
|
36
|
+
Orphaned resources are network devices and rules that remain after a VM fails during creation or is improperly deleted. These resources are not tracked because:
|
|
37
|
+
|
|
38
|
+
- Failed VMs never created a `config.json` file
|
|
39
|
+
- `list_vmm()` can only find VMs with `config.json`
|
|
40
|
+
- Resources were allocated before failure occurred
|
|
41
|
+
|
|
42
|
+
### How Orphaned Detection Works
|
|
43
|
+
|
|
44
|
+
The SDK detects orphaned resources by:
|
|
45
|
+
|
|
46
|
+
1. **Listing all TAP devices** on the system (those starting with `tap_`)
|
|
47
|
+
2. **Extracting VM IDs** from TAP device names (e.g., `tap_vm123` → `vm123`)
|
|
48
|
+
3. **Comparing with running VMs** from `list_vmm()`
|
|
49
|
+
4. **Identifying orphans**: TAP devices whose IDs are not in the running VMs list
|
|
50
|
+
|
|
51
|
+
### Cleaning Orphaned Resources
|
|
52
|
+
|
|
53
|
+
Use the `delete(all=True)` method to clean all VMs and orphaned resources:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from firecracker import MicroVM
|
|
57
|
+
|
|
58
|
+
# Delete all VMs and clean up any orphaned resources
|
|
59
|
+
vm = MicroVM()
|
|
60
|
+
result = vm.delete(all=True)
|
|
61
|
+
print(result) # "All VMMs and orphaned resources are deleted"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or manually clean orphaned resources:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from firecracker import MicroVM
|
|
68
|
+
|
|
69
|
+
vm = MicroVM(verbose=True)
|
|
70
|
+
vm._vmm.cleanup_orphaned_resources()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Resilient Cleanup
|
|
74
|
+
|
|
75
|
+
### How It Works
|
|
76
|
+
|
|
77
|
+
The cleanup system uses a resilient approach where each cleanup step runs independently:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# NetworkManager.cleanup() example
|
|
81
|
+
cleanup_errors = []
|
|
82
|
+
|
|
83
|
+
# Step 1: Delete NAT rules (best effort)
|
|
84
|
+
try:
|
|
85
|
+
self.delete_nat_rules(tap_device)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
cleanup_errors.append(f"Failed to delete NAT rules: {str(e)}")
|
|
88
|
+
# Log but don't raise - continue to next step
|
|
89
|
+
|
|
90
|
+
# Step 2: Delete masquerade (best effort)
|
|
91
|
+
try:
|
|
92
|
+
self.delete_masquerade()
|
|
93
|
+
except Exception as e:
|
|
94
|
+
cleanup_errors.append(f"Failed to delete masquerade: {str(e)}")
|
|
95
|
+
# Log but don't raise - continue to next step
|
|
96
|
+
|
|
97
|
+
# Step 3: Delete port forwarding (best effort)
|
|
98
|
+
try:
|
|
99
|
+
self.delete_all_port_forward(machine_id)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
cleanup_errors.append(f"Failed to delete port forwarding: {str(e)}")
|
|
102
|
+
# Log but don't raise - continue to next step
|
|
103
|
+
|
|
104
|
+
# Step 4: Delete TAP device (always try this)
|
|
105
|
+
try:
|
|
106
|
+
self.delete_tap(tap_device)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
cleanup_errors.append(f"Failed to delete TAP device: {str(e)}")
|
|
109
|
+
# Log but don't raise
|
|
110
|
+
|
|
111
|
+
# Report any issues
|
|
112
|
+
if cleanup_errors:
|
|
113
|
+
self._logger.error(f"Partial cleanup: {'; '.join(cleanup_errors)}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Benefits of Resilient Cleanup
|
|
117
|
+
|
|
118
|
+
1. **No dangling resources**: Even if NAT rule deletion fails, TAP device cleanup is attempted
|
|
119
|
+
2. **Partial success better than total failure**: Some resources cleaned is better than none
|
|
120
|
+
3. **Complete error logging**: All failures are logged for debugging
|
|
121
|
+
4. **No cascade failures**: One failure doesn't prevent cleanup of other resources
|
|
122
|
+
|
|
123
|
+
## API Reference
|
|
124
|
+
|
|
125
|
+
### MicroVM.delete()
|
|
126
|
+
|
|
127
|
+
Deletes a specific microVM or all microVMs.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
delete(id=None, all=False) -> str
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Parameters
|
|
134
|
+
|
|
135
|
+
- `id` (str, optional): ID of microVM to delete. If not provided, uses the current microVM's ID.
|
|
136
|
+
- `all` (bool, optional): If `True`, deletes all microVMs and orphaned resources. Defaults to `False`.
|
|
137
|
+
|
|
138
|
+
#### Returns
|
|
139
|
+
|
|
140
|
+
- `str`: Status message indicating the result of the delete operation.
|
|
141
|
+
|
|
142
|
+
#### Examples
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from firecracker import MicroVM
|
|
146
|
+
|
|
147
|
+
# Delete a specific microVM
|
|
148
|
+
vm = MicroVM()
|
|
149
|
+
vm.create()
|
|
150
|
+
vm.delete()
|
|
151
|
+
|
|
152
|
+
# Delete all microVMs and orphaned resources
|
|
153
|
+
vm = MicroVM()
|
|
154
|
+
vm.delete(all=True)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### VMMManager.cleanup_orphaned_resources()
|
|
158
|
+
|
|
159
|
+
Cleans up resources from VMs that failed during creation.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
def cleanup_orphaned_resources(self) -> None
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Description
|
|
166
|
+
|
|
167
|
+
This method:
|
|
168
|
+
|
|
169
|
+
1. Lists all running VMs
|
|
170
|
+
2. Finds TAP devices that don't belong to running VMs
|
|
171
|
+
3. Cleans orphaned TAP devices and associated network rules
|
|
172
|
+
|
|
173
|
+
#### Example
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from firecracker import MicroVM
|
|
177
|
+
|
|
178
|
+
vm = MicroVM(verbose=True)
|
|
179
|
+
vm._vmm.cleanup_orphaned_resources()
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### NetworkManager.cleanup_orphaned_tap_devices()
|
|
183
|
+
|
|
184
|
+
Removes TAP devices that don't belong to running VMs.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
def cleanup_orphaned_tap_devices(self, running_vm_ids: set) -> None
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Parameters
|
|
191
|
+
|
|
192
|
+
- `running_vm_ids` (set): Set of VM IDs that are currently running.
|
|
193
|
+
|
|
194
|
+
#### Example
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from firecracker import MicroVM
|
|
198
|
+
from firecracker.network import NetworkManager
|
|
199
|
+
|
|
200
|
+
network_manager = NetworkManager()
|
|
201
|
+
network_manager.cleanup_orphaned_tap_devices(running_vm_ids={"vm1", "vm2"})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Usage Examples
|
|
205
|
+
|
|
206
|
+
### Normal VM Lifecycle
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from firecracker import MicroVM
|
|
210
|
+
|
|
211
|
+
# Create a microVM
|
|
212
|
+
vm = MicroVM(name="web-server")
|
|
213
|
+
vm.create()
|
|
214
|
+
|
|
215
|
+
# VM is running with:
|
|
216
|
+
# - TAP device: tap_web-server
|
|
217
|
+
# - NAT rules for internet access
|
|
218
|
+
# - Firecracker process
|
|
219
|
+
# - VM directory: /var/lib/firecracker/web-server/
|
|
220
|
+
|
|
221
|
+
# Delete the VM (all resources cleaned automatically)
|
|
222
|
+
vm.delete()
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Handling Failed VMs
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from firecracker import MicroVM
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
# VM creation fails (e.g., due to resource constraints)
|
|
232
|
+
vm = MicroVM(name="failed-vm")
|
|
233
|
+
vm.create()
|
|
234
|
+
except Exception as e:
|
|
235
|
+
print(f"VM creation failed: {e}")
|
|
236
|
+
|
|
237
|
+
# VM didn't create config.json, so it's not in list_vmm()
|
|
238
|
+
# But TAP device tap_failed-vm still exists
|
|
239
|
+
|
|
240
|
+
# Clean up all VMs and orphaned resources
|
|
241
|
+
vm = MicroVM()
|
|
242
|
+
vm.delete(all=True)
|
|
243
|
+
# tap_failed-vm and its rules are now cleaned
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Managing Multiple VMs
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from firecracker import MicroVM
|
|
250
|
+
|
|
251
|
+
# Create multiple VMs
|
|
252
|
+
vm1 = MicroVM(name="app1")
|
|
253
|
+
vm1.create()
|
|
254
|
+
|
|
255
|
+
vm2 = MicroVM(name="app2")
|
|
256
|
+
vm2.create()
|
|
257
|
+
|
|
258
|
+
vm3 = MicroVM(name="app3")
|
|
259
|
+
# This one fails during creation
|
|
260
|
+
try:
|
|
261
|
+
vm3.create()
|
|
262
|
+
except Exception:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
# List running VMs (only shows vm1 and vm2)
|
|
266
|
+
vms = MicroVM.list()
|
|
267
|
+
print(f"Running VMs: {len(vms)}") # 2
|
|
268
|
+
|
|
269
|
+
# Delete all VMs and clean orphaned resources
|
|
270
|
+
vm = MicroVM()
|
|
271
|
+
result = vm.delete(all=True)
|
|
272
|
+
# All resources from vm1, vm2, and the orphaned tap_app3 are cleaned
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
### Issue: TAP Devices Still Showing After Delete
|
|
278
|
+
|
|
279
|
+
**Cause:** Network rule deletion may have failed, but TAP device cleanup wasn't attempted (in older versions).
|
|
280
|
+
|
|
281
|
+
**Solution:** Run cleanup with verbose logging to see what's happening:
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
vm = MicroVM(verbose=True, level="DEBUG")
|
|
285
|
+
vm.delete(all=True)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Check logs for:
|
|
289
|
+
|
|
290
|
+
- Which TAP devices were found
|
|
291
|
+
- Which cleanup steps succeeded/failed
|
|
292
|
+
- Any error messages
|
|
293
|
+
|
|
294
|
+
### Issue: Permission Errors During Cleanup
|
|
295
|
+
|
|
296
|
+
**Cause:** Insufficient permissions to modify network resources or device files.
|
|
297
|
+
|
|
298
|
+
**Solution:** Ensure proper permissions:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# Check user has access to KVM
|
|
302
|
+
ls -l /dev/kvm
|
|
303
|
+
|
|
304
|
+
# Add user to kvm group if needed
|
|
305
|
+
sudo usermod -aG kvm $USER
|
|
306
|
+
# Then log out and log back in
|
|
307
|
+
|
|
308
|
+
# For network operations, may need sudo
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Issue: Cleanup Partially Succeeds
|
|
312
|
+
|
|
313
|
+
**Expected behavior:** With resilient cleanup, partial success is normal and better than total failure.
|
|
314
|
+
|
|
315
|
+
**Solution:** Check logs to see which resources weren't cleaned and manually clean them:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Check for remaining TAP devices
|
|
319
|
+
ip link show | grep tap_
|
|
320
|
+
|
|
321
|
+
# Check for remaining NAT rules
|
|
322
|
+
sudo nft list ruleset
|
|
323
|
+
|
|
324
|
+
# Manually clean remaining resources
|
|
325
|
+
sudo ip link delete tap_<name>
|
|
326
|
+
sudo nft flush chain ip nat POSTROUTING
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Best Practices
|
|
330
|
+
|
|
331
|
+
### 1. Use `delete(all=True)` for Complete Cleanup
|
|
332
|
+
|
|
333
|
+
When you want to ensure all resources are cleaned:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from firecracker import MicroVM
|
|
337
|
+
|
|
338
|
+
vm = MicroVM()
|
|
339
|
+
vm.delete(all=True) # Cleans VMs and orphaned resources
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 2. Enable Verbose Logging for Debugging
|
|
343
|
+
|
|
344
|
+
When troubleshooting cleanup issues:
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
from firecracker import MicroVM
|
|
348
|
+
|
|
349
|
+
vm = MicroVM(verbose=True, level="DEBUG")
|
|
350
|
+
vm.delete(all=True)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 3. Clean Resources Regularly
|
|
354
|
+
|
|
355
|
+
For long-running systems, periodically clean orphaned resources:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
from firecracker import MicroVM
|
|
359
|
+
|
|
360
|
+
vm = MicroVM()
|
|
361
|
+
vm._vmm.cleanup_orphaned_resources()
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 4. Handle Exceptions Gracefully
|
|
365
|
+
|
|
366
|
+
When working with multiple VMs, handle failures:
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
from firecracker import MicroVM
|
|
370
|
+
from firecracker.exceptions import VMMError, NetworkError
|
|
371
|
+
|
|
372
|
+
vms_to_delete = ["vm1", "vm2", "vm3"]
|
|
373
|
+
|
|
374
|
+
for vm_id in vms_to_delete:
|
|
375
|
+
try:
|
|
376
|
+
vm = MicroVM(id=vm_id)
|
|
377
|
+
vm.delete()
|
|
378
|
+
print(f"Successfully deleted {vm_id}")
|
|
379
|
+
except (VMMError, NetworkError) as e:
|
|
380
|
+
print(f"Error deleting {vm_id}: {e}")
|
|
381
|
+
# Continue to next VM
|
|
382
|
+
except Exception as e:
|
|
383
|
+
print(f"Unexpected error deleting {vm_id}: {e}")
|
|
384
|
+
# Continue to next VM
|
|
385
|
+
|
|
386
|
+
# Clean any orphaned resources
|
|
387
|
+
vm = MicroVM()
|
|
388
|
+
try:
|
|
389
|
+
vm.delete(all=True)
|
|
390
|
+
except Exception as e:
|
|
391
|
+
print(f"Error cleaning orphaned resources: {e}")
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## What Does NOT Get Cleaned
|
|
395
|
+
|
|
396
|
+
The following resources are intentionally NOT cleaned by the automatic cleanup system:
|
|
397
|
+
|
|
398
|
+
### Snapshots
|
|
399
|
+
|
|
400
|
+
Snapshot files in `/var/lib/firecracker/snapshots/` are preserved:
|
|
401
|
+
|
|
402
|
+
- Snapshots contain VM state and memory dumps
|
|
403
|
+
- They may be needed for recovery or analysis
|
|
404
|
+
- Manual cleanup is required if you want to delete them
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
# Manually clean snapshots
|
|
408
|
+
sudo rm -rf /var/lib/firecracker/snapshots/*
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### VM Creation Failures
|
|
412
|
+
|
|
413
|
+
Resources from VM creation failures are NOT automatically cleaned:
|
|
414
|
+
|
|
415
|
+
- VM creation process does not trigger cleanup on failure
|
|
416
|
+
- This is intentional to avoid cleaning resources that might be in use
|
|
417
|
+
- Use `delete(all=True)` to clean orphaned resources after failures
|
|
418
|
+
|
|
419
|
+
## Design Decisions
|
|
420
|
+
|
|
421
|
+
### 1. Best-Effort Cleanup
|
|
422
|
+
|
|
423
|
+
Each resource type is cleaned independently:
|
|
424
|
+
|
|
425
|
+
- Failures in one area don't prevent cleanup of others
|
|
426
|
+
- Partial success is better than total failure
|
|
427
|
+
- All failures are logged for debugging
|
|
428
|
+
|
|
429
|
+
### 2. No Automatic Creation Cleanup
|
|
430
|
+
|
|
431
|
+
VM creation failures don't automatically trigger cleanup:
|
|
432
|
+
|
|
433
|
+
- Resources might be in use by other processes
|
|
434
|
+
- User should explicitly request cleanup
|
|
435
|
+
- Prevents accidental deletion of resources in use
|
|
436
|
+
|
|
437
|
+
### 3. Orphan Detection Based on Running VMs
|
|
438
|
+
|
|
439
|
+
TAP devices are considered orphaned if:
|
|
440
|
+
|
|
441
|
+
- They don't belong to a VM listed by `list_vmm()`
|
|
442
|
+
- This ensures we don't delete resources from VMs that are actually running
|
|
443
|
+
|
|
444
|
+
### 4. Preserve Snapshots
|
|
445
|
+
|
|
446
|
+
Snapshots are never cleaned automatically:
|
|
447
|
+
|
|
448
|
+
- They contain valuable state information
|
|
449
|
+
- Users should explicitly delete them when no longer needed
|
|
450
|
+
- Prevents accidental data loss
|
|
451
|
+
|
|
452
|
+
### 5. Global Masquerade Rule
|
|
453
|
+
|
|
454
|
+
The masquerade rule is a shared resource for all VMs:
|
|
455
|
+
|
|
456
|
+
- It's not deleted when a single VM is deleted
|
|
457
|
+
- It's only deleted when no VMs are using it
|
|
458
|
+
- This prevents breaking networking for other VMs
|
|
459
|
+
|
|
460
|
+
## Testing
|
|
461
|
+
|
|
462
|
+
The cleanup functionality is covered by comprehensive tests in `tests/test_cleanup.py`:
|
|
463
|
+
|
|
464
|
+
- Resilient cleanup tests (partial failures)
|
|
465
|
+
- Orphaned resource detection tests
|
|
466
|
+
- Delete behavior tests
|
|
467
|
+
- Integration scenarios
|
|
468
|
+
|
|
469
|
+
Run cleanup tests:
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
# Run all cleanup tests
|
|
473
|
+
pytest tests/test_cleanup.py -v
|
|
474
|
+
|
|
475
|
+
# Run specific test class
|
|
476
|
+
pytest tests/test_cleanup.py::TestResilientCleanup -v
|
|
477
|
+
pytest tests/test_cleanup.py::TestOrphanedResourceCleanup -v
|
|
478
|
+
pytest tests/test_cleanup.py::TestDeleteWithOrphans -v
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Advanced Topics
|
|
482
|
+
|
|
483
|
+
### Manual Resource Cleanup
|
|
484
|
+
|
|
485
|
+
For advanced scenarios, you can manually clean specific resources:
|
|
486
|
+
|
|
487
|
+
```python
|
|
488
|
+
from firecracker import MicroVM
|
|
489
|
+
from firecracker.network import NetworkManager
|
|
490
|
+
|
|
491
|
+
vm = MicroVM()
|
|
492
|
+
network_manager = vm._network
|
|
493
|
+
|
|
494
|
+
# Clean a specific TAP device
|
|
495
|
+
network_manager.delete_tap("tap_vm123")
|
|
496
|
+
|
|
497
|
+
# Clean NAT rules for a specific TAP device
|
|
498
|
+
network_manager.delete_nat_rules("tap_vm123")
|
|
499
|
+
|
|
500
|
+
# Clean port forwarding for a specific VM
|
|
501
|
+
network_manager.delete_all_port_forward("vm123")
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Checking Resource State
|
|
505
|
+
|
|
506
|
+
Query the state of resources before cleanup:
|
|
507
|
+
|
|
508
|
+
```python
|
|
509
|
+
from firecracker import MicroVM
|
|
510
|
+
|
|
511
|
+
vm = MicroVM()
|
|
512
|
+
|
|
513
|
+
# List running VMs
|
|
514
|
+
vms = MicroVM.list()
|
|
515
|
+
print(f"Running VMs: {vms}")
|
|
516
|
+
|
|
517
|
+
# List network interfaces
|
|
518
|
+
import os
|
|
519
|
+
tap_devices = [f for f in os.listdir('/sys/class/net') if f.startswith('tap_')]
|
|
520
|
+
print(f"TAP devices: {tap_devices}")
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Cleanup with Retry Logic
|
|
524
|
+
|
|
525
|
+
For transient failures, implement retry logic:
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
from firecracker import MicroVM
|
|
529
|
+
from firecracker.exceptions import NetworkError
|
|
530
|
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
531
|
+
|
|
532
|
+
vm = MicroVM()
|
|
533
|
+
|
|
534
|
+
@retry(
|
|
535
|
+
stop=stop_after_attempt(3),
|
|
536
|
+
wait=wait_exponential(multiplier=1, min=1, max=10),
|
|
537
|
+
reraise=True
|
|
538
|
+
)
|
|
539
|
+
def delete_with_retry(vm_id):
|
|
540
|
+
try:
|
|
541
|
+
vm = MicroVM(id=vm_id)
|
|
542
|
+
vm.delete()
|
|
543
|
+
return True
|
|
544
|
+
except NetworkError as e:
|
|
545
|
+
print(f"Attempt failed: {e}, retrying...")
|
|
546
|
+
raise
|
|
547
|
+
|
|
548
|
+
# Use retry logic for deletion
|
|
549
|
+
delete_with_retry("vm123")
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## Related Documentation
|
|
553
|
+
|
|
554
|
+
- [Getting Started](getting-started.md) - Learn how to create and manage microVMs
|
|
555
|
+
- [API Reference](api-reference.md) - Complete API documentation
|
|
556
|
+
- [Network](network.md) - Network configuration and management
|
|
557
|
+
- [Examples](examples.md) - Practical examples
|
|
558
|
+
|
|
559
|
+
## Summary
|
|
560
|
+
|
|
561
|
+
The Firecracker Python SDK provides a robust cleanup system that:
|
|
562
|
+
|
|
563
|
+
1. **Automatically cleans** all resources when VMs are deleted
|
|
564
|
+
2. **Detects and cleans** orphaned resources from failed VMs
|
|
565
|
+
3. **Continues cleanup** even when individual steps fail
|
|
566
|
+
4. **Logs all failures** for debugging
|
|
567
|
+
5. **Preserves snapshots** to prevent accidental data loss
|
|
568
|
+
|
|
569
|
+
For any issues or questions about cleanup, refer to the troubleshooting section or enable verbose logging to see detailed cleanup operations.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: Creating a MicroVM with Different Filesystem Formats
|
|
4
|
+
|
|
5
|
+
This example demonstrates how to create microVMs with different rootfs filesystem formats.
|
|
6
|
+
The firecracker-python library now supports ext3, ext4, and xfs filesystem formats.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from firecracker import MicroVM
|
|
10
|
+
|
|
11
|
+
# Example 1: Create a VM with ext4 filesystem (default)
|
|
12
|
+
print("Creating VM with ext4 filesystem...")
|
|
13
|
+
vm_ext4 = MicroVM(
|
|
14
|
+
name="vm-ext4",
|
|
15
|
+
image="alpine:latest",
|
|
16
|
+
base_rootfs="./rootfs_ext4.img",
|
|
17
|
+
rootfs_size="5G",
|
|
18
|
+
rootfs_format="ext4", # Explicitly specify ext4 format
|
|
19
|
+
kernel_file="/var/lib/firecracker/kernel/vmlinux",
|
|
20
|
+
vcpu=2,
|
|
21
|
+
memory="2G",
|
|
22
|
+
verbose=True,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Build the rootfs with ext4 filesystem
|
|
26
|
+
result = vm_ext4.build()
|
|
27
|
+
print(f"Result: {result}")
|
|
28
|
+
|
|
29
|
+
# Example 2: Create a VM with ext3 filesystem
|
|
30
|
+
print("\nCreating VM with ext3 filesystem...")
|
|
31
|
+
vm_ext3 = MicroVM(
|
|
32
|
+
name="vm-ext3",
|
|
33
|
+
image="alpine:latest",
|
|
34
|
+
base_rootfs="./rootfs_ext3.img",
|
|
35
|
+
rootfs_size="5G",
|
|
36
|
+
rootfs_format="ext3", # Use ext3 format
|
|
37
|
+
kernel_file="/var/lib/firecracker/kernel/vmlinux",
|
|
38
|
+
vcpu=2,
|
|
39
|
+
memory="2G",
|
|
40
|
+
verbose=True,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Build the rootfs with ext3 filesystem
|
|
44
|
+
result = vm_ext3.build()
|
|
45
|
+
print(f"Result: {result}")
|
|
46
|
+
|
|
47
|
+
# Example 3: Create a VM with xfs filesystem
|
|
48
|
+
print("\nCreating VM with xfs filesystem...")
|
|
49
|
+
vm_xfs = MicroVM(
|
|
50
|
+
name="vm-xfs",
|
|
51
|
+
image="alpine:latest",
|
|
52
|
+
base_rootfs="./rootfs_xfs.img",
|
|
53
|
+
rootfs_size="5G",
|
|
54
|
+
rootfs_format="xfs", # Use xfs format
|
|
55
|
+
kernel_file="/var/lib/firecracker/kernel/vmlinux",
|
|
56
|
+
vcpu=2,
|
|
57
|
+
memory="2G",
|
|
58
|
+
verbose=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Build the rootfs with xfs filesystem
|
|
62
|
+
result = vm_xfs.build()
|
|
63
|
+
print(f"Result: {result}")
|
|
64
|
+
|
|
65
|
+
# Example 4: Create and start a VM with a specific filesystem format
|
|
66
|
+
print("\nCreating and starting VM with xfs filesystem...")
|
|
67
|
+
vm = MicroVM(
|
|
68
|
+
name="running-vm-xfs",
|
|
69
|
+
image="ubuntu:24.04",
|
|
70
|
+
base_rootfs="./ubuntu_rootfs_xfs.img",
|
|
71
|
+
rootfs_size="10G",
|
|
72
|
+
rootfs_format="xfs",
|
|
73
|
+
kernel_file="/var/lib/firecracker/kernel/vmlinux",
|
|
74
|
+
ip_addr="172.16.0.10",
|
|
75
|
+
vcpu=2,
|
|
76
|
+
memory="4G",
|
|
77
|
+
expose_ports=True,
|
|
78
|
+
host_port=10222,
|
|
79
|
+
dest_port=22,
|
|
80
|
+
verbose=True,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create and start the VM
|
|
84
|
+
result = vm.create()
|
|
85
|
+
print(f"Result: {result}")
|
|
86
|
+
|
|
87
|
+
# Clean up (optional)
|
|
88
|
+
# vm.delete()
|
|
89
|
+
|
|
90
|
+
print("\n✓ All examples completed successfully!")
|
|
91
|
+
print("\nNotes:")
|
|
92
|
+
print("- Supported formats: ext3, ext4, xfs")
|
|
93
|
+
print("- Default format: ext4")
|
|
94
|
+
print("- The filesystem format is validated during initialization")
|
|
95
|
+
print("- Overlayfs also respects the specified format")
|