]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/configuration.py
5 from ceph_volume
import terminal
, conf
6 from ceph_volume
import exceptions
7 from sys
import version_info
as sys_version_info
9 if sys_version_info
.major
>= 3:
11 conf_parentclass
= configparser
.ConfigParser
12 elif sys_version_info
.major
< 3:
13 import ConfigParser
as configparser
14 conf_parentclass
= configparser
.SafeConfigParser
16 raise RuntimeError('Not expecting python version > 3 yet.')
19 logger
= logging
.getLogger(__name__
)
22 class _TrimIndentFile(object):
24 This is used to take a file-like object and removes any
25 leading tabs from each line when it's read. This is important
26 because some ceph configuration files include tabs which break
29 def __init__(self
, fp
):
33 line
= self
.fp
.readline()
34 return line
.lstrip(' \t')
37 return iter(self
.readline
, '')
40 def load_ceph_conf_path(cluster_name
='ceph'):
41 abspath
= '/etc/ceph/%s.conf' % cluster_name
42 conf
.path
= os
.getenv('CEPH_CONF', abspath
)
43 conf
.cluster
= cluster_name
46 def load(abspath
=None):
50 if not os
.path
.exists(abspath
):
51 raise exceptions
.ConfigurationError(abspath
=abspath
)
56 ceph_file
= open(abspath
)
57 trimmed_conf
= _TrimIndentFile(ceph_file
)
58 with contextlib
.closing(ceph_file
):
59 parser
.read_conf(trimmed_conf
)
62 except configparser
.ParsingError
as error
:
63 logger
.exception('Unable to parse INI-style file: %s' % abspath
)
64 terminal
.error(str(error
))
65 raise RuntimeError('Unable to read configuration file: %s' % abspath
)
68 class Conf(conf_parentclass
):
70 Subclasses from ConfigParser to give a few helpers for Ceph
74 def read_path(self
, path
):
76 return self
.read(path
)
80 self
.get('global', 'fsid')
81 except (configparser
.NoSectionError
, configparser
.NoOptionError
):
82 raise exceptions
.ConfigurationKeyError('global', 'fsid')
84 def optionxform(self
, s
):
85 s
= s
.replace('_', ' ')
86 s
= '_'.join(s
.split())
89 def get_safe(self
, section
, key
, default
=None, check_valid
=True):
91 Attempt to get a configuration value from a certain section
92 in a ``cfg`` object but returning None if not found. Avoids the need
93 to be doing try/except {ConfigParser Exceptions} every time.
98 return self
.get(section
, key
)
99 except (configparser
.NoSectionError
, configparser
.NoOptionError
):
102 def get_list(self
, section
, key
, default
=None, split
=','):
104 Assumes that the value for a given key is going to be a list separated
105 by commas. It gets rid of trailing comments. If just one item is
106 present it returns a list with a single item, if no key is found an
107 empty list is returned.
109 Optionally split on other characters besides ',' and return a fallback
110 value if no items are found.
113 value
= self
.get_safe(section
, key
, [])
115 if default
is not None:
120 value
= re
.split(r
'\s+#', value
)[0]
123 value
= value
.split(split
)
126 return [x
.strip() for x
in value
]
128 # XXX Almost all of it lifted from the original ConfigParser._read method,
129 # except for the parsing of '#' in lines. This is only a problem in Python 2.7, and can be removed
130 # once tooling is Python3 only with `Conf(inline_comment_prefixes=('#',';'))`
131 def _read(self
, fp
, fpname
):
132 """Parse a sectioned setup file.
134 The sections in setup file contains a title line at the top,
135 indicated by a name in square brackets (`[]'), plus key/value
136 options lines, indicated by `name: value' format lines.
137 Continuations are represented by an embedded newline then
138 leading whitespace. Blank lines, lines beginning with a '#',
139 and just about everything else are ignored.
141 cursect
= None # None, or a dictionary
144 e
= None # None, or an exception
150 # comment or blank line?
151 if line
.strip() == '' or line
[0] in '#;':
153 if line
.split(None, 1)[0].lower() == 'rem' and line
[0] in "rR":
154 # no leading whitespace
157 if line
[0].isspace() and cursect
is not None and optname
:
160 cursect
[optname
].append(value
)
161 # a section header or option header?
163 # is it a section header?
164 mo
= self
.SECTCRE
.match(line
)
166 sectname
= mo
.group('header')
167 if sectname
in self
._sections
:
168 cursect
= self
._sections
[sectname
]
169 elif sectname
== 'DEFAULT':
170 cursect
= self
._defaults
172 cursect
= self
._dict
()
173 cursect
['__name__'] = sectname
174 self
._sections
[sectname
] = cursect
175 # So sections can't start with a continuation line
177 # no section header in the file?
178 elif cursect
is None:
179 raise configparser
.MissingSectionHeaderError(fpname
, lineno
, line
)
182 mo
= self
._optcre
.match(line
)
184 optname
, vi
, optval
= mo
.group('option', 'vi', 'value')
185 optname
= self
.optionxform(optname
.rstrip())
186 # This check is fine because the OPTCRE cannot
187 # match if it would set optval to None
188 if optval
is not None:
189 # XXX Added support for '#' inline comments
190 if vi
in ('=', ':') and (';' in optval
or '#' in optval
):
192 optval
= re
.split(r
'\s+(;|#)', optval
)[0]
193 # if what is left is comment as a value, fallback to an empty string
194 # that is: `foo = ;` would mean `foo` is '', which brings parity with
195 # what ceph-conf tool does
196 if optval
in [';','#']:
198 optval
= optval
.strip()
202 cursect
[optname
] = [optval
]
204 # valueless option handling
205 cursect
[optname
] = optval
207 # a non-fatal parsing error occurred. set up the
208 # exception but keep going. the exception will be
209 # raised at the end of the file and will contain a
210 # list of all bogus lines
212 e
= configparser
.ParsingError(fpname
)
213 e
.append(lineno
, repr(line
))
214 # if any parsing errors occurred, raise an exception
218 # join the multi-line values collected while reading
219 all_sections
= [self
._defaults
]
220 all_sections
.extend(self
._sections
.values())
221 for options
in all_sections
:
222 for name
, val
in options
.items():
223 if isinstance(val
, list):
224 options
[name
] = '\n'.join(val
)
226 def read_conf(self
, conffile
):
227 if sys_version_info
.major
>= 3:
228 self
.read_file(conffile
)
229 elif sys_version_info
.major
< 3:
230 self
.readfp(conffile
)
232 raise RuntimeError('Not expecting python version > 3 yet.')