virt-back 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.
- {virt-back-0.2.4 → virt_back-0.2.6}/PKG-INFO +14 -2
- {virt-back-0.2.4 → virt_back-0.2.6}/README.md +1 -0
- virt_back-0.2.6/setup.py +71 -0
- {virt-back-0.2.4 → virt_back-0.2.6}/virt-back +69 -1
- {virt-back-0.2.4 → virt_back-0.2.6}/virt_back.egg-info/PKG-INFO +14 -2
- {virt-back-0.2.4 → virt_back-0.2.6}/virtback.py +69 -1
- virt-back-0.2.4/setup.py +0 -47
- {virt-back-0.2.4 → virt_back-0.2.6}/setup.cfg +0 -0
- {virt-back-0.2.4 → virt_back-0.2.6}/virt_back.egg-info/SOURCES.txt +0 -0
- {virt-back-0.2.4 → virt_back-0.2.6}/virt_back.egg-info/dependency_links.txt +0 -0
- {virt-back-0.2.4 → virt_back-0.2.6}/virt_back.egg-info/requires.txt +0 -0
- {virt-back-0.2.4 → virt_back-0.2.6}/virt_back.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: virt-back
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: virt-back: A backup utility for QEMU, KVM, XEN, and Virtualbox guests
|
|
5
5
|
Home-page: https://git.unturf.com/python/virt-back
|
|
6
6
|
Author: Russell Ballestrini
|
|
@@ -9,6 +9,17 @@ License: Public Domain
|
|
|
9
9
|
Keywords: backup virtual hypervisor QEMU KVM XEN Virtualbox
|
|
10
10
|
Platform: All
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: libvirt-python
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: description
|
|
16
|
+
Dynamic: description-content-type
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: keywords
|
|
19
|
+
Dynamic: license
|
|
20
|
+
Dynamic: platform
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: summary
|
|
12
23
|
|
|
13
24
|
A backup utility for QEMU, KVM, XEN, and Virtualbox guests.
|
|
14
25
|
|
|
@@ -39,6 +50,7 @@ options:
|
|
|
39
50
|
-g, --no-gzip do not gzip the resulting tar file
|
|
40
51
|
-a amount, --retention amount
|
|
41
52
|
backups to retain [default: 3]
|
|
53
|
+
(applies to both rotated backup files AND ZFS @backup-* snapshots)
|
|
42
54
|
-p 'PATH', --path 'PATH'
|
|
43
55
|
backup path [default: '/KVMBACK']
|
|
44
56
|
-u 'URI', --uri 'URI'
|
|
@@ -27,6 +27,7 @@ options:
|
|
|
27
27
|
-g, --no-gzip do not gzip the resulting tar file
|
|
28
28
|
-a amount, --retention amount
|
|
29
29
|
backups to retain [default: 3]
|
|
30
|
+
(applies to both rotated backup files AND ZFS @backup-* snapshots)
|
|
30
31
|
-p 'PATH', --path 'PATH'
|
|
31
32
|
backup path [default: '/KVMBACK']
|
|
32
33
|
-u 'URI', --uri 'URI'
|
virt_back-0.2.6/setup.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
# Read the contents of your README file
|
|
5
|
+
with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8") as f:
|
|
6
|
+
long_description = f.read()
|
|
7
|
+
|
|
8
|
+
setup(
|
|
9
|
+
name="virt-back",
|
|
10
|
+
version="0.2.6",
|
|
11
|
+
description="virt-back: A backup utility for QEMU, KVM, XEN, and Virtualbox guests",
|
|
12
|
+
keywords="backup virtual hypervisor QEMU KVM XEN Virtualbox",
|
|
13
|
+
long_description=long_description,
|
|
14
|
+
long_description_content_type="text/markdown",
|
|
15
|
+
author="Russell Ballestrini",
|
|
16
|
+
author_email="russell@ballestrini.net",
|
|
17
|
+
url="https://git.unturf.com/python/virt-back",
|
|
18
|
+
platforms=["All"],
|
|
19
|
+
license="Public Domain",
|
|
20
|
+
py_modules=["virtback"],
|
|
21
|
+
include_package_data=True,
|
|
22
|
+
scripts=["virt-back"],
|
|
23
|
+
install_requires=[
|
|
24
|
+
"libvirt-python",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Release process (automated via .gitlab-ci.yml on tag push)
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
#
|
|
32
|
+
# Cutting a release:
|
|
33
|
+
# 1. Bump `version=` above
|
|
34
|
+
# 2. git commit -m "X.Y.Z: <one-line summary>"
|
|
35
|
+
# 3. git tag -a X.Y.Z -m "X.Y.Z"
|
|
36
|
+
# 4. git push origin master X.Y.Z
|
|
37
|
+
#
|
|
38
|
+
# The CI pipeline builds (python -m build) and uploads (twine upload) on the
|
|
39
|
+
# tag push. No manual `python setup.py sdist && twine upload` needed.
|
|
40
|
+
#
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Maintainer setup (one-time, per project) — required for CI uploads to work
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
#
|
|
45
|
+
# git.unturf.com -> python/<this-project> -> Settings -> Repository ->
|
|
46
|
+
# Protected Tags -> "Protect tag":
|
|
47
|
+
# - Pattern: *
|
|
48
|
+
# - Allowed to Create: Maintainers (match erldistpy's setting)
|
|
49
|
+
#
|
|
50
|
+
# This makes tags Protected refs, which lets the Protected/Masked
|
|
51
|
+
# TWINE_USERNAME and TWINE_PASSWORD variables (set once at the python/
|
|
52
|
+
# group level) reach the tag pipeline. Without Protected Tags, twine 6
|
|
53
|
+
# falls through to OIDC trusted publishing and fails because PyPI's
|
|
54
|
+
# self-hosted GitLab support is hardcoded to gitlab.com.
|
|
55
|
+
#
|
|
56
|
+
# Group variables live at:
|
|
57
|
+
# git.unturf.com -> python (group) -> Settings -> CI/CD -> Variables
|
|
58
|
+
# TWINE_USERNAME = __token__
|
|
59
|
+
# TWINE_PASSWORD = <pypi-AgEI...> (Masked + Protected)
|
|
60
|
+
#
|
|
61
|
+
# setuptools keyword args: http://peak.telecommunity.com/DevCenter/setuptools
|
|
62
|
+
|
|
63
|
+
# Installation Instructions:
|
|
64
|
+
# To install virt-back, you can use pip:
|
|
65
|
+
# * pip install virt-back
|
|
66
|
+
#
|
|
67
|
+
# Note: You need to have the libvirt development libraries installed.
|
|
68
|
+
# On Fedora/RedHat, you can install it with:
|
|
69
|
+
# * sudo dnf install libvirt-devel
|
|
70
|
+
# On Ubuntu, you can install it with:
|
|
71
|
+
# * sudo apt-get install libvirt-dev
|
|
@@ -23,6 +23,7 @@ import libvirt
|
|
|
23
23
|
import tarfile
|
|
24
24
|
import syslog
|
|
25
25
|
import re
|
|
26
|
+
import glob
|
|
26
27
|
from time import sleep
|
|
27
28
|
from datetime import date
|
|
28
29
|
from sys import exit
|
|
@@ -195,6 +196,9 @@ def backup(doms):
|
|
|
195
196
|
except subprocess.CalledProcessError as e:
|
|
196
197
|
logit("error", f"Failed to send ZFS snapshot {zfs_snapshot}: {e}")
|
|
197
198
|
continue
|
|
199
|
+
|
|
200
|
+
# Prune old @backup-* snapshots, keep newest `retention`
|
|
201
|
+
prune_zfs_snapshots(zfs_dataset, options.retention)
|
|
198
202
|
else:
|
|
199
203
|
# Handle QCOW2 or other file-based disk
|
|
200
204
|
logit("backup", f"{disk_source} is not a ZFS dataset")
|
|
@@ -281,6 +285,48 @@ def is_zfs_dataset(disk_source):
|
|
|
281
285
|
return False
|
|
282
286
|
|
|
283
287
|
|
|
288
|
+
def prune_zfs_snapshots(zfs_dataset, retention):
|
|
289
|
+
"""List @backup-* snapshots on dataset, destroy oldest until count <= retention.
|
|
290
|
+
|
|
291
|
+
Only touches snapshots virt-back created (@backup-* prefix). Never destroys
|
|
292
|
+
user or TrueNAS periodic snapshots. Never destroys more than necessary —
|
|
293
|
+
`retention` snapshots always survive as the recovery floor.
|
|
294
|
+
"""
|
|
295
|
+
if retention < 1:
|
|
296
|
+
logit("error", f"retention must be >= 1, got {retention} — skipping prune")
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
# INVARIANT: reclamation eats from the tail (oldest first).
|
|
300
|
+
# `-s creation` sorts ascending by the ZFS creation timestamp (epoch
|
|
301
|
+
# precision, immune to TODAY timezone drift), so snaps[:surplus] below
|
|
302
|
+
# always slices the OLDEST entries. Never reorder this — destroying
|
|
303
|
+
# newer snapshots first would wipe the recovery floor while leaving
|
|
304
|
+
# stale tails behind.
|
|
305
|
+
try:
|
|
306
|
+
result = subprocess.run(
|
|
307
|
+
["zfs", "list", "-t", "snapshot", "-H", "-o", "name",
|
|
308
|
+
"-s", "creation", zfs_dataset],
|
|
309
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True,
|
|
310
|
+
)
|
|
311
|
+
except subprocess.CalledProcessError as e:
|
|
312
|
+
logit("error", f"Failed to list snapshots for {zfs_dataset}: {e}")
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
prefix = f"{zfs_dataset}@backup-"
|
|
316
|
+
snaps = [line for line in result.stdout.splitlines() if line.startswith(prefix)]
|
|
317
|
+
|
|
318
|
+
surplus = len(snaps) - retention
|
|
319
|
+
if surplus <= 0:
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
for snap in snaps[:surplus]:
|
|
323
|
+
logit("backup", f"pruning old ZFS snapshot {snap} (retention={retention})")
|
|
324
|
+
try:
|
|
325
|
+
subprocess.run(["zfs", "destroy", snap], check=True)
|
|
326
|
+
except subprocess.CalledProcessError as e:
|
|
327
|
+
logit("error", f"Failed to destroy snapshot {snap}: {e}")
|
|
328
|
+
|
|
329
|
+
|
|
284
330
|
def shutdown(doms, wait=180):
|
|
285
331
|
"""Accept a list of dom objects, attempt to shutdown the active ones"""
|
|
286
332
|
# get all running guests from list and invoke shutdown
|
|
@@ -401,7 +447,13 @@ def logit(context, message, quiet=False):
|
|
|
401
447
|
|
|
402
448
|
|
|
403
449
|
def rotate(target, retention=3):
|
|
404
|
-
"""file rotation routine
|
|
450
|
+
"""file rotation routine — shift within window then reap any stale files
|
|
451
|
+
at indices >= retention.
|
|
452
|
+
|
|
453
|
+
Without the reap step, lowering retention (e.g. 5 -> 2) leaves orphan
|
|
454
|
+
.2, .3, .4 around forever — the same accumulation pattern that ZFS
|
|
455
|
+
snapshots had before 0.2.5.
|
|
456
|
+
"""
|
|
405
457
|
for i in range(retention - 2, 0, -1): # count backwards
|
|
406
458
|
old_name = "%s.%s" % (target, i)
|
|
407
459
|
new_name = "%s.%s" % (target, i + 1)
|
|
@@ -411,6 +463,22 @@ def rotate(target, retention=3):
|
|
|
411
463
|
pass
|
|
412
464
|
move(target, target + ".1")
|
|
413
465
|
|
|
466
|
+
# Reap files at indices >= retention. INVARIANT: never touches the
|
|
467
|
+
# current target (no numeric suffix) and never touches indices in
|
|
468
|
+
# [1, retention-1] (the active rotation window).
|
|
469
|
+
for stale in glob.glob("%s.*" % target):
|
|
470
|
+
suffix = stale.rsplit(".", 1)[-1]
|
|
471
|
+
try:
|
|
472
|
+
idx = int(suffix)
|
|
473
|
+
except ValueError:
|
|
474
|
+
continue # not a rotation index (e.g., backup.tar.gz where .gz isn't int)
|
|
475
|
+
if idx >= retention:
|
|
476
|
+
logit("backup", "removing stale backup file %s (retention=%s)" % (stale, retention))
|
|
477
|
+
try:
|
|
478
|
+
remove(stale)
|
|
479
|
+
except OSError as e:
|
|
480
|
+
logit("error", "failed to remove %s: %s" % (stale, e))
|
|
481
|
+
|
|
414
482
|
|
|
415
483
|
def getoptions():
|
|
416
484
|
"""Fetch cli args, parse and map to python, test sanity"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: virt-back
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: virt-back: A backup utility for QEMU, KVM, XEN, and Virtualbox guests
|
|
5
5
|
Home-page: https://git.unturf.com/python/virt-back
|
|
6
6
|
Author: Russell Ballestrini
|
|
@@ -9,6 +9,17 @@ License: Public Domain
|
|
|
9
9
|
Keywords: backup virtual hypervisor QEMU KVM XEN Virtualbox
|
|
10
10
|
Platform: All
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: libvirt-python
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: description
|
|
16
|
+
Dynamic: description-content-type
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: keywords
|
|
19
|
+
Dynamic: license
|
|
20
|
+
Dynamic: platform
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: summary
|
|
12
23
|
|
|
13
24
|
A backup utility for QEMU, KVM, XEN, and Virtualbox guests.
|
|
14
25
|
|
|
@@ -39,6 +50,7 @@ options:
|
|
|
39
50
|
-g, --no-gzip do not gzip the resulting tar file
|
|
40
51
|
-a amount, --retention amount
|
|
41
52
|
backups to retain [default: 3]
|
|
53
|
+
(applies to both rotated backup files AND ZFS @backup-* snapshots)
|
|
42
54
|
-p 'PATH', --path 'PATH'
|
|
43
55
|
backup path [default: '/KVMBACK']
|
|
44
56
|
-u 'URI', --uri 'URI'
|
|
@@ -23,6 +23,7 @@ import libvirt
|
|
|
23
23
|
import tarfile
|
|
24
24
|
import syslog
|
|
25
25
|
import re
|
|
26
|
+
import glob
|
|
26
27
|
from time import sleep
|
|
27
28
|
from datetime import date
|
|
28
29
|
from sys import exit
|
|
@@ -195,6 +196,9 @@ def backup(doms):
|
|
|
195
196
|
except subprocess.CalledProcessError as e:
|
|
196
197
|
logit("error", f"Failed to send ZFS snapshot {zfs_snapshot}: {e}")
|
|
197
198
|
continue
|
|
199
|
+
|
|
200
|
+
# Prune old @backup-* snapshots, keep newest `retention`
|
|
201
|
+
prune_zfs_snapshots(zfs_dataset, options.retention)
|
|
198
202
|
else:
|
|
199
203
|
# Handle QCOW2 or other file-based disk
|
|
200
204
|
logit("backup", f"{disk_source} is not a ZFS dataset")
|
|
@@ -281,6 +285,48 @@ def is_zfs_dataset(disk_source):
|
|
|
281
285
|
return False
|
|
282
286
|
|
|
283
287
|
|
|
288
|
+
def prune_zfs_snapshots(zfs_dataset, retention):
|
|
289
|
+
"""List @backup-* snapshots on dataset, destroy oldest until count <= retention.
|
|
290
|
+
|
|
291
|
+
Only touches snapshots virt-back created (@backup-* prefix). Never destroys
|
|
292
|
+
user or TrueNAS periodic snapshots. Never destroys more than necessary —
|
|
293
|
+
`retention` snapshots always survive as the recovery floor.
|
|
294
|
+
"""
|
|
295
|
+
if retention < 1:
|
|
296
|
+
logit("error", f"retention must be >= 1, got {retention} — skipping prune")
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
# INVARIANT: reclamation eats from the tail (oldest first).
|
|
300
|
+
# `-s creation` sorts ascending by the ZFS creation timestamp (epoch
|
|
301
|
+
# precision, immune to TODAY timezone drift), so snaps[:surplus] below
|
|
302
|
+
# always slices the OLDEST entries. Never reorder this — destroying
|
|
303
|
+
# newer snapshots first would wipe the recovery floor while leaving
|
|
304
|
+
# stale tails behind.
|
|
305
|
+
try:
|
|
306
|
+
result = subprocess.run(
|
|
307
|
+
["zfs", "list", "-t", "snapshot", "-H", "-o", "name",
|
|
308
|
+
"-s", "creation", zfs_dataset],
|
|
309
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True,
|
|
310
|
+
)
|
|
311
|
+
except subprocess.CalledProcessError as e:
|
|
312
|
+
logit("error", f"Failed to list snapshots for {zfs_dataset}: {e}")
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
prefix = f"{zfs_dataset}@backup-"
|
|
316
|
+
snaps = [line for line in result.stdout.splitlines() if line.startswith(prefix)]
|
|
317
|
+
|
|
318
|
+
surplus = len(snaps) - retention
|
|
319
|
+
if surplus <= 0:
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
for snap in snaps[:surplus]:
|
|
323
|
+
logit("backup", f"pruning old ZFS snapshot {snap} (retention={retention})")
|
|
324
|
+
try:
|
|
325
|
+
subprocess.run(["zfs", "destroy", snap], check=True)
|
|
326
|
+
except subprocess.CalledProcessError as e:
|
|
327
|
+
logit("error", f"Failed to destroy snapshot {snap}: {e}")
|
|
328
|
+
|
|
329
|
+
|
|
284
330
|
def shutdown(doms, wait=180):
|
|
285
331
|
"""Accept a list of dom objects, attempt to shutdown the active ones"""
|
|
286
332
|
# get all running guests from list and invoke shutdown
|
|
@@ -401,7 +447,13 @@ def logit(context, message, quiet=False):
|
|
|
401
447
|
|
|
402
448
|
|
|
403
449
|
def rotate(target, retention=3):
|
|
404
|
-
"""file rotation routine
|
|
450
|
+
"""file rotation routine — shift within window then reap any stale files
|
|
451
|
+
at indices >= retention.
|
|
452
|
+
|
|
453
|
+
Without the reap step, lowering retention (e.g. 5 -> 2) leaves orphan
|
|
454
|
+
.2, .3, .4 around forever — the same accumulation pattern that ZFS
|
|
455
|
+
snapshots had before 0.2.5.
|
|
456
|
+
"""
|
|
405
457
|
for i in range(retention - 2, 0, -1): # count backwards
|
|
406
458
|
old_name = "%s.%s" % (target, i)
|
|
407
459
|
new_name = "%s.%s" % (target, i + 1)
|
|
@@ -411,6 +463,22 @@ def rotate(target, retention=3):
|
|
|
411
463
|
pass
|
|
412
464
|
move(target, target + ".1")
|
|
413
465
|
|
|
466
|
+
# Reap files at indices >= retention. INVARIANT: never touches the
|
|
467
|
+
# current target (no numeric suffix) and never touches indices in
|
|
468
|
+
# [1, retention-1] (the active rotation window).
|
|
469
|
+
for stale in glob.glob("%s.*" % target):
|
|
470
|
+
suffix = stale.rsplit(".", 1)[-1]
|
|
471
|
+
try:
|
|
472
|
+
idx = int(suffix)
|
|
473
|
+
except ValueError:
|
|
474
|
+
continue # not a rotation index (e.g., backup.tar.gz where .gz isn't int)
|
|
475
|
+
if idx >= retention:
|
|
476
|
+
logit("backup", "removing stale backup file %s (retention=%s)" % (stale, retention))
|
|
477
|
+
try:
|
|
478
|
+
remove(stale)
|
|
479
|
+
except OSError as e:
|
|
480
|
+
logit("error", "failed to remove %s: %s" % (stale, e))
|
|
481
|
+
|
|
414
482
|
|
|
415
483
|
def getoptions():
|
|
416
484
|
"""Fetch cli args, parse and map to python, test sanity"""
|
virt-back-0.2.4/setup.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from setuptools import setup
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
# Read the contents of your README file
|
|
5
|
-
with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8") as f:
|
|
6
|
-
long_description = f.read()
|
|
7
|
-
|
|
8
|
-
setup(
|
|
9
|
-
name="virt-back",
|
|
10
|
-
version="0.2.4",
|
|
11
|
-
description="virt-back: A backup utility for QEMU, KVM, XEN, and Virtualbox guests",
|
|
12
|
-
keywords="backup virtual hypervisor QEMU KVM XEN Virtualbox",
|
|
13
|
-
long_description=long_description,
|
|
14
|
-
long_description_content_type="text/markdown",
|
|
15
|
-
author="Russell Ballestrini",
|
|
16
|
-
author_email="russell@ballestrini.net",
|
|
17
|
-
url="https://git.unturf.com/python/virt-back",
|
|
18
|
-
platforms=["All"],
|
|
19
|
-
license="Public Domain",
|
|
20
|
-
py_modules=["virtback"],
|
|
21
|
-
include_package_data=True,
|
|
22
|
-
scripts=["virt-back"],
|
|
23
|
-
install_requires=[
|
|
24
|
-
"libvirt-python",
|
|
25
|
-
],
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
setup()
|
|
30
|
-
keyword args: http://peak.telecommunity.com/DevCenter/setuptools
|
|
31
|
-
|
|
32
|
-
# built and uploaded to pypi with this:
|
|
33
|
-
|
|
34
|
-
python setup.py sdist
|
|
35
|
-
twine upload dist/*
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
# Installation Instructions:
|
|
40
|
-
# To install virt-back, you can use pip:
|
|
41
|
-
# * pip install virt-back
|
|
42
|
-
#
|
|
43
|
-
# Note: You need to have the libvirt development libraries installed.
|
|
44
|
-
# On Fedora/RedHat, you can install it with:
|
|
45
|
-
# * sudo dnf install libvirt-devel
|
|
46
|
-
# On Ubuntu, you can install it with:
|
|
47
|
-
# * sudo apt-get install libvirt-dev
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|