diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index 01205ca0288..9f2236fb9f5 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -65,6 +65,7 @@ the developer guide includes information about Neutron testing infrastructure. neutron_api client_command_extensions alembic_migrations + upgrade_checks testing/index Neutron Internals diff --git a/doc/source/contributor/upgrade_checks.rst b/doc/source/contributor/upgrade_checks.rst new file mode 100644 index 00000000000..61038a11c5c --- /dev/null +++ b/doc/source/contributor/upgrade_checks.rst @@ -0,0 +1,59 @@ +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +.. _upgrade_checks: + +Upgrade checks +============== + +Introduction +------------ + +CLI tool ``neutron-status upgrade check`` contains checks which perform a +release-specific readiness check before restarting services with new code. +For more details see `neutron-status command-line client +`_ page. + +3rd party plugins checks +------------------------ + +Neutron upgrade checks script allows to add checks by stadium and 3rd party +projects. +The ``neutron-status`` script detects which sub-projects have been installed by +enumerating the ``neutron.status.upgrade.checks`` entrypoints. For more details +see the `Entry Points section of Contributing extensions to Neutron +`_. +Checks can be run in random order and should be independent from each other. + +The recommended entry point name is a repository name: For example, +'neutron-fwaas' for FWaaS and 'networking-sfc' for SFC: + +.. code-block:: ini + + neutron.status.upgrade.checks = + neutron-fwaas = neutron_fwaas.upgrade.checks:Checks + +Entrypoint should be class which inherits from +``neutron.cmd.upgrade_checks.base.BaseChecks``. + +An example of a checks class can be found in +``neutron.cmd.upgrade_checks.checks.CoreChecks``. diff --git a/neutron/cmd/status.py b/neutron/cmd/status.py index 52356fca9b6..dd4e1e0b85d 100644 --- a/neutron/cmd/status.py +++ b/neutron/cmd/status.py @@ -12,13 +12,35 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron_lib.utils import runtime from oslo_config import cfg +from oslo_log import log as logging from oslo_upgradecheck import upgradecheck -from neutron._i18n import _ +CHECKS_ENTRYPOINTS = 'neutron.status.upgrade.checks' +LOG = logging.getLogger(__name__) -class Checks(upgradecheck.UpgradeCommands): +def load_checks(): + checks = [] + ns_plugin = runtime.NamespacedPlugins(CHECKS_ENTRYPOINTS) + # TODO(slaweq): stop using private attribute of runtime.NamespacedPlugins + # class when it will provide some better way to access extensions + for module_name, module in ns_plugin._extensions.items(): + try: + project_checks_class = module.entry_point.load() + project_checks = project_checks_class().get_checks() + if project_checks: + checks.append(project_checks) + except Exception as e: + LOG.exception("Checks class %(entrypoint)s failed to load. " + "Error: %(err)s", + {'entrypoint': module_name, 'err': e}) + continue + return tuple(checks) + + +class Checker(upgradecheck.UpgradeCommands): """Various upgrade checks should be added as separate methods in this class and added to _upgrade_checks tuple. @@ -29,11 +51,6 @@ class Checks(upgradecheck.UpgradeCommands): like the database schema migrations. """ - def _check_nothing(self): - # NOTE(slaweq) This is only example Noop check, it can be removed when - # some real check methods will be added - return upgradecheck.Result(upgradecheck.Code.SUCCESS) - # The format of the check functions is to return an # oslo_upgradecheck.upgradecheck.Result # object with the appropriate @@ -41,13 +58,9 @@ class Checks(upgradecheck.UpgradeCommands): # If the check hits warnings or failures then those should be stored # in the returned Result's "details" attribute. The # summary will be rolled up at the end of the check() method. - _upgrade_checks = ( - # Check nothing - # In the future there should be some real checks added here - (_('Check nothing'), _check_nothing), - ) + _upgrade_checks = load_checks() def main(): return upgradecheck.main( - cfg.CONF, project='neutron', upgrade_command=Checks()) + cfg.CONF, project='neutron', upgrade_command=Checker()) diff --git a/neutron/cmd/upgrade_checks/__init__.py b/neutron/cmd/upgrade_checks/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/cmd/upgrade_checks/base.py b/neutron/cmd/upgrade_checks/base.py new file mode 100644 index 00000000000..4263044de86 --- /dev/null +++ b/neutron/cmd/upgrade_checks/base.py @@ -0,0 +1,36 @@ +# 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class BaseChecks(object): + + """Base class providing upgrade checks. + + Stadium projects which want to provide their own upgrade checks to + neutron-status CLI tool should inherit from this class. + + Each check method have to accept neutron.cmd.status.Checker + class as an argument because all checkes will be run in context of + this class. + """ + + @abc.abstractmethod + def get_checks(self): + """Get tuple with check methods and check names to run.""" + pass diff --git a/neutron/cmd/upgrade_checks/checks.py b/neutron/cmd/upgrade_checks/checks.py new file mode 100644 index 00000000000..6c7a9a403c0 --- /dev/null +++ b/neutron/cmd/upgrade_checks/checks.py @@ -0,0 +1,33 @@ +# 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 oslo_upgradecheck import upgradecheck + +from neutron._i18n import _ +from neutron.cmd.upgrade_checks import base + + +class CoreChecks(base.BaseChecks): + + def get_checks(self): + return ( + (_("Check nothing"), self.noop_check) + ) + + @staticmethod + def noop_check(checker): + # NOTE(slaweq) This is only example Noop check, it can be removed when + # some real check methods will be added + return upgradecheck.Result( + upgradecheck.Code.SUCCESS, _("Always succeed (placeholder)")) diff --git a/neutron/tests/unit/cmd/test_status.py b/neutron/tests/unit/cmd/test_status.py index da488109f9f..5d80549ac28 100644 --- a/neutron/tests/unit/cmd/test_status.py +++ b/neutron/tests/unit/cmd/test_status.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_upgradecheck.upgradecheck import Code +import mock from neutron.cmd import status from neutron.tests import base @@ -20,10 +20,19 @@ from neutron.tests import base class TestUpgradeChecks(base.BaseTestCase): - def setUp(self): - super(TestUpgradeChecks, self).setUp() - self.cmd = status.Checks() - - def test__check_nothing(self): - check_result = self.cmd._check_nothing() - self.assertEqual(Code.SUCCESS, check_result.code) + def test_load_checks(self): + expected_checks = tuple( + ("test check", "test_check_method")) + checks_class_1 = mock.MagicMock() + checks_class_1.entry_point.load()().get_checks.return_value = ( + expected_checks) + checks_class_2 = mock.MagicMock() + checks_class_2.entry_point.load()().get_checks.return_value = None + with mock.patch( + "neutron_lib.utils.runtime.NamespacedPlugins" + ) as namespace_plugins_mock: + namespace_plugins = namespace_plugins_mock.return_value + namespace_plugins._extensions = { + "tests": checks_class_1, + "no-checks-class": checks_class_2} + self.assertEqual((expected_checks,), status.load_checks()) diff --git a/neutron/tests/unit/cmd/upgrade_checks/__init__.py b/neutron/tests/unit/cmd/upgrade_checks/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/unit/cmd/upgrade_checks/test_checks.py b/neutron/tests/unit/cmd/upgrade_checks/test_checks.py new file mode 100644 index 00000000000..c7211bd26c6 --- /dev/null +++ b/neutron/tests/unit/cmd/upgrade_checks/test_checks.py @@ -0,0 +1,33 @@ +# 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 oslo_upgradecheck.upgradecheck import Code + +from neutron.cmd.upgrade_checks import checks +from neutron.tests import base + + +class TestChecks(base.BaseTestCase): + + def setUp(self): + super(TestChecks, self).setUp() + self.checks = checks.CoreChecks() + + def test_get_checks_list(self): + self.assertIsInstance(self.checks.get_checks(), tuple) + + def test_noop_check(self): + check_result = checks.CoreChecks.noop_check(mock.Mock()) + self.assertEqual(Code.SUCCESS, check_result.code) diff --git a/releasenotes/notes/Neutron-status-upgrade-check-framework-fc34d03c8829672c.yaml b/releasenotes/notes/Neutron-status-upgrade-check-framework-fc34d03c8829672c.yaml index b10c3853692..639692bbb5c 100644 --- a/releasenotes/notes/Neutron-status-upgrade-check-framework-fc34d03c8829672c.yaml +++ b/releasenotes/notes/Neutron-status-upgrade-check-framework-fc34d03c8829672c.yaml @@ -6,6 +6,9 @@ features: New framework for ``neutron-status upgrade check`` command is added. This framework allows adding various checks which can be run before a Neutron upgrade to ensure if the upgrade can be performed safely. + Stadium and 3rd party projects can register their own checks to this new + neutron-status CLI tool using entrypoints in + ``neutron.status.upgrade.checks`` namespace. upgrade: - | Operator can now use new CLI tool ``neutron-status upgrade check`` diff --git a/setup.cfg b/setup.cfg index 24a7a31443c..7f3da2a1e3d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -239,7 +239,8 @@ neutron.objects = SubnetServiceType = neutron.objects.subnet:SubnetServiceType Tag = neutron.objects.tag:Tag Trunk = neutron.objects.trunk:Trunk - +neutron.status.upgrade.checks = + neutron = neutron.cmd.upgrade_checks.checks:CoreChecks [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext