From 2509c9b72cdcdbe46c141685a99b03cd934803be Mon Sep 17 00:00:00 2001 From: Roman Prykhodchenko Date: Thu, 5 Mar 2015 14:06:09 +0100 Subject: [PATCH] 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 --- fuelclient/actions/__init__.py | 0 fuel => fuelclient/actions/fuel_version.py | 17 +++-- fuelclient/main.py | 76 ++++++++++++++++++++++ fuelclient/tests/cli/__init__.py | 0 fuelclient/tests/cli/test_v2_engine.py | 57 ++++++++++++++++ fuelclient/v1/__init__.py | 0 requirements.txt | 1 + setup.cfg | 3 +- 8 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 fuelclient/actions/__init__.py rename fuel => fuelclient/actions/fuel_version.py (59%) mode change 100755 => 100644 create mode 100644 fuelclient/main.py create mode 100644 fuelclient/tests/cli/__init__.py create mode 100644 fuelclient/tests/cli/test_v2_engine.py create mode 100644 fuelclient/v1/__init__.py diff --git a/fuelclient/actions/__init__.py b/fuelclient/actions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fuel b/fuelclient/actions/fuel_version.py old mode 100755 new mode 100644 similarity index 59% rename from fuel rename to fuelclient/actions/fuel_version.py index 735538ba..bf1da3d3 --- a/fuel +++ b/fuelclient/actions/fuel_version.py @@ -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() \ No newline at end of file +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()) diff --git a/fuelclient/main.py b/fuelclient/main.py new file mode 100644 index 00000000..16f9144c --- /dev/null +++ b/fuelclient/main.py @@ -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) diff --git a/fuelclient/tests/cli/__init__.py b/fuelclient/tests/cli/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fuelclient/tests/cli/test_v2_engine.py b/fuelclient/tests/cli/test_v2_engine.py new file mode 100644 index 00000000..c643c8cd --- /dev/null +++ b/fuelclient/tests/cli/test_v2_engine.py @@ -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) diff --git a/fuelclient/v1/__init__.py b/fuelclient/v1/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/requirements.txt b/requirements.txt index d0970a34..798d1da0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/setup.cfg b/setup.cfg index ad0acefb..8c161cb2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 =