20 hosts.add_option('--hosts-domain', metavar='DOMAIN', |
20 hosts.add_option('--hosts-domain', metavar='DOMAIN', |
21 help="Default domain for hosts. Default uses config file basename") |
21 help="Default domain for hosts. Default uses config file basename") |
22 |
22 |
23 hosts.add_option('--hosts-include', metavar='PATH', |
23 hosts.add_option('--hosts-include', metavar='PATH', |
24 help="Optional path for hosts includes, in addition to the host config dir") |
24 help="Optional path for hosts includes, in addition to the host config dir") |
|
25 |
|
26 hosts.add_option('--hosts-include-trace', metavar='FILE', |
|
27 help="Write out all included file paths") |
25 |
28 |
26 return hosts |
29 return hosts |
27 |
30 |
28 class HostConfigError (Exception): |
31 class HostConfigError (Exception): |
29 """ |
32 """ |
205 |
208 |
206 |
209 |
207 log.info("%s: include: %s", config_path, path) |
210 log.info("%s: include: %s", config_path, path) |
208 yield path |
211 yield path |
209 |
212 |
210 def apply_hosts_configs (options, path, name, config, parent=None, defaults={}): |
213 def apply_hosts_configs (options, path, name, config, parent=None, defaults={}, include_trace=None): |
211 """ |
214 """ |
212 Load hosts from a configobj.Section (which can be the top-level ConfigObj). |
215 Load hosts from a configobj.Section (which can be the top-level ConfigObj). |
213 |
216 |
214 options global options |
217 options global options |
215 path filesystem path of file (for errors) |
218 path filesystem path of file (for errors) |
216 name name of this section/file |
219 name name of this section/file |
217 config configobj.Section |
220 config configobj.Section |
218 parent parent section from included files or --hosts-domain |
221 parent parent section from included files or --hosts-domain |
219 defaults hierarchial section defaults |
222 defaults hierarchial section defaults |
|
223 include_trace - optional list to append loaded files to |
220 """ |
224 """ |
221 |
225 |
222 # items in this section |
226 # items in this section |
223 section = dict(defaults) |
227 section = dict(defaults) |
224 for scalar in config.scalars: |
228 for scalar in config.scalars: |
225 section[scalar] = config[scalar] |
229 section[scalar] = config[scalar] |
226 |
230 |
227 # process includes? |
231 # process includes? |
228 if 'include' in section: |
232 if 'include' in section: |
229 includes = section.pop('include').split() |
233 # convert from unicode |
|
234 includes = [str(include) for include in section.pop('include').split()] |
230 |
235 |
231 includes = list(parse_config_includes(options, path, includes)) |
236 includes = list(parse_config_includes(options, path, includes)) |
232 |
237 |
233 # within our domain context |
238 # within our domain context |
234 for host in apply_hosts_files(options, includes, parent=name, defaults=section): |
239 for host in apply_hosts_files(options, includes, include_trace=include_trace, |
|
240 parent=name, defaults=section |
|
241 ): |
235 yield host |
242 yield host |
236 else: |
243 else: |
237 includes = None |
244 includes = None |
238 |
245 |
239 if config.sections: |
246 if config.sections: |
302 except configobj.ConfigObjError as ex: |
309 except configobj.ConfigObjError as ex: |
303 raise HostConfigObjError(path, ex) |
310 raise HostConfigObjError(path, ex) |
304 |
311 |
305 return apply_hosts_configs(options, path, name, config, **opts) |
312 return apply_hosts_configs(options, path, name, config, **opts) |
306 |
313 |
307 def apply_hosts_file (options, path, **opts): |
314 def apply_hosts_file (options, path, include_trace=None, **opts): |
308 """ |
315 """ |
309 Load Hosts from a file path. |
316 Load Hosts from a file path. |
310 """ |
317 |
|
318 include_trace - optional list to append loaded files to |
|
319 """ |
|
320 |
|
321 if include_trace is not None: |
|
322 log.debug("%s: include trace", path) |
|
323 include_trace.append(path) |
311 |
324 |
312 try: |
325 try: |
313 file = open(path) |
326 file = open(path) |
314 except IOError as ex: |
327 except IOError as ex: |
315 raise HostConfigError(path, ex.strerror) |
328 raise HostConfigError(path, ex.strerror) |
316 |
329 |
317 for host in apply_hosts_config(options, file, **opts): |
330 for host in apply_hosts_config(options, file, include_trace=include_trace, **opts): |
318 yield host |
331 yield host |
319 |
332 |
320 def apply_hosts_directory (options, root, **opts): |
333 def apply_hosts_directory (options, root, include_trace=None, **opts): |
321 """ |
334 """ |
322 Load Hosts from a directory, loading each file within the directory. |
335 Load Hosts from a directory, loading each file within the directory. |
323 |
336 |
|
337 include_trace - optional list to append loaded files to |
|
338 |
324 Skips .dotfiles. |
339 Skips .dotfiles. |
325 """ |
340 """ |
|
341 |
|
342 if include_trace is not None: |
|
343 log.debug("%s: include trace", root) |
|
344 include_trace.append(root) |
326 |
345 |
327 for name in sorted(os.listdir(root)): |
346 for name in sorted(os.listdir(root)): |
328 path = os.path.join(root, name) |
347 path = os.path.join(root, name) |
329 |
348 |
330 if name.startswith('.'): |
349 if name.startswith('.'): |
333 |
352 |
334 if os.path.isdir(path): |
353 if os.path.isdir(path): |
335 log.debug("%s: skip directory: %s", root, name) |
354 log.debug("%s: skip directory: %s", root, name) |
336 continue |
355 continue |
337 |
356 |
338 for host in apply_hosts_file(options, path, **opts): |
357 for host in apply_hosts_file(options, path, include_trace=include_trace, **opts): |
339 yield host |
358 yield host |
340 |
359 |
341 def apply_hosts_files (options, files, **opts): |
360 def apply_hosts_files (options, files, **opts): |
342 """ |
361 """ |
343 Load Hosts from files. |
362 Load Hosts from files. |
357 """ |
376 """ |
358 Load Hosts from arguments. |
377 Load Hosts from arguments. |
359 |
378 |
360 Exits with status=2 if loading the confs fails. |
379 Exits with status=2 if loading the confs fails. |
361 """ |
380 """ |
|
381 |
|
382 if options.hosts_include_trace: |
|
383 log.debug("include trace") |
|
384 include_trace = [ ] |
|
385 else: |
|
386 include_trace = None |
362 |
387 |
363 try: |
388 try: |
364 # load hosts from configs |
389 # load hosts from configs |
365 hosts = list(apply_hosts_files(options, args)) |
390 hosts = list(apply_hosts_files(options, args, include_trace=include_trace)) |
366 except HostConfigObjError as error: |
391 except HostConfigObjError as error: |
367 log.error("%s", error) |
392 log.error("%s", error) |
368 log.error("\t%s", error.line_contents) |
393 log.error("\t%s", error.line_contents) |
369 sys.exit(2) |
394 sys.exit(2) |
370 |
395 |
371 except HostConfigError as error: |
396 except HostConfigError as error: |
372 log.error("%s", error) |
397 log.error("%s", error) |
373 sys.exit(2) |
398 sys.exit(2) |
|
399 |
|
400 if options.hosts_include_trace: |
|
401 with pvl.args.apply_file(options.hosts_include_trace, 'w') as file: |
|
402 for include in include_trace: |
|
403 print >>file, include |
374 |
404 |
375 # stable ordering |
405 # stable ordering |
376 return sorted(hosts, key=Host.sort_key) |
406 return sorted(hosts, key=Host.sort_key) |
377 |
407 |