Add designate-status command for upgrade checks

This checks for duplicate entries in the service_statuses table
which will be an error after change
I307a8f7dd8b8a83effa447a846db3288efa32dba.

Related-Bug: 1768824

Story: 2003657
Task: 26127

Change-Id: Ie0350b034f0eb03749138aadd0951d30073214c0
Co-authored-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Ben Nemec 2018-09-19 20:34:26 +00:00
parent 7c0ebd310c
commit 1842802de4
9 changed files with 232 additions and 0 deletions

57
designate/cmd/status.py Normal file
View File

@ -0,0 +1,57 @@
# 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_config import cfg
from oslo_upgradecheck import upgradecheck
from sqlalchemy import MetaData, Table, select, func
from designate.i18n import _
from designate.sqlalchemy import session
# This import is not used, but is needed to register the storage:sqlalchemy
# group.
import designate.storage.impl_sqlalchemy # noqa
from designate import utils
class Checks(upgradecheck.UpgradeCommands):
def _duplicate_service_status(self):
engine = session.get_engine('storage:sqlalchemy')
metadata = MetaData(bind=engine)
status = Table('service_statuses', metadata, autoload=True)
service_select = (select([func.count()])
.select_from(status)
.group_by('service_name', 'hostname')
)
service_counts = engine.execute(service_select).fetchall()
duplicated_services = [i for i in service_counts if i[0] > 1]
if duplicated_services:
return upgradecheck.Result(upgradecheck.Code.FAILURE,
_('Duplicated services found in '
'service_statuses table.'))
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
_upgrade_checks = ((_('Duplicate service status'),
_duplicate_service_status),
)
def main():
config_files = utils.find_config('designate.conf')
checker = Checks()
return upgradecheck.main(
conf=cfg.CONF,
project='designate',
upgrade_command=checker,
default_config_files=config_files,
)

View File

@ -0,0 +1,74 @@
# 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 migrate.changeset.constraint import UniqueConstraint
from oslo_upgradecheck import upgradecheck
from sqlalchemy.schema import MetaData
from sqlalchemy.schema import Table
from designate.cmd import status
from designate import tests
from designate.sqlalchemy import session
class TestDuplicateServiceStatus(tests.TestCase):
def setUp(self):
super(TestDuplicateServiceStatus, self).setUp()
self.engine = session.get_engine('storage:sqlalchemy')
self.meta = MetaData()
self.meta.bind = self.engine
self.service_statuses_table = Table('service_statuses', self.meta,
autoload=True)
def test_success(self):
fake_record = {'id': '1',
'service_name': 'worker',
'hostname': 'localhost',
'status': 'UP',
'stats': '',
'capabilities': '',
}
self.service_statuses_table.insert().execute(fake_record)
# Different hostname should be fine
fake_record['id'] = '2'
fake_record['hostname'] = 'otherhost'
self.service_statuses_table.insert().execute(fake_record)
# Different service_name should be fine
fake_record['id'] = '3'
fake_record['service_name'] = 'producer'
self.service_statuses_table.insert().execute(fake_record)
checks = status.Checks()
self.assertEqual(upgradecheck.Code.SUCCESS,
checks._duplicate_service_status().code)
def test_failure(self):
# Drop unique constraint so we can test error cases
constraint = UniqueConstraint('service_name', 'hostname',
table=self.service_statuses_table,
name="unique_service_status")
constraint.drop()
fake_record = {'id': '1',
'service_name': 'worker',
'hostname': 'localhost',
'status': 'UP',
'stats': '',
'capabilities': '',
}
self.service_statuses_table.insert().execute(fake_record)
fake_record['id'] = '2'
self.service_statuses_table.insert().execute(fake_record)
checks = status.Checks()
self.assertEqual(upgradecheck.Code.FAILURE,
checks._duplicate_service_status().code)

View File

@ -4,6 +4,10 @@ Upgrades
In this section, you will find documentation relevant for upgrading Designate.
.. note:: The :ref:`designate-status upgrade check <designate-status-upgrade-check>`
command can be used to verify a deployment before starting services
with new code.
Contents:
.. toctree::

View File

@ -0,0 +1,86 @@
====================
Designate Status CLI
====================
This chapter documents :command:`designate-status`.
For help on a specific :command:`designate-status` command, enter:
.. code-block:: console
$ designate-status COMMAND --help
designate-status
================
:program:`designate-status` is a tool that provides routines for checking the
status of a Designate deployment.
The standard pattern for executing a :program:`designate-status` command is:
.. code-block:: console
designate-status <category> <command> [<args>]
Run without arguments to see a list of available command categories:
.. code-block:: console
designate-status
Categories are:
* ``upgrade``
Detailed descriptions are below.
You can also run with a category argument such as ``upgrade`` to see a list of
all commands in that category:
.. code-block:: console
designate-status upgrade
The following sections describe the available categories and arguments for
:program:`designate-status`.
designate-status upgrade
========================
.. _designate-status-upgrade-check:
designate-status upgrade check
------------------------------
``designate-status upgrade check``
Performs a release-specific readiness check before running db sync for the
new version. This command expects to have complete configuration and access
to the database.
**Return Codes**
.. list-table::
:widths: 20 80
:header-rows: 1
* - Return code
- Description
* - 0
- All upgrade readiness checks passed successfully and there is nothing
to do.
* - 1
- At least one check encountered an issue and requires further
investigation. This is considered a warning but the upgrade may be OK.
* - 2
- There was an upgrade status check failure that needs to be
investigated. This should be considered something that stops an
upgrade.
* - 255
- An unexpected error occurred.
**History of Checks**
**8.0.0 (Stein)**
* Checks that duplicate entries do not exist in the ``service_statuses``
table.

View File

@ -8,3 +8,4 @@ Interface (CLI) can be found in this section.
:maxdepth: 1
designate-manage
designate-status

View File

@ -89,6 +89,7 @@ oslo.reports==1.18.0
oslo.rootwrap==5.8.0
oslo.serialization==2.18.0
oslo.service==1.24.0
oslo.upgradecheck==0.1.0
oslo.utils==3.33.0
oslo.versionedobjects==1.31.2
oslotest==3.2.0

View File

@ -0,0 +1,7 @@
---
features:
- |
A new ``designate-status upgrade check`` command has been added which can
be used to validate a deployment before starting services with new code.
See the documentation for details:
https://docs.openstack.org/designate/latest/cli/designate-status.html

View File

@ -20,6 +20,7 @@ oslo.reports>=1.18.0 # Apache-2.0
oslo.rootwrap>=5.8.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
oslo.service!=1.28.1,>=1.24.0 # Apache-2.0
oslo.upgradecheck>=0.1.0
oslo.utils>=3.33.0 # Apache-2.0
oslo.versionedobjects>=1.31.2 # Apache-2.0
Paste>=2.0.2 # MIT

View File

@ -63,6 +63,7 @@ console_scripts =
designate-agent = designate.cmd.agent:main
designate-worker = designate.cmd.worker:main
designate-producer = designate.cmd.producer:main
designate-status = designate.cmd.status:main
designate.api.admin.extensions =