Add a placement-manage CLI

This change adds a rudimentary command line tool, 'placement-manage'
that can do three things:

* placement-manage --version: report the current version of the software
* placement-manage db sync: sync the db to the 'head' alembic version
* placement-manage db version: report the current version of the db

It is written following the examples set by both nova and ironic for
using oslo config driven command line parsing. It is not as full
featured as the nova version (with decorators for argument handling and
help text etc) because we don't need that yet. There's plenty of room
for improvement, but the intention here is to keep things simple until
we need them to be otherwise.

The provided unit tests cover that the arg parsing behaves as expected
and that the right commands are connected the right args. The actual
functionality of the 'db sync' command (a migration.upgrade) is tested
in runs that use devstack as well as the test_migrations functional
test.

Errors during the migration simply raise exceptions which will output
noisily to the console. At this stage this is desirable and suitable.

A new doc for the cli is added, with basic information for
placement-manage. Note that at this time the deployment documentation
has not been updated. That will be done in a later change.

Future changes may include (if we can't avoid it) tools for initiating
online migrations from the command line.

Needed-By: https://review.openstack.org/600162/
Change-Id: I1ff472106610150a587f5286f26a6bd7c1aa84f4
This commit is contained in:
Chris Dent 2018-09-05 18:28:10 +01:00
parent f63b14c6a4
commit 7b5218d8e3
8 changed files with 265 additions and 6 deletions

25
doc/source/cli/index.rst Normal file
View File

@ -0,0 +1,25 @@
..
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.
Command-line Utilities
======================
In this section you will find information on placement's command line
utilities:
.. toctree::
:maxdepth: 1
placement-manage

View File

@ -0,0 +1,67 @@
..
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.
================
placement-manage
================
Synopsis
========
::
placement-manage <category> <action>
Description
===========
:program:`placement-manage` is used to perform administrative tasks with the
placement service. It is designed for use by operators and deployers.
Options
=======
The standard pattern for executing a ``placement-manage`` command is::
placement-manage [-h] [--config-dir DIR] [--config-file PATH]
<category> <command> [<args>]
Run without arguments to see a list of available command categories::
placement-manage
You can also run with a category argument such as ``db`` to see a list of all
commands in that category::
placement-manage db
Configuration options (for example the ``[placement_database]/connection``
URL) are by default found in a file at ``/etc/placement/placement.conf``. The
``config-dir`` and ``config-file`` arguments may be used to select a different
file.
The following sections describe the available categories and arguments for
placement-manage.
Placement Database
~~~~~~~~~~~~~~~~~~
``placement-manage db version``
Print the current database version.
``placement-manage db sync``
Upgrade the database schema to the most recent version. The local database
connection is determined by ``[placement_database]/connection`` in the
configuration file used by placement-manage. If the ``connection`` option
is not set, the command will fail. The defined database must already exist.

View File

@ -42,12 +42,15 @@ example, available disk may be solid state drives (SSD).
References
~~~~~~~~~~
For an overview of some of the features provided by placement, see
:doc:`Placement Usage <usage/index>`.
For a command line reference, see :doc:`cli/index`.
The following specifications represent the stages of design and development of
resource providers and the Placement service. Implementation details may have
changed or be partially complete at this time.
For an overview of some of the features, see the
:doc:`Placement Usage <usage/index>`.
* `Generic Resource Pools <https://specs.openstack.org/openstack/nova-specs/specs/newton/implemented/generic-resource-pools.html>`_
* `Compute Node Inventory <https://specs.openstack.org/openstack/nova-specs/specs/newton/implemented/compute-node-inventory-newton.html>`_
@ -356,6 +359,7 @@ contribute to the placement project.
.. toctree::
:hidden:
cli/index
configuration/index
contributor/index
contributor/api-ref-guideline

View File

84
placement/cmd/manage.py Normal file
View File

@ -0,0 +1,84 @@
# 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 sys
from oslo_config import cfg
import pbr.version
from placement import conf
from placement.db.sqlalchemy import migration
from placement import db_api
from placement.i18n import _
version_info = pbr.version.VersionInfo('openstack-placement')
class DbCommands(object):
def db_sync(self):
# Let exceptions raise for now, they will go to stderr.
migration.upgrade('head')
return 0
def db_version(self):
print(migration.version())
return 0
def add_db_command_parsers(subparsers):
command_object = DbCommands()
# If we set False here, we avoid having an exit during the parse
# args part of CONF processing and we can thus print out meaningful
# help text.
subparsers.required = False
parser = subparsers.add_parser('db')
parser.set_defaults(func=parser.print_help)
db_parser = parser.add_subparsers(description='database commands')
help = _('Sync the datatabse to the current version.')
sync_parser = db_parser.add_parser('sync', help=help, description=help)
sync_parser.set_defaults(func=command_object.db_sync)
help = _('Report the current database version.')
version_parser = db_parser.add_parser(
'version', help=help, description=help)
version_parser.set_defaults(func=command_object.db_version)
def setup_commands():
# This is a separate method because it facilitates unit testing.
# Use an additional SubCommandOpt and parser for each new sub command.
command_opt = cfg.SubCommandOpt(
'db', dest='command', title='Command', help=_('Available DB commands'),
handler=add_db_command_parsers)
return [command_opt]
def main():
CONF = conf.CONF
command_opts = setup_commands()
CONF.register_cli_opts(command_opts)
CONF(sys.argv[1:], project='placement',
version=version_info.version_string(),
default_config_files=None)
db_api.configure(CONF)
try:
func = CONF.command.func
return_code = func()
# If return_code ends up None we assume 0.
sys.exit(return_code or 0)
except cfg.NoSuchOptError:
CONF.print_help()
sys.exit(1)

View File

View File

@ -0,0 +1,81 @@
# 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_config import cfg
from oslo_config import fixture as config_fixture
from oslotest import output
import six
import testtools
from placement.cmd import manage
class TestCommandParsers(testtools.TestCase):
def setUp(self):
super(TestCommandParsers, self).setUp()
self.conf = cfg.CONF
conf_fixture = config_fixture.Config(self.conf)
self.useFixture(conf_fixture)
# Quiet output from argparse (used within oslo_config).
# If you are debugging, commenting this out might be useful.
self.useFixture(output.CaptureOutput(do_stderr=True))
# We don't use a database, but we need to set the opt as
# it's required for a valid config.
conf_fixture.config(group="placement_database", connection='sqlite://')
command_opts = manage.setup_commands()
# Command line opts must be registered on the conf_fixture, otherwise
# they carry over globally.
conf_fixture.register_cli_opts(command_opts)
def test_commands_associated(self):
"""Test that commands get parsed as desired.
This leaves out --version, which is built into oslo.config's handling.
"""
for command, args in [
('db_version', ['db', 'version']),
('db_sync', ['db', 'sync']),
]:
with mock.patch('placement.cmd.manage.DbCommands.'
+ command) as mock_command:
self.conf(args, default_config_files=[])
self.conf.command.func()
mock_command.assert_called_once_with()
def test_non_command(self):
"""A non-existent command should fail."""
self.assertRaises(SystemExit,
self.conf, ['pony'], default_config_files=[])
def test_empty_command(self):
"""An empty command should create no func."""
# Python 2.7 and 3.x behave differently here, but the result is
# satisfactory. Both result in some help output, but the Python 3
# help is better.
def parse_conf():
self.conf([], default_config_files=[])
def get_func():
return self.conf.command.func
if six.PY2:
self.assertRaises(SystemExit, parse_conf)
else:
parse_conf()
self.assertRaises(cfg.NoSuchOptError, get_func)
def test_too_many_args(self):
self.assertRaises(SystemExit,
self.conf, ['version', '5'], default_config_files=[])

View File

@ -41,10 +41,8 @@ oslo.policy.policies =
placement = placement.policies:list_rules
console_scripts =
# TODO(cdent): We will need some kind of placement-manage and
# placement-status, eventually
# nova-manage = nova.cmd.manage:main
# nova-status = nova.cmd.status:main
# TODO(cdent): We will need some kind of placement-status, eventually
placement-manage = placement.cmd.manage:main
wsgi_scripts =
placement-api = placement.wsgi:init_application