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