diff --git a/setup.cfg b/setup.cfg index 87211f4ad..58acb1f16 100644 --- a/setup.cfg +++ b/setup.cfg @@ -89,6 +89,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_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 tripleo_container_image_delete = tripleoclient.v1.container_image:TripleOContainerImageDelete diff --git a/tripleoclient/tests/v1/overcloud_restore/__init__.py b/tripleoclient/tests/v1/overcloud_restore/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tripleoclient/tests/v1/overcloud_restore/test_backup.py b/tripleoclient/tests/v1/overcloud_restore/test_backup.py new file mode 100644 index 000000000..c30500e84 --- /dev/null +++ b/tripleoclient/tests/v1/overcloud_restore/test_backup.py @@ -0,0 +1,73 @@ +# Copyright 2021 Red Hat, Inc. +# +# 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. +# + +from unittest import mock + +from osc_lib.tests import utils + +from tripleoclient import constants +from tripleoclient.tests import fakes +from tripleoclient.v1 import overcloud_restore + + +class TestOvercloudRestore(utils.TestCommand): + + def setUp(self): + super(TestOvercloudRestore, 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_restore.RestoreOvercloud(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_restore_controller_(self, + mock_playbook, + mock_access, + mock_isfile): + arglist = [ + '--node-name', + 'overcloud-controller-0' + ] + verifylist = [] + mock_isfile.return_value = True + mock_access.return_value = True + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + parameter = 'tripleo_backup_and_restore_overcloud_restore_name' + + self.cmd.take_action(parsed_args) + mock_playbook.assert_called_once_with( + workdir=mock.ANY, + playbook='cli-overcloud-restore-node.yaml', + inventory=parsed_args.inventory, + tags=None, + skip_tags=None, + playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS, + verbosity=3, + extra_vars={ + parameter: arglist[1] + }, + ssh_user='stack' + ) diff --git a/tripleoclient/v1/overcloud_restore.py b/tripleoclient/v1/overcloud_restore.py new file mode 100644 index 000000000..b8d5ae909 --- /dev/null +++ b/tripleoclient/v1/overcloud_restore.py @@ -0,0 +1,147 @@ +# Copyright 2021 Red Hat, Inc. +# +# 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 argparse +import logging +import os +import yaml + +from osc_lib.command import command +from osc_lib.i18n import _ + +from tripleoclient import constants +from tripleoclient import utils + +LOG = logging.getLogger(__name__ + ".RestoreOvercloud") + +INVENTORY = '~/overcloud-deploy/overcloud/'+constants.TRIPLEO_STATIC_INVENTORY + + +class RestoreOvercloud(command.Command): + """Restore the Overcloud""" + + 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( + '--node-name', + required=True, + help=_("Controller name is a required parameter " + "which defines the controller node to be " + "restored.") + ) + + 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: + return {} + if 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_restore_overcloud(self, parsed_args): + """Backup defined overcloud nodes.""" + + extra_vars = self._parse_extra_vars(parsed_args.extra_vars) + node = parsed_args.node_name + parameter = 'tripleo_backup_and_restore_overcloud_restore_name' + extra_vars[parameter] = node + + self._run_ansible_playbook( + playbook='cli-overcloud-restore-node.yaml', + inventory=parsed_args.inventory, + tags=None, + skip_tags=None, + extra_vars=extra_vars, + ssh_user='stack' + ) + + def _run_ansible_playbook(self, + playbook, + inventory, + tags, + skip_tags, + extra_vars, + ssh_user): + """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, + ssh_user=ssh_user + ) + + def take_action(self, parsed_args): + + self._run_restore_overcloud(parsed_args) + + print( + '\n' + ' #############################################################\n' + ' # Disclaimer #\n' + ' # Backup verification is the End Users responsibility #\n' + ' # Please verify backup integrity before any possible #\n' + ' # disruptive actions against the Overcloud. The resulting #\n' + ' # backup file path will be shown on a successful execution. #\n' + ' # #\n' + ' # .-Stay safe and avoid future issues-. #\n' + ' #############################################################\n' + )