cli.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import argparse
  2. import os
  3. from .config import yaml_load, ConfigRoot
  4. from .main import get_hosts_and_tasks, run_batch, info
  5. from .utils import (enable_logging_color, log_handler, logger, ByrdException,
  6. ObjectDict, Env, abort)
  7. basedir, _ = os.path.split(__file__)
  8. PKG_DIR = os.path.join(basedir, 'pkg')
  9. def load_cfg(path, prefix=None):
  10. load_sections = ('networks', 'tasks', 'auth', 'env')
  11. if os.path.isfile(path):
  12. logger.debug('Load config %s' % os.path.relpath(path))
  13. cfg = yaml_load(open(path))
  14. cfg = ConfigRoot.parse(cfg)
  15. else:
  16. raise ByrdException('Config file "%s" not found' % path)
  17. # Define useful defaults
  18. cfg.networks = cfg.networks or ObjectDict()
  19. cfg.tasks = cfg.tasks or ObjectDict()
  20. cfg.auth = cfg.auth or ObjectDict()
  21. # Create backrefs between tasks to the local config
  22. if cfg.get('tasks'):
  23. cfg_cp = cfg.copy()
  24. for k, v in cfg['tasks'].items():
  25. v._cfg = cfg_cp
  26. # Recursive load
  27. if cfg.load:
  28. cfg_path = os.path.dirname(path)
  29. for item in cfg.load:
  30. if item.get('file'):
  31. rel_path = item.file
  32. child_path = os.path.join(cfg_path, item.file)
  33. elif item.get('pkg'):
  34. rel_path = item.pkg
  35. child_path = os.path.join(PKG_DIR, item.pkg)
  36. if item.get('as'):
  37. child_prefix = item['as']
  38. else:
  39. child_prefix, _ = os.path.splitext(rel_path)
  40. child_cfg = load_cfg(child_path, child_prefix)
  41. key_fn = lambda x: '/'.join([child_prefix, x])
  42. for section in load_sections:
  43. if section not in child_cfg:
  44. continue
  45. items = {key_fn(k): v for k, v in child_cfg[section].items()}
  46. cfg[section].update(items)
  47. return cfg
  48. def load_cli(args=None):
  49. parser = argparse.ArgumentParser()
  50. parser.add_argument('names', nargs='*',
  51. help='Hosts and commands to run them on')
  52. parser.add_argument('-c', '--config', default='bd.yaml',
  53. help='Config file')
  54. parser.add_argument('-R', '--run', nargs='*', default=[],
  55. help='Run remote task')
  56. parser.add_argument('-L', '--run-local', nargs='*', default=[],
  57. help='Run local task')
  58. parser.add_argument('-P', '--run-python', nargs='*', default=[],
  59. help='Run python task')
  60. parser.add_argument('-d', '--dry-run', action='store_true',
  61. help='Do not run actual tasks, just print them')
  62. parser.add_argument('-e', '--env', nargs='*', default=[],
  63. help='Add value to execution environment '
  64. '(ex: -e foo=bar "name=John Doe")')
  65. parser.add_argument('-s', '--sudo', default='auto',
  66. help='Enable sudo (auto|yes|no')
  67. parser.add_argument('-v', '--verbose', action='count',
  68. default=0, help='Increase verbosity')
  69. parser.add_argument('-q', '--quiet', action='count',
  70. default=0, help='Decrease verbosity')
  71. parser.add_argument('-n', '--no-color', action='store_true',
  72. help='Disable colored logs')
  73. parser.add_argument('-i', '--info', action='store_true',
  74. help='Print info')
  75. cli = parser.parse_args(args=args)
  76. cli = ObjectDict(vars(cli))
  77. # Load config
  78. cfg = load_cfg(cli.config)
  79. cli.cfg = cfg
  80. cli.update(get_hosts_and_tasks(cli, cfg))
  81. # Transformt env string into dict
  82. cli.env = dict(e.split('=') for e in cli.env)
  83. return cli
  84. def run():
  85. cli = None
  86. try:
  87. cli = load_cli()
  88. if not cli.no_color:
  89. enable_logging_color()
  90. cli.verbose = max(0, 1 + cli.verbose - cli.quiet)
  91. level = ['WARNING', 'INFO', 'DEBUG'][min(cli.verbose, 2)]
  92. logger.setLevel(level)
  93. log_handler.setLevel(level)
  94. if cli.info:
  95. info(cli)
  96. return
  97. base_env = Env(
  98. cli.env, # Highest-priority
  99. cli.cfg.get('env'),
  100. os.environ, # Lowest
  101. )
  102. for task in cli.tasks:
  103. run_batch(task, cli.hosts, cli, base_env)
  104. except ByrdException as e:
  105. if cli and cli.verbose > 2:
  106. raise
  107. abort(str(e))
  108. if __name__ == '__main__':
  109. run()