1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os
17 import sys
18 import glob
19 import stat
20 import re
21
22 from rhn.UserDictCase import UserDictCase
23 from spacewalk.common.usix import raise_with_tb
24
25
26
27
28 _CONFIG_ROOT = '/etc/rhn'
29 _CONFIG_FILE = '%s/rhn.conf' % _CONFIG_ROOT
30 _CONFIG_DEFAULTS_ROOT = '/usr/share/rhn/config-defaults'
31
32
34 """
35 Function used for debugging purposes
36 """
37 sys.stderr.write("CONFIG PARSE WARNING: %s\n" % " ".join(map(str, args)))
38
39
41
42 """
43 Exception class we're using to expose fatal errors
44 """
45 pass
46
47
48
49
50
52
53 """ Main options class
54 The basic idea is to share the important pieces of information - the
55 component and the configuration tree - across all instances of this
56 class.
57 """
58
59 - def __init__(self, component=None, root=None, filename=None):
60 self.__component = None
61
62 self.__defaults = {}
63
64 self.__parsedConfig = {}
65
66
67 self.__configs = {}
68
69 self.__timestamp = 0
70
71 self.root = None
72 self.filename = None
73 self.init(component, root, filename)
74
75 - def init(self, component, root=None, filename=None):
87
89 if not comp:
90 comp = ()
91 self.__component = comp
92
94 return self.__component
95
97 return (self.__component is not None) and \
98 self.__component in self.__configs
99
101 """returns last modified time diff if rhn.conf has changed."""
102
103 try:
104 si = os.stat(self.filename)
105 except OSError:
106 e = sys.exc_info()[1]
107 if e[0] == 13:
108 sys.stderr.write("ERROR: must be root to execute\n")
109 else:
110 sys.stderr.write("ERROR: " + self.filename + " is not accesible\n")
111 sys.exit(e[0])
112 lm = si[stat.ST_MTIME]
113
114
115 return lm - self.__timestamp
116
118 """ update the last modified time of the rhn.conf file. """
119 if timeDiff is None:
120 timeDiff = self.modifiedYN()
121 self.__timestamp = self.__timestamp + timeDiff
122
124 """
125 This function parses the config file, if needed, and populates
126 the configuration cache self.__configs
127 """
128
129 timeDiff = self.modifiedYN()
130 if not timeDiff and self.is_initialized():
131
132
133 return
134 else:
135
136
137 self.updateLastModified(timeDiff)
138 self.__configs.clear()
139
140
141 self._parseDefaults(allCompsYN=0)
142
143
144
145 self.__parsedConfig = parse_file(self.filename)
146
147
148 self.__merge()
149
151 """ Parsing of the /usr/share/rhn/config-defaults/*.conf (or equivalent)
152 Make sure we have all the needed default config files loaded
153 We store the defaults in a dictionary, keyed on the component tuple
154 """
155 comps = parse_comps(self.__component)
156 if allCompsYN:
157 comps = getAllComponents_tuples()
158 for comp in comps:
159 if comp in self.__defaults:
160
161
162 continue
163
164 conffile = "%s/rhn.conf" % (_CONFIG_DEFAULTS_ROOT)
165 if comp:
166 conffile = "%s/rhn_%s.conf" % (_CONFIG_DEFAULTS_ROOT,
167 '_'.join(comp))
168
169 if not os.access(conffile, os.R_OK):
170 warn("File not found or can't be read", conffile)
171 continue
172
173 _dict = parse_file(conffile, single_key=1)
174
175
176
177
178 def_dict = {}
179 for k in _dict[()].keys():
180
181
182
183 def_dict[k] = _dict[()][k][0]
184 self.__defaults[comp] = def_dict
185
187 self.__check()
188 return list(self.__configs[self.__component].keys())
189
191 self.__check()
192 return key in self.__configs[self.__component]
193
197
201
202 - def set(self, key, value):
203 self.__check()
204 self.__configs[self.__component][key] = value
205 __setitem__ = set
206
208 self.__check()
209
210 vals = list(self.__configs[self.__component].items())
211 vals.sort(key=lambda a: a[0])
212 for k, v in vals:
213 if v is None:
214 v = ""
215 print("%-20s = %s" % (k, v))
216
217
218
220 """fetch option you want in a self.DEBUG kind of syntax
221 (can force component selection)
222
223 e.g.: say for example we have an option proxy.debug = 5
224 stored in the dictionary. proxy just says that only proxy
225 can access this option. So for this exmple,
226 self.__component is proxy.
227 cfg = RHNOptions("proxy")
228 print cfg.DEBUG ---> yields 5
229 """
230 self.__check()
231 if key not in self.__configs[self.__component]:
232 raise AttributeError(key)
233 return self.__configs[self.__component][key]
234 __getitem__ = __getattr__
235
236 - def get(self, key, default=None):
237 ret = default
238 if key in self.__configs[self.__component]:
239 ret = self.__configs[self.__component][key]
240 return ret
241
243 s = "Uninitialized"
244 if self.__component and self.__component in self.__configs:
245 s = str(self.__configs[self.__component])
246 return "<RHNOptions instance at %s: %s>" % (id(self), s)
247 __repr__ = __str__
248
249
250
255
256 - def __merge(self, component=None):
257 """
258 merge the config options between the default comp dictionaries
259 and the file we're parsing now
260 """
261
262 if component is None:
263 component = self.__component
264
265 opts = UserDictCase()
266 comps = parse_comps(component)
267 for comp in comps:
268 if comp not in self.__defaults:
269 warn('key not found in config default dict', comp)
270 continue
271 opts.update(self.__defaults[comp])
272
273
274 for comp in comps:
275 if comp not in self.__parsedConfig:
276
277 continue
278 for key, (values, _lineno_) in self.__parsedConfig[comp].items():
279
280
281
282
283
284
285
286 opts[key] = values
287
288 self.__configs[component] = opts
289
290
291
293 """returns the __defaults dict (dictionary of parsed defaults).
294 """
295 self.__check()
296 return self.__defaults
297
299 """returns the __parsedConfig dict (dictionary of parsed
300 /etc/rhn/rhn.conf file).
301 """
302 self.__check()
303 return self.__parsedConfig
304
306 """returns the __configs dict (dictionary of the merged options
307 keyed by component.
308 """
309 self.__check()
310 return self.__configs
311
313 from pprint import pprint
314 print("__defaults: dictionary of parsed defaults.")
315 pprint(self.__defaults)
316 print("")
317 print("__parsedConfig: dictionary of parsed /etc/rhn/rhn.conf file.")
318 pprint(self.__parsedConfig)
319 print("")
320 print("__configs: dictionary of the merged options keyed by component.")
321 pprint(self.__configs)
322
323
325 """
326 Splits a component name (a.b.c) into a list of tuples that can be
327 joined together to determine a config file name
328 Eg. a.b.c --> [(), ('a',), ('a','b'), ('a','b','c')]
329 """
330
331 if not component:
332 return [()]
333 comps = [c.lower() for c in component.split('.')]
334
335 return [tuple(comps[:i]) for i in range(len(comps) + 1)]
336
337
339 """
340 Parse a config line...
341 Returns a tuple (keys, values), or (None, None) if we don't care
342 about this line
343 """
344 varSeparator = '.'
345 optSeparator = ','
346
347 def sanitize_value(key, val):
348 """
349 attempt to convert a string value to the proper type
350 """
351 converTable = {'proxy.http_proxy_username': str,
352 'proxy.http_proxy_password': str,
353 'server.satellite.http_proxy_username': str,
354 'server.satellite.http_proxy_password': str,
355 'server.satellite.rhn_parent': str,
356 'db_name': str,
357 'db_user': str,
358 'db_password': str,
359 'db_host': str}
360 val = val.strip()
361
362 if converTable.get(key):
363 try:
364 val = converTable.get(key)(val)
365 except ValueError:
366 pass
367 else:
368 try:
369 val = int(val)
370 except ValueError:
371 try:
372 val = float(val)
373 except ValueError:
374 pass
375 if val == '':
376 val = None
377 return val
378
379
380 if re.match(r'[ \t]*(#|$)', line):
381 return (None, None)
382
383
384
385 (keys, vals) = [c.strip() for c in line.split('=', 1)]
386
387
388 keys = keys.lower()
389 if not keys:
390 raise ConfigParserError("Missing Key = expression")
391
392
393 if not vals:
394 keys = keys.split(varSeparator)
395 return (keys, None)
396
397 vals = list(map(sanitize_value, [keys] * len(vals.split(optSeparator)),
398 vals.split(optSeparator)))
399 if len(vals) == 1:
400
401 vals = vals[0]
402 keys = keys.split(varSeparator)
403
404 return (keys, vals)
405
406
408 """
409 parse a config file (read it in, parse its lines)
410 """
411 lines = read_file(filename)
412
413 ret = {(): {}}
414 lineno = 0
415
416 for line in lines:
417
418 lineno = lineno + 1
419 try:
420 (keys, values) = parse_line(line)
421 except:
422 raise_with_tb(ConfigParserError("Parse Error: <%s:%s>: '%s'" % (
423 filename, lineno, line)), sys.exc_info()[2])
424 if keys is None:
425 continue
426
427 if single_key and len(keys) > 1:
428
429
430
431
432
433
434 del keys[:-1]
435
436 comp = tuple(keys[:-1])
437 key = keys[-1]
438 if comp not in ret:
439
440
441 ret[comp] = {}
442 ret[comp][key] = (values, lineno)
443 return ret
444
445
447 """
448 reads a text config file and returns its lines in a list
449 """
450 try:
451 lines = open(filename, 'rb').readlines()
452 new_lines = []
453 combined = ''
454 for line in lines:
455
456 if line.find('\\\n') < 0:
457 combined = combined + line
458 new_lines.append(combined)
459 combined = ''
460 else:
461 combined = combined + line.replace('\\\n', ' ')
462 return new_lines
463 except (IOError, OSError):
464 e = sys.exc_info()[1]
465 raise_with_tb(ConfigParserError("Can not read config file", filename, e.args[1]), sys.exc_info()[2])
466
467
469 """Figure out all components and return them in a tree-like structure
470
471 {'server', {'server.app':{},
472 'server.satellite':{},
473 'server.applet':{}, 'server.bugzilla':{},
474 'server.iss':{}, 'server.xmlrpc':{}, 'server.xp':{}},
475 'web': {},
476 'tools': {}}
477
478 NOTE: this was begging for recursion... I avoided that like the plague
479 """
480
481 if defaultDir is None:
482 defaultDir = _CONFIG_DEFAULTS_ROOT
483 comps = glob.glob('%s/*.conf' % defaultDir)
484 compTree = {}
485 for comp in comps:
486 comp = os.path.basename(comp)
487 comp = comp[:comp.find('.')]
488 parts = comp.split('_')[1:]
489 if not parts:
490 continue
491 d = compTree
492 for i in range(len(parts)):
493 key = '.'.join(parts[:i + 1])
494 if key not in d:
495 d[key] = {}
496 d = d[key]
497 return compTree
498
499
501 """recursively flattens the results of getAllComponents_tree returning
502 a list of all components"""
503
504 if compsTree is None:
505 compsTree = getAllComponents_tree(defaultDir)
506 l = []
507 for k, v in compsTree.items():
508 l.extend(getAllComponents(None, v))
509 l.append(k)
510 return l
511
512
514 """returns a list of ALL components in the tuple-ified format:
515 E.g., [(), ('a',), ('a','b'), ('a','b','c'), ...]
516 """
517 comps = getAllComponents(defaultDir)
518 d = {}
519 for comp in comps:
520 for c in parse_comps(comp):
521 d[c] = None
522 return list(d.keys())
523
524
525 CFG = RHNOptions()
526
527
528 -def initCFG(component=None, root=None, filename=None):
535
536 ALL_CFG = RHNOptions('')
537 ALL_CFG.parse()
538 PRODUCT_NAME = ALL_CFG.PRODUCT_NAME
539
540
542 print("Test script:")
543 import pprint
544 print("Component tree of all installed components:")
545 pprint.pprint(getAllComponents_tree())
546 print("")
547 test_cfg = RHNOptions(sys.argv[1])
548
549
550
551
552
553 test_cfg.parse()
554 print("=============== the object's repr ================================")
555 print(test_cfg)
556 print("=============== the object's defaults ============================")
557 pprint.pprint(test_cfg.getDefaults())
558 print("=============== an erronous lookup example =======================")
559 print("testing __getattr__")
560 try:
561 print(test_cfg.lkasjdfxxxxxxxxxxxxxx)
562 except AttributeError:
563 e = sys.exc_info()[1]
564 print('Testing: "AttributeError: %s"' % e)
565 print("")
566 print("=============== the object's merged settings ======================")
567 test_cfg.show()
568 print("=============== dump of all relevant dictionaries =================")
569 test_cfg.showall()
570 print("===================================================================")
571
572
573
574
575
576
577
578 if __name__ == "__main__":
579 do_list = 0
580 comp_arg = None
581 key_arg = None
582
583 if len(sys.argv) == 4 and sys.argv[1] == "get":
584 comp_arg = sys.argv[2]
585 key_arg = sys.argv[3]
586 elif len(sys.argv) == 3 and sys.argv[1] == "list":
587 comp_arg = sys.argv[2]
588 do_list = 1
589 else:
590
591 runTest()
592 sys.exit(1)
593
594 cfg = RHNOptions(comp_arg)
595 cfg.parse()
596
597 if do_list:
598 cfg.show()
599 else:
600 print(cfg.get(key_arg))
601