Enable snapshots on the CLI
There is a new feature for Backup and Restore that replaces the ReaR
operations with a simple and fast LVM snapshot. This change enables the
operator to take snapshots using the openstack overcloud backup
snapshot command.
Change-Id: I975dada7dc559f708f4b9b0cba6328c87befb5bd
(cherry picked from commit 80290ad9b8
)
This commit is contained in:
parent
1cc14bef83
commit
17de18b40e
|
@ -91,6 +91,7 @@ openstack.tripleoclient.v2 =
|
|||
overcloud_external-upgrade_run = tripleoclient.v1.overcloud_external_upgrade:ExternalUpgradeRun
|
||||
overcloud_generate_fencing = tripleoclient.v1.overcloud_parameters:GenerateFencingParameters
|
||||
overcloud_backup = tripleoclient.v1.overcloud_backup:BackupOvercloud
|
||||
overcloud_backup_snapshot = tripleoclient.v1.overcloud_backup:BackupSnapshot
|
||||
overcloud_restore = tripleoclient.v1.overcloud_restore:RestoreOvercloud
|
||||
tripleo_container_image_build = tripleoclient.v2.tripleo_container_image:Build
|
||||
tripleo_container_image_hotfix = tripleoclient.v2.tripleo_container_image:HotFix
|
||||
|
|
|
@ -366,3 +366,137 @@ class TestOvercloudBackup(utils.TestCommand):
|
|||
'The inventory file',
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
|
||||
class TestOvercloudSnapshot(utils.TestCommand):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOvercloudSnapshot, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
app_args = mock.Mock()
|
||||
app_args.verbose_level = 1
|
||||
self.app.options = fakes.FakeOptions()
|
||||
self.cmd = overcloud_backup.BackupSnapshot(self.app, app_args)
|
||||
self.app.client_manager.workflow_engine = mock.Mock()
|
||||
self.workflow = self.app.client_manager.workflow_engine
|
||||
self.inventory = '/tmp/test_inventory.yaml'
|
||||
self.file = open(self.inventory, 'w').close()
|
||||
|
||||
@mock.patch('os.path.isfile')
|
||||
@mock.patch('os.access')
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_overcloud_snapshot_noargs(self,
|
||||
mock_playbook,
|
||||
mock_access,
|
||||
mock_isfile):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
mock_isfile.return_value = True
|
||||
mock_access.return_value = True
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_playbook.assert_called_once_with(
|
||||
workdir=mock.ANY,
|
||||
playbook='cli-overcloud-snapshot.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
verbosity=3,
|
||||
extra_vars={}
|
||||
)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True)
|
||||
def test_overcloud_snapshot_inventory(self, mock_playbook):
|
||||
arglist = [
|
||||
'--inventory',
|
||||
self.inventory
|
||||
]
|
||||
verifylist = []
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_playbook.assert_called_once_with(
|
||||
workdir=mock.ANY,
|
||||
playbook='cli-overcloud-snapshot.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
verbosity=3,
|
||||
extra_vars={}
|
||||
)
|
||||
|
||||
@mock.patch('os.path.isfile')
|
||||
@mock.patch('os.access')
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_overcloud_snapshot_extra_vars_inline(self,
|
||||
mock_playbook,
|
||||
mock_access,
|
||||
mock_isfile):
|
||||
arglist = [
|
||||
'--extra-vars',
|
||||
'{"tripleo_snapshot_revert_var_size": "2G"}'
|
||||
]
|
||||
verifylist = []
|
||||
mock_isfile.return_value = True
|
||||
mock_access.return_value = True
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
extra_vars_dict = {
|
||||
'tripleo_snapshot_revert_var_size': '2G'
|
||||
}
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_playbook.assert_called_once_with(
|
||||
workdir=mock.ANY,
|
||||
playbook='cli-overcloud-snapshot.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
verbosity=3,
|
||||
extra_vars=extra_vars_dict
|
||||
)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_overcloud_backup_no_inventory(self, mock_playbook):
|
||||
arglist = [
|
||||
'--inventory',
|
||||
'/tmp/no_inventory.yaml'
|
||||
]
|
||||
verifylist = []
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
'The inventory file',
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
@mock.patch('os.access')
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_overcloud_backup_no_readable_inventory(self,
|
||||
mock_playbook,
|
||||
mock_access):
|
||||
arglist = [
|
||||
'--inventory',
|
||||
self.inventory
|
||||
]
|
||||
verifylist = []
|
||||
mock_access.return_value = False
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
'The inventory file',
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
|
|
@ -24,14 +24,14 @@ from osc_lib.i18n import _
|
|||
from tripleoclient import constants
|
||||
from tripleoclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__ + ".BackupOvercloud")
|
||||
|
||||
INVENTORY = constants.ANSIBLE_INVENTORY.format('overcloud')
|
||||
|
||||
|
||||
class BackupOvercloud(command.Command):
|
||||
"""Backup the Overcloud"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".BackupOvercloud")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = argparse.ArgumentParser(
|
||||
description=self.get_description(),
|
||||
|
@ -160,52 +160,52 @@ class BackupOvercloud(command.Command):
|
|||
if not (os.path.isfile(parsed_args.inventory) and
|
||||
os.access(parsed_args.inventory, os.R_OK)):
|
||||
raise RuntimeError(
|
||||
_('The inventory file {} does not exist or is not '
|
||||
'readable'.format(parsed_args.inventory)))
|
||||
_('The inventory file {} does not exist or is not '
|
||||
'readable'.format(parsed_args.inventory)))
|
||||
|
||||
if parsed_args.setup_nfs is True or parsed_args.init == 'nfs':
|
||||
|
||||
LOG.debug(_('Setting up NFS Backup node'))
|
||||
self.log.debug(_('Setting up NFS Backup node'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='prepare-nfs-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_nfs_server',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
playbook='prepare-nfs-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_nfs_server',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
if parsed_args.setup_rear is True or parsed_args.init == 'rear':
|
||||
|
||||
LOG.debug(_('Installing ReaR on controller nodes'))
|
||||
self.log.debug(_('Installing ReaR on controller nodes'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='prepare-overcloud-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_rear',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
playbook='prepare-overcloud-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_rear',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
if parsed_args.setup_ironic is True or parsed_args.init == 'ironic':
|
||||
|
||||
LOG.debug(_('Installing and configuring Rear/Ironic on nodes'))
|
||||
self.log.debug(_('Installing Rear/Ironic on nodes'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='cli-overcloud-conf-ironic.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_rear',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
playbook='cli-overcloud-conf-ironic.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_setup_rear',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
if parsed_args.cron is True:
|
||||
|
||||
LOG.debug(_('Programming cron backup'))
|
||||
self.log.debug(_('Programming cron backup'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='cli-overcloud-backup-cron.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
playbook='cli-overcloud-backup-cron.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
if (parsed_args.setup_nfs is False and
|
||||
parsed_args.setup_rear is False and
|
||||
|
@ -213,14 +213,14 @@ class BackupOvercloud(command.Command):
|
|||
parsed_args.cron is False and
|
||||
parsed_args.init is None):
|
||||
|
||||
LOG.debug(_('Starting Overcloud Backup'))
|
||||
self.log.debug(_('Starting Overcloud Backup'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='cli-overcloud-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_create_recover_image',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
playbook='cli-overcloud-backup.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags='bar_create_recover_image',
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
def _run_ansible_playbook(self,
|
||||
playbook,
|
||||
|
@ -246,8 +246,8 @@ class BackupOvercloud(command.Command):
|
|||
|
||||
if parsed_args.init:
|
||||
|
||||
LOG.warning("The following flags will be deprecated:"
|
||||
"[--init, --storage-ip]")
|
||||
self.log.warning("The following flags will be deprecated:"
|
||||
"[--init, --storage-ip]")
|
||||
|
||||
self._run_backup_overcloud(parsed_args)
|
||||
print(
|
||||
|
@ -262,3 +262,111 @@ class BackupOvercloud(command.Command):
|
|||
' # .-Stay safe and avoid future issues-. #\n'
|
||||
' #############################################################\n'
|
||||
)
|
||||
|
||||
|
||||
class BackupSnapshot(command.Command):
|
||||
"""Takes and LVM snapshot ignoring all the rest
|
||||
of the parameters passed. To be able to take
|
||||
a snapshot, the following conditions must
|
||||
be met:
|
||||
- The disk must be configured to use LVM
|
||||
- There must be an lv called lv_snapshot
|
||||
- lv_snapshot must be 8GB or more
|
||||
This operation will destroy the lv_snapshot volume
|
||||
and replace it with snapshots of the disks.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".BackupSnapshotOvercloud")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = argparse.ArgumentParser(
|
||||
description=self.get_description(),
|
||||
prog=prog_name,
|
||||
add_help=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--inventory',
|
||||
default=INVENTORY,
|
||||
help=_("Tripleo inventory file generated with "
|
||||
"tripleo-ansible-inventory command. "
|
||||
"Defaults to: " + INVENTORY)
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--extra-vars',
|
||||
default=None,
|
||||
action='store',
|
||||
help=_("Set additional variables as Dict or as "
|
||||
"an absolute path of a JSON or YAML file type. "
|
||||
"i.e. --extra-vars '{\"key\": \"val\", "
|
||||
" \"key2\": \"val2\"}' "
|
||||
"i.e. --extra-vars /path/to/my_vars.yaml "
|
||||
"i.e. --extra-vars /path/to/my_vars.json. "
|
||||
"For more information about the variables that "
|
||||
"can be passed, visit: https://opendev.org/openstack/"
|
||||
"tripleo-ansible/src/branch/master/tripleo_ansible/"
|
||||
"roles/backup_and_restore/defaults/main.yml.")
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _parse_extra_vars(self, raw_extra_vars):
|
||||
|
||||
if raw_extra_vars is None:
|
||||
extra_vars = {}
|
||||
elif os.path.exists(raw_extra_vars):
|
||||
with open(raw_extra_vars, 'r') as fp:
|
||||
extra_vars = yaml.safe_load(fp.read())
|
||||
else:
|
||||
try:
|
||||
extra_vars = yaml.safe_load(raw_extra_vars)
|
||||
except yaml.YAMLError as exc:
|
||||
raise RuntimeError(
|
||||
_('--extra-vars is not an existing file and cannot be '
|
||||
'parsed as YAML / JSON: %s') % exc)
|
||||
|
||||
return extra_vars
|
||||
|
||||
def _run_snapshot_overcloud(self, parsed_args):
|
||||
"""Snapshot defined overcloud nodes."""
|
||||
|
||||
extra_vars = self._parse_extra_vars(parsed_args.extra_vars)
|
||||
|
||||
if not (os.path.isfile(parsed_args.inventory) and
|
||||
os.access(parsed_args.inventory, os.R_OK)):
|
||||
raise RuntimeError(
|
||||
_('The inventory file {} does not exist or is not '
|
||||
'readable'.format(parsed_args.inventory)))
|
||||
|
||||
self.log.debug(_('Starting Overcloud Snapshot'))
|
||||
self._run_ansible_playbook(
|
||||
playbook='cli-overcloud-snapshot.yaml',
|
||||
inventory=parsed_args.inventory,
|
||||
tags=None,
|
||||
skip_tags=None,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
def _run_ansible_playbook(self,
|
||||
playbook,
|
||||
inventory,
|
||||
tags,
|
||||
skip_tags,
|
||||
extra_vars):
|
||||
"""Run ansible playbook"""
|
||||
|
||||
with utils.TempDirs() as tmp:
|
||||
utils.run_ansible_playbook(
|
||||
playbook=playbook,
|
||||
inventory=inventory,
|
||||
workdir=tmp,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
tags=tags,
|
||||
skip_tags=skip_tags,
|
||||
verbosity=utils.playbook_verbosity(self=self),
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self._run_snapshot_overcloud(parsed_args)
|
||||
|
|
Loading…
Reference in New Issue