Upgrade check command - add support for 3rd party checks

CLI command "neutron-status upgrade check" now can load
checks from 3rd party and stadium projects using entry_points.

Such additional checks should be available under
"neutron.status.upgrade.checks" namespace in entry_points.

Change-Id: I416abbcdd7b397ec6acd2827129d7a56095ea63d
Story: 2003657
This commit is contained in:
Slawek Kaplonski 2018-11-02 15:16:46 +01:00
parent 5ad2bfdbfe
commit f5b01e0e12
11 changed files with 210 additions and 22 deletions

View File

@ -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

View File

@ -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
</cli/neutron-status.html>`_ 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
<contribute.html#entry-points>`_.
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``.

View File

@ -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())

View File

View File

@ -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

View File

@ -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)"))

View File

@ -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())

View File

@ -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)

View File

@ -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``

View File

@ -231,7 +231,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