]> git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/arg_validators.py
Import ceph 15.2.8
[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 from ceph_volume.util.device import Device
7
8
9 class ValidDevice(object):
10
11 def __init__(self, as_string=False, gpt_ok=False):
12 self.as_string = as_string
13 self.gpt_ok = gpt_ok
14
15 def __call__(self, dev_path):
16 device = self._is_valid_device(dev_path)
17 return self._format_device(device)
18
19 def _format_device(self, device):
20 if self.as_string:
21 if device.is_lv:
22 # all codepaths expect an lv path to be returned in this format
23 return "{}/{}".format(device.vg_name, device.lv_name)
24 return device.path
25 return device
26
27 def _is_valid_device(self, dev_path):
28 device = Device(dev_path)
29 error = None
30 if not device.exists:
31 error = "Unable to proceed with non-existing device: %s" % dev_path
32 # FIXME this is not a nice API, this validator was meant to catch any
33 # non-existing devices upfront, not check for gpt headers. Now this
34 # needs to optionally skip checking gpt headers which is beyond
35 # verifying if the device exists. The better solution would be to
36 # configure this with a list of checks that can be excluded/included on
37 # __init__
38 elif device.has_gpt_headers and not self.gpt_ok:
39 error = "GPT headers found, they must be removed on: %s" % dev_path
40
41 if error:
42 raise argparse.ArgumentError(None, error)
43
44 return device
45
46
47 class ValidBatchDevice(ValidDevice):
48
49 def __call__(self, dev_path):
50 dev = self._is_valid_device(dev_path)
51 if dev.is_partition:
52 raise argparse.ArgumentError(
53 None,
54 '{} is a partition, please pass '
55 'LVs or raw block devices'.format(dev_path))
56 return self._format_device(dev)
57
58
59 class OSDPath(object):
60 """
61 Validate path exists and it looks like an OSD directory.
62 """
63
64 @decorators.needs_root
65 def __call__(self, string):
66 if not os.path.exists(string):
67 error = "Path does not exist: %s" % string
68 raise argparse.ArgumentError(None, error)
69
70 arg_is_partition = disk.is_partition(string)
71 if arg_is_partition:
72 return os.path.abspath(string)
73 absolute_path = os.path.abspath(string)
74 if not os.path.isdir(absolute_path):
75 error = "Argument is not a directory or device which is required to scan"
76 raise argparse.ArgumentError(None, error)
77 key_files = ['ceph_fsid', 'fsid', 'keyring', 'ready', 'type', 'whoami']
78 dir_files = os.listdir(absolute_path)
79 for key_file in key_files:
80 if key_file not in dir_files:
81 terminal.error('All following files must exist in path: %s' % ' '.join(key_files))
82 error = "Required file (%s) was not found in OSD dir path: %s" % (
83 key_file,
84 absolute_path
85 )
86 raise argparse.ArgumentError(None, error)
87
88 return os.path.abspath(string)
89
90
91 def exclude_group_options(parser, groups, argv=None):
92 """
93 ``argparse`` has the ability to check for mutually exclusive options, but
94 it only allows a basic XOR behavior: only one flag can be used from
95 a defined group of options. This doesn't help when two groups of options
96 need to be separated. For example, with filestore and bluestore, neither
97 set can be used in conjunction with the other set.
98
99 This helper validator will consume the parser to inspect the group flags,
100 and it will group them together from ``groups``. This allows proper error
101 reporting, matching each incompatible flag with its group name.
102
103 :param parser: The argparse object, once it has configured all flags. It is
104 required to contain the group names being used to validate.
105 :param groups: A list of group names (at least two), with the same used for
106 ``add_argument_group``
107 :param argv: Consume the args (sys.argv) directly from this argument
108
109 .. note: **Unfortunately** this will not be able to validate correctly when
110 using default flags. In the case of filestore vs. bluestore, ceph-volume
111 defaults to --bluestore, but we can't check that programmatically, we can
112 only parse the flags seen via argv
113 """
114 # Reduce the parser groups to only the groups we need to intersect
115 parser_groups = [g for g in parser._action_groups if g.title in groups]
116 # A mapping of the group name to flags/options
117 group_flags = {}
118 flags_to_verify = []
119 for group in parser_groups:
120 # option groups may have more than one item in ``option_strings``, this
121 # will loop over ``_group_actions`` which contains the
122 # ``option_strings``, like ``['--filestore']``
123 group_flags[group.title] = [
124 option for group_action in group._group_actions
125 for option in group_action.option_strings
126 ]
127
128 # Gather all the flags present in the groups so that we only check on those.
129 for flags in group_flags.values():
130 flags_to_verify.extend(flags)
131
132 seen = []
133 last_flag = None
134 last_group = None
135 for flag in argv:
136 if flag not in flags_to_verify:
137 continue
138 for group_name, flags in group_flags.items():
139 if flag in flags:
140 seen.append(group_name)
141 # We are mutually excluding groups, so having more than 1 group
142 # in ``seen`` means we must raise an error
143 if len(set(seen)) == len(groups):
144 terminal.warning('Incompatible flags were found, some values may get ignored')
145 msg = 'Cannot use %s (%s) with %s (%s)' % (
146 last_flag, last_group, flag, group_name
147 )
148 terminal.warning(msg)
149 last_group = group_name
150 last_flag = flag