Initial implementation

This commit is contained in:
Ben Nemec 2018-09-12 18:20:50 +00:00
parent d671294399
commit 302c40a857
3 changed files with 188 additions and 0 deletions

View File

@ -0,0 +1,38 @@
# 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.
"""Example CLI command for running upgrade checks"""
from oslo_upgradecheck import upgradecheck
class Checks(upgradecheck.UpgradeCommands):
def success(self):
return upgradecheck.UpgradeCheckResult(upgradecheck.UpgradeCheckCode.SUCCESS,
'Always succeeds')
def failure(self):
return upgradecheck.UpgradeCheckResult(upgradecheck.UpgradeCheckCode.FAILURE,
'Always fails')
_upgrade_checks = (('always succeeds', success),
('always fails', failure),
)
def main():
inst = Checks()
upgradecheck.main(inst.check)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,147 @@
# Copyright 2018 Red Hat Inc.
# Copyright 2016 IBM Corp.
#
# 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 functools
import sys
import textwrap
import enum
from oslo_config import cfg
import prettytable
def _(s):
return s
class UpgradeCheckCode(enum.IntEnum):
"""These are the status codes for the nova-status upgrade check command
and internal check commands.
"""
# All upgrade readiness checks passed successfully and there is
# nothing to do.
SUCCESS = 0
# At least one check encountered an issue and requires further
# investigation. This is considered a warning but the upgrade may be OK.
WARNING = 1
# There was an upgrade status check failure that needs to be
# investigated. This should be considered something that stops an upgrade.
FAILURE = 2
UPGRADE_CHECK_MSG_MAP = {
UpgradeCheckCode.SUCCESS: _('Success'),
UpgradeCheckCode.WARNING: _('Warning'),
UpgradeCheckCode.FAILURE: _('Failure'),
}
class UpgradeCheckResult(object):
"""Class used for 'nova-status upgrade check' results.
The 'code' attribute is an UpgradeCheckCode enum.
The 'details' attribute is a translated message generally only used for
checks that result in a warning or failure code. The details should provide
information on what issue was discovered along with any remediation.
"""
def __init__(self, code, details=None):
super(UpgradeCheckResult, self).__init__()
self.code = code
self.details = details
class UpgradeCommands(object):
_upgrade_checks = ()
def _get_details(self, upgrade_check_result):
if upgrade_check_result.details is not None:
# wrap the text on the details to 60 characters
return '\n'.join(textwrap.wrap(upgrade_check_result.details, 60,
subsequent_indent=' '))
def check(self):
"""Performs checks to see if the deployment is ready for upgrade.
These checks are expected to be run BEFORE services are restarted with
new code. These checks also require access to potentially all of the
Nova databases (nova, nova_api, nova_api_cell0) and external services
such as the placement API service.
:returns: UpgradeCheckCode
"""
return_code = UpgradeCheckCode.SUCCESS
# This is a list if 2-item tuples for the check name and it's results.
check_results = []
for name, func in self._upgrade_checks:
result = func(self)
# store the result of the check for the summary table
check_results.append((name, result))
# we want to end up with the highest level code of all checks
if result.code > return_code:
return_code = result.code
# TODO(bnemec): Consider using cliff for this so we can output in
# different formats like JSON or CSV.
# We're going to build a summary table that looks like:
# +----------------------------------------------------+
# | Upgrade Check Results |
# +----------------------------------------------------+
# | Check: Cells v2 |
# | Result: Success |
# | Details: None |
# +----------------------------------------------------+
# | Check: Placement API |
# | Result: Failure |
# | Details: There is no placement-api endpoint in the |
# | service catalog. |
# +----------------------------------------------------+
t = prettytable.PrettyTable([_('Upgrade Check Results')],
hrules=prettytable.ALL)
t.align = 'l'
for name, result in check_results:
cell = (
_('Check: %(name)s\n'
'Result: %(result)s\n'
'Details: %(details)s') %
{
'name': name,
'result': UPGRADE_CHECK_MSG_MAP[result.code],
'details': self._get_details(result),
}
)
t.add_row([cell])
print(t)
return return_code
def _add_parsers(subparsers, check_callback):
upgrade_action = subparsers.add_parser('upgrade')
upgrade_action.add_argument('check')
upgrade_action.set_defaults(action_fn=check_callback)
def main(check_callback):
add_parsers = functools.partial(_add_parsers,
check_callback=check_callback)
opt = cfg.SubCommandOpt('category', handler=add_parsers)
conf = cfg.ConfigOpts()
conf.register_cli_opt(opt)
conf(sys.argv[1:])
conf.category.action_fn()

View File

@ -3,3 +3,6 @@
# process, which may cause wedges in the gate later.
Babel>=1.3
oslo.config
enum34
prettytable