From 50c1651bfa993e4dd714f3b59bd1469b7338cd8d Mon Sep 17 00:00:00 2001 From: Feng Shengqin Date: Thu, 15 Mar 2018 14:32:39 +0800 Subject: [PATCH] Check restart and auto_remove are setted for per container Co-Authored-By: Hongbin Lu Change-Id: Ifcfb7389a603f0d4d22d60fd2f00b9145d53d40a --- zunclient/common/cliutils.py | 28 +++++++++++++++ zunclient/osc/v1/containers.py | 30 ++++++++-------- zunclient/shell.py | 60 +++++++++++++++++++++----------- zunclient/v1/containers_shell.py | 36 +++++++++++-------- 4 files changed, 105 insertions(+), 49 deletions(-) diff --git a/zunclient/common/cliutils.py b/zunclient/common/cliutils.py index 51c0de60..83e5ab5a 100644 --- a/zunclient/common/cliutils.py +++ b/zunclient/common/cliutils.py @@ -18,6 +18,7 @@ from __future__ import print_function +import collections import getpass import inspect import os @@ -89,6 +90,15 @@ def arg(*args, **kwargs): return _decorator +def exclusive_arg(group_name, *args, **kwargs): + """Decorator for CLI mutually exclusive args.""" + def _decorator(func): + required = kwargs.pop('required', None) + add_exclusive_arg(func, group_name, required, *args, **kwargs) + return func + return _decorator + + def env(*args, **kwargs): """Returns the first environment variable set. @@ -115,6 +125,24 @@ def add_arg(func, *args, **kwargs): func.arguments.insert(0, (args, kwargs)) +def add_exclusive_arg(func, group_name, required, *args, **kwargs): + """Bind CLI mutally exclusive arguments to a shell.py `do_foo` function.""" + + if not hasattr(func, 'exclusive_args'): + func.exclusive_args = collections.defaultdict(list) + # Default required to False + func.exclusive_args['__required__'] = collections.defaultdict(bool) + + # NOTE(sirp): avoid dups that can occur when the module is shared across + # tests. + if (args, kwargs) not in func.exclusive_args[group_name]: + # Because of the semantics of decorator composition if we just append + # to the options list positional options will appear to be backwards. + func.exclusive_args[group_name].insert(0, (args, kwargs)) + if required is not None: + func.exclusive_args['__required__'][group_name] = required + + def unauthenticated(func): """Adds 'unauthenticated' attribute to decorated function. diff --git a/zunclient/osc/v1/containers.py b/zunclient/osc/v1/containers.py index f93d734a..5fa73f56 100644 --- a/zunclient/osc/v1/containers.py +++ b/zunclient/osc/v1/containers.py @@ -87,11 +87,18 @@ class CreateContainer(command.ShowOne): 'already exist on the node. ' '"always": Always pull the image from repository.' '"never": never pull the image') - parser.add_argument( + restart_auto_remove_args = parser.add_mutually_exclusive_group() + restart_auto_remove_args.add_argument( '--restart', metavar='', help='Restart policy to apply when a container exits' '(no, on-failure[:max-retry], always, unless-stopped)') + restart_auto_remove_args.add_argument( + '--auto-remove', + dest='auto_remove', + action='store_true', + default=False, + help='Automatically remove the container when it exits') parser.add_argument( '--image-driver', metavar='', @@ -142,12 +149,6 @@ class CreateContainer(command.ShowOne): metavar='', help='A dictionary to configure volumes mounted inside the ' 'container.') - parser.add_argument( - '--auto-remove', - dest='auto_remove', - action='store_true', - default=False, - help='Automatically remove the container when it exits') parser.add_argument( '--runtime', metavar='', @@ -705,11 +706,18 @@ class RunContainer(command.ShowOne): 'already exist on the node. ' '"always": Always pull the image from repository.' '"never": never pull the image') - parser.add_argument( + restart_auto_remove_args = parser.add_mutually_exclusive_group() + restart_auto_remove_args.add_argument( '--restart', metavar='', help='Restart policy to apply when a container exits' '(no, on-failure[:max-retry], always, unless-stopped)') + restart_auto_remove_args.add_argument( + '--auto-remove', + dest='auto_remove', + action='store_true', + default=False, + help='Automatically remove the container when it exits') parser.add_argument( '--image-driver', metavar='', @@ -760,12 +768,6 @@ class RunContainer(command.ShowOne): metavar='', help='A dictionary to configure volumes mounted inside the ' 'container.') - parser.add_argument( - '--auto-remove', - dest='auto_remove', - action='store_true', - default=False, - help='Automatically remove the container when it exits') parser.add_argument( '--runtime', metavar='', diff --git a/zunclient/shell.py b/zunclient/shell.py index e921a7d3..d6988d56 100644 --- a/zunclient/shell.py +++ b/zunclient/shell.py @@ -429,6 +429,7 @@ class OpenStackZunShell(object): continue action_help = desc.strip() + exclusive_args = getattr(callback, 'exclusive_args', {}) arguments = getattr(callback, 'arguments', []) subparser = ( @@ -443,29 +444,46 @@ class OpenStackZunShell(object): help=argparse.SUPPRESS,) self.subcommands[command] = subparser - for (args, kwargs) in arguments: - start_version = kwargs.get("start_version", None) - if start_version: - start_version = api_versions.APIVersion(start_version) - end_version = kwargs.get("end_version", None) - if end_version: - end_version = api_versions.APIVersion(end_version) - else: - end_version = api_versions.APIVersion( - "%s.latest" % start_version.ver_major) - if do_help: - kwargs["help"] = kwargs.get("help", "") + (msg % { - "start": start_version.get_string(), - "end": end_version.get_string()}) - else: - if not version.matches(start_version, end_version): - continue - kw = kwargs.copy() - kw.pop("start_version", None) - kw.pop("end_version", None) - subparser.add_argument(*args, **kwargs) + self._add_subparser_args(subparser, arguments, version, do_help, + msg) + self._add_subparser_exclusive_args(subparser, exclusive_args, + version, do_help, msg) subparser.set_defaults(func=callback) + def _add_subparser_exclusive_args(self, subparser, exclusive_args, + version, do_help, msg): + for group_name, arguments in exclusive_args.items(): + if group_name == '__required__': + continue + required = exclusive_args['__required__'][group_name] + exclusive_group = subparser.add_mutually_exclusive_group( + required=required) + self._add_subparser_args(exclusive_group, arguments, + version, do_help, msg) + + def _add_subparser_args(self, subparser, arguments, version, do_help, msg): + for (args, kwargs) in arguments: + start_version = kwargs.get("start_version", None) + if start_version: + start_version = api_versions.APIVersion(start_version) + end_version = kwargs.get("end_version", None) + if end_version: + end_version = api_versions.APIVersion(end_version) + else: + end_version = api_versions.APIVersion( + "%s.latest" % start_version.ver_major) + if do_help: + kwargs["help"] = kwargs.get("help", "") + (msg % { + "start": start_version.get_string(), + "end": end_version.get_string()}) + else: + if not version.matches(start_version, end_version): + continue + kw = kwargs.copy() + kw.pop("start_version", None) + kw.pop("end_version", None) + subparser.add_argument(*args, **kwargs) + def setup_debugging(self, debug): if debug: streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" diff --git a/zunclient/v1/containers_shell.py b/zunclient/v1/containers_shell.py index 8fb42567..2f30c8a1 100644 --- a/zunclient/v1/containers_shell.py +++ b/zunclient/v1/containers_shell.py @@ -33,6 +33,17 @@ def _show_container(container): utils.print_dict(container._info) +@utils.exclusive_arg( + 'restart_auto_remove', + '--auto-remove', + required=False, action='store_true', + help='Automatically remove the container when it exits') +@utils.exclusive_arg( + 'restart_auto_remove', + '--restart', + required=False, metavar='', + help='Restart policy to apply when a container exits' + '(no, on-failure[:max-retry], always, unless-stopped)') @utils.arg('-n', '--name', metavar='', help='name of the container') @@ -49,9 +60,6 @@ def _show_container(container): @utils.arg('--workdir', metavar='', help='The working directory for commands to run in') -@utils.arg('--auto-remove', - action='store_true', - help='Automatically remove the container when it exits') @utils.arg('--label', metavar='', action='append', default=[], @@ -69,10 +77,6 @@ def _show_container(container): '"always": Always pull the image from repository.' '"never": never pull the image') @utils.arg('image', metavar='', help='name or ID of the image') -@utils.arg('--restart', - metavar='', - help='Restart policy to apply when a container exits' - '(no, on-failure[:max-retry], always, unless-stopped)') @utils.arg('-i', '--interactive', dest='interactive', action='store_true', @@ -517,6 +521,17 @@ def do_kill(cs, args): {'container': container, 'e': e}) +@utils.exclusive_arg( + 'restart_auto_remove', + '--auto-remove', + required=False, action='store_true', + help='Automatically remove the container when it exits') +@utils.exclusive_arg( + 'restart_auto_remove', + '--restart', + required=False, metavar='', + help='Restart policy to apply when a container exits' + '(no, on-failure[:max-retry], always, unless-stopped)') @utils.arg('-n', '--name', metavar='', help='name of the container') @@ -533,9 +548,6 @@ def do_kill(cs, args): @utils.arg('--workdir', metavar='', help='The working directory for commands to run in') -@utils.arg('--auto-remove', - action='store_true', - help='Automatically remove the container when it exits') @utils.arg('--label', metavar='', action='append', default=[], @@ -553,10 +565,6 @@ def do_kill(cs, args): '"always": Always pull the image from repository.' '"never": never pull the image') @utils.arg('image', metavar='', help='name or ID of the image') -@utils.arg('--restart', - metavar='', - help='Restart policy to apply when a container exits' - '(no, on-failure[:max-retry], always, unless-stopped)') @utils.arg('-i', '--interactive', dest='interactive', action='store_true',