From df9cbd400e2cecd2cfbbeead7bfec15aea329bbe Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Thu, 3 May 2018 11:36:17 -0700 Subject: [PATCH] Implement 'openstack tripleo upgrade' Adding a new command: $ openstack tripleo upgrade This new command will run underneath: $ openstack tripleo deploy --upgrade The only difference when running this command is that we'll execute upgrade tasks generated by config-download before the deployment tasks. It'll be used by the containerized undercloud to upgrade by re-using the upgrade tasks in THT, that were designed for the overcloud. We'll try to re-use them so we avoid code duplication. Change-Id: Ic932b3b308c83bc72fff4c0ef8ce94e878f33997 --- setup.cfg | 1 + .../tests/v1/tripleo/test_tripleo_deploy.py | 6 +- .../tests/v1/tripleo/test_tripleo_upgrade.py | 63 +++++++++++++++++++ tripleoclient/v1/tripleo_deploy.py | 25 ++++++-- tripleoclient/v1/tripleo_upgrade.py | 32 ++++++++++ 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 tripleoclient/tests/v1/tripleo/test_tripleo_upgrade.py create mode 100644 tripleoclient/v1/tripleo_upgrade.py diff --git a/setup.cfg b/setup.cfg index 023b5b843..5d5652e16 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,6 +48,7 @@ openstack.cli.extension = openstack.tripleoclient.v1 = tripleo_deploy = tripleoclient.v1.tripleo_deploy:Deploy + tripleo_upgrade = tripleoclient.v1.tripleo_deploy:Upgrade overcloud_netenv_validate = tripleoclient.v1.overcloud_netenv_validate:ValidateOvercloudNetenv overcloud_config_download = tripleoclient.v1.overcloud_config:DownloadConfig overcloud_container_image_upload = tripleoclient.v1.container_image:UploadImage diff --git a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py index ea01cd620..970a080d4 100644 --- a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py +++ b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py @@ -332,9 +332,9 @@ class TestDeployUndercloud(TestPluginV1): 'run_command_and_log', autospec=True) @mock.patch('os.chdir') @mock.patch('os.execvp') - def test_launch_ansible(self, mock_execvp, mock_chdir, mock_run): + def test_launch_ansible_deploy(self, mock_execvp, mock_chdir, mock_run): - self.cmd._launch_ansible('/tmp') + self.cmd._launch_ansible_deploy('/tmp') mock_chdir.assert_called_once() mock_run.assert_called_once_with(self.cmd.log, [ 'ansible-playbook', '-i', '/tmp/inventory.yaml', @@ -368,7 +368,7 @@ class TestDeployUndercloud(TestPluginV1): @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' '_create_install_artifact', return_value='/tmp/foo.tar.bzip2') @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' - '_launch_ansible') + '_launch_ansible_deploy') @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' '_cleanup_working_dirs') @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' diff --git a/tripleoclient/tests/v1/tripleo/test_tripleo_upgrade.py b/tripleoclient/tests/v1/tripleo/test_tripleo_upgrade.py new file mode 100644 index 000000000..b17a434c9 --- /dev/null +++ b/tripleoclient/tests/v1/tripleo/test_tripleo_upgrade.py @@ -0,0 +1,63 @@ +# 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 + +# Load the plugin init module for the plugin list and show commands +from tripleoclient.v1 import tripleo_upgrade + + +class TestUpgrade(utils.TestCommand): + + def setUp(self): + super(TestUpgrade, self).setUp() + + # Get the command object to test + self.cmd = tripleo_upgrade.Upgrade(self.app, None) + + @mock.patch('tripleoclient.utils.' + 'run_command_and_log', autospec=True) + @mock.patch('os.chdir') + @mock.patch('os.execvp') + def test_launch_ansible_upgrade(self, mock_execvp, mock_chdir, mock_run): + + self.cmd._launch_ansible_upgrade('/tmp') + mock_chdir.assert_called_once() + mock_run.assert_called_once_with(self.cmd.log, [ + 'ansible-playbook', '-i', '/tmp/inventory.yaml', + 'upgrade_steps_playbook.yaml', '-e', 'role_name=Undercloud', + '-e', 'deploy_server_id=undercloud', '-e', + 'bootstrap_server_id=undercloud', '--skip-tags', 'validation']) + + @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.take_action', + autospec=True) + def test_take_action(self, mock_deploy): + parsed_args = self.check_parser(self.cmd, + ['--local-ip', '127.0.0.1', + '--templates', '/tmp/thtroot', + '--stack', 'undercloud', + '--output-dir', '/my', + '-e', '/tmp/thtroot/puppet/foo.yaml', + '-e', '/tmp/thtroot//docker/bar.yaml', + '-e', '/tmp/thtroot42/notouch.yaml', + '-e', '~/custom.yaml', + '-e', 'something.yaml', + '-e', '../../../outside.yaml'], []) + self.cmd.take_action(parsed_args) + parsed_args.standlone = True + parsed_args.upgrade = True + mock_deploy.assert_called_with(self.cmd, parsed_args) diff --git a/tripleoclient/v1/tripleo_deploy.py b/tripleoclient/v1/tripleo_deploy.py index 22061f75e..8137a1747 100644 --- a/tripleoclient/v1/tripleo_deploy.py +++ b/tripleoclient/v1/tripleo_deploy.py @@ -517,15 +517,26 @@ class Deploy(command.Command): return self.tmp_ansible_dir # Never returns, calls exec() - def _launch_ansible(self, ansible_dir): - self.log.warning('** Running ansible.. **') + def _launch_ansible_deploy(self, ansible_dir): + self.log.warning('** Running ansible deploy tasks **') os.chdir(ansible_dir) playbook_inventory = os.path.join(ansible_dir, 'inventory.yaml') cmd = ['ansible-playbook', '-i', playbook_inventory, 'deploy_steps_playbook.yaml', '-e', 'role_name=Undercloud', '-e', 'deploy_server_id=undercloud', '-e', 'bootstrap_server_id=undercloud'] - self.log.debug('Running Ansible: %s' % (' '.join(cmd))) + self.log.debug('Running Ansible Deploy tasks: %s' % (' '.join(cmd))) + return utils.run_command_and_log(self.log, cmd) + + def _launch_ansible_upgrade(self, ansible_dir): + self.log.warning('** Running ansible upgrade tasks **') + os.chdir(ansible_dir) + playbook_inventory = os.path.join(ansible_dir, 'inventory.yaml') + cmd = ['ansible-playbook', '-i', playbook_inventory, + 'upgrade_steps_playbook.yaml', '-e', 'role_name=Undercloud', + '-e', 'deploy_server_id=undercloud', '-e', + 'bootstrap_server_id=undercloud', '--skip-tags', 'validation'] + self.log.debug('Running Ansible Upgrade tasks: %s' % (' '.join(cmd))) return utils.run_command_and_log(self.log, cmd) def get_parser(self, prog_name): @@ -541,6 +552,8 @@ class Deploy(command.Command): parser.add_argument('--standalone', default=False, action='store_true', help=_("Run deployment as a standalone deployment " "with no undercloud.")) + parser.add_argument('--upgrade', default=False, action='store_true', + help=_("Upgrade an existing deployment.")) parser.add_argument('--stack', help=_("Stack name to create"), default='undercloud') @@ -735,8 +748,10 @@ class Deploy(command.Command): # Kill heat, we're done with it now. self._kill_heat(parsed_args) if not parsed_args.output_only: - # Never returns.. We exec() it directly. - rc = self._launch_ansible(ansible_dir) + # Run Upgrade tasks before the deployment + if parsed_args.upgrade: + rc = self._launch_ansible_upgrade(ansible_dir) + rc = self._launch_ansible_deploy(ansible_dir) except Exception as e: self.log.error("Exception: %s" % e) self.log.error(traceback.format_exception(*sys.exc_info())) diff --git a/tripleoclient/v1/tripleo_upgrade.py b/tripleoclient/v1/tripleo_upgrade.py new file mode 100644 index 000000000..0549e95ea --- /dev/null +++ b/tripleoclient/v1/tripleo_upgrade.py @@ -0,0 +1,32 @@ +# 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. +# +from __future__ import print_function + +import logging + +from tripleoclient.v1.tripleo_deploy import Deploy + + +class Upgrade(Deploy): + """Upgrade TripleO""" + + log = logging.getLogger(__name__ + ".Upgrade") + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + parsed_args.standalone = True + parsed_args.upgrade = True + super(Upgrade, self).take_action(parsed_args)