class MkConfig(Command): """${cmd_usage} Generate a config file for a backup plugin. ${cmd_option_list} """ name = 'mk-config' aliases = [ 'mc' ] options = [ option('--name', help='Name of the backupset'), option('--edit', action='store_true', help='Edit the generated config'), option('--provider', action='store_true', help='Generate a provider config'), option('--file', '-f', help='Save the final config to the specified file'), option('--minimal', '-m', action='store_true', default=False, help="Do not include comment from a backup " "plugin's configspec"), ] description = 'Generate a config file for a backup plugin' # After initial validation: # run through and flag required parameters with a 'REQUIRED' comment # run through and comment out default=None parameters def _cleanup_config(self, config, skip_comments=False): errors = config.validate(validator, preserve_errors=True,copy=True) # First flag any required parameters for entry in flatten_errors(config, errors): section_list, key, error = entry section_name, = section_list if error is False: config[section_name][key] = '' config[section_name].comments[key].append('REQUIRED') elif error: print >>sys.stderr, "Bad configspec generated error", error pending_comments = [] for section in list(config): if pending_comments: if skip_comments: comments = [] else: comments = config.comments.get(section, []) comments = pending_comments + comments config.comments[section] = comments del pending_comments[:] for idx, (key, value) in enumerate(config[section].items()): if value is None: if not skip_comments: pending_comments.extend(config[section].comments.get(key, [])) pending_comments.append('%s = "" # no default' % key) del config[section][key] else: if skip_comments: del config[section].comments[key][:] if pending_comments: if skip_comments: comments = [] else: comments = config[section].comments.get(key, []) comments = pending_comments + comments config[section].comments[key] = comments del pending_comments[:] if value is True or value is False: config[section][key] = ['no','yes'][value] if pending_comments: if skip_comments: config.final_comment = pending_comments else: config.final_comment = pending_comments + config.final_comment # drop initial whitespace config.initial_comment = [] # insert a blank between [holland:backup] and first section try: config.comments[config.sections[1]].insert(0, '') except IndexError: pass def run(self, cmd, opts, plugin_type): if opts.name and opts.provider: print >>sys.stderr, "Can't specify a name for a global provider config" return 1 try: plugin_cls = load_first_entrypoint('holland.backup', plugin_type) except PluginLoadError, exc: logging.info("Failed to load backup plugin %r: %s", plugin_type, exc) return 1 try: cfgspec = sys.modules[plugin_cls.__module__].CONFIGSPEC except: print >>sys.stderr, "Could not load config-spec from plugin %r" % plugin_type return 1 base_config = """ [holland:backup] plugin = "" backups-to-keep = 1 auto-purge-failures = yes purge-policy = after-backup """.lstrip().splitlines() cfg = ConfigObj(base_config, configspec=cfgspec, list_values=True,stringify=True) cfg['holland:backup']['plugin'] = plugin_type self._cleanup_config(cfg, skip_comments=opts.minimal) if opts.edit: done = False editor = _find_editor() if not editor: print >>sys.stderr, "Could not find a valid editor" return 1 tmpfileobj = tempfile.NamedTemporaryFile() cfg.filename = tmpfileobj.name cfg.write() while not done: status = subprocess.call([editor, cfg.filename]) if status != 0: if not confirm("Editor exited with non-zero status[%d]. " "Would you like to retry?" % status): print >>sys.stderr, "Aborting" return 1 else: continue try: cfg.reload() except ParseError, exc: print >>sys.stderr, "%s : %s" % \ (exc.msg, exc.line) else: errors = cfg.validate(validator,preserve_errors=True) if errors is True: done = True continue else: _report_errors(cfg, errors) if not confirm('There were configuration errors. Continue?'): print >>sys.stderr, "Aborting" return 1 tmpfileobj.close()
def run(self, cmd, opts, *plugin_type): if not plugin_type: print("Missing plugin name", file=sys.stderr) return 1 if opts.name and opts.provider: print("Can't specify a name for a global provider config", file=sys.stderr) return 1 try: plugin_cls = load_first_entrypoint('holland.backup', plugin_type[0]) except PluginLoadError as exc: logging.info("Failed to load backup plugin %r: %s", plugin_type[0], exc) return 1 try: cfgspec = sys.modules[plugin_cls.__module__].CONFIGSPEC except BaseException as ex: print("Could not load config-spec from plugin %r, %s" % (plugin_type[0], ex), file=sys.stderr) return 1 base_config = """ [holland:backup] plugin = "" backups-to-keep = 1 auto-purge-failures = yes purge-policy = after-backup estimated-size-factor = 1.0 """.lstrip().splitlines() cfg = ConfigObj(base_config, configspec=cfgspec, list_values=True, stringify=True) cfg['holland:backup']['plugin'] = plugin_type[0] self._cleanup_config(cfg, skip_comments=opts.minimal) if opts.edit: done = False editor = _find_editor() if not editor: print("Could not find a valid editor", file=sys.stderr) return 1 tmpfileobj = tempfile.NamedTemporaryFile() cfg.filename = tmpfileobj.name cfg.write() while not done: status = subprocess.call([editor, cfg.filename]) if status != 0: if not confirm("Editor exited with non-zero status[%d]. " "Would you like to retry?" % status): print("Aborting", file=sys.stderr) return 1 try: cfg.reload() except ParseError as exc: print("%s : %s" % \ (exc.msg, exc.line), file=sys.stderr) else: errors = cfg.validate(VALIDATOR, preserve_errors=True) if errors is True: done = True continue else: _report_errors(cfg, errors) if not confirm('There were configuration errors. Continue?'): print("Aborting", file=sys.stderr) return 1 tmpfileobj.close() if not opts.name and not opts.file: buf = getattr(sys.stdout, 'buffer', sys.stdout) cfg.write(buf) buf.flush() if opts.file: print("Saving config to %r" % opts.file, file=sys.stderr) filehandle = open(opts.file, 'w') buf = getattr(filehandle, 'buffer', filehandle) cfg.write(buf) buf.flush() elif opts.name: base_dir = os.path.dirname(HOLLANDCFG.filename) path = os.path.join(base_dir, 'backupsets', opts.name + '.conf') print("Saving config to %r" % path, file=sys.stderr) filehandle = open(path, 'w') buf = getattr(filehandle, 'buffer', filehandle) cfg.write(buf) buf.flush() return 0
def run(self, cmd, opts, *plugin_type): if not plugin_type: print("Missing plugin name", file=sys.stderr) return 1 if opts.name and opts.provider: print("Can't specify a name for a global provider config", file=sys.stderr) return 1 try: plugin_cls = load_first_entrypoint("holland.backup", plugin_type[0]) except PluginLoadError as exc: logging.info("Failed to load backup plugin %r: %s", plugin_type[0], exc) return 1 try: cfgspec = sys.modules[plugin_cls.__module__].CONFIGSPEC except BaseException as ex: print( "Could not load config-spec from plugin %r, %s" % (plugin_type[0], ex), file=sys.stderr, ) return 1 base_config = """ [holland:backup] plugin = "" backups-to-keep = 1 auto-purge-failures = yes purge-policy = after-backup estimated-size-factor = 1.0 """.lstrip().splitlines() cfg = ConfigObj(base_config, configspec=cfgspec, list_values=True, stringify=True) cfg["holland:backup"]["plugin"] = plugin_type[0] self._cleanup_config(cfg, skip_comments=opts.minimal) if opts.edit: done = False editor = _find_editor() if not editor: print("Could not find a valid editor", file=sys.stderr) return 1 tmpfileobj = tempfile.NamedTemporaryFile() cfg.filename = tmpfileobj.name cfg.write() while not done: status = subprocess.call([editor, cfg.filename]) if status != 0: if not confirm( "Editor exited with non-zero status[%d]. " "Would you like to retry?" % status ): print("Aborting", file=sys.stderr) return 1 try: cfg.reload() except ParseError as exc: print("%s : %s" % (exc.msg, exc.line), file=sys.stderr) else: errors = cfg.validate(VALIDATOR, preserve_errors=True) if errors is True: done = True continue else: _report_errors(cfg, errors) if not confirm("There were configuration errors. Continue?"): print("Aborting", file=sys.stderr) return 1 tmpfileobj.close() if not opts.name and not opts.file: buf = getattr(sys.stdout, "buffer", sys.stdout) cfg.write(buf) buf.flush() if opts.file: print("Saving config to %r" % opts.file, file=sys.stderr) filehandle = open(opts.file, "w") buf = getattr(filehandle, "buffer", filehandle) cfg.write(buf) buf.flush() elif opts.name: base_dir = os.path.dirname(HOLLANDCFG.filename) path = os.path.join(base_dir, "backupsets", opts.name + ".conf") print("Saving config to %r" % path, file=sys.stderr) filehandle = open(path, "w") buf = getattr(filehandle, "buffer", filehandle) cfg.write(buf) buf.flush() return 0