]> git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/arg_validators.py
3866027ef97eca8838412d2336ae76c2664e6d4a
[ceph.git] / ceph / src / ceph-volume / ceph_volume / util / arg_validators.py
1 import argparse
2 import os
3 from ceph_volume import terminal
4 from ceph_volume import decorators
5 from ceph_volume.util import disk
6
7
8 class LVPath(object):
9 """
10 A simple validator to ensure that a logical volume is specified like::
11
12 <vg name>/<lv name>
13
14 Or a full path to a device, like ``/dev/sda``
15
16 Because for LVM it is better to be specific on what group does an lv
17 belongs to.
18 """
19
20 def __call__(self, string):
21 error = None
22 if string.startswith('/'):
23 if not os.path.exists(string):
24 error = "Argument (device) does not exist: %s" % string
25 raise argparse.ArgumentError(None, error)
26 else:
27 return string
28 try:
29 vg, lv = string.split('/')
30 except ValueError:
31 error = "Logical volume must be specified as 'volume_group/logical_volume' but got: %s" % string
32 raise argparse.ArgumentError(None, error)
33
34 if not vg:
35 error = "Didn't specify a volume group like 'volume_group/logical_volume', got: %s" % string
36 if not lv:
37 error = "Didn't specify a logical volume like 'volume_group/logical_volume', got: %s" % string
38
39 if error:
40 raise argparse.ArgumentError(None, error)
41 return string
42
43
44 class OSDPath(object):
45 """
46 Validate path exists and it looks like an OSD directory.
47 """
48
49 @decorators.needs_root
50 def __call__(self, string):
51 if not os.path.exists(string):
52 error = "Path does not exist: %s" % string
53 raise argparse.ArgumentError(None, error)
54
55 arg_is_partition = disk.is_partition(string)
56 if arg_is_partition:
57 return os.path.abspath(string)
58 absolute_path = os.path.abspath(string)
59 if not os.path.isdir(absolute_path):
60 error = "Argument is not a directory or device which is required to scan"
61 raise argparse.ArgumentError(None, error)
62 key_files = ['ceph_fsid', 'fsid', 'keyring', 'ready', 'type', 'whoami']
63 dir_files = os.listdir(absolute_path)
64 for key_file in key_files:
65 if key_file not in dir_files:
66 terminal.error('All following files must exist in path: %s' % ' '.join(key_files))
67 error = "Required file (%s) was not found in OSD dir path: %s" % (
68 key_file,
69 absolute_path
70 )
71 raise argparse.ArgumentError(None, error)
72
73 return os.path.abspath(string)
74
75
76 def exclude_group_options(parser, groups, argv=None):
77 """
78 ``argparse`` has the ability to check for mutually exclusive options, but
79 it only allows a basic XOR behavior: only one flag can be used from
80 a defined group of options. This doesn't help when two groups of options
81 need to be separated. For example, with filestore and bluestore, neither
82 set can be used in conjunction with the other set.
83
84 This helper validator will consume the parser to inspect the group flags,
85 and it will group them together from ``groups``. This allows proper error
86 reporting, matching each incompatible flag with its group name.
87
88 :param parser: The argparse object, once it has configured all flags. It is
89 required to contain the group names being used to validate.
90 :param groups: A list of group names (at least two), with the same used for
91 ``add_argument_group``
92 :param argv: Consume the args (sys.argv) directly from this argument
93
94 .. note: **Unfortunately** this will not be able to validate correctly when
95 using default flags. In the case of filestore vs. bluestore, ceph-volume
96 defaults to --bluestore, but we can't check that programmatically, we can
97 only parse the flags seen via argv
98 """
99 # Reduce the parser groups to only the groups we need to intersect
100 parser_groups = [g for g in parser._action_groups if g.title in groups]
101 # A mapping of the group name to flags/options
102 group_flags = {}
103 flags_to_verify = []
104 for group in parser_groups:
105 # option groups may have more than one item in ``option_strings``, this
106 # will loop over ``_group_actions`` which contains the
107 # ``option_strings``, like ``['--filestore']``
108 group_flags[group.title] = [
109 option for group_action in group._group_actions
110 for option in group_action.option_strings
111 ]
112
113 # Gather all the flags present in the groups so that we only check on those.
114 for flags in group_flags.values():
115 flags_to_verify.extend(flags)
116
117 seen = []
118 last_flag = None
119 last_group = None
120 for flag in argv:
121 if flag not in flags_to_verify:
122 continue
123 for group_name, flags in group_flags.items():
124 if flag in flags:
125 seen.append(group_name)
126 # We are mutually excluding groups, so having more than 1 group
127 # in ``seen`` means we must raise an error
128 if len(set(seen)) == len(groups):
129 terminal.warning('Incompatible flags were found, some values may get ignored')
130 msg = 'Cannot use %s (%s) with %s (%s)' % (
131 last_flag, last_group, flag, group_name
132 )
133 terminal.warning(msg)
134 last_group = group_name
135 last_flag = flag