]>
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):
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.
97 return self
.get(section
, key
)
98 except (configparser
.NoSectionError
, configparser
.NoOptionError
):
101 def get_list(self
, section
, key
, default
=None, split
=','):
103 Assumes that the value for a given key is going to be a list separated
104 by commas. It gets rid of trailing comments. If just one item is
105 present it returns a list with a single item, if no key is found an
106 empty list is returned.
108 Optionally split on other characters besides ',' and return a fallback
109 value if no items are found.
112 value
= self
.get_safe(section
, key
, [])
114 if default
is not None:
119 value
= re
.split(r
'\s+#', value
)[0]
122 value
= value
.split(split
)
125 return [x
.strip() for x
in value
]
127 # XXX Almost all of it lifted from the original ConfigParser._read method,
128 # except for the parsing of '#' in lines. This is only a problem in Python 2.7, and can be removed
129 # once tooling is Python3 only with `Conf(inline_comment_prefixes=('#',';'))`
130 def _read(self
, fp
, fpname
):
131 """Parse a sectioned setup file.
133 The sections in setup file contains a title line at the top,
134 indicated by a name in square brackets (`[]'), plus key/value
135 options lines, indicated by `name: value' format lines.
136 Continuations are represented by an embedded newline then
137 leading whitespace. Blank lines, lines beginning with a '#',
138 and just about everything else are ignored.
140 cursect
= None # None, or a dictionary
143 e
= None # None, or an exception
149 # comment or blank line?
150 if line
.strip() == '' or line
[0] in '#;':
152 if line
.split(None, 1)[0].lower() == 'rem' and line
[0] in "rR":
153 # no leading whitespace
156 if line
[0].isspace() and cursect
is not None and optname
:
159 cursect
[optname
].append(value
)
160 # a section header or option header?
162 # is it a section header?
163 mo
= self
.SECTCRE
.match(line
)
165 sectname
= mo
.group('header')
166 if sectname
in self
._sections
:
167 cursect
= self
._sections
[sectname
]
168 elif sectname
== 'DEFAULT':
169 cursect
= self
._defaults
171 cursect
= self
._dict
()
172 cursect
['__name__'] = sectname
173 self
._sections
[sectname
] = cursect
174 # So sections can't start with a continuation line
176 # no section header in the file?
177 elif cursect
is None:
178 raise configparser
.MissingSectionHeaderError(fpname
, lineno
, line
)
181 mo
= self
._optcre
.match(line
)
183 optname
, vi
, optval
= mo
.group('option', 'vi', 'value')
184 optname
= self
.optionxform(optname
.rstrip())
185 # This check is fine because the OPTCRE cannot
186 # match if it would set optval to None
187 if optval
is not None:
188 # XXX Added support for '#' inline comments
189 if vi
in ('=', ':') and (';' in optval
or '#' in optval
):
191 optval
= re
.split(r
'\s+(;|#)', optval
)[0]
192 # if what is left is comment as a value, fallback to an empty string
193 # that is: `foo = ;` would mean `foo` is '', which brings parity with
194 # what ceph-conf tool does
195 if optval
in [';','#']:
197 optval
= optval
.strip()
201 cursect
[optname
] = [optval
]
203 # valueless option handling
204 cursect
[optname
] = optval
206 # a non-fatal parsing error occurred. set up the
207 # exception but keep going. the exception will be
208 # raised at the end of the file and will contain a
209 # list of all bogus lines
211 e
= configparser
.ParsingError(fpname
)
212 e
.append(lineno
, repr(line
))
213 # if any parsing errors occurred, raise an exception
217 # join the multi-line values collected while reading
218 all_sections
= [self
._defaults
]
219 all_sections
.extend(self
._sections
.values())
220 for options
in all_sections
:
221 for name
, val
in options
.items():
222 if isinstance(val
, list):
223 options
[name
] = '\n'.join(val
)
225 def read_conf(self
, conffile
):
226 if sys_version_info
.major
>= 3:
227 self
.read_file(conffile
)
228 elif sys_version_info
.major
< 3:
229 self
.readfp(conffile
)
231 raise RuntimeError('Not expecting python version > 3 yet.')