genesis-devtools 0.2.4__tar.gz → 0.2.6__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.
- genesis_devtools-0.2.6/ChangeLog +7 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/PKG-INFO +23 -3
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/README.md +22 -3
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/backup.py +73 -5
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/packer.py +6 -1
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/cmd/cli.py +75 -6
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/constants.py +1 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/utils.py +90 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/PKG-INFO +23 -3
- genesis_devtools-0.2.6/genesis_devtools.egg-info/pbr.json +1 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/requires.txt +1 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/requirements.txt +1 -0
- genesis_devtools-0.2.4/ChangeLog +0 -7
- genesis_devtools-0.2.4/genesis_devtools.egg-info/pbr.json +0 -1
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/.github/workflows/publish-to-pypi.yml +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/.github/workflows/tests.yml +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/AUTHORS +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/LICENSE +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/data/stands/stand-small.yaml +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/genesis.yaml +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/bootstrap.sh +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/genesis-bootstrap.service +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/genesis-root-autoresize.service +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/install_genesis_base.sh +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/root_autoresize.sh +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/base.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/builder.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/dependency.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/cmd/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/driver/base.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/driver/libvirt.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/constants.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/libvirt.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/logger.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/debian_12/debian-12.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/debian_12/plugins.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_base/genesis-base.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_base/plugins.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_core/genesis-core.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_core/plugins.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_custom/genesis-custom.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_custom/plugins.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/ubuntu_24/plugins.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/ubuntu_24/ubuntu-24.pkr.hcl +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/stand/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/stand/models.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/__init__.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/conftest.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_basic.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_builder.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_dependency.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_packer.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/SOURCES.txt +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/dependency_links.txt +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/entry_points.txt +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/not-zip-safe +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/top_level.txt +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/setup.cfg +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/setup.py +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/test-requirements.txt +0 -0
- {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: genesis-devtools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Tools to manager life cycle of Genesis projects.
|
|
5
5
|
Home-page: https://github.com/infraguys/genesis_devtools
|
|
6
6
|
Author: Genesis Corporation
|
|
@@ -23,6 +23,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
|
23
23
|
Requires-Dist: prettytable<4.0.0,>=3.7.0
|
|
24
24
|
Requires-Dist: GitPython<4.0.0,>=3.1.30
|
|
25
25
|
Requires-Dist: bazooka<2.0.0,>=1.0.0
|
|
26
|
+
Requires-Dist: cryptography<47.0.0,>=45.0.5
|
|
26
27
|
Dynamic: author
|
|
27
28
|
Dynamic: author-email
|
|
28
29
|
Dynamic: classifier
|
|
@@ -54,7 +55,7 @@ Before you can install and use genesis tools you need to install several require
|
|
|
54
55
|
Install packages
|
|
55
56
|
```sh
|
|
56
57
|
sudo apt update
|
|
57
|
-
sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
|
|
58
|
+
sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
Add user to group
|
|
@@ -342,7 +343,7 @@ genesis backup -oneshot
|
|
|
342
343
|
|
|
343
344
|
This command will backup the current installation to the specified directory once and exit.
|
|
344
345
|
|
|
345
|
-
###
|
|
346
|
+
### Compressed Backup
|
|
346
347
|
|
|
347
348
|
Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
|
|
348
349
|
|
|
@@ -350,6 +351,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
|
|
|
350
351
|
genesis backup --compress
|
|
351
352
|
```
|
|
352
353
|
|
|
354
|
+
### Encrypted Backup
|
|
355
|
+
|
|
356
|
+
Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
|
|
357
|
+
|
|
358
|
+
NOTE: It works only with `--compress` flag
|
|
359
|
+
|
|
360
|
+
You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
|
|
361
|
+
```bash
|
|
362
|
+
export GEN_DEV_BACKUP_KEY=secret_key
|
|
363
|
+
export GEN_DEV_BACKUP_IV=secret_iv
|
|
364
|
+
|
|
365
|
+
genesis backup --compress --encrypt
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
For decryption, use the `genesis backup-decrypt` command.
|
|
369
|
+
```bash
|
|
370
|
+
genesis backup-decrypt backup.tar.gz.encrypted
|
|
371
|
+
```
|
|
372
|
+
|
|
353
373
|
### Rotation
|
|
354
374
|
|
|
355
375
|
For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
|
|
@@ -20,7 +20,7 @@ Before you can install and use genesis tools you need to install several require
|
|
|
20
20
|
Install packages
|
|
21
21
|
```sh
|
|
22
22
|
sudo apt update
|
|
23
|
-
sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
|
|
23
|
+
sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Add user to group
|
|
@@ -308,7 +308,7 @@ genesis backup -oneshot
|
|
|
308
308
|
|
|
309
309
|
This command will backup the current installation to the specified directory once and exit.
|
|
310
310
|
|
|
311
|
-
###
|
|
311
|
+
### Compressed Backup
|
|
312
312
|
|
|
313
313
|
Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
|
|
314
314
|
|
|
@@ -316,6 +316,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
|
|
|
316
316
|
genesis backup --compress
|
|
317
317
|
```
|
|
318
318
|
|
|
319
|
+
### Encrypted Backup
|
|
320
|
+
|
|
321
|
+
Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
|
|
322
|
+
|
|
323
|
+
NOTE: It works only with `--compress` flag
|
|
324
|
+
|
|
325
|
+
You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
|
|
326
|
+
```bash
|
|
327
|
+
export GEN_DEV_BACKUP_KEY=secret_key
|
|
328
|
+
export GEN_DEV_BACKUP_IV=secret_iv
|
|
329
|
+
|
|
330
|
+
genesis backup --compress --encrypt
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
For decryption, use the `genesis backup-decrypt` command.
|
|
334
|
+
```bash
|
|
335
|
+
genesis backup-decrypt backup.tar.gz.encrypted
|
|
336
|
+
```
|
|
337
|
+
|
|
319
338
|
### Rotation
|
|
320
339
|
|
|
321
340
|
For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
|
|
@@ -330,4 +349,4 @@ Backups can take a lot of disk space and it can be a reason to crash the whole s
|
|
|
330
349
|
|
|
331
350
|
```bash
|
|
332
351
|
genesis backup --min-free-space 50
|
|
333
|
-
```
|
|
352
|
+
```
|
|
@@ -29,8 +29,57 @@ from genesis_devtools import utils
|
|
|
29
29
|
from genesis_devtools.infra.libvirt import libvirt
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
class EnctryptionCreds(tp.NamedTuple):
|
|
33
|
+
LEN = 16
|
|
34
|
+
MIN_LEN = 6
|
|
35
|
+
|
|
36
|
+
key: bytes
|
|
37
|
+
iv: bytes
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def validate_env(cls):
|
|
41
|
+
if not os.environ.get("GEN_DEV_BACKUP_KEY") or not os.environ.get(
|
|
42
|
+
"GEN_DEV_BACKUP_IV"
|
|
43
|
+
):
|
|
44
|
+
raise ValueError(
|
|
45
|
+
(
|
|
46
|
+
"Define environment variables GEN_DEV_BACKUP_KEY "
|
|
47
|
+
"and GEN_DEV_BACKUP_IV."
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
key = os.environ["GEN_DEV_BACKUP_KEY"]
|
|
52
|
+
iv = os.environ["GEN_DEV_BACKUP_IV"]
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
cls.MIN_LEN <= len(key) < cls.LEN
|
|
56
|
+
and cls.MIN_LEN <= len(iv) < cls.LEN
|
|
57
|
+
):
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"Key and IV must be greater or equal than {cls.MIN_LEN} "
|
|
62
|
+
f"bytes and less or equal to {cls.LEN} bytes."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_env(cls):
|
|
67
|
+
key = os.environ["GEN_DEV_BACKUP_KEY"]
|
|
68
|
+
iv = os.environ["GEN_DEV_BACKUP_IV"]
|
|
69
|
+
key = key + "0" * (cls.LEN - len(key))
|
|
70
|
+
iv = iv + "0" * (cls.LEN - len(iv))
|
|
71
|
+
|
|
72
|
+
return cls(
|
|
73
|
+
key=key.encode(),
|
|
74
|
+
iv=iv.encode(),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
32
78
|
def _do_backup(
|
|
33
|
-
backup_path: str,
|
|
79
|
+
backup_path: str,
|
|
80
|
+
domains: tp.List[str],
|
|
81
|
+
compress: bool = False,
|
|
82
|
+
encryption: EnctryptionCreds | None = None,
|
|
34
83
|
) -> None:
|
|
35
84
|
os.makedirs(backup_path, exist_ok=True)
|
|
36
85
|
|
|
@@ -75,19 +124,35 @@ def _do_backup(
|
|
|
75
124
|
return
|
|
76
125
|
|
|
77
126
|
click.echo(f"Compressing {backup_path}")
|
|
127
|
+
compressed_backup_path = f"{backup_path}.tar.gz"
|
|
78
128
|
compress_directory = os.path.dirname(backup_path)
|
|
79
129
|
try:
|
|
80
130
|
utils.compress_dir(backup_path, compress_directory)
|
|
81
131
|
except Exception:
|
|
82
132
|
click.secho(f"Compression of {backup_path} failed", fg="red")
|
|
83
|
-
compressed_backup_path = f"{backup_path}.tar.gz"
|
|
84
|
-
|
|
85
133
|
if os.path.exists(compressed_backup_path):
|
|
86
134
|
os.remove(compressed_backup_path)
|
|
87
135
|
return
|
|
88
136
|
|
|
89
137
|
click.secho(f"Compression of {backup_path} done", fg="green")
|
|
90
|
-
|
|
138
|
+
|
|
139
|
+
if not encryption:
|
|
140
|
+
shutil.rmtree(backup_path)
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
click.echo(f"Encrypting {compressed_backup_path}")
|
|
144
|
+
try:
|
|
145
|
+
utils.encrypt_file(
|
|
146
|
+
compressed_backup_path, encryption.key, encryption.iv
|
|
147
|
+
)
|
|
148
|
+
except Exception:
|
|
149
|
+
click.secho(f"Encryption of {compressed_backup_path} failed", fg="red")
|
|
150
|
+
return
|
|
151
|
+
finally:
|
|
152
|
+
shutil.rmtree(backup_path)
|
|
153
|
+
|
|
154
|
+
os.remove(compressed_backup_path)
|
|
155
|
+
click.secho(f"Encryption of {compressed_backup_path} done", fg="green")
|
|
91
156
|
|
|
92
157
|
|
|
93
158
|
def _terminate_backup_process(backup_process: mp.Process) -> None:
|
|
@@ -115,6 +180,7 @@ def backup(
|
|
|
115
180
|
backup_path: str,
|
|
116
181
|
domains: tp.List[str],
|
|
117
182
|
compress: bool = False,
|
|
183
|
+
encryption: EnctryptionCreds | None = None,
|
|
118
184
|
min_free_disk_space_gb: int = 50,
|
|
119
185
|
) -> None:
|
|
120
186
|
if not os.path.exists(backup_path):
|
|
@@ -134,7 +200,9 @@ def backup(
|
|
|
134
200
|
# Run the actual backup process in another process.
|
|
135
201
|
# The current process will track the free disk space.
|
|
136
202
|
backup_process = mp.Process(
|
|
137
|
-
target=_do_backup,
|
|
203
|
+
target=_do_backup,
|
|
204
|
+
args=(backup_path, domains, compress, encryption),
|
|
205
|
+
daemon=True,
|
|
138
206
|
)
|
|
139
207
|
backup_process.start()
|
|
140
208
|
|
|
@@ -127,8 +127,13 @@ class PackerBuilder(base.DummyImageBuilder):
|
|
|
127
127
|
result = []
|
|
128
128
|
|
|
129
129
|
for env in envs:
|
|
130
|
+
# Check if the env is a wildcard
|
|
131
|
+
if env.endswith("*"):
|
|
132
|
+
for _env in (e for e in os.environ if e.startswith(env[:-1])):
|
|
133
|
+
result.append(f'{_env}="{os.environ[_env]}"')
|
|
134
|
+
continue
|
|
130
135
|
# Check if there a default value for the env
|
|
131
|
-
|
|
136
|
+
elif "=" in env:
|
|
132
137
|
name, value = tuple(e.strip() for e in env.split("="))
|
|
133
138
|
else:
|
|
134
139
|
name, value = env.strip(), ""
|
|
@@ -385,7 +385,7 @@ def get_project_version_cmd(element_dir: str) -> None:
|
|
|
385
385
|
"-p",
|
|
386
386
|
"--period",
|
|
387
387
|
default=c.BackupPeriod.D1.value,
|
|
388
|
-
type=click.Choice([p for p in c.BackupPeriod]),
|
|
388
|
+
type=click.Choice([p.value for p in c.BackupPeriod]),
|
|
389
389
|
show_default=True,
|
|
390
390
|
help="the regularity of backups",
|
|
391
391
|
)
|
|
@@ -393,7 +393,7 @@ def get_project_version_cmd(element_dir: str) -> None:
|
|
|
393
393
|
"-o",
|
|
394
394
|
"--offset",
|
|
395
395
|
default=None,
|
|
396
|
-
type=click.Choice([p for p in c.BackupPeriod]),
|
|
396
|
+
type=click.Choice([p.value for p in c.BackupPeriod]),
|
|
397
397
|
show_default=True,
|
|
398
398
|
help=(
|
|
399
399
|
"The time offset of the first backup. If not provided, "
|
|
@@ -411,12 +411,22 @@ def get_project_version_cmd(element_dir: str) -> None:
|
|
|
411
411
|
@click.option(
|
|
412
412
|
"-c",
|
|
413
413
|
"--compress",
|
|
414
|
-
default=False,
|
|
415
|
-
type=bool,
|
|
416
414
|
show_default=True,
|
|
417
415
|
is_flag=True,
|
|
418
416
|
help="Compress the backup.",
|
|
419
417
|
)
|
|
418
|
+
@click.option(
|
|
419
|
+
"-e",
|
|
420
|
+
"--encrypt",
|
|
421
|
+
show_default=True,
|
|
422
|
+
is_flag=True,
|
|
423
|
+
help=(
|
|
424
|
+
"Encrypt the backup. Works only with the compress flag. "
|
|
425
|
+
"Use environment variable to specify the encryption key "
|
|
426
|
+
"and the initialization vector: "
|
|
427
|
+
"GEN_DEV_BACKUP_KEY and GEN_DEV_BACKUP_IV"
|
|
428
|
+
),
|
|
429
|
+
)
|
|
420
430
|
@click.option(
|
|
421
431
|
"-s",
|
|
422
432
|
"--min-free-space",
|
|
@@ -447,14 +457,42 @@ def bakcup_cmd(
|
|
|
447
457
|
offset: c.BackupPeriod | None,
|
|
448
458
|
oneshot: bool,
|
|
449
459
|
compress: bool,
|
|
460
|
+
encrypt: bool,
|
|
450
461
|
min_free_space: int,
|
|
451
462
|
rotate: int,
|
|
452
463
|
) -> None:
|
|
464
|
+
if not compress and encrypt:
|
|
465
|
+
raise click.UsageError(
|
|
466
|
+
"The encrypt flag can be used only with the compress flag."
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
# Need to specify encryption key and initialization vector via
|
|
470
|
+
# environment variables.
|
|
471
|
+
if encrypt:
|
|
472
|
+
try:
|
|
473
|
+
backup.EnctryptionCreds.validate_env()
|
|
474
|
+
except ValueError:
|
|
475
|
+
raise click.UsageError(
|
|
476
|
+
(
|
|
477
|
+
"Define environment variables GEN_DEV_BACKUP_KEY "
|
|
478
|
+
"and GEN_DEV_BACKUP_IV. "
|
|
479
|
+
"Key and IV must be greater or equal than "
|
|
480
|
+
f"{backup.EnctryptionCreds.MIN_LEN} bytes and less or "
|
|
481
|
+
f"equal to {backup.EnctryptionCreds.LEN} bytes."
|
|
482
|
+
)
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
encryption = backup.EnctryptionCreds.from_env()
|
|
486
|
+
else:
|
|
487
|
+
encryption = None
|
|
488
|
+
|
|
453
489
|
# Do a single backup and exit
|
|
454
490
|
if oneshot:
|
|
455
491
|
domains = _domains_for_backup(name, raise_on_domain_absence=True)
|
|
456
492
|
backup_path = utils.backup_path(backup_dir)
|
|
457
|
-
backup.backup(
|
|
493
|
+
backup.backup(
|
|
494
|
+
backup_path, domains, compress, encryption, min_free_space
|
|
495
|
+
)
|
|
458
496
|
return
|
|
459
497
|
|
|
460
498
|
offset = offset or period
|
|
@@ -475,7 +513,9 @@ def bakcup_cmd(
|
|
|
475
513
|
click.secho(f"Backup started at {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
476
514
|
backup_path = utils.backup_path(backup_dir)
|
|
477
515
|
|
|
478
|
-
backup.backup(
|
|
516
|
+
backup.backup(
|
|
517
|
+
backup_path, domains, compress, encryption, min_free_space
|
|
518
|
+
)
|
|
479
519
|
click.secho(
|
|
480
520
|
f"Next backup at: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(next_ts))}"
|
|
481
521
|
)
|
|
@@ -490,6 +530,35 @@ def bakcup_cmd(
|
|
|
490
530
|
time.sleep(timeout)
|
|
491
531
|
|
|
492
532
|
|
|
533
|
+
@main.command("backup-decrypt", help="Decrypt a backup file")
|
|
534
|
+
@click.argument("path", type=click.Path(exists=True))
|
|
535
|
+
def bakcup_decrypt_cmd(path: str) -> None:
|
|
536
|
+
# Need to specify encryption key and initialization vector via
|
|
537
|
+
# environment variables.
|
|
538
|
+
|
|
539
|
+
try:
|
|
540
|
+
backup.EnctryptionCreds.validate_env()
|
|
541
|
+
except ValueError:
|
|
542
|
+
raise click.UsageError(
|
|
543
|
+
(
|
|
544
|
+
"Define environment variables GEN_DEV_BACKUP_KEY "
|
|
545
|
+
"and GEN_DEV_BACKUP_IV. "
|
|
546
|
+
"Key and IV must be greater or equal than "
|
|
547
|
+
f"{backup.EnctryptionCreds.MIN_LEN} bytes and less or "
|
|
548
|
+
f"equal to {backup.EnctryptionCreds.LEN} bytes."
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
encryption = backup.EnctryptionCreds.from_env()
|
|
553
|
+
|
|
554
|
+
utils.decrypt_file(
|
|
555
|
+
path,
|
|
556
|
+
encryption.key,
|
|
557
|
+
encryption.iv,
|
|
558
|
+
)
|
|
559
|
+
click.secho(f"The {path} file has been decrypted.", fg="green")
|
|
560
|
+
|
|
561
|
+
|
|
493
562
|
def _domains_for_backup(
|
|
494
563
|
names: tp.List[str] | None = None, raise_on_domain_absence: bool = False
|
|
495
564
|
) -> tp.List[str]:
|
|
@@ -24,6 +24,10 @@ from importlib.metadata import entry_points
|
|
|
24
24
|
import yaml
|
|
25
25
|
|
|
26
26
|
import git
|
|
27
|
+
from cryptography.hazmat.primitives import ciphers
|
|
28
|
+
from cryptography.hazmat.primitives.ciphers import modes as cipher_models
|
|
29
|
+
from cryptography.hazmat.primitives.ciphers import algorithms
|
|
30
|
+
from cryptography.hazmat import backends as crypto_back
|
|
27
31
|
|
|
28
32
|
import genesis_devtools.constants as c
|
|
29
33
|
|
|
@@ -234,3 +238,89 @@ def compress_dir(
|
|
|
234
238
|
|
|
235
239
|
# Create a zip archive of the directory
|
|
236
240
|
shutil.make_archive(archive_base_name, compression_format, directory)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def encrypt_file(
|
|
244
|
+
path: str,
|
|
245
|
+
key: bytes,
|
|
246
|
+
iv: bytes,
|
|
247
|
+
chunk_size_kb: int = 128,
|
|
248
|
+
extension: str = c.ENCRYPTED_EXTENSION,
|
|
249
|
+
) -> None:
|
|
250
|
+
# Ensure that the key and iv are the correct lengths
|
|
251
|
+
# for AES (16 bytes for AES-128)
|
|
252
|
+
if len(key) != 16 or len(iv) != 16:
|
|
253
|
+
raise ValueError("Key and IV must be 16 bytes long")
|
|
254
|
+
|
|
255
|
+
# Create a cipher object
|
|
256
|
+
cipher = ciphers.Cipher(
|
|
257
|
+
algorithms.AES(key),
|
|
258
|
+
cipher_models.CFB(iv),
|
|
259
|
+
backend=crypto_back.default_backend(),
|
|
260
|
+
)
|
|
261
|
+
chunk_size = chunk_size_kb << 10
|
|
262
|
+
encrypted_path = path + extension
|
|
263
|
+
|
|
264
|
+
# Open the input file and the temporary output file
|
|
265
|
+
try:
|
|
266
|
+
with open(path, "rb") as infile, open(encrypted_path, "wb") as outfile:
|
|
267
|
+
# Create an encryptor object
|
|
268
|
+
encryptor = cipher.encryptor()
|
|
269
|
+
|
|
270
|
+
# Read the file in chunks and write the encrypted
|
|
271
|
+
# data to the temp file
|
|
272
|
+
while True:
|
|
273
|
+
chunk = infile.read(chunk_size)
|
|
274
|
+
if len(chunk) == 0:
|
|
275
|
+
break
|
|
276
|
+
outfile.write(encryptor.update(chunk))
|
|
277
|
+
outfile.write(encryptor.finalize())
|
|
278
|
+
except Exception as e:
|
|
279
|
+
os.remove(encrypted_path)
|
|
280
|
+
raise e
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def decrypt_file(
|
|
284
|
+
path: str,
|
|
285
|
+
key: bytes,
|
|
286
|
+
iv: bytes,
|
|
287
|
+
chunk_size_kb: int = 128,
|
|
288
|
+
extension: str = c.ENCRYPTED_EXTENSION,
|
|
289
|
+
) -> None:
|
|
290
|
+
# Ensure that the key and iv are the correct lengths
|
|
291
|
+
# for AES (16 bytes for AES-128)
|
|
292
|
+
if len(key) != 16 or len(iv) != 16:
|
|
293
|
+
raise ValueError("Key and IV must be 16 bytes long")
|
|
294
|
+
|
|
295
|
+
# Create a cipher object
|
|
296
|
+
cipher = ciphers.Cipher(
|
|
297
|
+
algorithms.AES(key),
|
|
298
|
+
cipher_models.CFB(iv),
|
|
299
|
+
backend=crypto_back.default_backend(),
|
|
300
|
+
)
|
|
301
|
+
chunk_size = chunk_size_kb << 10
|
|
302
|
+
plain_path = path[: -len(extension)] if path.endswith(extension) else path
|
|
303
|
+
tmp_path = plain_path + ".tmp"
|
|
304
|
+
|
|
305
|
+
# Open the encrypted file and the temporary output file
|
|
306
|
+
try:
|
|
307
|
+
with open(path, "rb") as infile, open(tmp_path, "wb") as outfile:
|
|
308
|
+
# Create a decryptor object
|
|
309
|
+
decryptor = cipher.decryptor()
|
|
310
|
+
|
|
311
|
+
# Read the file in chunks and write the decrypted data to the temp file
|
|
312
|
+
while True:
|
|
313
|
+
chunk = infile.read(chunk_size)
|
|
314
|
+
if len(chunk) == 0:
|
|
315
|
+
break
|
|
316
|
+
outfile.write(decryptor.update(chunk))
|
|
317
|
+
outfile.write(decryptor.finalize())
|
|
318
|
+
except Exception as e:
|
|
319
|
+
os.remove(tmp_path)
|
|
320
|
+
raise e
|
|
321
|
+
|
|
322
|
+
# Replace the encrypted file with the decrypted temp file
|
|
323
|
+
if plain_path == path:
|
|
324
|
+
os.replace(tmp_path, plain_path)
|
|
325
|
+
else:
|
|
326
|
+
shutil.move(tmp_path, plain_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: genesis-devtools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Tools to manager life cycle of Genesis projects.
|
|
5
5
|
Home-page: https://github.com/infraguys/genesis_devtools
|
|
6
6
|
Author: Genesis Corporation
|
|
@@ -23,6 +23,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
|
23
23
|
Requires-Dist: prettytable<4.0.0,>=3.7.0
|
|
24
24
|
Requires-Dist: GitPython<4.0.0,>=3.1.30
|
|
25
25
|
Requires-Dist: bazooka<2.0.0,>=1.0.0
|
|
26
|
+
Requires-Dist: cryptography<47.0.0,>=45.0.5
|
|
26
27
|
Dynamic: author
|
|
27
28
|
Dynamic: author-email
|
|
28
29
|
Dynamic: classifier
|
|
@@ -54,7 +55,7 @@ Before you can install and use genesis tools you need to install several require
|
|
|
54
55
|
Install packages
|
|
55
56
|
```sh
|
|
56
57
|
sudo apt update
|
|
57
|
-
sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
|
|
58
|
+
sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
Add user to group
|
|
@@ -342,7 +343,7 @@ genesis backup -oneshot
|
|
|
342
343
|
|
|
343
344
|
This command will backup the current installation to the specified directory once and exit.
|
|
344
345
|
|
|
345
|
-
###
|
|
346
|
+
### Compressed Backup
|
|
346
347
|
|
|
347
348
|
Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
|
|
348
349
|
|
|
@@ -350,6 +351,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
|
|
|
350
351
|
genesis backup --compress
|
|
351
352
|
```
|
|
352
353
|
|
|
354
|
+
### Encrypted Backup
|
|
355
|
+
|
|
356
|
+
Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
|
|
357
|
+
|
|
358
|
+
NOTE: It works only with `--compress` flag
|
|
359
|
+
|
|
360
|
+
You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
|
|
361
|
+
```bash
|
|
362
|
+
export GEN_DEV_BACKUP_KEY=secret_key
|
|
363
|
+
export GEN_DEV_BACKUP_IV=secret_iv
|
|
364
|
+
|
|
365
|
+
genesis backup --compress --encrypt
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
For decryption, use the `genesis backup-decrypt` command.
|
|
369
|
+
```bash
|
|
370
|
+
genesis backup-decrypt backup.tar.gz.encrypted
|
|
371
|
+
```
|
|
372
|
+
|
|
353
373
|
### Rotation
|
|
354
374
|
|
|
355
375
|
For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "6371a1f", "is_release": false}
|
genesis_devtools-0.2.4/ChangeLog
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "5308a95", "is_release": false}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/root_autoresize.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/__init__.py
RENAMED
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/debian_12/plugins.pkr.hcl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/ubuntu_24/plugins.pkr.hcl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_builder.py
RENAMED
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_dependency.py
RENAMED
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_packer.py
RENAMED
|
File without changes
|
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|