diff --git a/senlinclient/cliargs.py b/senlinclient/cliargs.py deleted file mode 100644 index 694d017..0000000 --- a/senlinclient/cliargs.py +++ /dev/null @@ -1,210 +0,0 @@ -# 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 argparse - -from senlinclient.common.i18n import _ -from senlinclient.common import sdk -from senlinclient.common import utils - - -def add_global_identity_args(parser): - parser.add_argument( - '--os-auth-plugin', dest='auth_plugin', metavar='AUTH_PLUGIN', - default=utils.env('OS_AUTH_PLUGIN', default=None), - help=_('Authentication plugin, default to env[OS_AUTH_PLUGIN]')) - - parser.add_argument( - '--os-auth-url', dest='auth_url', metavar='AUTH_URL', - default=utils.env('OS_AUTH_URL'), - help=_('Defaults to env[OS_AUTH_URL]')) - - parser.add_argument( - '--os-project-id', dest='project_id', metavar='PROJECT_ID', - default=utils.env('OS_PROJECT_ID'), - help=_('Defaults to env[OS_PROJECT_ID].')) - - parser.add_argument( - '--os-project-name', dest='project_name', metavar='PROJECT_NAME', - default=utils.env('OS_PROJECT_NAME'), - help=_('Defaults to env[OS_PROJECT_NAME].')) - - parser.add_argument( - '--os-tenant-id', dest='tenant_id', metavar='TENANT_ID', - default=utils.env('OS_TENANT_ID'), - help=_('Defaults to env[OS_TENANT_ID].')) - - parser.add_argument( - '--os-tenant-name', dest='tenant_name', metavar='TENANT_NAME', - default=utils.env('OS_TENANT_NAME'), - help=_('Defaults to env[OS_TENANT_NAME].')) - - parser.add_argument( - '--os-domain-id', dest='domain_id', metavar='DOMAIN_ID', - default=utils.env('OS_DOMAIN_ID'), - help=_('Domain ID for scope of authorization, defaults to ' - 'env[OS_DOMAIN_ID].')) - - parser.add_argument( - '--os-domain-name', dest='domain_name', metavar='DOMAIN_NAME', - default=utils.env('OS_DOMAIN_NAME'), - help=_('Domain name for scope of authorization, defaults to ' - 'env[OS_DOMAIN_NAME].')) - - parser.add_argument( - '--os-project-domain-id', dest='project_domain_id', - metavar='PROJECT_DOMAIN_ID', - default=utils.env('OS_PROJECT_DOMAIN_ID'), - help=_('Project domain ID for scope of authorization, defaults to ' - 'env[OS_PROJECT_DOMAIN_ID].')) - - parser.add_argument( - '--os-project-domain-name', dest='project_domain_name', - metavar='PROJECT_DOMAIN_NAME', - default=utils.env('OS_PROJECT_DOMAIN_NAME'), - help=_('Project domain name for scope of authorization, defaults to ' - 'env[OS_PROJECT_DOMAIN_NAME].')) - - parser.add_argument( - '--os-user-domain-id', dest='user_domain_id', - metavar='USER_DOMAIN_ID', - default=utils.env('OS_USER_DOMAIN_ID'), - help=_('User domain ID for scope of authorization, defaults to ' - 'env[OS_USER_DOMAIN_ID].')) - - parser.add_argument( - '--os-user-domain-name', dest='user_domain_name', - metavar='USER_DOMAIN_NAME', - default=utils.env('OS_USER_DOMAIN_NAME'), - help=_('User domain name for scope of authorization, defaults to ' - 'env[OS_USER_DOMAIN_NAME].')) - - parser.add_argument( - '--os-username', dest='username', metavar='USERNAME', - default=utils.env('OS_USERNAME'), - help=_('Defaults to env[OS_USERNAME].')) - - parser.add_argument( - '--os-user-id', dest='user_id', metavar='USER_ID', - default=utils.env('OS_USER_ID'), - help=_('Defaults to env[OS_USER_ID].')) - - parser.add_argument( - '--os-password', dest='password', metavar='PASSWORD', - default=utils.env('OS_PASSWORD'), - help=_('Defaults to env[OS_PASSWORD]')) - - parser.add_argument( - '--os-trust-id', dest='trust_id', metavar='TRUST_ID', - default=utils.env('OS_TRUST_ID'), - help=_('Defaults to env[OS_TRUST_ID]')) - - verify_group = parser.add_mutually_exclusive_group() - - verify_group.add_argument( - '--os-cacert', dest='verify', metavar='CA_BUNDLE_FILE', - default=utils.env('OS_CACERT', default=True), - help=_('Path of CA TLS certificate(s) used to verify the remote ' - 'server\'s certificate. Without this option senlin looks ' - 'for the default system CA certificates.')) - - verify_group.add_argument( - '--verify', - action='store_true', - help=_('Verify server certificate (default)')) - - verify_group.add_argument( - '--insecure', dest='verify', action='store_false', - help=_('Explicitly allow senlinclient to perform "insecure SSL" ' - '(HTTPS) requests. The server\'s certificate will not be ' - 'verified against any certificate authorities. This ' - 'option should be used with caution.')) - - parser.add_argument( - '--os-token', dest='token', metavar='TOKEN', - default=utils.env('OS_TOKEN', default=None), - help=_('A string token to bootstrap the Keystone database, defaults ' - 'to env[OS_TOKEN]')) - - parser.add_argument( - '--os-access-info', dest='access_info', metavar='ACCESS_INFO', - default=utils.env('OS_ACCESS_INFO'), - help=_('Access info, defaults to env[OS_ACCESS_INFO]')) - - parser.add_argument( - '--os-api-name', dest='user_preferences', - metavar='=', - action=sdk.ProfileAction, - default=sdk.ProfileAction.env('OS_API_NAME'), - help=_('Desired API names, defaults to env[OS_API_NAME]')) - - parser.add_argument( - '--os-api-region', dest='user_preferences', - metavar='=', - action=sdk.ProfileAction, - default=sdk.ProfileAction.env('OS_API_REGION', 'OS_REGION_NAME'), - help=_('Desired API region, defaults to env[OS_API_REGION]')) - - parser.add_argument( - '--os-api-version', dest='user_preferences', - metavar='=', - action=sdk.ProfileAction, - default=sdk.ProfileAction.env('OS_API_VERSION'), - help=_('Desired API versions, defaults to env[OS_API_VERSION]')) - - parser.add_argument( - '--os-api-interface', dest='user_preferences', - metavar='=', - action=sdk.ProfileAction, - default=sdk.ProfileAction.env('OS_INTERFACE'), - help=_('Desired API interface, defaults to env[OS_INTERFACE]')) - - -# parser.add_argument( -# '--os-cert', -# help=_('Path of certificate file to use in SSL connection. This ' -# 'file can optionally be prepended with the private key.')) -# -# parser.add_argument( -# '--os-key', -# help=_('Path of client key to use in SSL connection. This option is ' -# 'not necessary if your key is prepended to your cert file.')) - - -def add_global_args(parser, version): - # GLOBAL ARGUMENTS - parser.add_argument( - '-h', '--help', action='store_true', - help=argparse.SUPPRESS) - - parser.add_argument( - '--version', action='version', version=version, - help=_("Shows the client version and exits.")) - - parser.add_argument( - '-d', '--debug', action='store_true', - default=bool(utils.env('SENLINCLIENT_DEBUG')), - help=_('Defaults to env[SENLINCLIENT_DEBUG].')) - - parser.add_argument( - '-v', '--verbose', action="store_true", default=False, - help=_("Print more verbose output.")) - - parser.add_argument( - '--api-timeout', - help=_('Number of seconds to wait for an API response, ' - 'defaults to system socket timeout')) - - parser.add_argument( - '--senlin-api-version', - default=utils.env('SENLIN_API_VERSION', default='1'), - help=_('Version number for Senlin API to use, Default to "1".')) diff --git a/senlinclient/shell.py b/senlinclient/shell.py deleted file mode 100644 index 5954982..0000000 --- a/senlinclient/shell.py +++ /dev/null @@ -1,321 +0,0 @@ -# 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 interface to the Senlin clustering API. -""" - -from __future__ import print_function - -import argparse -import logging -import sys - -from oslo_utils import encodeutils -from oslo_utils import importutils -import six - -import senlinclient -from senlinclient import cliargs -from senlinclient import client as senlin_client -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import utils - -osprofiler_profiler = importutils.try_import("osprofiler.profiler") -USER_AGENT = 'python-senlinclient' -LOG = logging.getLogger(__name__) - - -class HelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(HelpFormatter, self).start_section(heading) - - -class SenlinShell(object): - def _setup_logging(self, debug): - log_lvl = logging.DEBUG if debug else logging.WARNING - logging.basicConfig(format="%(levelname)s (%(module)s) %(message)s", - level=log_lvl) - logging.getLogger('iso8601').setLevel(logging.WARNING) - logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) - - def _setup_verbose(self, verbose): - if verbose: - exc.verbose = 1 - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - - # get callback documentation string - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser(command, - help=help, - description=desc, - add_help=False, - formatter_class=HelpFormatter) - - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS) - - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - self.subcommands[command] = subparser - - def do_bash_completion(self, args): - """Prints all of the commands and options to stdout. - - The senlin.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in self.subcommands.items(): - if sc_str == 'bash_completion' or sc_str == 'bash-completion': - continue - - commands.add(sc_str) - for option in list(sc._optionals._option_string_actions): - options.add(option) - - print(' '.join(commands | options)) - - def add_profiler_args(self, parser): - if osprofiler_profiler: - parser.add_argument( - '--profile', metavar='HMAC_KEY', - help=_('HMAC key to use for encrypting context data for ' - 'performance profiling of operation. This key should ' - 'be the value of HMAC key configured in osprofiler ' - 'middleware in senlin, it is specified in the paste ' - 'deploy configuration (/etc/senlin/api-paste.ini). ' - 'Without the key, profiling will not be triggered ' - 'even if osprofiler is enabled on server side.')) - - def _add_bash_completion_subparser(self, subparsers): - subparser = subparsers.add_parser('bash_completion', - add_help=False, - formatter_class=HelpFormatter) - - subparser.set_defaults(func=self.do_bash_completion) - self.subcommands['bash_completion'] = subparser - - def get_subcommand_parser(self, base_parser, version): - parser = base_parser - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='') - submodule = utils.import_versioned_module(version, 'shell') - self._find_actions(subparsers, submodule) - self._find_actions(subparsers, self) - self._add_bash_completion_subparser(subparsers) - - return parser - - @utils.arg('command', metavar='', nargs='?', - help=_('Display help for .')) - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - def _check_identity_arguments(self, args): - # TODO(Qiming): validate the token authentication path and the trust - # authentication path - - if not args.auth_url: - msg = _('You must provide an auth url via --os-auth-url (or ' - ' env[OS_AUTH_URL])') - raise exc.CommandError(msg) - - # username or user_id or token must be specified - if not (args.username or args.user_id or args.token): - msg = _('You must provide a user name, a user_id or a ' - 'token for authentication') - raise exc.CommandError(msg) - - # if both username and user_id are specified, user_id takes precedence - if (args.username and args.user_id): - msg = _('Both user name and user ID are specified, Senlin will ' - 'use user ID for authentication') - print(_('WARNING: %s') % msg) - - if 'v3' in args.auth_url: - if (args.username and not args.user_id): - if not (args.user_domain_id or args.user_domain_name): - msg = _('Either user domain ID (--user-domain-id / ' - 'env[OS_USER_DOMAIN_ID]) or user domain name ' - '(--user-domain-name / env[OS_USER_DOMAIN_NAME]) ' - 'must be specified, because user name may not be ' - 'unique.') - raise exc.CommandError(msg) - - # password is needed if username or user_id is present - if (args.username or args.user_id) and not (args.password): - msg = _('You must provide a password for user %s') % ( - args.username or args.user_id) - raise exc.CommandError(msg) - - # project name or ID is needed, or else sdk may find the wrong project - if (not (args.project_id or args.project_name or args.tenant_id - or args.tenant_name)): - if not (args.user_id): - msg = _('Either project/tenant ID or project/tenant name ' - 'must be specified, or else Senlin cannot know ' - 'which project to use.') - raise exc.CommandError(msg) - else: - msg = _('Neither project ID nor project name is specified. ' - 'Senlin will use user\'s default project which may ' - 'result in authentication error.') - print(_('WARNING: %s') % msg) - - # both project name and ID are specified, ID takes precedence - if ((args.project_id or args.tenant_id) and - (args.project_name or args.tenant_name)): - msg = _('Both project/tenant name and project/tenant ID are ' - 'specified, Senlin will use project ID for ' - 'authentication') - print(_('WARNING: %s') % msg) - - # project name may not be unique - if 'v3' in args.auth_url: - if (not (args.project_id or args.tenant_id) and - (args.project_name or args.tenant_name) and - not (args.project_domain_id or args.project_domain_name)): - msg = _('Either project domain ID (--project-domain-id / ' - 'env[OS_PROJECT_DOMAIN_ID]) orr project domain name ' - '(--project-domain-name / env[OS_PROJECT_DOMAIN_NAME ' - 'must be specified, because project/tenant name may ' - 'not be unique.') - raise exc.CommandError(msg) - - def _setup_senlin_client(self, api_ver, args): - """Create senlin client using given args.""" - kwargs = { - 'auth_plugin': args.auth_plugin or 'password', - 'auth_url': args.auth_url, - 'project_name': args.project_name or args.tenant_name, - 'project_id': args.project_id or args.tenant_id, - 'domain_name': args.domain_name, - 'domain_id': args.domain_id, - 'project_domain_name': args.project_domain_name, - 'project_domain_id': args.project_domain_id, - 'user_domain_name': args.user_domain_name, - 'user_domain_id': args.user_domain_id, - 'username': args.username, - 'user_id': args.user_id, - 'password': args.password, - 'verify': args.verify, - 'token': args.token, - 'trust_id': args.trust_id, - } - - return senlin_client.Client('1', args.user_preferences, USER_AGENT, - **kwargs) - - def main(self, argv): - # Parse args once to find version - parser = argparse.ArgumentParser( - prog='senlin', - description=__doc__.strip(), - epilog=_('Type "senlin help " for help on a specific ' - 'command.'), - add_help=False, - formatter_class=HelpFormatter, - ) - - cliargs.add_global_args(parser, version=senlinclient.__version__) - cliargs.add_global_identity_args(parser) - self.add_profiler_args(parser) - base_parser = parser - - (options, args) = base_parser.parse_known_args(argv) - - self._setup_logging(options.debug) - self._setup_verbose(options.verbose) - - # build available subcommands based on version - api_ver = options.senlin_api_version - LOG.info(api_ver) - subcommand_parser = self.get_subcommand_parser(base_parser, api_ver) - self.parser = subcommand_parser - - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if not args and options.help or not argv: - self.do_help(options) - return 0 - - # Parse args again and call whatever callback was selected - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help command right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - - # Check if identity information are sufficient - self._check_identity_arguments(args) - - # Setup Senlin client connection - sc = self._setup_senlin_client(api_ver, args) - - profile = osprofiler_profiler and options.profile - if profile: - osprofiler_profiler.init(options.profile) - - args.func(sc.service, args) - - if profile: - trace_id = osprofiler_profiler.get().get_base_id() - print(_("Trace ID: %s") % trace_id) - print(_("To display trace use next command:\n" - "osprofiler trace show --html %s ") % trace_id) - - -def main(args=None): - try: - if args is None: - args = sys.argv[1:] - - SenlinShell().main(args) - except KeyboardInterrupt: - print(_("... terminating senlin client"), file=sys.stderr) - sys.exit(130) - except Exception as e: - if '--debug' in args or '-d' in args: - raise - else: - print(encodeutils.safe_encode(six.text_type(e)), file=sys.stderr) - sys.exit(1) - -if __name__ == "__main__": - main() diff --git a/senlinclient/tests/unit/test_cliargs.py b/senlinclient/tests/unit/test_cliargs.py deleted file mode 100644 index 8bc6df8..0000000 --- a/senlinclient/tests/unit/test_cliargs.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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 -import testtools - -from senlinclient import cliargs - - -class TestCliArgs(testtools.TestCase): - - def test_add_global_identity_args(self): - parser = mock.Mock() - - cliargs.add_global_identity_args(parser) - expected = [ - '--os-auth-plugin', - '--os-auth-url', - '--os-project-id', - '--os-project-name', - '--os-tenant-id', - '--os-tenant-name', - '--os-domain-id', - '--os-domain-name', - '--os-project-domain-id', - '--os-project-domain-name', - '--os-user-domain-id', - '--os-user-domain-name', - '--os-username', - '--os-user-id', - '--os-password', - '--os-trust-id', - '--os-token', - '--os-access-info', - '--os-api-name', - '--os-api-region', - '--os-api-version', - '--os-api-interface' - ] - - options = [arg[0][0] for arg in parser.add_argument.call_args_list] - self.assertEqual(expected, options) - - parser.add_mutually_exclusive_group.assert_called_once_with() - group = parser.add_mutually_exclusive_group.return_value - - verify_opts = [arg[0][0] for arg in group.add_argument.call_args_list] - verify_args = [ - '--os-cacert', - '--verify', - '--insecure' - ] - self.assertEqual(verify_args, verify_opts) - - def test_add_global_args(self): - parser = mock.Mock() - - cliargs.add_global_args(parser, '1') - expected = [ - '-h', - '--version', - '-d', - '-v', - '--api-timeout', - '--senlin-api-version' - ] - - options = [arg[0][0] for arg in parser.add_argument.call_args_list] - self.assertEqual(expected, options) diff --git a/senlinclient/tests/unit/test_shell.py b/senlinclient/tests/unit/test_shell.py deleted file mode 100644 index dbeaaf7..0000000 --- a/senlinclient/tests/unit/test_shell.py +++ /dev/null @@ -1,373 +0,0 @@ -# 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 argparse -import logging -import sys - -import mock -import six -from six.moves import builtins -import testtools - -from senlinclient import client as senlin_client -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import sdk -from senlinclient.common import utils -from senlinclient import shell -from senlinclient.tests.unit import fakes - - -class HelpFormatterTest(testtools.TestCase): - - def test_start_section(self): - fmtr = shell.HelpFormatter('senlin') - res = fmtr.start_section(('heading', 'text1', 30)) - self.assertIsNone(res) - h = fmtr._current_section.heading - self.assertEqual("HEADING('text1', 30)", h) - - -class TestArgs(testtools.TestCase): - - def __init__(self): - self.auth_url = 'http://fakeurl/v3' - self.auth_plugin = 'test_plugin' - self.username = 'test_user_name' - self.user_id = 'test_user_id' - self.token = 'test_token' - self.project_id = 'test_project_id' - self.project_name = 'test_project_name' - self.tenant_id = 'test_tenant_id' - self.tenant_name = 'test_tenant_name' - self.password = 'test_password' - self.user_domain_id = 'test_user_domain_id' - self.user_domain_name = 'test_user_domain_name' - self.project_domain_id = 'test_project_domain_id' - self.project_domain_name = 'test_project_domain_name' - self.domain_name = 'test_domain_name' - self.domain_id = 'test_domain_id' - self.verify = 'test_verify' - self.user_preferences = 'test_preferences' - self.trust_id = 'test_trust' - - -class ShellTest(testtools.TestCase): - - def setUp(self): - super(ShellTest, self).setUp() - - def SHELL(self, func, *args, **kwargs): - orig_out = sys.stdout - sys.stdout = six.StringIO() - func(*args, **kwargs) - output = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig_out - - return output - - @mock.patch.object(logging, 'basicConfig') - @mock.patch.object(logging, 'getLogger') - def test_setup_logging_debug(self, x_get, x_config): - sh = shell.SenlinShell() - sh._setup_logging(True) - - x_config.assert_called_once_with( - format="%(levelname)s (%(module)s) %(message)s", - level=logging.DEBUG) - mock_calls = [ - mock.call('iso8601'), - mock.call().setLevel(logging.WARNING), - mock.call('urllib3.connectionpool'), - mock.call().setLevel(logging.WARNING), - ] - x_get.assert_has_calls(mock_calls) - - @mock.patch.object(logging, 'basicConfig') - @mock.patch.object(logging, 'getLogger') - def test_setup_logging_no_debug(self, x_get, x_config): - sh = shell.SenlinShell() - sh._setup_logging(False) - - x_config.assert_called_once_with( - format="%(levelname)s (%(module)s) %(message)s", - level=logging.WARNING) - mock_calls = [ - mock.call('iso8601'), - mock.call().setLevel(logging.WARNING), - mock.call('urllib3.connectionpool'), - mock.call().setLevel(logging.WARNING), - ] - x_get.assert_has_calls(mock_calls) - - def test_setup_verbose(self): - sh = shell.SenlinShell() - sh._setup_verbose(True) - self.assertEqual(1, exc.verbose) - - sh._setup_verbose(False) - self.assertEqual(1, exc.verbose) - - def test_find_actions(self): - sh = shell.SenlinShell() - sh.subcommands = {} - subparsers = mock.Mock() - x_subparser1 = mock.Mock() - x_subparser2 = mock.Mock() - x_add_parser = mock.MagicMock(side_effect=[x_subparser1, x_subparser2]) - subparsers.add_parser = x_add_parser - - # subparsers.add_parser = mock.Mock(return_value=x_subparser) - sh._find_actions(subparsers, fakes) - - self.assertEqual({'command-bar': x_subparser1, - 'command-foo': x_subparser2}, - sh.subcommands) - add_calls = [ - mock.call('command-bar', help='This is the command doc.', - description='This is the command doc.', - add_help=False, - formatter_class=shell.HelpFormatter), - mock.call('command-foo', help='Pydoc for command foo.', - description='Pydoc for command foo.', - add_help=False, - formatter_class=shell.HelpFormatter), - ] - x_add_parser.assert_has_calls(add_calls) - - calls_1 = [ - mock.call('-h', '--help', action='help', - help=argparse.SUPPRESS), - mock.call('-F', '--flag', metavar='', - help='Flag desc.'), - mock.call('arg1', metavar='', - help='Arg1 desc') - ] - x_subparser1.add_argument.assert_has_calls(calls_1) - x_subparser1.set_defaults.assert_called_once_with( - func=fakes.do_command_bar) - - calls_2 = [ - mock.call('-h', '--help', action='help', - help=argparse.SUPPRESS), - ] - x_subparser2.add_argument.assert_has_calls(calls_2) - x_subparser2.set_defaults.assert_called_once_with( - func=fakes.do_command_foo) - - def test_do_bash_completion(self): - sh = shell.SenlinShell() - sc1 = mock.Mock() - sc2 = mock.Mock() - sc1._optionals._option_string_actions = ('A1', 'A2', 'C') - sc2._optionals._option_string_actions = ('B1', 'B2', 'C') - sh.subcommands = { - 'command-foo': sc1, - 'command-bar': sc2, - 'bash-completion': None, - 'bash_completion': None, - } - - output = self.SHELL(sh.do_bash_completion, None) - - output = output.split('\n')[0] - output_list = output.split(' ') - for option in ('A1', 'A2', 'C', 'B1', 'B2', - 'command-foo', 'command-bar'): - self.assertIn(option, output_list) - - def test_do_add_profiler_args(self): - sh = shell.SenlinShell() - parser = mock.Mock() - - sh.add_profiler_args(parser) - - self.assertEqual(0, parser.add_argument.call_count) - - def test_add_bash_completion_subparser(self): - sh = shell.SenlinShell() - sh.subcommands = {} - x_subparser = mock.Mock() - x_subparsers = mock.Mock() - x_subparsers.add_parser.return_value = x_subparser - - sh._add_bash_completion_subparser(x_subparsers) - - x_subparsers.add_parser.assert_called_once_with( - 'bash_completion', add_help=False, - formatter_class=shell.HelpFormatter) - self.assertEqual({'bash_completion': x_subparser}, sh.subcommands) - x_subparser.set_defaults.assert_called_once_with( - func=sh.do_bash_completion) - - @mock.patch.object(utils, 'import_versioned_module') - @mock.patch.object(shell.SenlinShell, '_find_actions') - @mock.patch.object(shell.SenlinShell, '_add_bash_completion_subparser') - def test_get_subcommand_parser(self, x_add, x_find, x_import): - x_base = mock.Mock() - x_module = mock.Mock() - x_import.return_value = x_module - sh = shell.SenlinShell() - - res = sh.get_subcommand_parser(x_base, 'v100') - - self.assertEqual(x_base, res) - x_base.add_subparsers.assert_called_once_with( - metavar='') - x_subparsers = x_base.add_subparsers.return_value - x_import.assert_called_once_with('v100', 'shell') - find_calls = [ - mock.call(x_subparsers, x_module), - mock.call(x_subparsers, sh) - ] - - x_find.assert_has_calls(find_calls) - x_add.assert_called_once_with(x_subparsers) - - @mock.patch.object(argparse.ArgumentParser, 'print_help') - def test_do_help(self, mock_print): - sh = shell.SenlinShell() - args = mock.Mock() - args.command = mock.Mock() - sh.subcommands = {args.command: argparse.ArgumentParser} - sh.do_help(args) - self.assertTrue(mock_print.called) - - sh.subcommands = {} - ex = self.assertRaises(exc.CommandError, - sh.do_help, args) - msg = _("'%s' is not a valid subcommand") % args.command - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(builtins, 'print') - def test_check_identity_arguments(self, mock_print): - sh = shell.SenlinShell() - # auth_url is not specified. - args = TestArgs() - args.auth_url = None - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - msg = _('You must provide an auth url via --os-auth-url (or ' - ' env[OS_AUTH_URL])') - self.assertEqual(msg, six.text_type(ex)) - # username, user_id and token are not specified. - args = TestArgs() - args.username = None - args.user_id = None - args.token = None - msg = _('You must provide a user name, a user_id or a ' - 'token for authentication') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - # Both username and user_id are specified. - args = TestArgs() - args.project_id = None - args.tenant_id = None - sh._check_identity_arguments(args) - msg = _('WARNING: Both user name and user ID are specified, ' - 'Senlin will use user ID for authentication') - mock_print.assert_called_with(msg) - - # 'v3' in auth_url but neither user_domain_id nor user_domain_name - # is specified. - args = TestArgs() - args.user_id = None - args.user_domain_id = None - args.user_domain_name = None - msg = _('Either user domain ID (--user-domain-id / ' - 'env[OS_USER_DOMAIN_ID]) or user domain name ' - '(--user-domain-name / env[OS_USER_DOMAIN_NAME]) ' - 'must be specified, because user name may not be ' - 'unique.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - # user_id, project_id, project_name, tenant_id and tenant_name are all - # not specified. - args = TestArgs() - args.project_id = None - args.project_name = None - args.tenant_id = None - args.tenant_name = None - args.user_id = None - msg = _('Either project/tenant ID or project/tenant name ' - 'must be specified, or else Senlin cannot know ' - 'which project to use.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - args.user_id = 'test_user_id' - sh._check_identity_arguments(args) - msg = _('Neither project ID nor project name is specified. ' - 'Senlin will use user\'s default project which may ' - 'result in authentication error.') - mock_print.assert_called_with(_('WARNING: %s') % msg) - - # Both project_name and project_id are specified - args = TestArgs() - args.user_id = None - sh._check_identity_arguments(args) - msg = _('Both project/tenant name and project/tenant ID are ' - 'specified, Senlin will use project ID for ' - 'authentication') - mock_print.assert_called_with(_('WARNING: %s') % msg) - # Project name may not be unique - args = TestArgs() - args.user_id = None - args.project_id = None - args.tenant_id = None - args.project_domain_id = None - args.project_domain_name = None - msg = _('Either project domain ID (--project-domain-id / ' - 'env[OS_PROJECT_DOMAIN_ID]) orr project domain name ' - '(--project-domain-name / env[OS_PROJECT_DOMAIN_NAME ' - 'must be specified, because project/tenant name may ' - 'not be unique.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sdk, 'create_connection') - def test_setup_senlinclient(self, mock_conn): - USER_AGENT = 'python-senlinclient' - args = TestArgs() - kwargs = { - 'auth_plugin': args.auth_plugin, - 'auth_url': args.auth_url, - 'project_name': args.project_name or args.tenant_name, - 'project_id': args.project_id or args.tenant_id, - 'domain_name': args.domain_name, - 'domain_id': args.domain_id, - 'project_domain_name': args.project_domain_name, - 'project_domain_id': args.project_domain_id, - 'user_domain_name': args.user_domain_name, - 'user_domain_id': args.user_domain_id, - 'username': args.username, - 'user_id': args.user_id, - 'password': args.password, - 'verify': args.verify, - 'token': args.token, - 'trust_id': args.trust_id, - } - sh = shell.SenlinShell() - conn = mock.Mock() - mock_conn.return_value = conn - conn.session = mock.Mock() - sh._setup_senlin_client('1', args) - mock_conn.assert_called_once_with(args.user_preferences, USER_AGENT, - **kwargs) - client = mock.Mock() - senlin_client.Client = mock.MagicMock(return_value=client) - self.assertEqual(client, sh._setup_senlin_client('1', args)) diff --git a/senlinclient/tests/unit/v1/test_shell.py b/senlinclient/tests/unit/v1/test_shell.py deleted file mode 100644 index e2d1ef2..0000000 --- a/senlinclient/tests/unit/v1/test_shell.py +++ /dev/null @@ -1,1348 +0,0 @@ -# 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 copy -import mock -import six -import testtools - -from openstack import exceptions as oexc -from oslotest import mockpatch -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import utils -from senlinclient.v1 import shell as sh - - -class ShellTest(testtools.TestCase): - - def setUp(self): - super(ShellTest, self).setUp() - self.profile_args = { - 'spec_file': mock.Mock(), - 'name': 'stack_spec', - 'metadata': {'user': 'demo'} - } - self.profile_spec = { - 'type': 'os.heat.stack', - 'version': 1.0, - 'properties': { - 'name': 'stack1', - 'template': {"Template": "data"} - } - } - self.patch('senlinclient.v1.shell.show_deprecated') - - # NOTE(pshchelo): this overrides the testtools.TestCase.patch method - # that does simple monkey-patching in favor of mock's patching - def patch(self, target, **kwargs): - mockfixture = self.useFixture(mockpatch.Patch(target, **kwargs)) - return mockfixture.mock - - def _make_args(self, args): - """Convert a dict to an object.""" - class Args(object): - def __init__(self, entries): - self.__dict__.update(entries) - - return Args(args) - - @mock.patch.object(utils, 'print_dict') - def test_do_build_info(self, mock_print): - service = mock.Mock() - result = mock.Mock() - service.get_build_info.return_value = result - sh.do_build_info(service) - formatters = { - 'api': utils.json_formatter, - 'engine': utils.json_formatter, - } - mock_print.assert_called_once_with(result, formatters=formatters) - self.assertTrue(service.get_build_info.called) - - @mock.patch.object(utils, 'print_list') - def test_do_profile_type_list(self, mock_print): - service = mock.Mock() - types = mock.Mock() - service.profile_types.return_value = types - sh.do_profile_type_list(service) - mock_print.assert_called_once_with(types, ['name'], sortby_index=0) - self.assertTrue(service.profile_types.called) - - @mock.patch.object(utils, 'format_output') - def test_do_profile_type_show(self, mock_format): - service = mock.Mock() - fake_pt = mock.Mock() - fake_pt.to_dict.return_value = {'foo': 'bar'} - service.get_profile_type = mock.Mock(return_value=fake_pt) - args_dict = { - 'format': 'json', - 'type_name': 'os.nova.server' - } - args = self._make_args(args_dict) - sh.do_profile_type_show(service, args) - mock_format.assert_called_with({'foo': 'bar'}, format=args.format) - service.get_profile_type.assert_called_with('os.nova.server') - args.format = None - sh.do_profile_type_show(service, args) - mock_format.assert_called_with({'foo': 'bar'}) - - def test_do_profile_type_show_type_not_found(self): - service = mock.Mock() - args = { - 'type_name': 'wrong_type', - 'format': 'json' - } - args = self._make_args(args) - ex = oexc.ResourceNotFound - service.get_profile_type = mock.Mock(side_effect=ex) - ex = self.assertRaises(exc.CommandError, - sh.do_profile_type_show, - service, args) - self.assertEqual(_('Profile Type not found: wrong_type'), - six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_profile_list(self, mock_print): - service = mock.Mock() - profiles = mock.Mock() - service.profiles.return_value = profiles - fields = ['id', 'name', 'type', 'created_at'] - args = { - 'limit': 20, - 'marker': 'mark_id', - 'sort': 'key:dir', - 'global_project': True, - 'filters': ['name=stack_spec'] - } - queries = copy.deepcopy(args) - del queries['filters'] - queries['name'] = 'stack_spec' - formatters = {} - args = self._make_args(args) - args.full_id = True - sh.do_profile_list(service, args) - service.profiles.assert_called_once_with(**queries) - mock_print.assert_called_with(profiles, fields, formatters=formatters, - sortby_index=None) - - args.sort = None - sh.do_profile_list(service, args) - mock_print.assert_called_with(profiles, fields, formatters=formatters, - sortby_index=1) - - @mock.patch.object(utils, 'nested_dict_formatter') - @mock.patch.object(utils, 'print_dict') - def test_show_profile(self, mock_print, mock_dict): - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - service.get_profile.return_value = profile - pro_to_dict = mock.Mock() - profile.to_dict.return_value = pro_to_dict - json_formatter = mock.Mock() - utils.json_formatter = json_formatter - dict_formatter = mock.Mock() - mock_dict.return_value = dict_formatter - formatters = { - 'metadata': json_formatter, - 'spec': dict_formatter - } - sh._show_profile(service, profile_id) - service.get_profile.assert_called_once_with(profile_id) - mock_dict.assert_called_once_with(['type', 'version', 'properties'], - ['property', 'value']) - mock_print.assert_called_once_with(pro_to_dict, formatters=formatters) - - def test_show_profile_not_found(self): - service = mock.Mock() - ex = oexc.ResourceNotFound - service.get_profile.side_effect = ex - profile_id = 'wrong_id' - ex = self.assertRaises(exc.CommandError, - sh._show_profile, - service, profile_id) - self.assertEqual(_('Profile not found: wrong_id'), six.text_type(ex)) - service.get_profile.assert_called_once_with(profile_id) - - @mock.patch.object(sh, '_show_profile') - @mock.patch.object(utils, 'format_parameters') - @mock.patch.object(utils, 'process_stack_spec') - @mock.patch.object(utils, 'get_spec_content') - def test_do_profile_create(self, mock_get, mock_proc, mock_format, - mock_show): - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - spec = copy.deepcopy(self.profile_spec) - mock_get.return_value = spec - stack_properties = mock.Mock() - mock_proc.return_value = stack_properties - mock_format.return_value = {'user': 'demo'} - params = { - 'name': 'stack_spec', - 'spec': spec, - 'metadata': {'user': 'demo'}, - } - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - profile.id = profile_id - service.create_profile.return_value = profile - - sh.do_profile_create(service, args) - - mock_get.assert_called_once_with(args.spec_file) - mock_proc.assert_called_once_with(self.profile_spec['properties']) - mock_format.assert_called_once_with(args.metadata) - service.create_profile.assert_called_once_with(**params) - mock_show.assert_called_once_with(service, profile_id) - - # Miss 'type' key in spec file - del spec['type'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'type' key in spec file."), - six.text_type(ex)) - # Miss 'version' key in spec file - spec['type'] = 'os.heat.stack' - del spec['version'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'version' key in spec file."), - six.text_type(ex)) - # Miss 'properties' key in spec file - spec['version'] = 1.0 - del spec['properties'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'properties' key in spec file."), - six.text_type(ex)) - - @mock.patch.object(sh, '_show_profile') - def test_do_profile_show(self, mock_show): - service = mock.Mock() - args = {'id': 'profile_id'} - args = self._make_args(args) - sh.do_profile_show(service, args) - mock_show.assert_called_once_with(service, args.id) - - @mock.patch.object(sh, '_show_profile') - @mock.patch.object(utils, 'format_parameters') - def test_do_profile_update(self, mock_format, mock_show): - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - mock_format.return_value = {'user': 'demo'} - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - profile.id = profile_id - args.id = 'FAKE_ID' - service.get_profile.return_value = profile - - sh.do_profile_update(service, args) - - mock_format.assert_called_once_with(args.metadata) - service.get_profile.assert_called_once_with('FAKE_ID') - params = { - 'name': 'stack_spec', - 'metadata': {'user': 'demo'}, - } - service.update_profile.assert_called_once_with(profile_id, **params) - mock_show.assert_called_once_with(service, profile_id) - - @mock.patch.object(utils, 'format_parameters') - def test_do_profile_update_not_found(self, mock_format): - service = mock.Mock() - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - args.id = 'FAKE_ID' - ex = oexc.ResourceNotFound - service.get_profile.side_effect = ex - ex = self.assertRaises(exc.CommandError, - sh.do_profile_update, - service, args) - self.assertEqual(_('Profile not found: FAKE_ID'), - six.text_type(ex)) - mock_format.assert_called_once_with(args.metadata) - - def test_do_profile_delete(self): - service = mock.Mock() - args = {'id': ['profile_id']} - args = self._make_args(args) - sh.do_profile_delete(service, args) - service.delete_profile.assert_called_with('profile_id', False) - - def test_do_profile_delete_not_found(self): - service = mock.Mock() - args = {'id': ['profile1', 'profile2']} - args = self._make_args(args) - sh.do_profile_delete(service, args) - service.delete_profile.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_profile_delete, - service, args) - msg = _("Failed to delete some of the specified profile(s).") - self.assertEqual(msg, six.text_type(ex)) - service.delete_profile.assert_called_with('profile2', False) - - @mock.patch.object(utils, 'print_list') - def test_do_policy_type_list(self, mock_print): - service = mock.Mock() - args = mock.Mock() - types = mock.Mock() - service.policy_types.return_value = types - sh.do_policy_type_list(service, args) - mock_print.assert_called_once_with(types, ['name'], sortby_index=0) - - @mock.patch.object(utils, 'format_output') - def test_do_policy_type_show(self, mock_format): - service = mock.Mock() - args = { - 'type_name': 'senlin.policy.deletion', - 'format': 'yaml' - } - args = self._make_args(args) - res = mock.Mock() - pt = mock.Mock() - res.to_dict.return_value = pt - service.get_policy_type.return_value = res - sh.do_policy_type_show(service, args) - mock_format.assert_called_with(pt, format=args.format) - - # no format attribute - args = { - 'type_name': 'senlin.policy.deletion', - 'format': None - } - args = self._make_args(args) - service.get_policy_type.return_value = res - sh.do_policy_type_show(service, args) - mock_format.assert_called_with(pt) - - def test_do_policy_type_show_not_found(self): - service = mock.Mock() - args = {'type_name': 'BAD'} - args = self._make_args(args) - - service.get_policy_type.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_policy_type_show, service, args) - msg = _('Policy type not found: BAD') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_receiver_list(self, mock_print): - service = mock.Mock() - params = { - 'limit': 10, - 'marker': 'fake_id', - 'sort': 'key:dir', - 'filters': ['filter_key=filter_value'], - 'global_project': False, - 'full_id': False, - } - fields = ['id', 'name', 'type', 'cluster_id', 'action', 'created_at'] - args = self._make_args(params) - queries = copy.deepcopy(params) - del queries['filters'] - queries['filter_key'] = 'filter_value' - r1 = mock.Mock() - r1.id = '01234567-abcd-efgh' - r1.cluster_id = 'abcdefgh-abcd-efgh' - receivers = [r1] - service.receivers.return_value = receivers - formatters = { - 'id': mock.ANY, - 'cluster_id': mock.ANY - } - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters=formatters, - sortby_index=None) - # full_id is requested - args.full_id = True - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters={}, - sortby_index=None) - - # default sorting - args.sort = None - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters={}, - sortby_index=0) - - @mock.patch.object(utils, 'print_dict') - def test_show_receiver(self, mock_print): - service = mock.Mock() - receiver = mock.Mock() - receiver_id = '01234567-abcd-abcd-abcdef' - receiver.id = receiver_id - service.get_receiver.return_value = receiver - receiver_dict = mock.Mock() - receiver.to_dict.return_value = receiver_dict - sh._show_receiver(service, receiver_id) - formatters = { - 'actor': utils.json_formatter, - 'params': utils.json_formatter, - 'channel': utils.json_formatter, - } - service.get_receiver.assert_called_once_with(receiver_id) - mock_print.assert_called_once_with(receiver_dict, - formatters=formatters) - - def test_show_receiver_not_found(self): - service = mock.Mock() - receiver = mock.Mock() - receiver_id = 'wrong_id' - receiver.id = receiver_id - - service.get_receiver.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh._show_receiver, service, receiver_id) - self.assertEqual(_('Receiver not found: wrong_id'), six.text_type(ex)) - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_show(self, mock_show): - service = mock.Mock() - args = {'id': 'receiver_id'} - args = self._make_args(args) - sh.do_receiver_show(service, args) - mock_show.assert_called_once_with(service, - receiver_id='receiver_id') - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_create(self, mock_show): - service = mock.Mock() - args = { - 'name': 'receiver1', - 'type': 'webhook', - 'cluster': 'cluster1', - 'action': 'CLUSTER_SCALE_IN', - 'params': {} - } - args = self._make_args(args) - params = { - 'name': 'receiver1', - 'type': 'webhook', - 'cluster_id': 'cluster1', - 'action': 'CLUSTER_SCALE_IN', - 'params': {} - } - receiver = mock.Mock() - receiver.id = 'FAKE_ID' - service.create_receiver.return_value = receiver - sh.do_receiver_create(service, args) - service.create_receiver.assert_called_once_with(**params) - mock_show.assert_called_once_with(service, 'FAKE_ID') - - def test_do_receiver_delete(self): - service = mock.Mock() - args = {'id': ['FAKE']} - args = self._make_args(args) - service.delete_receiver = mock.Mock() - sh.do_receiver_delete(service, args) - service.delete_receiver.assert_called_once_with('FAKE', False) - - def test_do_receiver_delete_not_found(self): - service = mock.Mock() - args = {'id': ['receiver_id']} - args = self._make_args(args) - - service.delete_receiver.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_receiver_delete, service, args) - msg = _("Failed to delete some of the specified receiver(s).") - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_policy_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'type', 'created_at'] - args = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'name', - 'global_project': False, - 'full_id': True, - 'filters': ['name=stack_spec'] - } - args = self._make_args(args) - queries = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'name', - 'global_project': False, - 'name': 'stack_spec', - } - policies = mock.Mock() - service.policies.return_value = policies - formatters = {} - sh.do_policy_list(service, args) - service.policies.assert_called_once_with(**queries) - mock_print.assert_called_once_with( - policies, fields, formatters=formatters, sortby_index=None) - mock_print.reset_mock() - - args.sort = None - sh.do_policy_list(service, args) - mock_print.assert_called_once_with( - policies, fields, formatters=formatters, sortby_index=1) - - @mock.patch.object(utils, 'print_dict') - def test_show_policy(self, mock_print): - service = mock.Mock() - formatters = { - 'metadata': utils.json_formatter, - 'spec': utils.json_formatter, - } - policy_id = 'fake_policy_id' - policy = mock.Mock() - policy.id = policy_id - service.get_policy.return_value = policy - policy_dict = mock.Mock() - policy.to_dict.return_value = policy_dict - sh._show_policy(service, policy_id) - mock_print.assert_called_once_with(policy_dict, - formatters=formatters) - - # policy not found - ex = oexc.ResourceNotFound - service.get_policy.side_effect = ex - ex = self.assertRaises(exc.CommandError, - sh._show_policy, - service, policy_id) - msg = _('Policy not found: fake_policy_id') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sh, '_show_policy') - @mock.patch.object(utils, 'get_spec_content') - def test_do_policy_create(self, mock_get, mock_show): - service = mock.Mock() - spec = mock.Mock() - mock_get.return_value = spec - args = { - 'name': 'new_policy', - 'spec_file': 'policy_file', - } - args = self._make_args(args) - attrs = { - 'name': 'new_policy', - 'spec': spec, - } - policy = mock.Mock() - policy.id = 'policy_id' - service.create_policy.return_value = policy - sh.do_policy_create(service, args) - mock_get.assert_called_once_with(args.spec_file) - service.create_policy.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, policy.id) - - @mock.patch.object(sh, '_show_policy') - def test_do_policy_show(self, mock_show): - service = mock.Mock() - args = {'id': 'policy_id'} - args = self._make_args(args) - sh.do_policy_show(service, args) - mock_show.assert_called_once_with(service, policy_id='policy_id') - - @mock.patch.object(sh, '_show_policy') - def test_do_policy_update(self, mock_show): - service = mock.Mock() - args = { - 'name': 'deletion_policy', - 'id': 'policy_id', - } - args = self._make_args(args) - params = { - 'name': 'deletion_policy', - } - policy = mock.Mock() - service.get_policy.return_value = policy - policy.id = 'policy_id' - sh.do_policy_update(service, args) - service.get_policy.assert_called_once_with('policy_id') - service.update_policy.assert_called_once_with( - 'policy_id', **params) - mock_show(service, policy_id=policy.id) - - def test_do_policy_delete(self): - service = mock.Mock() - args = {'id': ['policy_id']} - args = self._make_args(args) - service.delete_policy = mock.Mock() - sh.do_policy_delete(service, args) - service.delete_policy.assert_called_once_with('policy_id', False) - - def test_do_policy_delete_not_found(self): - service = mock.Mock() - args = {'id': ['policy_id']} - args = self._make_args(args) - - service.delete_policy.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_policy_delete, service, args) - msg = _("Failed to delete some of the specified policy(s).") - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'status', 'created_at', 'updated_at'] - args = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'key:dir', - 'global_project': False, - 'filters': ['status=ACTIVE'], - } - queries = copy.deepcopy(args) - del queries['filters'] - queries['status'] = 'ACTIVE' - args = self._make_args(args) - clusters = mock.Mock() - service.clusters.return_value = clusters - args.full_id = True - formatters = {} - sh.do_cluster_list(service, args) - service.clusters.assert_called_once_with(**queries) - mock_print.assert_called_once_with(clusters, fields, - formatters=formatters, - sortby_index=None) - args.sort = None - sh.do_cluster_list(service, args) - mock_print.assert_called_with(clusters, fields, - formatters={}, sortby_index=3) - - @mock.patch.object(utils, 'print_dict') - def test_show_cluster(self, mock_print): - service = mock.Mock() - cluster_id = 'cluster_id' - cluster = mock.Mock() - cluster.id = cluster_id - service.get_cluster.return_value = cluster - formatters = { - 'metadata': utils.json_formatter, - 'nodes': utils.list_formatter, - } - cluster_dict = mock.Mock() - cluster.to_dict.return_value = cluster_dict - sh._show_cluster(service, cluster_id) - mock_print.assert_called_once_with(cluster_dict, formatters=formatters) - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_create(self, mock_show): - service = mock.Mock() - args = { - 'name': 'CLUSTER1', - 'profile': 'profile1', - 'min_size': 1, - 'max_size': 10, - 'desired_capacity': 5, - 'metadata': ['user=demo'], - 'timeout': 200, - } - attrs = copy.deepcopy(args) - attrs['profile_id'] = args['profile'] - args = self._make_args(args) - del attrs['profile'] - attrs['metadata'] = {'user': 'demo'} - cluster = mock.Mock() - service.create_cluster.return_value = cluster - cluster.id = 'cluster_id' - sh.do_cluster_create(service, args) - service.create_cluster.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, 'cluster_id') - - def test_do_cluster_delete(self): - service = mock.Mock() - args = {'id': ['CID']} - args = self._make_args(args) - service.delete_cluster = mock.Mock() - sh.do_cluster_delete(service, args) - service.delete_cluster.assert_called_once_with('CID', False) - - def test_do_cluster_delete_not_found(self): - service = mock.Mock() - args = {'id': ['cluster_id']} - args = self._make_args(args) - - service.delete_cluster.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_delete, service, args) - msg = _('Failed to delete some of the specified clusters.') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_update(self, mock_show): - service = mock.Mock() - args = { - 'profile': 'test_profile', - 'name': 'CLUSTER1', - 'metadata': ['user=demo'], - 'timeout': 100, - } - attrs = copy.deepcopy(args) - attrs['metadata'] = {'user': 'demo'} - attrs['profile_id'] = 'test_profile' - del attrs['profile'] - args = self._make_args(args) - args.id = 'CID' - cluster = mock.Mock() - cluster.id = 'CID' - service.get_cluster.return_value = cluster - service.update_cluster = mock.Mock() - - sh.do_cluster_update(service, args) - - service.get_cluster.assert_called_once_with('CID') - service.update_cluster.assert_called_once_with('CID', **attrs) - mock_show.assert_called_once_with(service, 'CID') - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_show(self, mock_show): - service = mock.Mock() - args = {'id': 'cluster_id'} - args = self._make_args(args) - sh.do_cluster_show(service, args) - mock_show.assert_called_once_with(service, 'cluster_id') - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_node_list(self, mock_print): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'limit': 20, - 'marker': 'marker_id', - 'filters': ['status=ACTIVE'], - } - queries = copy.deepcopy(args) - queries['cluster_id'] = args['id'] - del queries['id'] - del queries['filters'] - queries['status'] = 'ACTIVE' - args = self._make_args(args) - args.full_id = True - nodes = mock.Mock() - service.nodes.return_value = nodes - formatters = {} - fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] - sh.do_cluster_node_list(service, args) - service.nodes.assert_called_once_with(**queries) - mock_print.assert_called_once_with(nodes, fields, - formatters=formatters, - sortby_index=5) - - def test_do_cluster_node_add(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'nodes': 'node1,node2' - } - args = self._make_args(args) - node_ids = ['node1', 'node2'] - resp = {'action': 'CLUSTER_NODE_ADD'} - service.cluster_add_nodes.return_value = resp - sh.do_cluster_node_add(service, args) - service.cluster_add_nodes.assert_called_once_with( - 'cluster_id', node_ids) - - def test_do_cluster_node_del(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'nodes': 'node1,node2' - } - args = self._make_args(args) - node_ids = ['node1', 'node2'] - resp = {'action': 'CLUSTER_NODE_DEL'} - service.cluster_del_nodes.return_value = resp - - sh.do_cluster_node_del(service, args) - - service.cluster_del_nodes.assert_called_once_with('cluster_id', - node_ids) - - def test_do_cluster_resize(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'capacity': 2, - 'adjustment': 1, - 'percentage': 50.0, - 'min_size': 1, - 'max_size': 10, - 'min_step': 1, - 'strict': True, - } - args = self._make_args(args) - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _("Only one of 'capacity', 'adjustment' and " - "'percentage' can be specified.") - self.assertEqual(msg, six.text_type(ex)) - - # capacity - args.adjustment = None - args.percentage = None - args.min_step = None - action_args = { - 'adjustment_type': 'EXACT_CAPACITY', - 'number': 2, - 'min_size': 1, - 'max_size': 10, - 'strict': True, - 'min_step': None, - } - resp = {'action': 'action_id'} - service.cluster_resize.return_value = resp - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # capacity is smaller than 0 - args.capacity = -1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Cluster capacity must be larger than ' - ' or equal to zero.') - self.assertEqual(msg, six.text_type(ex)) - - # adjustment - args.capacity = None - args.percentage = None - args.adjustment = 1 - action_args['adjustment_type'] = 'CHANGE_IN_CAPACITY' - action_args['number'] = 1 - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # adjustment is 0 - args.adjustment = 0 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Adjustment cannot be zero.') - self.assertEqual(msg, six.text_type(ex)) - - # percentage - args.capacity = None - args.percentage = 50.0 - args.adjustment = None - action_args['adjustment_type'] = 'CHANGE_IN_PERCENTAGE' - action_args['number'] = 50.0 - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # percentage is 0 - args.percentage = 0 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Percentage cannot be zero.') - self.assertEqual(msg, six.text_type(ex)) - - # min_step is not None while percentage is None - args.capacity = 2 - args.percentage = None - args.adjustment = None - args.min_step = 1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min step is only used with percentage.') - self.assertEqual(msg, six.text_type(ex)) - - # min_size < 0 - args.capacity = 2 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = -1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be less than zero.') - self.assertEqual(msg, six.text_type(ex)) - - # max_size < min_size - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 5 - args.max_size = 4 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be larger than max size.') - self.assertEqual(msg, six.text_type(ex)) - - # min_size > capacity - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 6 - args.max_size = 8 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be larger than the specified capacity') - self.assertEqual(msg, six.text_type(ex)) - - # max_size < capacity - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 1 - args.max_size = 4 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Max size cannot be less than the specified capacity.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_cluster_scale_out(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'count': 3, - } - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_scale_out.return_value = resp - sh.do_cluster_scale_out(service, args) - service.cluster_scale_out.assert_called_once_with( - 'cluster_id', 3) - - def test_do_cluster_scale_in(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'count': 3, - } - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_scale_in.return_value = resp - - sh.do_cluster_scale_in(service, args) - - service.cluster_scale_in.assert_called_once_with('cluster_id', 3) - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_policy_list(self, mock_print): - fields = ['policy_id', 'policy_name', 'policy_type', 'enabled'] - service = mock.Mock() - args = { - 'id': 'C1', - 'filters': ['enabled=True'], - 'sort': 'enabled:asc', - 'full_id': True, - } - args = self._make_args(args) - queries = { - 'sort': 'enabled:asc', - 'enabled': 'True', - } - cluster = mock.Mock() - cluster.id = 'C1' - service.get_cluster.return_value = cluster - policies = mock.Mock() - service.cluster_policies.return_value = policies - sh.do_cluster_policy_list(service, args) - service.get_cluster.assert_called_once_with('C1') - service.cluster_policies.assert_called_once_with('C1', **queries) - formatters = {} - mock_print.assert_called_once_with(policies, fields, - formatters=formatters, - sortby_index=None) - - @mock.patch.object(utils, 'print_dict') - def test_do_cluster_policy_show(self, mock_print): - class Binding(object): - def to_dict(self): - pass - - service = mock.Mock() - args = { - 'id': 'CC', - 'policy': 'PP', - } - args = self._make_args(args) - binding = Binding() - service.get_cluster_policy.return_value = binding - sh.do_cluster_policy_show(service, args) - service.get_cluster_policy.assert_called_once_with('PP', 'CC') - mock_print.assert_called_once_with(binding.to_dict()) - - def test_do_cluster_policy_attach(self): - service = mock.Mock() - args = { - 'id': 'C1', - 'policy': 'P1', - 'enabled': 'True', - } - args = self._make_args(args) - kwargs = { - 'enabled': 'True', - } - service.cluster_attach_policy.return_value = {'action': 'action_id'} - sh.do_cluster_policy_attach(service, args) - service.cluster_attach_policy.assert_called_once_with('C1', 'P1', - **kwargs) - - def test_do_cluster_policy_detach(self): - args = { - 'id': 'CC', - 'policy': 'PP' - } - service = mock.Mock() - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_detach_policy.return_value = resp - sh.do_cluster_policy_detach(service, args) - service.cluster_detach_policy.assert_called_once_with('CC', 'PP') - - def test_do_cluster_policy_update(self): - service = mock.Mock() - args = { - 'id': 'C1', - 'policy': 'policy1', - 'enabled': 'True', - } - args = self._make_args(args) - kwargs = { - 'enabled': 'True', - } - service.cluster_update_policy.return_value = {'action': 'action_id'} - - sh.do_cluster_policy_update(service, args) - - service.cluster_update_policy.assert_called_once_with('C1', 'policy1', - **kwargs) - - def test_do_cluster_check(self): - service = mock.Mock() - args = self._make_args({'id': ['cluster1']}) - service.check_cluster = mock.Mock() - service.check_cluster.return_value = {'action': 'action_id'} - sh.do_cluster_check(service, args) - - service.check_cluster.assert_called_once_with('cluster1') - - def test_do_cluster_recover(self): - service = mock.Mock() - args = self._make_args({'id': ['cluster1']}) - service.recover_cluster = mock.Mock() - service.recover_cluster.return_value = {'action': 'action_id'} - - sh.do_cluster_recover(service, args) - - service.recover_cluster.assert_called_once_with('cluster1') - - @mock.patch.object(utils, 'print_list') - def test_do_node_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'index', 'status', 'cluster_id', 'physical_id', - 'profile_name', 'created_at', 'updated_at'] - args = { - 'cluster': 'cluster1', - 'sort': 'name:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'filters': ['status=active'], - 'full_id': True, - } - queries = { - 'cluster_id': 'cluster1', - 'sort': 'name:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'status': 'active', - } - args = self._make_args(args) - nodes = mock.Mock() - service.nodes.return_value = nodes - formatters = {} - sh.do_node_list(service, args) - mock_print.assert_called_once_with(nodes, fields, - formatters=formatters, - sortby_index=None) - service.nodes.assert_called_once_with(**queries) - - @mock.patch.object(utils, 'print_dict') - @mock.patch.object(utils, 'nested_dict_formatter') - def test_show_node(self, mock_nested, mock_print): - service = mock.Mock() - node_id = 'node1' - node = mock.Mock() - service.get_node.return_value = node - formatters = { - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - } - data = mock.Mock() - node.to_dict.return_value = data - - sh._show_node(service, node_id, show_details=False) - - service.get_node.assert_called_once_with(node_id, args=None) - mock_print.assert_called_once_with(data, formatters=formatters) - - @mock.patch.object(sh, '_show_node') - def test_do_node_create(self, mock_show): - args = { - 'name': 'node1', - 'cluster': 'cluster1', - 'profile': 'profile1', - 'role': 'master', - 'metadata': ['user=demo'], - } - args = self._make_args(args) - attrs = { - 'name': 'node1', - 'cluster_id': 'cluster1', - 'profile_id': 'profile1', - 'role': 'master', - 'metadata': {'user': 'demo'}, - } - service = mock.Mock() - node = mock.Mock() - node.id = 'node_id' - service.create_node.return_value = node - sh.do_node_create(service, args) - service.create_node.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, 'node_id') - - @mock.patch.object(sh, '_show_node') - def test_do_node_show(self, mock_show): - service = mock.Mock() - args = { - 'id': 'node1', - 'details': False - } - args = self._make_args(args) - sh.do_node_show(service, args) - mock_show.assert_called_once_with(service, 'node1', False) - - def test_do_node_delete(self): - service = mock.Mock() - args = self._make_args({'id': ['node1']}) - service.delete_node = mock.Mock() - - sh.do_node_delete(service, args) - - service.delete_node.assert_called_once_with('node1', False) - - def test_do_node_delete_not_found(self): - service = mock.Mock() - ex = oexc.ResourceNotFound - service.delete_node.side_effect = ex - - args = self._make_args({'id': ['node1']}) - ex = self.assertRaises(exc.CommandError, - sh.do_node_delete, service, args) - msg = _('Failed to delete some of the specified nodes.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_node_check(self): - service = mock.Mock() - args = self._make_args({'id': ['node1']}) - service.check_node = mock.Mock() - - sh.do_node_check(service, args) - - service.check_node.assert_called_once_with('node1') - - def test_do_node_check_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.check_node.side_effect = ex - - args = self._make_args({'id': ['node1']}) - ex = self.assertRaises(exc.CommandError, - sh.do_node_check, service, args) - msg = _('Failed to check some of the specified nodes.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_node_recover(self): - service = mock.Mock() - args = self._make_args({'id': ['node1']}) - service.check_node = mock.Mock() - - sh.do_node_recover(service, args) - - service.recover_node.assert_called_once_with('node1') - - def test_do_node_recover_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.recover_node.side_effect = ex - - args = self._make_args({'id': ['node1']}) - ex = self.assertRaises(exc.CommandError, - sh.do_node_recover, service, args) - msg = _('Failed to recover some of the specified nodes.') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sh, '_show_node') - def test_do_node_update(self, mock_show): - service = mock.Mock() - args = { - 'id': 'node_id', - 'name': 'node1', - 'role': 'master', - 'profile': 'profile1', - 'metadata': ['user=demo'], - } - args = self._make_args(args) - attrs = { - 'name': 'node1', - 'role': 'master', - 'profile_id': 'profile1', - 'metadata': {'user': 'demo'}, - } - node = mock.Mock() - node.id = 'node_id' - service.get_node.return_value = node - sh.do_node_update(service, args) - service.get_node.assert_called_once_with('node_id') - service.update_node.assert_called_once_with('node_id', **attrs) - mock_show.assert_called_once_with(service, 'node_id') - - @mock.patch.object(utils, 'print_list') - def test_do_event_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', - 'action', 'status', 'status_reason', 'level'] - args = { - 'sort': 'timestamp:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'filters': ['action=NODE_DELETE'], - 'full_id': True, - } - queries = copy.deepcopy(args) - del queries['full_id'] - del queries['filters'] - queries['action'] = 'NODE_DELETE' - args = self._make_args(args) - formatters = {} - events = mock.Mock() - service.events.return_value = events - - sh.do_event_list(service, args) - - service.events.assert_called_once_with(**queries) - mock_print.assert_called_once_with(events, fields, - formatters=formatters) - - @mock.patch.object(utils, 'print_dict') - def test_do_event_show(self, mock_print): - class FakeEvent(object): - def to_dict(self): - pass - - service = mock.Mock() - args = { - 'id': 'event_id' - } - args = self._make_args(args) - - event = FakeEvent() - service.get_event.return_value = event - sh.do_event_show(service, args) - service.get_event.assert_called_once_with('event_id') - mock_print.assert_called_once_with(event.to_dict()) - - def test_do_event_show_not_found(self): - service = mock.Mock() - args = self._make_args({'id': 'FAKE'}) - # event not found - ex = exc.CommandError - service.get_event.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(ex, - sh.do_event_show, - service, args) - - @mock.patch.object(utils, 'print_list') - def test_do_action_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'action', 'status', 'target', 'depends_on', - 'depended_by', 'created_at'] - args = { - 'sort': 'status', - 'limit': 20, - 'marker': 'marker_id', - } - queries = copy.deepcopy(args) - args = self._make_args(args) - args.filters = ['status=ACTIVE'] - queries['status'] = 'ACTIVE' - actions = mock.Mock() - service.actions.return_value = actions - formatters = { - 'depends_on': mock.ANY, - 'depended_by': mock.ANY - } - args.full_id = True - sortby_index = None - sh.do_action_list(service, args) - service.actions.assert_called_once_with(**queries) - mock_print.assert_called_once_with(actions, fields, - formatters=formatters, - sortby_index=sortby_index) - - @mock.patch.object(utils, 'print_dict') - def test_do_action_show(self, mock_print): - class FakeAction(object): - def to_dict(self): - pass - - service = mock.Mock() - args = self._make_args({'id': 'action_id'}) - - action = FakeAction() - service.get_action.return_value = action - formatters = { - 'inputs': utils.json_formatter, - 'outputs': utils.json_formatter, - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'depends_on': utils.list_formatter, - 'depended_by': utils.list_formatter, - } - sh.do_action_show(service, args) - service.get_action.assert_called_once_with('action_id') - mock_print.assert_called_once_with(action.to_dict(), - formatters=formatters) - - def test_do_action_show_not_found(self): - service = mock.Mock() - args = self._make_args({'id': 'fake_id'}) - - service.get_action.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_action_show, - service, args) - msg = _('Action not found: fake_id') - self.assertEqual(msg, six.text_type(ex)) diff --git a/senlinclient/v1/shell.py b/senlinclient/v1/shell.py deleted file mode 100644 index 6ef78e3..0000000 --- a/senlinclient/v1/shell.py +++ /dev/null @@ -1,1314 +0,0 @@ -# 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 - -from openstack import exceptions as sdk_exc -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common.i18n import _LW -from senlinclient.common import utils - -logger = logging.getLogger(__name__) - - -def show_deprecated(deprecated, recommended): - logger.warning(_LW('"%(old)s" is deprecated, ' - 'please use "%(new)s" instead.'), - {'old': deprecated, - 'new': recommended} - ) - - -def do_build_info(service, args=None): - """Retrieve build information. - - :param sc: Instance of senlinclient. - :param args: Additional command line arguments, if any. - """ - show_deprecated('senlin build-info', 'openstack cluster build info') - result = service.get_build_info() - - formatters = { - 'api': utils.json_formatter, - 'engine': utils.json_formatter, - } - utils.print_dict(result, formatters=formatters) - - -# PROFILE TYPES - - -def do_profile_type_list(service, args=None): - """List the available profile types. - - :param sc: Instance of senlinclient. - :param args: Additional command line arguments, if any. - """ - show_deprecated('senlin profile-type-list', - 'openstack cluster profile type list') - types = service.profile_types() - utils.print_list(types, ['name'], sortby_index=0) - - -@utils.arg('type_name', metavar='', - help=_('Profile type to retrieve.')) -@utils.arg('-F', '--format', metavar='', - choices=utils.supported_formats.keys(), - help=_("The template output format, one of: %s.") - % ', '.join(utils.supported_formats.keys())) -def do_profile_type_show(service, args): - """Get the details about a profile type.""" - show_deprecated('senlin profile-type-show', - 'openstack cluster profile type show') - try: - res = service.get_profile_type(args.type_name) - except sdk_exc.ResourceNotFound: - raise exc.CommandError( - _('Profile Type not found: %s') % args.type_name) - - pt = res.to_dict() - - if args.format: - print(utils.format_output(pt, format=args.format)) - else: - print(utils.format_output(pt)) - - -# PROFILES - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned profiles. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of profiles returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return profiles that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include profiles from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_profile_list(service, args=None): - """List profiles that meet the criteria.""" - show_deprecated('senlin profile-list', 'openstack cluster profile list') - fields = ['id', 'name', 'type', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 1 - - profiles = service.profiles(**queries) - formatters = {} - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - } - utils.print_list(profiles, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_profile(service, profile_id): - try: - profile = service.get_profile(profile_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Profile not found: %s') % profile_id) - - formatters = { - 'metadata': utils.json_formatter, - } - - formatters['spec'] = utils.nested_dict_formatter( - ['type', 'version', 'properties'], - ['property', 'value']) - - utils.print_dict(profile.to_dict(), formatters=formatters) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file used to create the profile.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the profile. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the profile to create.')) -def do_profile_create(service, args): - """Create a profile.""" - show_deprecated('senlin profile-create', - 'openstack cluster profile create') - spec = utils.get_spec_content(args.spec_file) - type_name = spec.get('type', None) - type_version = spec.get('version', None) - properties = spec.get('properties', None) - if type_name is None: - raise exc.CommandError(_("Missing 'type' key in spec file.")) - if type_version is None: - raise exc.CommandError(_("Missing 'version' key in spec file.")) - if properties is None: - raise exc.CommandError(_("Missing 'properties' key in spec file.")) - - if type_name == 'os.heat.stack': - stack_properties = utils.process_stack_spec(properties) - spec['properties'] = stack_properties - - params = { - 'name': args.name, - 'spec': spec, - 'metadata': utils.format_parameters(args.metadata), - } - - profile = service.create_profile(**params) - _show_profile(service, profile.id) - - -@utils.arg('id', metavar='', - help=_('Name or ID of profile to show.')) -def do_profile_show(service, args): - """Show the profile details.""" - show_deprecated('senlin profile-show', 'openstack cluster profile show') - _show_profile(service, args.id) - - -@utils.arg('-n', '--name', metavar='', - help=_('The new name for the profile.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the profile. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of the profile to update.')) -def do_profile_update(service, args): - """Update a profile.""" - show_deprecated('senlin profile-update', - 'openstack cluster profile update') - params = { - 'name': args.name, - } - if args.metadata: - params['metadata'] = utils.format_parameters(args.metadata) - - # Find the profile first, we need its id - try: - profile = service.get_profile(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Profile not found: %s') % args.id) - service.update_profile(profile.id, **params) - _show_profile(service, profile.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of profile(s) to delete.')) -def do_profile_delete(service, args): - """Delete profile(s).""" - show_deprecated('senlin profile-delete', - 'openstack cluster profile delete') - failure_count = 0 - - for pid in args.id: - try: - service.delete_profile(pid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified profile(s).') - raise exc.CommandError(msg) - print('Profile deleted: %s' % args.id) - - -# POLICY TYPES - - -def do_policy_type_list(service, args): - """List the available policy types.""" - show_deprecated('senlin policy-type-list', - 'openstack cluster policy type list') - types = service.policy_types() - utils.print_list(types, ['name'], sortby_index=0) - - -@utils.arg('type_name', metavar='', - help=_('Policy type to retrieve.')) -@utils.arg('-F', '--format', metavar='', - choices=utils.supported_formats.keys(), - help=_("The template output format, one of: %s.") - % ', '.join(utils.supported_formats.keys())) -def do_policy_type_show(service, args): - """Get the details about a policy type.""" - show_deprecated('senlin policy-type-show', - 'openstack cluster policy type show') - try: - res = service.get_policy_type(args.type_name) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Policy type not found: %s') % args.type_name) - - pt = res.to_dict() - if args.format: - print(utils.format_output(pt, format=args.format)) - else: - print(utils.format_output(pt)) - - -# POLICIES - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned policies. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of policies returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return policies that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include policies from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_policy_list(service, args=None): - """List policies that meet the criteria.""" - show_deprecated('senlin policy-list', 'openstack cluster policy list') - fields = ['id', 'name', 'type', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 1 - policies = service.policies(**queries) - formatters = {} - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8] - } - utils.print_list(policies, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_policy(service, policy_id): - try: - policy = service.get_policy(policy_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Policy not found: %s') % policy_id) - - formatters = { - 'metadata': utils.json_formatter, - 'spec': utils.json_formatter, - } - utils.print_dict(policy.to_dict(), formatters=formatters) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file used to create the policy.')) -@utils.arg('name', metavar='', - help=_('Name of the policy to create.')) -def do_policy_create(service, args): - """Create a policy.""" - show_deprecated('senlin policy-create', 'openstack cluster policy create') - spec = utils.get_spec_content(args.spec_file) - attrs = { - 'name': args.name, - 'spec': spec, - } - - policy = service.create_policy(**attrs) - _show_policy(service, policy.id) - - -@utils.arg('id', metavar='', - help=_('Name of the policy to be updated.')) -def do_policy_show(service, args): - """Show the policy details.""" - show_deprecated('senlin policy-show', 'openstack cluster policy show') - _show_policy(service, policy_id=args.id) - - -@utils.arg('-n', '--name', metavar='', - help=_('New name of the policy to be updated.')) -@utils.arg('id', metavar='', - help=_('Name of the policy to be updated.')) -def do_policy_update(service, args): - """Update a policy.""" - show_deprecated('senlin policy-update', 'openstack cluster policy update') - params = { - 'name': args.name, - } - - policy = service.get_policy(args.id) - if policy is not None: - service.update_policy(policy.id, **params) - _show_policy(service, policy_id=policy.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of policy(s) to delete.')) -def do_policy_delete(service, args): - """Delete policy(s).""" - show_deprecated('senlin policy-delete', 'openstack cluster policy delete') - failure_count = 0 - - for pid in args.id: - try: - service.delete_policy(pid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified policy(s).') - raise exc.CommandError(msg) - print('Policy deleted: %s' % args.id) - - -# CLUSTERS - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned clusters. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of clusters returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return clusters that appear after the given cluster ' - 'ID.')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the cluster list should include clusters from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_cluster_list(service, args=None): - """List the user's clusters.""" - show_deprecated('senlin cluster-list', 'openstack cluster list') - fields = ['id', 'name', 'status', 'created_at', 'updated_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 3 - - clusters = service.clusters(**queries) - formatters = {} - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8] - } - utils.print_list(clusters, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_cluster(service, cluster_id): - try: - cluster = service.get_cluster(cluster_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Cluster not found: %s') % cluster_id) - - formatters = { - 'metadata': utils.json_formatter, - 'nodes': utils.list_formatter, - } - utils.print_dict(cluster.to_dict(), formatters=formatters) - - -@utils.arg('-p', '--profile', metavar='', required=True, - help=_('Profile Id used for this cluster.')) -@utils.arg('-n', '--min-size', metavar='', default=0, - help=_('Min size of the cluster. Default to 0.')) -@utils.arg('-m', '--max-size', metavar='', default=-1, - help=_('Max size of the cluster. Default to -1, means unlimited.')) -@utils.arg('-c', '--desired-capacity', metavar='', default=0, - help=_('Desired capacity of the cluster. Default to min_size if ' - 'min_size is specified else 0.')) -@utils.arg('-t', '--timeout', metavar='', type=int, - help=_('Cluster creation timeout in seconds.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the cluster. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the cluster to create.')) -def do_cluster_create(service, args): - """Create the cluster.""" - show_deprecated('senlin cluster-create', 'openstack cluster create') - if args.min_size and not args.desired_capacity: - args.desired_capacity = args.min_size - attrs = { - 'name': args.name, - 'profile_id': args.profile, - 'min_size': args.min_size, - 'max_size': args.max_size, - 'desired_capacity': args.desired_capacity, - 'metadata': utils.format_parameters(args.metadata), - 'timeout': args.timeout - } - - cluster = service.create_cluster(**attrs) - _show_cluster(service, cluster.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of cluster(s) to delete.')) -def do_cluster_delete(service, args): - """Delete the cluster(s).""" - show_deprecated('senlin cluster-delete', 'openstack cluster delete') - failure_count = 0 - - for cid in args.id: - try: - service.delete_cluster(cid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified clusters.') - raise exc.CommandError(msg) - print('Request accepted') - - -@utils.arg('-p', '--profile', metavar='', - help=_('ID of new profile to use.')) -@utils.arg('-t', '--timeout', metavar='', - help=_('New timeout (in seconds) value for the cluster.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the cluster. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('-n', '--name', metavar='', - help=_('New name for the cluster to update.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to be updated.')) -def do_cluster_update(service, args): - """Update the cluster.""" - show_deprecated('senlin cluster-update', 'openstack cluster update') - cluster = service.get_cluster(args.id) - attrs = { - 'name': args.name, - 'profile_id': args.profile, - 'metadata': utils.format_parameters(args.metadata), - 'timeout': args.timeout, - } - - service.update_cluster(cluster.id, **attrs) - _show_cluster(service, cluster.id) - - -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to show.')) -def do_cluster_show(service, args): - """Show details of the cluster.""" - show_deprecated('senlin cluster-show', 'openstack cluster show') - _show_cluster(service, args.id) - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned nodes. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of nodes returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return nodes that appear after the given node ID.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to nodes from.')) -def do_cluster_node_list(service, args): - """List nodes from cluster.""" - show_deprecated('senlin cluster-node-list', - 'openstack cluster node members list') - queries = { - 'cluster_id': args.id, - 'limit': args.limit, - 'marker': args.marker, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - nodes = service.nodes(**queries) - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'physical_id': lambda x: x.physical_id[:8] if x.physical_id else '' - } - else: - formatters = {} - - fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] - utils.print_list(nodes, fields, formatters=formatters, sortby_index=5) - - -@utils.arg('-n', '--nodes', metavar='', required=True, - help=_('ID of nodes to be added; multiple nodes can be separated ' - 'with ","')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_node_add(service, args): - """Add specified nodes to cluster.""" - show_deprecated('senlin cluster-node-add', - 'openstack cluster node members add') - node_ids = args.nodes.split(',') - resp = service.cluster_add_nodes(args.id, node_ids) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-n', '--nodes', metavar='', required=True, - help=_('ID of nodes to be deleted; multiple nodes can be separated ' - 'with ",".')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_node_del(service, args): - """Delete specified nodes from cluster.""" - show_deprecated('senlin cluster-node-del', - 'openstack cluster node members del') - node_ids = args.nodes.split(',') - resp = service.cluster_del_nodes(args.id, node_ids) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-c', '--capacity', metavar='', type=int, - help=_('The desired number of nodes of the cluster.')) -@utils.arg('-a', '--adjustment', metavar='', type=int, - help=_('A positive integer meaning the number of nodes to add, ' - 'or a negative integer indicating the number of nodes to ' - 'remove.')) -@utils.arg('-p', '--percentage', metavar='', type=float, - help=_('A value that is interpreted as the percentage of size ' - 'adjustment. This value can be positive or negative.')) -@utils.arg('-t', '--min-step', metavar='', type=int, - help=_('An integer specifying the number of nodes for adjustment ' - 'when is specified.')) -@utils.arg('-s', '--strict', action='store_true', default=False, - help=_('A boolean specifying whether the resize should be ' - 'performed on a best-effort basis when the new capacity ' - 'may go beyond size constraints.')) -@utils.arg('-n', '--min-size', metavar='MIN', type=int, - help=_('New lower bound of cluster size.')) -@utils.arg('-m', '--max-size', metavar='MAX', type=int, - help=_('New upper bound of cluster size. A value of -1 indicates ' - 'no upper limit on cluster size.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_resize(service, args): - """Resize a cluster.""" - # validate parameters - # NOTE: this will be much simpler if cliutils supports exclusive groups - show_deprecated('senlin cluster-resize', 'openstack cluster resize') - action_args = {} - - capacity = args.capacity - adjustment = args.adjustment - percentage = args.percentage - min_size = args.min_size - max_size = args.max_size - min_step = args.min_step - - if sum(v is not None for v in (capacity, adjustment, percentage)) > 1: - raise exc.CommandError(_("Only one of 'capacity', 'adjustment' and " - "'percentage' can be specified.")) - - action_args['adjustment_type'] = None - action_args['number'] = None - - if capacity is not None: - if capacity < 0: - raise exc.CommandError(_('Cluster capacity must be larger than ' - ' or equal to zero.')) - action_args['adjustment_type'] = 'EXACT_CAPACITY' - action_args['number'] = capacity - - if adjustment is not None: - if adjustment == 0: - raise exc.CommandError(_('Adjustment cannot be zero.')) - action_args['adjustment_type'] = 'CHANGE_IN_CAPACITY' - action_args['number'] = adjustment - - if percentage is not None: - if (percentage == 0 or percentage == 0.0): - raise exc.CommandError(_('Percentage cannot be zero.')) - action_args['adjustment_type'] = 'CHANGE_IN_PERCENTAGE' - action_args['number'] = percentage - - if min_step is not None: - if percentage is None: - raise exc.CommandError(_('Min step is only used with percentage.')) - - if min_size is not None: - if min_size < 0: - raise exc.CommandError(_('Min size cannot be less than zero.')) - if max_size is not None and max_size >= 0 and min_size > max_size: - raise exc.CommandError(_('Min size cannot be larger than ' - 'max size.')) - if capacity is not None and min_size > capacity: - raise exc.CommandError(_('Min size cannot be larger than the ' - 'specified capacity')) - - if max_size is not None: - if capacity is not None and max_size > 0 and max_size < capacity: - raise exc.CommandError(_('Max size cannot be less than the ' - 'specified capacity.')) - # do a normalization - if max_size < 0: - max_size = -1 - - action_args['min_size'] = min_size - action_args['max_size'] = max_size - action_args['min_step'] = min_step - action_args['strict'] = args.strict - - resp = service.cluster_resize(args.id, **action_args) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-c', '--count', metavar='', - help=_('Number of nodes to be added to the specified cluster.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_scale_out(service, args): - """Scale out a cluster by the specified number of nodes.""" - show_deprecated('senlin cluster-scale-out', 'openstack cluster scale out') - resp = service.cluster_scale_out(args.id, args.count) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-c', '--count', metavar='', - help=_('Number of nodes to be deleted from the specified cluster.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_scale_in(service, args): - """Scale in a cluster by the specified number of nodes.""" - show_deprecated('senlin cluster-scale-in', 'openstack cluster scale in') - resp = service.cluster_scale_in(args.id, args.count) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned results. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to query on.')) -def do_cluster_policy_list(service, args): - """List policies from cluster.""" - show_deprecated('senlin cluster-policy-list', - 'openstack cluster policy binding list') - fields = ['policy_id', 'policy_name', 'policy_type', 'enabled'] - - cluster = service.get_cluster(args.id) - queries = { - 'sort': args.sort, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 3 - policies = service.cluster_policies(cluster.id, **queries) - formatters = {} - if not args.full_id: - formatters = { - 'policy_id': lambda x: x.id[:8] - } - - utils.print_list(policies, fields, formatters=formatters, - sortby_index=sortby_index) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of the policy to query on.')) -@utils.arg('id', metavar='', - help=_('ID or name of the cluster to query on.')) -def do_cluster_policy_show(service, args): - """Show a specific policy that is bound to the specified cluster.""" - show_deprecated('senlin cluster-policy-show', - 'openstack cluster policy binding show') - binding = service.get_cluster_policy(args.policy, args.id) - utils.print_dict(binding.to_dict()) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be attached.')) -@utils.arg('-e', '--enabled', default=True, action="store_true", - help=_('Whether the policy should be enabled once attached. ' - 'Default to enabled.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_attach(service, args): - """Attach policy to cluster.""" - show_deprecated('senlin cluster-policy-attach', - 'openstack cluster policy attach') - kwargs = { - 'enabled': args.enabled, - } - - resp = service.cluster_attach_policy(args.id, args.policy, **kwargs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be detached.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_detach(service, args): - """Detach policy from cluster.""" - show_deprecated('senlin cluster-policy-detach', - 'openstack cluster policy detach') - resp = service.cluster_detach_policy(args.id, args.policy) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be updated.')) -@utils.arg('-e', '--enabled', metavar='', - help=_('Whether the policy should be enabled.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_update(service, args): - """Update a policy's properties on a cluster.""" - show_deprecated('senlin cluster-policy-update', - 'openstack cluster policy binding update') - kwargs = { - 'enabled': args.enabled, - } - - resp = service.cluster_update_policy(args.id, args.policy, **kwargs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of cluster(s) to operate on.')) -def do_cluster_check(service, args): - """Check the cluster(s).""" - show_deprecated('senlin cluster-check', 'openstack cluster check') - for cid in args.id: - resp = service.check_cluster(cid) - print('Cluster check request on cluster %(cid)s is accepted by ' - 'action %(action)s.' % {'cid': cid, 'action': resp['action']}) - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of cluster(s) to operate on.')) -def do_cluster_recover(service, args): - """Recover the cluster(s).""" - show_deprecated('senlin cluster-recover', 'openstack cluster recover') - for cid in args.id: - resp = service.recover_cluster(cid) - print('Cluster recover request on cluster %(cid)s is accepted by ' - 'action %(action)s.' % {'cid': cid, 'action': resp['action']}) - - -# NODES - - -@utils.arg('-c', '--cluster', metavar='', - help=_('ID or name of cluster from which nodes are to be listed.')) -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned nodes. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of nodes returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return nodes that appear after the given node ID.')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that this node list should include nodes from ' - 'all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_node_list(service, args): - """Show list of nodes.""" - show_deprecated('senlin node-list', 'openstack cluster node list') - - fields = ['id', 'name', 'index', 'status', 'cluster_id', 'physical_id', - 'profile_name', 'created_at', 'updated_at'] - queries = { - 'cluster_id': args.cluster, - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 6 - - nodes = service.nodes(**queries) - - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'cluster_id': lambda x: x.cluster_id[:8] if x.cluster_id else '', - 'physical_id': lambda x: x.physical_id[:8] if x.physical_id else '' - } - else: - formatters = {} - - utils.print_list(nodes, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_node(service, node_id, show_details=False): - """Show detailed info about the specified node.""" - args = {'show_details': True} if show_details else None - try: - node = service.get_node(node_id, args=args) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Node not found: %s') % node_id) - - formatters = { - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - } - data = node.to_dict() - if show_details: - formatters['details'] = utils.nested_dict_formatter( - list(node['details'].keys()), ['property', 'value']) - - utils.print_dict(data, formatters=formatters) - - -@utils.arg('-p', '--profile', metavar='', required=True, - help=_('Profile Id used for this node.')) -@utils.arg('-c', '--cluster', metavar='', - help=_('Cluster Id for this node.')) -@utils.arg('-r', '--role', metavar='', - help=_('Role for this node in the specific cluster.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the node. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the node to create.')) -def do_node_create(service, args): - """Create the node.""" - show_deprecated('senlin node-create', 'openstack cluster node create') - attrs = { - 'name': args.name, - 'cluster_id': args.cluster, - 'profile_id': args.profile, - 'role': args.role, - 'metadata': utils.format_parameters(args.metadata), - } - - node = service.create_node(**attrs) - _show_node(service, node.id) - - -@utils.arg('-D', '--details', default=False, action="store_true", - help=_('Include physical object details.')) -@utils.arg('id', metavar='', - help=_('Name or ID of the node to show the details for.')) -def do_node_show(service, args): - """Show detailed info about the specified node.""" - show_deprecated('senlin node-show', 'openstack cluster node show') - _show_node(service, args.id, args.details) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of node(s) to delete.')) -def do_node_delete(service, args): - """Delete the node(s).""" - show_deprecated('senlin node-delete', 'openstack cluster node delete') - failure_count = 0 - - for nid in args.id: - try: - service.delete_node(nid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified nodes.') - raise exc.CommandError(msg) - print('Request accepted') - - -@utils.arg('-n', '--name', metavar='', - help=_('New name for the node.')) -@utils.arg('-p', '--profile', metavar='', - help=_('ID of new profile to use.')) -@utils.arg('-r', '--role', metavar='', - help=_('Role for this node in the specific cluster.')) -@utils.arg('-M', '--metadata', metavar='', - help=_('Metadata values to be attached to the node. ' - 'Metadata can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of node to update.')) -def do_node_update(service, args): - """Update the node.""" - show_deprecated('senlin node-update', 'openstack cluster node update') - # Find the node first, we need its UUID - try: - node = service.get_node(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Node not found: %s') % args.id) - - attrs = { - 'name': args.name, - 'role': args.role, - 'profile_id': args.profile, - 'metadata': utils.format_parameters(args.metadata), - } - - service.update_node(args.id, **attrs) - _show_node(service, node.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID of node(s) to check.')) -def do_node_check(service, args): - """Check the node(s).""" - show_deprecated('senlin node-check', 'openstack cluster node check') - failure_count = 0 - - for nid in args.id: - try: - service.check_node(nid) - except exc.HTTPNotFound: - failure_count += 1 - print('Node id "%s" not found' % nid) - if failure_count > 0: - msg = _('Failed to check some of the specified nodes.') - raise exc.CommandError(msg) - print('Request accepted') - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID of node(s) to recover.')) -def do_node_recover(service, args): - """Recover the node(s).""" - show_deprecated('senlin node-recover', 'openstack cluster node recover') - failure_count = 0 - - for nid in args.id: - try: - service.recover_node(nid) - except exc.HTTPNotFound: - failure_count += 1 - print('Node id "%s" not found' % nid) - if failure_count > 0: - msg = _('Failed to recover some of the specified nodes.') - raise exc.CommandError(msg) - print('Request accepted') - - -# RECEIVERS - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned receivers. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of receivers returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return receivers that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include receivers from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_receiver_list(service, args): - """List receivers that meet the criteria.""" - show_deprecated('senlin receiver-list', 'openstack cluster receiver list') - fields = ['id', 'name', 'type', 'cluster_id', 'action', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 0 - - receivers = service.receivers(**queries) - formatters = {} - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'cluster_id': lambda x: x.cluster_id[:8], - } - utils.print_list(receivers, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_receiver(service, receiver_id): - try: - receiver = service.get_receiver(receiver_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Receiver not found: %s') % receiver_id) - - formatters = { - 'actor': utils.json_formatter, - 'params': utils.json_formatter, - 'channel': utils.json_formatter, - } - - utils.print_dict(receiver.to_dict(), formatters=formatters) - - -@utils.arg('id', metavar='', - help=_('Name or ID of the receiver to show.')) -def do_receiver_show(service, args): - """Show the receiver details.""" - show_deprecated('senlin receiver-show', 'openstack cluster receiver show') - _show_receiver(service, receiver_id=args.id) - - -@utils.arg('-t', '--type', metavar='', default='webhook', - help=_('Type of the receiver to create.')) -@utils.arg('-c', '--cluster', metavar='', required=True, - help=_('Targeted cluster for this receiver.')) -@utils.arg('-a', '--action', metavar='', required=True, - help=_('Name or ID of the targeted action to be triggered.')) -@utils.arg('-P', '--params', metavar='', - help=_('A dictionary of parameters that will be passed to target ' - 'action when the receiver is triggered.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the receiver to create.')) -def do_receiver_create(service, args): - """Create a receiver.""" - show_deprecated('senlin receiver-create', - 'openstack cluster receiver create') - - params = { - 'name': args.name, - 'type': args.type, - 'cluster_id': args.cluster, - 'action': args.action, - 'params': utils.format_parameters(args.params) - } - - receiver = service.create_receiver(**params) - _show_receiver(service, receiver.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of receiver(s) to delete.')) -def do_receiver_delete(service, args): - """Delete receiver(s).""" - show_deprecated('senlin receiver-delete', - 'openstack cluster receiver delete') - failure_count = 0 - - for wid in args.id: - try: - service.delete_receiver(wid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified receiver(s).') - raise exc.CommandError(msg) - print('Receivers deleted: %s' % args.id) - - -# EVENTS - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned events. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of events returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return events that appear after the given event ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Whether events from all projects should be listed. ' - ' Default to False. Setting this to True may demand ' - 'for an admin privilege.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_event_list(service, args): - """List events.""" - show_deprecated('senlin event-list', 'openstack cluster event list') - fields = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', 'action', - 'status', 'status_reason', 'level'] - queries = { - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - formatters = {} - if not args.full_id: - formatters['id'] = lambda x: x.id[:8] - formatters['obj_id'] = lambda x: x.obj_id[:8] if x.obj_id else '' - - events = service.events(**queries) - utils.print_list(events, fields, formatters=formatters) - - -@utils.arg('id', metavar='', - help=_('ID of event to display details for.')) -def do_event_show(service, args): - """Describe the event.""" - show_deprecated('senlin event-show', 'openstack cluster event show') - try: - event = service.get_event(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_("Event not found: %s") % args.id) - - utils.print_dict(event.to_dict()) - - -# ACTIONS - - -@utils.arg('-f', '--filters', metavar='', - help=_('Filter parameters to apply on returned actions. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of actions returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return actions that appear after the given node ID.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_action_list(service, args): - """List actions.""" - show_deprecated('senlin action-list', 'openstack cluster action list') - fields = ['id', 'name', 'action', 'status', 'target', 'depends_on', - 'depended_by', 'created_at'] - - queries = { - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 0 - - actions = service.actions(**queries) - - formatters = {} - if args.full_id: - f_depon = lambda x: '\n'.join(a for a in x.depends_on) - f_depby = lambda x: '\n'.join(a for a in x.depended_by) - - formatters['depends_on'] = f_depon - formatters['depended_by'] = f_depby - else: - formatters['id'] = lambda x: x.id[:8] - formatters['target'] = lambda x: x.target[:8] - f_depon = lambda x: '\n'.join(a[:8] for a in x.depends_on) - f_depby = lambda x: '\n'.join(a[:8] for a in x.depended_by) - formatters['depends_on'] = f_depon - formatters['depended_by'] = f_depby - - utils.print_list(actions, fields, formatters=formatters, - sortby_index=sortby_index) - - -@utils.arg('id', metavar='', - help=_('Name or ID of the action to show the details for.')) -def do_action_show(service, args): - """Show detailed info about the specified action.""" - show_deprecated('senlin action-show', 'openstack cluster action show') - try: - action = service.get_action(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Action not found: %s') % args.id) - - formatters = { - 'inputs': utils.json_formatter, - 'outputs': utils.json_formatter, - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'depends_on': utils.list_formatter, - 'depended_by': utils.list_formatter, - } - - utils.print_dict(action.to_dict(), formatters=formatters) diff --git a/setup.cfg b/setup.cfg index 65a456d..c55ba74 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,9 +23,6 @@ packages = senlinclient [entry_points] -console_scripts = - senlin = senlinclient.shell:main - openstack.cli.extension = clustering = senlinclient.plugin