Added Validation layer for freezer jobs
validating arguments before starting freezer job. each job will validate job-specific arguments and the _general_validate method in base Job class will takecare of the common ones. Implements: blueprint validation-layer-for-freezer-jobs Change-Id: Ia964bcc72abe39408dd72338951e083926a130ca
This commit is contained in:
parent
88041c1684
commit
4277d4a4f3
|
@ -42,7 +42,7 @@ _DEFAULT_LOG_LEVELS = ['amqp=WARN', 'amqplib=WARN', 'boto=WARN',
|
|||
'iso8601=WARN',
|
||||
'requests.packages.urllib3.connectionpool=WARN',
|
||||
'urllib3.connectionpool=WARN', 'websocket=WARN',
|
||||
'keystonemiddleware=WARN', 'freezer=INFO']
|
||||
'keystoneauth1=WARN', 'freezer=INFO']
|
||||
|
||||
_DEFAULT_LOGGING_CONTEXT_FORMAT = (
|
||||
'%(asctime)s.%(msecs)03d %(process)d '
|
||||
|
@ -52,32 +52,32 @@ _DEFAULT_LOGGING_CONTEXT_FORMAT = (
|
|||
|
||||
DEFAULT_PARAMS = {
|
||||
'os_identity_api_version': None,
|
||||
'lvm_auto_snap': None, 'lvm_volgroup': False,
|
||||
'exclude': False, 'sql_server_conf': False,
|
||||
'backup_name': False, 'quiet': False,
|
||||
'container': 'freezer_backups', 'no_incremental': False,
|
||||
'max_segment_size': 33554432, 'lvm_srcvol': False,
|
||||
'download_limit': -1, 'hostname': False, 'remove_from_date': False,
|
||||
'lvm_auto_snap': None, 'lvm_volgroup': None,
|
||||
'exclude': None, 'sql_server_conf': False,
|
||||
'backup_name': None, 'quiet': False,
|
||||
'container': 'freezer_backups', 'no_incremental': None,
|
||||
'max_segment_size': 33554432, 'lvm_srcvol': None,
|
||||
'download_limit': -1, 'hostname': None, 'remove_from_date': None,
|
||||
'restart_always_level': False, 'lvm_dirmount': None,
|
||||
'dereference_symlink': '',
|
||||
'config': False, 'mysql_conf': False,
|
||||
'dereference_symlink': None,
|
||||
'config': None, 'mysql_conf': False,
|
||||
'insecure': False, 'lvm_snapname': None,
|
||||
'lvm_snapperm': 'ro', 'snapshot': False,
|
||||
'max_priority': False, 'max_level': False, 'path_to_backup': False,
|
||||
'encrypt_pass_file': False, 'volume': False, 'proxy': False,
|
||||
'lvm_snapperm': 'ro', 'snapshot': None,
|
||||
'max_priority': None, 'max_level': False, 'path_to_backup': None,
|
||||
'encrypt_pass_file': None, 'volume': None, 'proxy': None,
|
||||
'cinder_vol_id': '', 'cindernative_vol_id': '',
|
||||
'nova_inst_id': '', '__version__': FREEZER_VERSION,
|
||||
'remove_older_than': None, 'restore_from_date': False,
|
||||
'upload_limit': -1, 'always_level': False, 'version': False,
|
||||
'remove_older_than': None, 'restore_from_date': None,
|
||||
'upload_limit': -1, 'always_level': False, 'version': None,
|
||||
'dry_run': False, 'lvm_snapsize': DEFAULT_LVM_SNAPSIZE,
|
||||
'restore_abs_path': False, 'log_file': None, 'log_level': "info",
|
||||
'restore_abs_path': None, 'log_file': None, 'log_level': "info",
|
||||
'mode': 'fs', 'action': 'backup', 'shadow': '', 'shadow_path': '',
|
||||
'windows_volume': '', 'command': None, 'metadata_out': False,
|
||||
'windows_volume': '', 'command': None, 'metadata_out': None,
|
||||
'storage': 'swift', 'ssh_key': '', 'ssh_username': '', 'ssh_host': '',
|
||||
'ssh_port': DEFAULT_SSH_PORT, 'compression': 'gzip',
|
||||
'overwrite': False,
|
||||
'overwrite': False, 'incremental': None,
|
||||
'consistency_check': False, 'consistency_checksum': None,
|
||||
'nova_restore_network': None,
|
||||
'nova_restore_network': None, 'cindernative_backup_id': None
|
||||
}
|
||||
|
||||
_COMMON = [
|
||||
|
@ -92,55 +92,63 @@ _COMMON = [
|
|||
),
|
||||
cfg.StrOpt('path-to-backup',
|
||||
short='F',
|
||||
default=False,
|
||||
dest='path_to_backup',
|
||||
default=DEFAULT_PARAMS['path_to_backup'],
|
||||
help='The file or directory you want to back up to Swift'
|
||||
),
|
||||
cfg.StrOpt('backup-name',
|
||||
short='N',
|
||||
default=False,
|
||||
default=DEFAULT_PARAMS['backup_name'],
|
||||
help="The backup name you want to use to identify your backup "
|
||||
"on Swift"
|
||||
),
|
||||
cfg.StrOpt('mode',
|
||||
short='m',
|
||||
dest='mode',
|
||||
default=DEFAULT_PARAMS['mode'],
|
||||
help="Set the technology to back from. Options are, fs "
|
||||
"(filesystem),mongo (MongoDB), mysql (MySQL), sqlserver "
|
||||
"(SQL Server) Default set to fs"),
|
||||
cfg.StrOpt('container',
|
||||
short='C',
|
||||
default='freezer_backups',
|
||||
default=DEFAULT_PARAMS['container'],
|
||||
dest='container',
|
||||
help="The Swift container (or path to local storage) used to "
|
||||
"upload files to"),
|
||||
cfg.StrOpt('snapshot',
|
||||
short='s',
|
||||
dest='snapshot',
|
||||
default=DEFAULT_PARAMS['snapshot'],
|
||||
help="Create a snapshot of the fs containing the resource to "
|
||||
"backup. When used, the lvm parameters will be guessed "
|
||||
"and/or the default values will be used, on windows it "
|
||||
"will invoke vssadmin"),
|
||||
cfg.StrOpt('lvm-auto-snap',
|
||||
dest='lvm_auto_snap',
|
||||
default=DEFAULT_PARAMS['lvm_auto_snap'],
|
||||
help="(Deprecated) Please use --snapshot instead"
|
||||
"Automatically guess the volume group and volume name for "
|
||||
"given PATH."),
|
||||
cfg.StrOpt('lvm-srcvol',
|
||||
dest='lvm_srcvol',
|
||||
default=DEFAULT_PARAMS['lvm_srcvol'],
|
||||
help="Set the lvm volume you want to take a snaphost from. "
|
||||
"Default no volume"),
|
||||
cfg.StrOpt('lvm-snapname',
|
||||
dest='lvm_snapname',
|
||||
default=DEFAULT_PARAMS['lvm_snapname'],
|
||||
help="Set the name of the snapshot that will be created."
|
||||
" If not provided, a unique name will be generated."),
|
||||
cfg.StrOpt('lvm-snap-perm',
|
||||
choices=['ro', 'rw'],
|
||||
dest='lvm_snapperm',
|
||||
default=DEFAULT_PARAMS['lvm_snapperm'],
|
||||
help="Set the lvm snapshot permission to use. If the permission"
|
||||
" is set to ro The snapshot will be immutable - read only"
|
||||
" -. If the permission is set to rw it will be mutable"),
|
||||
cfg.StrOpt('lvm-snapsize',
|
||||
dest='lvm_snapsize',
|
||||
default=DEFAULT_PARAMS['lvm_snapsize'],
|
||||
help="Set the lvm snapshot size when creating a new "
|
||||
"snapshot. Please add G for Gigabytes or "
|
||||
"M for Megabytes, i.e. 500M or 8G. It is also possible "
|
||||
|
@ -148,16 +156,19 @@ _COMMON = [
|
|||
"80%%FREE Default {0}.".format(DEFAULT_LVM_SNAPSIZE)),
|
||||
cfg.StrOpt('lvm-dirmount',
|
||||
dest='lvm_dirmount',
|
||||
default=DEFAULT_PARAMS['lvm_dirmount'],
|
||||
help="Set the directory you want to mount the lvm snapshot to. "
|
||||
"If not provided, a unique name will be generated with "
|
||||
"thebasename {0} ".format(DEFAULT_LVM_MOUNT_BASENAME)),
|
||||
cfg.StrOpt('lvm-volgroup',
|
||||
dest='lvm_volgroup',
|
||||
default=DEFAULT_PARAMS['lvm_volgroup'],
|
||||
help="Specify the volume group of your logical volume. This is "
|
||||
"important to mount your snapshot volume. Default not "
|
||||
"set"),
|
||||
cfg.IntOpt('max-level',
|
||||
dest='max_level',
|
||||
default=DEFAULT_PARAMS['max_level'],
|
||||
help="Set the backup level used with tar to implement "
|
||||
"incremental backup. If a level 1 is specified but "
|
||||
"no level 0 is already available, a level 0 will be "
|
||||
|
@ -166,6 +177,7 @@ _COMMON = [
|
|||
),
|
||||
cfg.IntOpt('always-level',
|
||||
dest='always_level',
|
||||
default=DEFAULT_PARAMS['always_level'],
|
||||
help="Set backup maximum level used with tar to implement "
|
||||
"incremental backup. If a level 3 is specified, the backup"
|
||||
" will be executed from level 0 to level 3 and to that "
|
||||
|
@ -174,6 +186,7 @@ _COMMON = [
|
|||
" --max-backup-level. Default False (Disabled)"),
|
||||
cfg.FloatOpt('restart-always-level',
|
||||
dest='restart_always_level',
|
||||
default=DEFAULT_PARAMS['restart_always_level'],
|
||||
help="Restart the backup from level 0 after n days. Valid "
|
||||
"only if --always-level option if set. If "
|
||||
"--always-level is used together with "
|
||||
|
@ -182,6 +195,7 @@ _COMMON = [
|
|||
"Default False (Disabled)"),
|
||||
cfg.FloatOpt('remove-older-than',
|
||||
short='R',
|
||||
default=DEFAULT_PARAMS['remove_older_than'],
|
||||
dest='remove_older_than',
|
||||
help="Checks in the specified container for objects older "
|
||||
"than the specified days. If i.e. 30 is specified, it "
|
||||
|
@ -189,18 +203,21 @@ _COMMON = [
|
|||
"Default False (Disabled)"),
|
||||
cfg.StrOpt('remove-from-date',
|
||||
dest='remove_from_date',
|
||||
default=DEFAULT_PARAMS['remove_from_date'],
|
||||
help="Checks the specified container and removes objects older "
|
||||
"than the provided datetime in the form "
|
||||
"'YYYY-MM-DDThh:mm:ss' i.e. '1974-03-25T23:23:23'. "
|
||||
"Make sure the 'T' is between date and time "),
|
||||
cfg.StrOpt('no-incremental',
|
||||
dest='no_incremental',
|
||||
default=DEFAULT_PARAMS['no_incremental'],
|
||||
help="Disable incremental feature. By default freezer build the"
|
||||
" meta data even for level 0 backup. By setting this "
|
||||
"option incremental meta data is not created at all. "
|
||||
"Default disabled"),
|
||||
cfg.StrOpt('hostname',
|
||||
dest='hostname',
|
||||
default=DEFAULT_PARAMS['hostname'],
|
||||
deprecated_name='restore-from-host',
|
||||
help="Set hostname to execute actions. If you are executing "
|
||||
"freezer from one host but you want to delete objects "
|
||||
|
@ -209,6 +226,7 @@ _COMMON = [
|
|||
"current node hostname."),
|
||||
cfg.StrOpt('mysql-conf',
|
||||
dest='mysql_conf',
|
||||
default=DEFAULT_PARAMS['mysql_conf'],
|
||||
help="Set the MySQL configuration file where freezer retrieve "
|
||||
"important information as db_name, user, password, host, "
|
||||
"port. Following is an example of config file: "
|
||||
|
@ -219,40 +237,47 @@ _COMMON = [
|
|||
"port = <db-port>"),
|
||||
cfg.StrOpt('metadata-out',
|
||||
dest='metadata_out',
|
||||
default=DEFAULT_PARAMS['metadata_out'],
|
||||
help="Set the filename to which write the metadata "
|
||||
"regarding the backup metrics. Use '-' to output to "
|
||||
"standard output."),
|
||||
cfg.StrOpt('exclude',
|
||||
dest='exclude',
|
||||
default=DEFAULT_PARAMS['exclude'],
|
||||
help="Exclude files,given as a PATTERN.Ex: --exclude '*.log' "
|
||||
"will exclude any file with name ending with .log. "
|
||||
"Default no exclude"
|
||||
),
|
||||
cfg.StrOpt('dereference-symlink',
|
||||
dest='dereference_symlink',
|
||||
choices=['none', 'soft', 'hard', 'all'],
|
||||
default=DEFAULT_PARAMS['dereference_symlink'],
|
||||
choices=[None, 'soft', 'hard', 'all'],
|
||||
help="Follow hard and soft links and archive and dump the files"
|
||||
" they refer to. Default False."
|
||||
),
|
||||
cfg.StrOpt('encrypt-pass-file',
|
||||
dest='encrypt_pass_file',
|
||||
default=DEFAULT_PARAMS['encrypt_pass_file'],
|
||||
help="Passing a private key to this option, allow you "
|
||||
"to encrypt the files before to be uploaded in Swift. "
|
||||
"Default do not encrypt."
|
||||
),
|
||||
cfg.IntOpt('max-segment-size',
|
||||
short='M',
|
||||
default=DEFAULT_PARAMS['max_segment_size'],
|
||||
dest='max_segment_size',
|
||||
help="Set the maximum file chunk size in bytes to upload to "
|
||||
"swift Default 33554432 bytes (32MB)"
|
||||
),
|
||||
cfg.StrOpt('restore-abs-path',
|
||||
dest='restore_abs_path',
|
||||
default=DEFAULT_PARAMS['restore_abs_path'],
|
||||
help="Set the absolute path where you want your data restored. "
|
||||
"Default False."
|
||||
),
|
||||
cfg.StrOpt('restore-from-date',
|
||||
dest='restore_from_date',
|
||||
default=DEFAULT_PARAMS['restore_from_date'],
|
||||
help="Set the date of the backup from which you want to "
|
||||
"restore.This will select the most recent backup "
|
||||
"previous to the specified date (included). Example: "
|
||||
|
@ -269,6 +294,7 @@ _COMMON = [
|
|||
),
|
||||
cfg.StrOpt('max-priority',
|
||||
dest='max_priority',
|
||||
default=DEFAULT_PARAMS['max_priority'],
|
||||
help="Set the cpu process to the highest priority (i.e. -20 on "
|
||||
"Linux) and real-time for I/O. The process priority "
|
||||
"will be set only if nice and ionice are installed "
|
||||
|
@ -276,70 +302,84 @@ _COMMON = [
|
|||
),
|
||||
cfg.BoolOpt('quiet',
|
||||
short='q',
|
||||
default=DEFAULT_PARAMS['quiet'],
|
||||
dest='quiet',
|
||||
help="Suppress error messages"
|
||||
),
|
||||
cfg.BoolOpt('insecure',
|
||||
dest='insecure',
|
||||
default=DEFAULT_PARAMS['insecure'],
|
||||
help='Allow to access swift servers without checking SSL '
|
||||
'certs.'),
|
||||
cfg.StrOpt('os-identity-api-version',
|
||||
deprecated_name='os-auth-ver',
|
||||
default=DEFAULT_PARAMS['os_identity_api_version'],
|
||||
dest='os_identity_api_version',
|
||||
choices=['1', '2', '2.0', '3'],
|
||||
help="Openstack identity api version, can be 1, 2, 2.0 or 3"
|
||||
),
|
||||
cfg.StrOpt('proxy',
|
||||
dest='proxy',
|
||||
default=DEFAULT_PARAMS['proxy'],
|
||||
help="Enforce proxy that alters system HTTP_PROXY and "
|
||||
"HTTPS_PROXY, use \'\' to eliminate all system proxies"
|
||||
),
|
||||
cfg.BoolOpt('dry-run',
|
||||
dest='dry_run',
|
||||
default=DEFAULT_PARAMS['dry_run'],
|
||||
help="Do everything except writing or removing objects"
|
||||
),
|
||||
cfg.IntOpt('upload-limit',
|
||||
dest='upload_limit',
|
||||
default=DEFAULT_PARAMS['upload_limit'],
|
||||
help="Upload bandwidth limit in Bytes per sec. "
|
||||
"Can be invoked with dimensions (10K, 120M, 10G)."),
|
||||
cfg.IntOpt('download-limit',
|
||||
dest='download_limit',
|
||||
default=DEFAULT_PARAMS['download_limit'],
|
||||
help="Download bandwidth limit in Bytes per sec. Can be "
|
||||
"invoked with dimensions (10K, 120M, 10G)."),
|
||||
cfg.StrOpt('cinder-vol-id',
|
||||
dest='cinder_vol_id',
|
||||
default=DEFAULT_PARAMS['cinder_vol_id'],
|
||||
help="Id of cinder volume for backup"
|
||||
),
|
||||
cfg.StrOpt('cindernative-vol-id',
|
||||
dest='cindernative_vol_id',
|
||||
default=DEFAULT_PARAMS['cindernative_vol_id'],
|
||||
help="Id of cinder volume for native backup"
|
||||
),
|
||||
cfg.StrOpt('cindernative-backup-id',
|
||||
default='',
|
||||
default=DEFAULT_PARAMS['cindernative_backup_id'],
|
||||
dest='cindernative_backup_id',
|
||||
help="Id of the cindernative backup to be restored"
|
||||
),
|
||||
cfg.StrOpt('nova-inst-id',
|
||||
dest='nova_inst_id',
|
||||
default=DEFAULT_PARAMS['nova_inst_id'],
|
||||
help="Id of nova instance for backup"
|
||||
),
|
||||
cfg.StrOpt('sql-server-conf',
|
||||
dest='sql_server_conf',
|
||||
default=DEFAULT_PARAMS['sql_server_conf'],
|
||||
help="Set the SQL Server configuration file where freezer "
|
||||
"retrieve the sql server instance. Following is an example"
|
||||
" of config file: instance = <db-instance>"
|
||||
),
|
||||
cfg.StrOpt('command',
|
||||
dest='command',
|
||||
default=DEFAULT_PARAMS['command'],
|
||||
help="Command executed by exec action"
|
||||
),
|
||||
cfg.StrOpt('compression',
|
||||
dest='compression',
|
||||
default=DEFAULT_PARAMS['compression'],
|
||||
choices=['gzip', 'bzip2', 'xz'],
|
||||
help="compression algorithm to use. gzip is default algorithm"
|
||||
),
|
||||
cfg.StrOpt('storage',
|
||||
dest='storage',
|
||||
default=DEFAULT_PARAMS['storage'],
|
||||
choices=['local', 'swift', 'ssh'],
|
||||
help="Storage for backups. Can be Swift or Local now. Swift is "
|
||||
"default storage now. Local stores backups on the same "
|
||||
|
@ -347,31 +387,38 @@ _COMMON = [
|
|||
),
|
||||
cfg.StrOpt('ssh-key',
|
||||
dest='ssh_key',
|
||||
default=DEFAULT_PARAMS['ssh_key'],
|
||||
help="Path to ssh-key for ssh storage only"
|
||||
),
|
||||
cfg.StrOpt('ssh-username',
|
||||
dest='ssh_username',
|
||||
default=DEFAULT_PARAMS['ssh_username'],
|
||||
help="Remote username for ssh storage only"
|
||||
),
|
||||
cfg.StrOpt('ssh-host',
|
||||
dest='ssh_host',
|
||||
default=DEFAULT_PARAMS['ssh_host'],
|
||||
help="Remote host for ssh storage only"
|
||||
),
|
||||
cfg.IntOpt('ssh-port',
|
||||
dest='ssh_port',
|
||||
default=DEFAULT_PARAMS['ssh_port'],
|
||||
help="Remote port for ssh storage only (default 22)"
|
||||
),
|
||||
cfg.StrOpt('config',
|
||||
dest='config',
|
||||
default=DEFAULT_PARAMS['config'],
|
||||
help="Config file abs path. Option arguments are provided from "
|
||||
"config file. When config file is used any option from "
|
||||
"command line provided take precedence."),
|
||||
cfg.BoolOpt('overwrite',
|
||||
dest='overwrite',
|
||||
default=DEFAULT_PARAMS['overwrite'],
|
||||
help='With overwrite removes files from restore path before '
|
||||
'restore.'),
|
||||
cfg.BoolOpt('consistency-check',
|
||||
dest='consistency_check',
|
||||
default=DEFAULT_PARAMS['consistency_check'],
|
||||
help="Computes the checksum of the fileset before backup. "
|
||||
"This checksum is stored as part of the backup metadata, "
|
||||
"which can be obtained either by using --metadata-out or "
|
||||
|
@ -384,18 +431,20 @@ _COMMON = [
|
|||
deprecated_name='consistency_check'),
|
||||
cfg.StrOpt('consistency-checksum',
|
||||
dest='consistency_checksum',
|
||||
default=DEFAULT_PARAMS['consistency_checksum'],
|
||||
help="Compute the checksum of the restored file(s) and compare "
|
||||
"it to the (provided) checksum to verify that the backup "
|
||||
"was successful",
|
||||
deprecated_name='consistency_checksum'),
|
||||
cfg.BoolOpt('incremental',
|
||||
default=False,
|
||||
default=DEFAULT_PARAMS['incremental'],
|
||||
help="When the option is set, freezer will perform a "
|
||||
"cindernative incremental backup instead of the default "
|
||||
"full backup. And if True, but volume do not have a base"
|
||||
"full backup, freezer will do a full backup first"),
|
||||
cfg.StrOpt('nova-restore-network',
|
||||
dest='nova_restore_network',
|
||||
default=DEFAULT_PARAMS['nova_restore_network'],
|
||||
help="ID of the network to attach to the restored VM. "
|
||||
"In the case of a project containing multiple networks, "
|
||||
"it is necessary to provide the ID of the network to "
|
||||
|
|
|
@ -48,6 +48,27 @@ class Job(object):
|
|||
self.conf = conf_dict
|
||||
self.storage = storage
|
||||
self.engine = conf_dict.engine
|
||||
self._general_validation()
|
||||
self._validate()
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate(self):
|
||||
"""
|
||||
Method that validates if all arguments available to execute the job
|
||||
or not
|
||||
:return: True or raise an error
|
||||
"""
|
||||
pass
|
||||
|
||||
def _general_validation(self):
|
||||
"""
|
||||
Apply general validation rules.
|
||||
:return: True or raise an error
|
||||
"""
|
||||
if not self.conf.action:
|
||||
raise ValueError("Please provide a valid action with --action")
|
||||
|
||||
LOG.info("Validating args for the {0} job.".format(self.conf.action))
|
||||
|
||||
@abc.abstractmethod
|
||||
def execute(self):
|
||||
|
@ -55,12 +76,33 @@ class Job(object):
|
|||
|
||||
|
||||
class InfoJob(Job):
|
||||
|
||||
def _validate(self):
|
||||
# no validation required for this job
|
||||
pass
|
||||
|
||||
def execute(self):
|
||||
self.storage.info()
|
||||
|
||||
|
||||
class BackupJob(Job):
|
||||
|
||||
def _validate(self):
|
||||
if not self.conf.path_to_backup:
|
||||
raise ValueError('path-to-backup argument must be provided')
|
||||
if self.conf.no_incremental and (self.conf.max_level or
|
||||
self.conf.always_level):
|
||||
raise Exception(
|
||||
'no-incremental option is not compatible '
|
||||
'with backup level options')
|
||||
|
||||
def execute(self):
|
||||
LOG.info('Backup job started. '
|
||||
'backup_name: {0}, container: {1}, hostname: {2}, mode: {3},'
|
||||
' Storage: {4}, compression: {5}'
|
||||
.format(self.conf.backup_name, self.conf.container,
|
||||
self.conf.hostname, self.conf.mode, self.conf.storage,
|
||||
self.conf.compression))
|
||||
try:
|
||||
(out, err) = utils.create_subprocess('sync')
|
||||
if err:
|
||||
|
@ -73,7 +115,6 @@ class BackupJob(Job):
|
|||
self.conf.mode, self.conf.mode.capitalize() + 'Mode')
|
||||
app_mode = importutils.import_object(mod_name, self.conf)
|
||||
backup_level = self.backup(app_mode)
|
||||
|
||||
level = backup_level or 0
|
||||
|
||||
metadata = {
|
||||
|
@ -125,6 +166,7 @@ class BackupJob(Job):
|
|||
self.conf.time_stamp = time_stamp
|
||||
|
||||
if backup_media == 'fs':
|
||||
LOG.info('Path to backup: {0}'.format(self.conf.path_to_backup))
|
||||
app_mode.prepare()
|
||||
snapshot_taken = snapshot.snapshot_create(self.conf)
|
||||
if snapshot_taken:
|
||||
|
@ -134,7 +176,7 @@ class BackupJob(Job):
|
|||
chdir_path = os.path.expanduser(
|
||||
os.path.normpath(self.conf.path_to_backup.strip()))
|
||||
if not os.path.exists(chdir_path):
|
||||
msg = 'Path to backup does not exist {}'.format(
|
||||
msg = 'Path to backup does not exist {0}'.format(
|
||||
chdir_path)
|
||||
LOG.critical(msg)
|
||||
raise IOError(msg)
|
||||
|
@ -174,14 +216,18 @@ class BackupJob(Job):
|
|||
self.storage)
|
||||
|
||||
if backup_media == 'nova':
|
||||
LOG.info('Executing nova backup')
|
||||
LOG.info('Executing nova backup. Instance ID: {0}'.format(
|
||||
self.conf.nova_inst_id))
|
||||
backup_os.backup_nova(self.conf.nova_inst_id)
|
||||
elif backup_media == 'cindernative':
|
||||
LOG.info('Executing cinder backup')
|
||||
LOG.info('Executing cinder native backup. Volume ID: {0}, '
|
||||
'incremental: {1}'.format(self.conf.cindernative_vol_id,
|
||||
self.conf.incremental))
|
||||
backup_os.backup_cinder(self.conf.cindernative_vol_id,
|
||||
incremental=self.conf.incremental)
|
||||
elif backup_media == 'cinder':
|
||||
LOG.info('Executing cinder snapshot')
|
||||
LOG.info('Executing cinder snapshot. Volume ID: {0}'.format(
|
||||
self.conf.cinder_vol_id))
|
||||
backup_os.backup_cinder_by_glance(self.conf.cinder_vol_id)
|
||||
else:
|
||||
raise Exception('unknown parameter backup_media %s' % backup_media)
|
||||
|
@ -190,6 +236,19 @@ class BackupJob(Job):
|
|||
|
||||
class RestoreJob(Job):
|
||||
|
||||
def _validate(self):
|
||||
if not self.conf.restore_abs_path and not self.conf.nova_inst_id \
|
||||
and not self.conf.cinder_vol_id and not \
|
||||
self.conf.cindernative_vol_id:
|
||||
raise ValueError("--restore-abs-path is required")
|
||||
if not self.conf.container:
|
||||
raise ValueError("--container is required")
|
||||
if self.conf.no_incremental and (self.conf.max_level or
|
||||
self.conf.always_level):
|
||||
raise Exception(
|
||||
'no-incremental option is not compatible '
|
||||
'with backup level options')
|
||||
|
||||
def execute(self):
|
||||
conf = self.conf
|
||||
LOG.info('Executing FS restore...')
|
||||
|
@ -222,17 +281,25 @@ class RestoreJob(Job):
|
|||
"Backup Consistency Check failed: could not checksum file"
|
||||
" {0} ({1})".format(e.filename, e.strerror))
|
||||
return {}
|
||||
|
||||
res = restore.RestoreOs(conf.client_manager, conf.container)
|
||||
if conf.backup_media == 'nova':
|
||||
LOG.info("Restoring nova backup. Instance ID: {0}, timestamp: {1}"
|
||||
.format(conf.nova_inst_id, restore_timestamp))
|
||||
nova_network = None
|
||||
if conf.nova_restore_network:
|
||||
nova_network = conf.nova_restore_network
|
||||
res.restore_nova(conf.nova_inst_id, restore_timestamp,
|
||||
nova_network)
|
||||
elif conf.backup_media == 'cinder':
|
||||
LOG.info("Restoring cinder backup from glance. Volume ID: {0}, "
|
||||
"timestamp: {1}".format(conf.cinder_vol_id,
|
||||
restore_timestamp))
|
||||
res.restore_cinder_by_glance(conf.cinder_vol_id, restore_timestamp)
|
||||
elif conf.backup_media == 'cindernative':
|
||||
LOG.info("Restoring cinder native backup. Volume ID {0}, Backup ID"
|
||||
" {1}, timestamp: {2}".format(conf.cindernative_vol_id,
|
||||
conf.cindernative_backup_id,
|
||||
restore_timestamp))
|
||||
res.restore_cinder(conf.cindernative_vol_id,
|
||||
conf.cindernative_backup_id,
|
||||
restore_timestamp)
|
||||
|
@ -243,6 +310,13 @@ class RestoreJob(Job):
|
|||
|
||||
class AdminJob(Job):
|
||||
|
||||
def _validate(self):
|
||||
# no validation required in this job
|
||||
if not self.conf.remove_from_date and not self.conf.remove_older_than:
|
||||
raise ValueError("You need to provide to remove backup older "
|
||||
"than this time. You can use --remove-older-than "
|
||||
"or --remove-from-date")
|
||||
|
||||
def execute(self):
|
||||
if self.conf.remove_from_date:
|
||||
timestamp = utils.date_to_timestamp(self.conf.remove_from_date)
|
||||
|
@ -259,10 +333,14 @@ class AdminJob(Job):
|
|||
|
||||
class ExecJob(Job):
|
||||
|
||||
def _validate(self):
|
||||
if not self.conf.command:
|
||||
raise ValueError("--command option is required")
|
||||
|
||||
def execute(self):
|
||||
LOG.info('exec job....')
|
||||
if self.conf.command:
|
||||
LOG.info('Executing exec job....')
|
||||
LOG.info('Executing exec job. Command: {0}'
|
||||
.format(self.conf.command))
|
||||
exec_cmd.execute(self.conf.command)
|
||||
else:
|
||||
LOG.warning(
|
||||
|
|
|
@ -34,7 +34,6 @@ from freezer.storage import ssh
|
|||
from freezer.storage import swift
|
||||
from freezer.utils import config
|
||||
from freezer.utils import utils
|
||||
from freezer.utils import validator
|
||||
from freezer.utils import winutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -55,8 +54,6 @@ def freezer_main(backup_args):
|
|||
backup_args.__dict__['hostname_backup_name'] = "{0}_{1}".format(
|
||||
backup_args.hostname, backup_args.backup_name)
|
||||
|
||||
validator.validate(backup_args)
|
||||
|
||||
max_segment_size = backup_args.max_segment_size
|
||||
if (backup_args.storage ==
|
||||
'swift' or
|
||||
|
|
|
@ -349,7 +349,7 @@ class BackupOpt1(object):
|
|||
|
||||
self.client_manager.get_nova = Mock(return_value=nova_client)
|
||||
|
||||
self.command = None
|
||||
self.command = ''
|
||||
|
||||
|
||||
class Os:
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
def validate(conf):
|
||||
if conf.no_incremental and (conf.max_level or conf.always_level):
|
||||
raise Exception(
|
||||
'no-incremental option is not compatible '
|
||||
'with backup level options')
|
||||
|
||||
if conf.action == "restore" and not conf.restore_abs_path and \
|
||||
not conf.nova_inst_id and not conf.cinder_vol_id and \
|
||||
not conf.cindernative_vol_id:
|
||||
raise Exception("Please provide restore_abs_path")
|
||||
|
||||
if conf.restore_abs_path and not conf.action == "restore":
|
||||
raise Exception('Restore abs path with {0} action'
|
||||
.format(conf.action))
|
||||
|
||||
if conf.storage == "ssh" and \
|
||||
not (conf.ssh_key and conf.ssh_username and conf.ssh_host):
|
||||
raise Exception("Please provide ssh_key, "
|
||||
"ssh_username and ssh_host")
|
|
@ -58,6 +58,8 @@ class TestBackupJob(TestJob):
|
|||
backup_opt = BackupOpt1()
|
||||
backup_opt.mode = 'default'
|
||||
backup_opt.no_incremental = True
|
||||
backup_opt.max_level = None
|
||||
backup_opt.always_level = None
|
||||
job = jobs.BackupJob(backup_opt, backup_opt.storage)
|
||||
self.assertRaises(Exception, job.execute)
|
||||
|
||||
|
@ -95,7 +97,9 @@ class TestExecJob(TestJob):
|
|||
self.popen.stop()
|
||||
|
||||
def test_execute_nothing_to_do(self):
|
||||
self.mock_popen.return_value.returncode = 0
|
||||
backup_opt = BackupOpt1()
|
||||
backup_opt.command = 'ls '
|
||||
jobs.ExecJob(backup_opt, backup_opt.storage).execute()
|
||||
|
||||
def test_execute_script(self):
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from freezer.utils import validator
|
||||
from freezer.utils import utils
|
||||
|
||||
class TestValidator(unittest.TestCase):
|
||||
def test_pass(self):
|
||||
bunch = utils.Bunch()
|
||||
validator.validate(bunch)
|
||||
|
||||
def test_no_increment(self):
|
||||
bunch = utils.Bunch(no_incremental=True, max_level=10)
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
||||
|
||||
def test_restore_without_path(self):
|
||||
bunch = utils.Bunch(action="restore")
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
||||
|
||||
def test_restore_with_path(self):
|
||||
bunch = utils.Bunch(action="restore", restore_abs_path="/tmp")
|
||||
validator.validate(bunch)
|
||||
|
||||
def test_backup_with_restore_path(self):
|
||||
bunch = utils.Bunch(action="backup", restore_abs_path="/tmp")
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
||||
|
||||
def test_ssh(self):
|
||||
bunch = utils.Bunch(storage="ssh", ssh_key="key", ssh_username="name",
|
||||
ssh_host="localhost")
|
||||
validator.validate(bunch)
|
||||
|
||||
def test_ssh_raises(self):
|
||||
bunch = utils.Bunch(storage="ssh", ssh_key="key", ssh_username="name")
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
||||
bunch = utils.Bunch(storage="ssh", ssh_key="key", ssh_host="localhost")
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
||||
bunch = utils.Bunch(storage="ssh", ssh_username="name",
|
||||
ssh_host="localhost")
|
||||
self.assertRaises(Exception, validator.validate, bunch)
|
Loading…
Reference in New Issue