1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
"""
Copyright (C) 2024 Michael Ablassmeier <abi@grinser.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
from typing import List, Any
from argparse import Namespace
from libvirt import virDomain
from libvirtnbdbackup import virt
from libvirtnbdbackup import common as lib
from libvirtnbdbackup import exceptions
log = logging.getLogger()
def arguments(args: Namespace) -> None:
"""Check passed arguments for validity"""
if args.compress is not False and args.type == "raw":
raise exceptions.BackupException("Compression not supported with raw output.")
if args.stdout is True and args.type == "raw":
raise exceptions.BackupException("Output type raw not supported to stdout.")
if args.stdout is True and args.raw is True:
raise exceptions.BackupException(
"Saving raw images to stdout is not supported."
)
if args.type == "raw" and args.level in ("inc", "diff"):
raise exceptions.BackupException(
"Stream format raw does not support incremental or differential backup."
)
def targetDir(args: Namespace) -> None:
"""Check if target directory backup is started to meets
all requirements based on the backup level executed"""
if (
args.level not in ("copy", "full", "auto")
and not lib.hasFullBackup(args)
and not args.stdout
):
raise exceptions.BackupException(
f"Unable to execute [{args.level}] backup: "
f"No full backup found in target directory: [{args.output}]"
)
if lib.targetIsEmpty(args) and args.level == "auto":
log.info("Backup mode auto, target folder is empty: executing full backup.")
args.level = "full"
elif not lib.targetIsEmpty(args) and args.level == "auto":
if not lib.hasFullBackup(args):
raise exceptions.BackupException(
"Can't execute switch to auto incremental backup: "
f"specified target folder [{args.output}] does not contain full backup.",
)
log.info("Backup mode auto: executing incremental backup.")
args.level = "inc"
elif not args.stdout and not args.startonly and not args.killonly:
if not lib.targetIsEmpty(args):
raise exceptions.BackupException(
"Target directory already contains full or copy backup."
)
def vmstate(args, virtClient: virt.client, domObj: virDomain) -> None:
"""Check virtual machine state before executing backup
and based on situation, either fallback to regular copy
backup or attempt to bring VM into paused state"""
if domObj.isActive() == 0:
args.offline = True
if args.start_domain is True:
log.info("Starting domain in paused state")
if virtClient.startDomain(domObj) == 0:
args.offline = False
else:
log.info("Failed to start VM in paused mode.")
if args.level == "full" and args.offline is True:
log.warning("Domain is offline, resetting backup options.")
args.level = "copy"
log.warning("New Backup level: [%s].", args.level)
if args.offline is True and args.startonly is True:
raise exceptions.BackupException(
"Domain is offline: must be active for this function."
)
def vmfeature(virtClient: virt.client, domObj: virDomain) -> None:
"""Check if required features are enabled in domain config"""
if virtClient.hasIncrementalEnabled(domObj) is False:
raise exceptions.BackupException(
"Virtual machine does not support required backup features, "
"please adjust virtual machine configuration."
)
def diskformat(args: Namespace, disks: List[Any]) -> None:
"""Check if disks meet requirements for backup mode, if not, switch
backup job to type copy."""
if args.level != "copy" and lib.hasQcowDisks(disks) is False:
args.level = "copy"
raise exceptions.BackupException(
"Only raw disks attached, switching to backup mode copy."
)
def blockjobs(
args, virtClient: virt.client, domObj: virDomain, disks: List[Any]
) -> None:
"""Check if there is an already active backup operation on the domain
disks. If so, fail accordingly"""
if (
not args.killonly
and not args.offline
and virtClient.blockJobActive(domObj, disks)
):
raise exceptions.BackupException(
"Active block job for running domain:"
f" Check with [virsh domjobinfo {args.domain}] or use option -k to kill the active job."
)
|