From eaaf2e8bc568cd71c9d6753ebf3c349a315cf8c1 Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Fri, 19 May 2017 09:24:03 +0200 Subject: [PATCH] Add Openstack Undercloud backup This new option allows to call the Mistral workflow to run an Undercloud full backup. Usage: openstack undercloud backup [--add-path ADD_FILES_TO_BACKUP] The --add-path allows to add additional files or folders to the Undercloud backup, for example: openstack undercloud backup --add-path /etc/hosts \ --add-path /var/log/ Depends-on: Iebd67568d5e72967e694b88fc8c73361543929a1 Change-Id: Ief10f5313244d2848b6de604e0493a51db1ed30f Related bp undercloud-backup-restore --- ...ck-undercloud-backup-b0c83afeb565c41d.yaml | 5 ++ setup.cfg | 2 +- .../v1/undercloud/test_undercloud_backup.py | 60 +++++++++++++ .../tests/workflows/test_undercloud_backup.py | 59 +++++++++++++ tripleoclient/v1/undercloud_backup.py | 84 +++++++++++++++++++ tripleoclient/workflows/undercloud_backup.py | 35 ++++++++ 6 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/openstack-undercloud-backup-b0c83afeb565c41d.yaml create mode 100644 tripleoclient/tests/v1/undercloud/test_undercloud_backup.py create mode 100644 tripleoclient/tests/workflows/test_undercloud_backup.py create mode 100644 tripleoclient/v1/undercloud_backup.py create mode 100644 tripleoclient/workflows/undercloud_backup.py diff --git a/releasenotes/notes/openstack-undercloud-backup-b0c83afeb565c41d.yaml b/releasenotes/notes/openstack-undercloud-backup-b0c83afeb565c41d.yaml new file mode 100644 index 000000000..0e245cfd4 --- /dev/null +++ b/releasenotes/notes/openstack-undercloud-backup-b0c83afeb565c41d.yaml @@ -0,0 +1,5 @@ +--- +features: + - Add a new option to the TripleO client in order to create an Undercloud + backup. + Usage, openstack undercloud backup [--add-path ADD_FILES_TO_BACKUP] diff --git a/setup.cfg b/setup.cfg index 26de7f61e..7a4f07081 100644 --- a/setup.cfg +++ b/setup.cfg @@ -93,6 +93,6 @@ openstack.tripleoclient.v1 = undercloud_deploy = tripleoclient.v1.undercloud_deploy:DeployUndercloud undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud undercloud_upgrade = tripleoclient.v1.undercloud:UpgradeUndercloud - + undercloud_backup = tripleoclient.v1.undercloud_backup:BackupUndercloud oslo.config.opts = undercloud_config = tripleoclient.v1.undercloud_config:list_opts diff --git a/tripleoclient/tests/v1/undercloud/test_undercloud_backup.py b/tripleoclient/tests/v1/undercloud/test_undercloud_backup.py new file mode 100644 index 000000000..c561decb3 --- /dev/null +++ b/tripleoclient/tests/v1/undercloud/test_undercloud_backup.py @@ -0,0 +1,60 @@ +# Copyright 2018 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 mock + +from osc_lib.tests import utils +from tripleoclient.v1 import undercloud_backup + + +class TestUndercloudBackup(utils.TestCommand): + + def setUp(self): + super(TestUndercloudBackup, self).setUp() + + # Get the command object to test + self.cmd = undercloud_backup.BackupUndercloud(self.app, None) + self.app.client_manager.workflow_engine = mock.Mock() + self.workflow = self.app.client_manager.workflow_engine + + @mock.patch('tripleoclient.workflows.undercloud_backup.backup', + autospec=True) + def test_undercloud_backup_noargs(self, plan_mock): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + plan_mock.assert_called_once_with( + mock.ANY, {'sources_path': '/home/stack/'}) + + @mock.patch('tripleoclient.workflows.undercloud_backup.backup', + autospec=True) + def test_undercloud_backup_withargs(self, plan_mock): + arglist = [ + '--add-path', + '/tmp/foo.yaml', + '--add-path', + '/tmp/bar.yaml' + ] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + plan_mock.assert_called_once_with( + mock.ANY, + {'sources_path': '/home/stack/,/tmp/bar.yaml,/tmp/foo.yaml'}) diff --git a/tripleoclient/tests/workflows/test_undercloud_backup.py b/tripleoclient/tests/workflows/test_undercloud_backup.py new file mode 100644 index 000000000..3ac5d74ff --- /dev/null +++ b/tripleoclient/tests/workflows/test_undercloud_backup.py @@ -0,0 +1,59 @@ +# Copyright 2017 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 mock + +from osc_lib.tests import utils +from tripleoclient.workflows import undercloud_backup + + +class TestUndercloudBackup(utils.TestCommand): + + def setUp(self): + super(TestUndercloudBackup, self).setUp() + self.app.client_manager = mock.Mock() + self.app.client_manager.workflow_engine = self.workflow = mock.Mock() + self.tripleoclient = mock.Mock() + self.websocket = mock.Mock() + self.websocket.__enter__ = lambda s: self.websocket + self.websocket.__exit__ = lambda s, *exc: None + self.tripleoclient.messaging_websocket.return_value = self.websocket + self.app.client_manager.tripleoclient = self.tripleoclient + + @mock.patch('tripleoclient.workflows.base.wait_for_messages') + @mock.patch('tripleoclient.workflows.base.start_workflow') + def test_run_backup(self, start_wf_mock, messages_mock): + messages_mock.return_value = [] + fetch_name = 'tripleo.undercloud_backup.v1.backup' + fetch_input = { + 'sources_path': '/home/stack/' + } + undercloud_backup.backup(self.app.client_manager, fetch_input) + start_wf_mock.assert_called_once_with(self.workflow, + fetch_name, + workflow_input=fetch_input) + + @mock.patch('tripleoclient.workflows.base.wait_for_messages') + @mock.patch('tripleoclient.workflows.base.start_workflow') + def test_run_backup_with_args(self, start_wf_mock, messages_mock): + messages_mock.return_value = [] + fetch_name = 'tripleo.undercloud_backup.v1.backup' + fetch_input = { + 'sources_path': '/tmp/file1.txt,/home/stack/' + } + undercloud_backup.backup(self.app.client_manager, fetch_input) + start_wf_mock.assert_called_once_with(self.workflow, + fetch_name, + workflow_input=fetch_input) diff --git a/tripleoclient/v1/undercloud_backup.py b/tripleoclient/v1/undercloud_backup.py new file mode 100644 index 000000000..643876abf --- /dev/null +++ b/tripleoclient/v1/undercloud_backup.py @@ -0,0 +1,84 @@ +# Copyright 2018 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 + +from openstackclient.i18n import _ +from osc_lib.command import command +from tripleoclient.workflows import undercloud_backup + +LOG = logging.getLogger(__name__ + ".BackupUndercloud") + + +class BackupUndercloud(command.Command): + """Backup the undercloud""" + + def get_parser(self, prog_name): + parser = argparse.ArgumentParser( + description=self.get_description(), + prog=prog_name, + add_help=False + ) + + # Parameter to choose the files to backup + parser.add_argument( + '--add-path', + action='append', + default=['/home/stack/'], + help=_("Add additional files to backup. " + "Defaults to: /home/stack/ " + "i.e. --add-path /this/is/a/folder/ " + " --add-path /this/is/a/texfile.txt") + ) + return parser + + def _run_backup_undercloud(self, parsed_args): + + clients = self.app.client_manager + + files_to_backup = ','.join(sorted(list(set(parsed_args.add_path)))) + + # Define the backup sources_path (files to backup). + # This is a comma separated string. + # I.e. "/this/is/a/folder/,/this/is/a/texfile.txt" + workflow_input = { + "sources_path": files_to_backup + } + + LOG.debug('Launch the Undercloud Backup') + try: + output = undercloud_backup.backup(clients, workflow_input) + LOG.info(output) + except Exception as e: + print ("Undercloud backup finished with errors") + print ('Output: {}'.format(e)) + LOG.info(e) + + def take_action(self, parsed_args): + + LOG.info( + '\n' + ' #############################################################\n' + ' # Disclaimer #\n' + ' # Backup verification is the End Users responsibility #\n' + ' # Please verify backup integrity before any possible #\n' + ' # disruptive actions against the Undercloud. The resulting #\n' + ' # backup file path will be shown on a successful execution. #\n' + ' # #\n' + ' # .-Stay safe and avoid future issues-. #\n' + ' #############################################################\n') + + self._run_backup_undercloud(parsed_args) diff --git a/tripleoclient/workflows/undercloud_backup.py b/tripleoclient/workflows/undercloud_backup.py new file mode 100644 index 000000000..8121f9108 --- /dev/null +++ b/tripleoclient/workflows/undercloud_backup.py @@ -0,0 +1,35 @@ +# Copyright 2018 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 pprint + +from tripleoclient.workflows import base + + +def backup(clients, workflow_input): + + workflow_client = clients.workflow_engine + tripleoclients = clients.tripleoclient + + with tripleoclients.messaging_websocket() as ws: + execution = base.start_workflow( + workflow_client, + 'tripleo.undercloud_backup.v1.backup', + workflow_input=workflow_input + ) + + for payload in base.wait_for_messages(workflow_client, ws, execution): + if 'message' in payload: + assert payload['status'] == "SUCCESS", pprint.pformat(payload) + print('Undercloud Backup succeed')