diff --git a/setup.cfg b/setup.cfg index db4337353..de2aa63b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -85,6 +85,7 @@ openstack.tripleoclient.v1 = overcloud_upgrade_prepare = tripleoclient.v1.overcloud_upgrade:UpgradePrepare overcloud_upgrade_run = tripleoclient.v1.overcloud_upgrade:UpgradeRun overcloud_upgrade_converge = tripleoclient.v1.overcloud_upgrade:UpgradeConvergeOvercloud + overcloud_ceph-upgrade_run = tripleoclient.v1.overcloud_ceph_upgrade:CephUpgrade overcloud_ffwd-upgrade_prepare = tripleoclient.v1.overcloud_ffwd_upgrade:FFWDUpgradePrepare overcloud_ffwd-upgrade_run = tripleoclient.v1.overcloud_ffwd_upgrade:FFWDUpgradeRun overcloud_ffwd-upgrade_converge = tripleoclient.v1.overcloud_ffwd_upgrade:FFWDUpgradeConverge diff --git a/tripleoclient/constants.py b/tripleoclient/constants.py index fee4a9a7a..22637287b 100644 --- a/tripleoclient/constants.py +++ b/tripleoclient/constants.py @@ -40,6 +40,7 @@ PUPPET_BASE = "/etc/puppet/" UPDATE_QUEUE = 'update' UPGRADE_QUEUE = 'upgrade' FFWD_UPGRADE_QUEUE = 'ffwdupgrade' +CEPH_UPGRADE_QUEUE = 'cephupgrade' STACK_TIMEOUT = 240 @@ -61,3 +62,5 @@ UPGRADE_PREPARE_ENV = "environments/lifecycle/upgrade-prepare.yaml" UPGRADE_CONVERGE_ENV = "environments/lifecycle/upgrade-converge.yaml" FFWD_UPGRADE_PREPARE_ENV = "environments/lifecycle/ffwd-upgrade-prepare.yaml" FFWD_UPGRADE_CONVERGE_ENV = "environments/lifecycle/ffwd-upgrade-converge.yaml" +CEPH_UPGRADE_PREPARE_ENV = "environments/lifecycle/ceph-upgrade-prepare.yaml" +CEPH_UPGRADE_CONVERGE_ENV = "environments/lifecycle/ceph-upgrade-converge.yaml" diff --git a/tripleoclient/tests/v1/overcloud_ceph_upgrade/__init__.py b/tripleoclient/tests/v1/overcloud_ceph_upgrade/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tripleoclient/tests/v1/overcloud_ceph_upgrade/fakes.py b/tripleoclient/tests/v1/overcloud_ceph_upgrade/fakes.py new file mode 100644 index 000000000..d0d86a495 --- /dev/null +++ b/tripleoclient/tests/v1/overcloud_ceph_upgrade/fakes.py @@ -0,0 +1,62 @@ +# 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.tests import fakes + + +class FakeClientWrapper(object): + + def __init__(self): + self._instance = mock.Mock() + self.object_store = FakeObjectClient() + + def messaging_websocket(self): + return fakes.FakeWebSocket() + + +class FakeObjectClient(object): + + def __init__(self): + self._instance = mock.Mock() + self.put_object = mock.Mock() + + def get_object(self, *args): + return + + +class TestCephUpgrade(utils.TestCommand): + + def setUp(self): + super(TestCephUpgrade, self).setUp() + + self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") + self.app.client_manager.baremetal = mock.Mock() + self.app.client_manager.orchestration = mock.Mock() + self.app.client_manager.tripleoclient = FakeClientWrapper() + self.app.client_manager.workflow_engine = mock.Mock() + + +class TestCephUpgradeConverge(utils.TestCommand): + + def setUp(self): + super(TestCephUpgradeConverge, self).setUp() + + self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") + self.app.client_manager.orchestration = mock.Mock() + self.app.client_manager.tripleoclient = FakeClientWrapper() + self.app.client_manager.workflow_engine = mock.Mock() diff --git a/tripleoclient/tests/v1/overcloud_ceph_upgrade/test_overcloud_ceph_upgrade.py b/tripleoclient/tests/v1/overcloud_ceph_upgrade/test_overcloud_ceph_upgrade.py new file mode 100644 index 000000000..0b14dbba2 --- /dev/null +++ b/tripleoclient/tests/v1/overcloud_ceph_upgrade/test_overcloud_ceph_upgrade.py @@ -0,0 +1,117 @@ +# 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.utils import ParserException +from tripleoclient import constants +from tripleoclient import exceptions +from tripleoclient.tests.v1.overcloud_ceph_upgrade import fakes +from tripleoclient.v1 import overcloud_ceph_upgrade + + +class TestCephUpgrade(fakes.TestCephUpgrade): + + def setUp(self): + super(TestCephUpgrade, self).setUp() + + # Get the command object to test + app_args = mock.Mock() + app_args.verbose_level = 1 + self.cmd = overcloud_ceph_upgrade.CephUpgrade(self.app, app_args) + uuid4_patcher = mock.patch('uuid.uuid4', return_value="UUID4") + self.mock_uuid4 = uuid4_patcher.start() + self.addCleanup(self.mock_uuid4.stop) + + @mock.patch('tripleoclient.workflows.package_update.ffwd_converge_nodes', + autospec=True) + @mock.patch('tripleoclient.utils.prepend_environment', autospec=True) + @mock.patch('tripleoclient.utils.get_stack', + autospec=True) + @mock.patch( + 'tripleoclient.v1.overcloud_ceph_upgrade.CephUpgrade.log', + autospec=True) + @mock.patch('tripleoclient.workflows.package_update.update', + autospec=True) + @mock.patch('yaml.load') + @mock.patch('shutil.copytree', autospec=True) + @mock.patch('six.moves.builtins.open') + @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' + '_deploy_tripleo_heat_templates', autospec=True) + def test_ceph_upgrade(self, mock_deploy, mock_open, mock_copy, mock_yaml, + mock_ceph_upgrade, mock_logger, + mock_get_stack, mock_prepend_env, + ffwd_converge_nodes): + mock_stack = mock.Mock() + mock_stack.stack_name = 'mystack' + mock_get_stack.return_value = mock_stack + mock_yaml.return_value = {'fake_container': 'fake_value'} + + argslist = ['--stack', 'mystack', '--templates'] + verifylist = [ + ('stack', 'mystack'), + ('templates', constants.TRIPLEO_HEAT_TEMPLATES) + ] + + parsed_args = self.check_parser(self.cmd, argslist, verifylist) + self.cmd.take_action(parsed_args) + mock_ceph_upgrade.assert_called_once_with( + self.app.client_manager, + container='mystack', + ceph_ansible_playbook='/usr/share/ceph-ansible' + '/infrastructure-playbooks' + '/rolling_update.yml', + ) + with mock.patch('os.path.exists') as mock_exists: + mock_exists.return_value = True + ffwd_converge_nodes.assert_called_once_with( + self.app.client_manager, + container='mystack') + + @mock.patch('tripleoclient.utils.prepend_environment', autospec=True) + @mock.patch('tripleoclient.workflows.package_update.update', + autospec=True) + @mock.patch('six.moves.builtins.open') + @mock.patch('yaml.load') + @mock.patch('shutil.copytree', autospec=True) + @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' + '_deploy_tripleo_heat_templates', autospec=True) + def test_ceph_upgrade_failed( + self, mock_deploy, mock_copy, mock_yaml, mock_open, + mock_ceph_upgrade, mock_prepend_env): + mock_ceph_upgrade.side_effect = exceptions.DeploymentError() + mock_yaml.return_value = {'fake_container': 'fake_value'} + argslist = ['--stack', 'overcloud', '--templates'] + verifylist = [ + ('stack', 'overcloud'), + ('templates', constants.TRIPLEO_HEAT_TEMPLATES) + ] + parsed_args = self.check_parser(self.cmd, argslist, verifylist) + + self.assertRaises(exceptions.DeploymentError, + self.cmd.take_action, parsed_args) + + @mock.patch('tripleoclient.workflows.package_update.update_ansible', + autospec=True) + @mock.patch('os.path.expanduser') + @mock.patch('oslo_concurrency.processutils.execute') + @mock.patch('six.moves.builtins.open') + def test_upgrade_no_nodes_or_roles(self, mock_open, mock_execute, + mock_expanduser, upgrade_ansible): + mock_expanduser.return_value = '/home/fake/' + argslist = ["--nodes", "controller-1", "--roles", "foo"] + verifylist = [] + self.assertRaises(ParserException, lambda: self.check_parser( + self.cmd, argslist, verifylist)) diff --git a/tripleoclient/v1/overcloud_ceph_upgrade.py b/tripleoclient/v1/overcloud_ceph_upgrade.py new file mode 100644 index 000000000..3e5fa05d7 --- /dev/null +++ b/tripleoclient/v1/overcloud_ceph_upgrade.py @@ -0,0 +1,83 @@ +# Copyright 2015 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 logging + +from osc_lib.i18n import _ + +from tripleoclient import constants +from tripleoclient import utils as oooutils +from tripleoclient.v1.overcloud_deploy import DeployOvercloud +from tripleoclient.workflows import package_update + + +class CephUpgrade(DeployOvercloud): + """Run heat stack update for overcloud nodes to run Ceph upgrade.""" + + log = logging.getLogger(__name__ + ".CephUpgrade") + + def get_parser(self, prog_name): + parser = super(CephUpgrade, self).get_parser(prog_name) + parser.add_argument('--ceph-ansible-playbook', + action="store", + default="/usr/share/ceph-ansible" + "/infrastructure-playbooks" + "/rolling_update.yml", + help=_('Path to switch the ceph-ansible playbook ' + 'used for update. ')) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + clients = self.app.client_manager + + stack = oooutils.get_stack(clients.orchestration, + parsed_args.stack) + + stack_name = stack.stack_name + + # Run update + ceph_ansible_playbook = parsed_args.ceph_ansible_playbook + # Run Overcloud deploy (stack update) + # In case of update and upgrade we need to force the + # update_plan_only. The heat stack update is done by the + # package_update mistral action + parsed_args.update_plan_only = True + + # Add the upgrade-prepare.yaml environment to set noops etc + templates_dir = (parsed_args.templates or + constants.TRIPLEO_HEAT_TEMPLATES) + parsed_args.environment_files = oooutils.prepend_environment( + parsed_args.environment_files, templates_dir, + constants.CEPH_UPGRADE_PREPARE_ENV) + + super(CephUpgrade, self).take_action(parsed_args) + package_update.update(clients, container=stack_name, + ceph_ansible_playbook=ceph_ansible_playbook) + package_update.get_config(clients, container=stack_name) + print("Ceph Upgrade on stack {0} complete. Cleaning up".format( + parsed_args.stack)) + + if not parsed_args.environment_files: + parsed_args.environment_files = [] + oooutils.prepend_environment( + parsed_args.environment_files, templates_dir, + constants.CEPH_UPGRADE_CONVERGE_ENV) + + super(CephUpgrade, self).take_action(parsed_args) + # Run converge steps + package_update.ffwd_converge_nodes(clients, container=stack_name) + print("Ceph Upgrade Cleanup on stack {0} complete.".format( + parsed_args.stack))