]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/configuration.py
4 import ConfigParser
as configparser
9 from ceph_volume
import terminal
, conf
10 from ceph_volume
import exceptions
13 logger
= logging
.getLogger(__name__
)
16 class _TrimIndentFile(object):
18 This is used to take a file-like object and removes any
19 leading tabs from each line when it's read. This is important
20 because some ceph configuration files include tabs which break
23 def __init__(self
, fp
):
27 line
= self
.fp
.readline()
28 return line
.lstrip(' \t')
31 return iter(self
.readline
, '')
34 def load_ceph_conf_path(cluster_name
='ceph'):
35 abspath
= '/etc/ceph/%s.conf' % cluster_name
36 conf
.path
= os
.getenv('CEPH_CONF', abspath
)
37 conf
.cluster
= cluster_name
40 def load(abspath
=None):
44 if not os
.path
.exists(abspath
):
45 raise exceptions
.ConfigurationError(abspath
=abspath
)
50 ceph_file
= open(abspath
)
51 trimmed_conf
= _TrimIndentFile(ceph_file
)
52 with contextlib
.closing(ceph_file
):
53 parser
.readfp(trimmed_conf
)
56 except configparser
.ParsingError
as error
:
57 logger
.exception('Unable to parse INI-style file: %s' % abspath
)
58 terminal
.error(str(error
))
59 raise RuntimeError('Unable to read configuration file: %s' % abspath
)
62 class Conf(configparser
.SafeConfigParser
):
64 Subclasses from SafeConfigParser to give a few helpers for Ceph
68 def read_path(self
, path
):
70 return self
.read(path
)
74 self
.get('global', 'fsid')
75 except (configparser
.NoSectionError
, configparser
.NoOptionError
):
76 raise exceptions
.ConfigurationKeyError('global', 'fsid')
78 def optionxform(self
, s
):
79 s
= s
.replace('_', ' ')
80 s
= '_'.join(s
.split())
83 def get_safe(self
, section
, key
, default
=None):
85 Attempt to get a configuration value from a certain section
86 in a ``cfg`` object but returning None if not found. Avoids the need
87 to be doing try/except {ConfigParser Exceptions} every time.
91 return self
.get(section
, key
)
92 except (configparser
.NoSectionError
, configparser
.NoOptionError
):
95 def get_list(self
, section
, key
, default
=None, split
=','):
97 Assumes that the value for a given key is going to be a list separated
98 by commas. It gets rid of trailing comments. If just one item is
99 present it returns a list with a single item, if no key is found an
100 empty list is returned.
102 Optionally split on other characters besides ',' and return a fallback
103 value if no items are found.
106 value
= self
.get_safe(section
, key
, [])
108 if default
is not None:
113 value
= re
.split(r
'\s+#', value
)[0]
116 value
= value
.split(split
)
119 return [x
.strip() for x
in value
]
121 # XXX Almost all of it lifted from the original ConfigParser._read method,
122 # except for the parsing of '#' in lines. This is only a problem in Python 2.7, and can be removed
123 # once tooling is Python3 only with `Conf(inline_comment_prefixes=('#',';'))`
124 def _read(self
, fp
, fpname
):
125 """Parse a sectioned setup file.
127 The sections in setup file contains a title line at the top,
128 indicated by a name in square brackets (`[]'), plus key/value
129 options lines, indicated by `name: value' format lines.
130 Continuations are represented by an embedded newline then
131 leading whitespace. Blank lines, lines beginning with a '#',
132 and just about everything else are ignored.
134 cursect
= None # None, or a dictionary
137 e
= None # None, or an exception
143 # comment or blank line?
144 if line
.strip() == '' or line
[0] in '#;':
146 if line
.split(None, 1)[0].lower() == 'rem' and line
[0] in "rR":
147 # no leading whitespace
150 if line
[0].isspace() and cursect
is not None and optname
:
153 cursect
[optname
].append(value
)
154 # a section header or option header?
156 # is it a section header?
157 mo
= self
.SECTCRE
.match(line
)
159 sectname
= mo
.group('header')
160 if sectname
in self
._sections
:
161 cursect
= self
._sections
[sectname
]
162 elif sectname
== 'DEFAULT':
163 cursect
= self
._defaults
165 cursect
= self
._dict
()
166 cursect
['__name__'] = sectname
167 self
._sections
[sectname
] = cursect
168 # So sections can't start with a continuation line
170 # no section header in the file?
171 elif cursect
is None:
172 raise configparser
.MissingSectionHeaderError(fpname
, lineno
, line
)
175 mo
= self
._optcre
.match(line
)
177 optname
, vi
, optval
= mo
.group('option', 'vi', 'value')
178 optname
= self
.optionxform(optname
.rstrip())
179 # This check is fine because the OPTCRE cannot
180 # match if it would set optval to None
181 if optval
is not None:
182 # XXX Added support for '#' inline comments
183 if vi
in ('=', ':') and (';' in optval
or '#' in optval
):
185 optval
= re
.split(r
'\s+(;|#)', optval
)[0]
186 # if what is left is comment as a value, fallback to an empty string
187 # that is: `foo = ;` would mean `foo` is '', which brings parity with
188 # what ceph-conf tool does
189 if optval
in [';','#']:
191 optval
= optval
.strip()
195 cursect
[optname
] = [optval
]
197 # valueless option handling
198 cursect
[optname
] = optval
200 # a non-fatal parsing error occurred. set up the
201 # exception but keep going. the exception will be
202 # raised at the end of the file and will contain a
203 # list of all bogus lines
205 e
= configparser
.ParsingError(fpname
)
206 e
.append(lineno
, repr(line
))
207 # if any parsing errors occurred, raise an exception
211 # join the multi-line values collected while reading
212 all_sections
= [self
._defaults
]
213 all_sections
.extend(self
._sections
.values())
214 for options
in all_sections
:
215 for name
, val
in options
.items():
216 if isinstance(val
, list):
217 options
[name
] = '\n'.join(val
)