]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume util.arg_validators create an arg group exclusion check
authorAlfredo Deza <adeza@redhat.com>
Mon, 19 Feb 2018 21:28:52 +0000 (16:28 -0500)
committerAndrew Schoen <aschoen@redhat.com>
Fri, 23 Feb 2018 15:33:10 +0000 (09:33 -0600)
Signed-off-by: Alfredo Deza <adeza@redhat.com>
(cherry picked from commit dec6e086cf221e9b90f1b227aa63717d387f9f8e)

src/ceph-volume/ceph_volume/util/arg_validators.py

index 349d5da173861dcd4d3766e307b42243ff8d77e3..3866027ef97eca8838412d2336ae76c2664e6d4a 100644 (file)
@@ -71,3 +71,65 @@ class OSDPath(object):
                 raise argparse.ArgumentError(None, error)
 
         return os.path.abspath(string)
+
+
+def exclude_group_options(parser, groups, argv=None):
+    """
+    ``argparse`` has the ability to check for mutually exclusive options, but
+    it only allows a basic XOR behavior: only one flag can be used from
+    a defined group of options. This doesn't help when two groups of options
+    need to be separated. For example, with filestore and bluestore, neither
+    set can be used in conjunction with the other set.
+
+    This helper validator will consume the parser to inspect the group flags,
+    and it will group them together from ``groups``. This allows proper error
+    reporting, matching each incompatible flag with its group name.
+
+    :param parser: The argparse object, once it has configured all flags. It is
+                   required to contain the group names being used to validate.
+    :param groups: A list of group names (at least two), with the same used for
+                  ``add_argument_group``
+    :param argv: Consume the args (sys.argv) directly from this argument
+
+    .. note: **Unfortunately** this will not be able to validate correctly when
+    using default flags. In the case of filestore vs. bluestore, ceph-volume
+    defaults to --bluestore, but we can't check that programmatically, we can
+    only parse the flags seen via argv
+    """
+    # Reduce the parser groups to only the groups we need to intersect
+    parser_groups = [g for g in parser._action_groups if g.title in groups]
+    # A mapping of the group name to flags/options
+    group_flags = {}
+    flags_to_verify = []
+    for group in parser_groups:
+        # option groups may have more than one item in ``option_strings``, this
+        # will loop over ``_group_actions`` which contains the
+        # ``option_strings``, like ``['--filestore']``
+        group_flags[group.title] = [
+            option for group_action in group._group_actions
+            for option in group_action.option_strings
+        ]
+
+    # Gather all the flags present in the groups so that we only check on those.
+    for flags in group_flags.values():
+        flags_to_verify.extend(flags)
+
+    seen = []
+    last_flag = None
+    last_group = None
+    for flag in argv:
+        if flag not in flags_to_verify:
+            continue
+        for group_name, flags in group_flags.items():
+            if flag in flags:
+                seen.append(group_name)
+                # We are mutually excluding groups, so having more than 1 group
+                # in ``seen`` means we must raise an error
+                if len(set(seen)) == len(groups):
+                    terminal.warning('Incompatible flags were found, some values may get ignored')
+                    msg = 'Cannot use %s (%s) with %s (%s)' % (
+                        last_flag, last_group, flag, group_name
+                    )
+                    terminal.warning(msg)
+            last_group = group_name
+        last_flag = flag