Add base Cliff application

This patch a mechanism for launching a Cliff
application which loads its commands as python
entry-points.

Blueprint: re-thinking-fuel-client
Change-Id: I77fc4b0f18f4c5781cd1bb4efa1a2841b210cb56
This commit is contained in:
Roman Prykhodchenko
2015-03-05 14:06:09 +01:00
parent cc64fff91f
commit 2509c9b72c
8 changed files with 148 additions and 6 deletions

View File

17
fuel → fuelclient/actions/fuel_version.py Executable file → Normal file
View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
# Copyright 2013-2014 Mirantis, Inc.
# Copyright 2015 Mirantis, 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
@@ -13,7 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from fuelclient.cli.parser import main
import argparse
if __name__ == "__main__":
main()
from fuelclient import client
class FuelVersionAction(argparse._VersionAction):
"""Custom argparse._VersionAction subclass to compute fuel server version
:returns: prints fuel server version
"""
def __call__(self, parser, namespace, values, option_string=None):
parser.exit(message=client.APIClient.get_fuel_version())

76
fuelclient/main.py Normal file
View File

@@ -0,0 +1,76 @@
# Copyright 2015 Mirantis, 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 logging
import sys
from cliff import app
from cliff.commandmanager import CommandManager
from fuelclient.actions import fuel_version
from fuelclient.cli.error import exceptions_decorator
LOG = logging.getLogger(__name__)
class FuelClient(app.App):
"""Main cliff application class.
Performs initialization of the command manager and
configuration of basic engines.
"""
def build_option_parser(self, description, version, argparse_kwargs=None):
"""Overrides default options for backwards compatibility."""
p_inst = super(FuelClient, self)
parser = p_inst.build_option_parser(description=description,
version=version,
argparse_kwargs=argparse_kwargs)
parser.add_argument(
'--fuel-version',
action=fuel_version.FuelVersionAction,
help="show Fuel server's version number and exit"
)
return parser
def configure_logging(self):
super(FuelClient, self).configure_logging()
# there is issue with management url processing by keystone client
# code in our workflow, so we have to mute appropriate keystone
# loggers in order to get rid from unprocessable errors
logging.getLogger('keystoneclient.httpclient').setLevel(logging.ERROR)
# increase level of loggin for urllib3 to avoid of displaying
# of useless messages. List of logger names is needed for
# consistency on different execution environments that could have
# installed requests packages (which is used urllib3) of different
# versions in turn
for logger_name in ('requests.packages.urllib3.connectionpool',
'urllib3.connectionpool'):
logging.getLogger(logger_name).setLevel(logging.WARNING)
@exceptions_decorator
def main(argv=sys.argv[1:]):
fuelclient_app = FuelClient(
description='Command line interface and Python API wrapper for Fuel.',
version='6.0.0',
command_manager=CommandManager('fuelclient', convert_underscores=True)
)
return fuelclient_app.run(argv)

View File

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
#
# Copyright 2015 Mirantis, 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 sys
import mock
from fuelclient import main as main_mod
from fuelclient.tests import base
class BaseCLITest(base.UnitTestCase):
def exec_v2_command(self, *args, **kwargs):
"""Executes fuelclient with the specified arguments."""
return main_mod.main(argv=args)
def exec_v2_command_interactive(self, commands):
"""Executes specified commands in one sesstion of interactive mode
Starts Fuel Client's interactive console and passes
supplied commands there.
:param commands: The list of commands to execute in the
Fuel Client's console.
:type commands: list of str
"""
with mock.patch.object(sys, 'stdin') as m_stdin:
m_stdin.readline.side_effect = commands
self.exec_v2_command()
@mock.patch('cliff.commandmanager.CommandManager.find_command')
def test_command_non_interactive(self, m_find_command):
args = ['help']
self.exec_v2_command(*args)
m_find_command.assert_called_once_with(args)
@mock.patch('cliff.commandmanager.CommandManager.find_command')
def test_command_interactive(self, m_find_command):
commands = ['quit']
self.exec_v2_command_interactive(commands)
m_find_command.assert_called_once_with(commands)

View File

View File

@@ -1,4 +1,5 @@
argparse==1.2.1
cliff>=1.9.0
pbr>=0.6,!=0.7,<1.0
python-keystoneclient>=0.7.1,<=0.7.2
PyYAML==3.10

View File

@@ -24,7 +24,8 @@ packages =
[entry_points]
console_scripts =
fuel = fuelclient.cli.parser:main
fuel=fuelclient.cli.parser:main
fuel2=fuelclient.main:main
[global]
setup-hooks =