def main(): with Inform(notify_if_no_tty=True, version=version) as inform: try: # assure config and log directories exist to_path(CONFIG_DIR).mkdir(parents=True, exist_ok=True) to_path(DATA_DIR).mkdir(parents=True, exist_ok=True) inform.set_logfile(to_path(DATA_DIR, LOG_FILE)) # read command line cmdline = docopt(synopsis, options_first=True, version=version) command = cmdline["<command>"] args = cmdline["<args>"] if cmdline["--quiet"]: inform.quiet = True # find and run command settings = Settings(cmdline) cmd, cmd_name = Command.find(command) cmd.execute(cmd_name, args, settings, cmdline) except KeyboardInterrupt: display("Terminated by user.") except Error as e: e.terminate() except OSError as e: fatal(os_error(e)) done()
def main(): with Inform(error_status=2, flush=True, version=version) as inform: # read command line cmdline = docopt(expanded_synopsis, options_first=True, version=version) config = cmdline["--config"] command = cmdline["<command>"] args = cmdline["<args>"] if cmdline["--mute"]: inform.mute = True if cmdline["--quiet"]: inform.quiet = True emborg_opts = cull( [ "verbose" if cmdline["--verbose"] else "", "narrate" if cmdline["--narrate"] else "", "dry-run" if cmdline["--dry-run"] else "", "no-log" if cmdline["--no-log"] else "", ] ) if cmdline["--narrate"]: inform.narrate = True try: # find the command cmd, cmd_name = Command.find(command) # execute the command initialization exit_status = cmd.execute_early(cmd_name, args, None, emborg_opts) if exit_status is not None: terminate(exit_status) worst_exit_status = 0 try: while True: with Settings(config, cmd, emborg_opts) as settings: try: exit_status = cmd.execute( cmd_name, args, settings, emborg_opts ) except Error as e: settings.fail(e, cmd=' '.join(sys.argv)) e.terminate() if exit_status and exit_status > worst_exit_status: worst_exit_status = exit_status except NoMoreConfigs: pass # execute the command termination exit_status = cmd.execute_late(cmd_name, args, None, emborg_opts) if exit_status and exit_status > worst_exit_status: worst_exit_status = exit_status except KeyboardInterrupt: display("Terminated by user.") except Error as e: e.terminate() except OSError as e: fatal(os_error(e)) terminate(worst_exit_status)
def main(): with Inform() as inform: # read command line cmdline = docopt( __doc__.format(commands=Command.summarize()), options_first=True ) config = cmdline['--config'] command = cmdline['<command>'] args = cmdline['<args>'] options = cull([ 'verbose' if cmdline['--verbose'] else '', 'narrate' if cmdline['--narrate'] else '', 'trial-run' if cmdline['--trial-run'] else '', ]) if cmdline['--narrate']: inform.narrate = True try: cmd, name = Command.find(command) with Settings(config, cmd.REQUIRES_EXCLUSIVITY) as settings: cmd.execute(name, args, settings, options) except KeyboardInterrupt: display('Terminated by user.') except Error as err: err.terminate() except OSError as err: fatal(os_error(err)) terminate()
def run_sftp(server, cmds): cmd = ['sftp', '-q', '-b', '-', server] comment(fmt(' sftp {server}:'), '; '.join(cmds)) try: Run(cmd, stdin='\n'.join(cmds), modes='sOEW') except KeyboardInterrupt: display('Continuing')
def test_jaguar(): with messenger() as (msg, stdout, stderr, logfile): expected = dedent(''' Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ''').strip() display(lorum_ipsum, wrap=40) assert msg.errors_accrued() == 0 assert errors_accrued() == 0 assert strip(stdout) == expected assert strip(stderr) == '' assert log_strip(logfile) == dedent(''' ack: invoked as: <exe> ack: invoked on: <date> {expected} ''').strip().format(expected=expected)
def create(self, contents, gpg_ids=None): path = self.path try: # check to see if file already exists if path.exists(): # file creation (init) requested, but file already exists # don't overwrite the file, instead read it so the information # can be used to create any remaining files. display("%s: already exists." % path) return # create the file display('%s: creating.' % path) if path.suffix in ['.gpg', '.asc']: narrate('encrypting.', culprit=path) # encrypt it if not gpg_ids: raise PasswordError('gpg_ids missing.') self.save(contents, gpg_ids) else: narrate('not encrypting.', culprit=path) # file is not encrypted with path.open('wb') as f: f.write(contents.encode(get_setting('encoding'))) except OSError as e: raise PasswordError(os_error(e))
def test_carbuncle(): with messenger() as (msg, stdout, stderr, logfile): display('fuzzy', file=stdout) assert get_culprit() == () assert get_culprit('x') == ('x',) assert get_culprit(('x', 'y')) == ('x', 'y') assert get_culprit(('x', 'y', 1)) == ('x', 'y', 1) with set_culprit('a'): assert get_culprit() == ('a',) assert get_culprit('x') == ('a', 'x') assert get_culprit(('x', 'y')) == ('a', 'x', 'y') with set_culprit('b'): assert get_culprit() == ('b',) assert get_culprit('x') == ('b', 'x') assert get_culprit(('x', 'y')) == ('b', 'x', 'y') with set_culprit('c'): assert get_culprit() == ('c',) assert get_culprit('x') == ('c', 'x') assert get_culprit(('x', 'y')) == ('c', 'x', 'y') with add_culprit('a'): assert get_culprit() == ('a',) assert get_culprit('x') == ('a', 'x') assert get_culprit(('x', 'y')) == ('a', 'x', 'y') with add_culprit('b'): assert get_culprit() == ('a', 'b',) assert get_culprit('x') == ('a', 'b', 'x') assert get_culprit(('x', 'y')) == ('a', 'b', 'x', 'y') with add_culprit('c'): assert get_culprit() == ('a', 'b', 'c',) assert get_culprit('x') == ('a', 'b', 'c', 'x') assert get_culprit(('x', 'y')) == ('a', 'b', 'c', 'x', 'y') assert join_culprit(get_culprit((45, 99))) == 'a, b, c, 45, 99'
def test_franc(): with messenger() as (msg, stdout, stderr, logfile): display('fuzzy', file=stdout, flush=True) assert msg.errors_accrued() == 0 assert errors_accrued(True) == 0 assert strip(stdout) == 'fuzzy' assert strip(stderr) == ''
def main(): # Parse the command-line arguments: import docopt args = docopt.docopt(__doc__) params = dict( algorithm=args['--algorithm'], limit=int(args['--limit'] or 0), ) # Setup the workspace (e.g. where input and output files will go): work_msa = MsaWorkspace.from_params( args['<homologs_workspace>'], **params, ) if args['--force']: work_msa.rmdir() work_msa.mkdir() work_msa.update_params(**params) # Create an input file containing the target sequence and (possibly) a # subset of the homologs: if not work_msa.input_fasta.exists(): seqs = remove_duplicates([ work_msa.shared.target_protein_record, *work_msa.homologs.output_records, ]) if work_msa.limit: seqs = seqs[:work_msa.limit] with work_msa.touch(work_msa.input_fasta) as p: SeqIO.write(seqs, p, 'fasta') # Create the MSA: if not work_msa.output_aln.exists(): display(f"Running '{work_msa.algorithm}'...") with report_elapsed_time(): with work_msa.touch(work_msa.output_aln): apps[work_msa.algorithm](work_msa) # Create a simplified MSA that's easier to visually inspect: msa = AlignIO.read(work_msa.output_aln, 'clustal') msa_pruned = prune_gaps(msa, work_msa.shared.target_id) if not work_msa.pruned_aln.exists(): with work_msa.touch(work_msa.pruned_aln) as p: with p.open('w') as f: AlignIO.write(msa_pruned, f, 'clustal') display(msa) work_msa.write_metadata()
def get_active_config(self): active_config = self.remaining_configs.pop() if self.show_config_name: if self.show_config_name != 'first': display() display("===", active_config, "===") self.show_config_name = True return active_config
def main(): with Inform(error_status=2, flush=True, version=version) as inform: # read command line cmdline = docopt(expanded_synopsis, options_first=True, version=version) config = cmdline['--config'] command = cmdline['<command>'] args = cmdline['<args>'] if cmdline['--mute']: inform.mute = True if cmdline['--quiet']: inform.quiet = True options = cull([ 'verbose' if cmdline['--verbose'] else '', 'narrate' if cmdline['--narrate'] else '', 'trial-run' if cmdline['--trial-run'] else '', 'no-log' if cmdline['--no-log'] else '', ]) if cmdline['--narrate']: inform.narrate = True try: # find the command cmd, cmd_name = Command.find(command) # execute the command initialization exit_status = cmd.execute_early(cmd_name, args, None, options) if exit_status is not None: terminate(exit_status) worst_exit_status = 0 try: while True: with Settings(config, cmd, options) as settings: try: exit_status = cmd.execute(cmd_name, args, settings, options) except Error as e: settings.fail(e) e.terminate() if exit_status and exit_status > worst_exit_status: worst_exit_status = exit_status except NoMoreConfigs: pass # execute the command termination exit_status = cmd.execute_late(cmd_name, args, None, options) if exit_status and exit_status > worst_exit_status: worst_exit_status = exit_status except KeyboardInterrupt: display('Terminated by user.') except Error as e: e.terminate() except OSError as e: fatal(os_error(e)) terminate(worst_exit_status)
def run(cls, command, args, settings, options): # read command line cmdline = docopt(cls.USAGE, argv=[command] + args) text = cmdline["<text>"] # display matches settings.read_hosts() for name in settings.hosts.hosts_by_name.keys(): if text in name: display(name)
def test_fathead(): with messenger() as (msg, stdout, stderr, logfile): display('aaa bbb ccc', codicil=('000 111 222', '!!! @@@ ###')) assert msg.errors_accrued() == 0 assert errors_accrued(True) == 0 assert strip(stdout) == dedent(''' aaa bbb ccc 000 111 222 !!! @@@ ### ''').strip() assert strip(stderr) == ''
def get_config(name, settings, composite_config_allowed, show_config_name): global config_queue if config_queue is not None: try: active_config = config_queue.pop(0) if show_config_name: display('\n===', active_config, '===') return active_config except IndexError: raise NoMoreConfigs() configs = Collection(settings.get(CONFIGS_SETTING, '')) default = settings.get(DEFAULT_CONFIG_SETTING) config_groups = {} for config in configs: if '=' in config: group, _, sub_configs = config.partition('=') sub_configs = sub_configs.split(',') else: group = config sub_configs = [config] config_groups[group] = sub_configs if not name: name = default if name: if name not in config_groups: raise Error('unknown configuration.', culprit=name) else: if len(configs) > 1: name = configs[0] else: raise Error('no known configurations.', culprit=(settings.settings_filename, CONFIGS_SETTING)) configs = list(config_groups[name]) num_configs = len(configs) if num_configs > 1 and composite_config_allowed is False: raise Error('command does not support composite configs.', culprit=name) elif num_configs < 1: raise Error('empty composite config.', culprit=name) active_config = configs.pop(0) if composite_config_allowed is None: config_queue = [] else: config_queue = configs if config_queue and show_config_name: display('===', active_config, '===') return active_config
def get_config(name, settings, composite_config_response, show_config_name): global config_queue if config_queue is not None: try: active_config = config_queue.pop(0) if show_config_name: display("\n===", active_config, "===") return active_config except IndexError: raise NoMoreConfigs() configs = Collection(settings.get(CONFIGS_SETTING, "")) default = settings.get(DEFAULT_CONFIG_SETTING) config_groups = {} for config in configs: if "=" in config: group, _, sub_configs = config.partition("=") sub_configs = sub_configs.split(",") else: group = config sub_configs = [config] config_groups[group] = sub_configs if not name: name = default if name: if name not in config_groups: raise Error("unknown configuration.", culprit=name) else: if len(configs) > 0: name = configs[0] else: raise Error("no known configurations.", culprit=CONFIGS_SETTING) configs = list(config_groups[name]) num_configs = len(configs) if num_configs > 1 and composite_config_response == "error": raise Error("command does not support composite configs.", culprit=name) elif num_configs < 1: raise Error("empty composite config.", culprit=name) active_config = configs.pop(0) if composite_config_response == "all": config_queue = configs else: config_queue = [] if config_queue and show_config_name: display("===", active_config, "===") return active_config
def get_psf_filename(psf_file): if not psf_file: try: with open(saved_psf_file_filename) as f: psf_file = f.read().strip() display('Using PSF file:', psf_file) except OSError: fatal('missing PSF file name.') try: with open(saved_psf_file_filename, 'w') as f: f.write(psf_file) except OSError as e: warn(os_error(e)) return psf_file
def initialize_network(self): network = self.network # run the init script if given try: if network.init_script: script = Run(network.init_script, "sOEW") if script.stdout: display(script.stdout.rstrip()) except AttributeError: pass except Error as e: warn("{} network init_script failed: {}".format( network.name(), network.init_script)) codicil(e.get_message())
def publish_scp(config, workspace): """ Copy the archive to one or more remote hosts via `scp`. """ hosts = require_one_or_more(config, 'host') remote_dir = config.get('remote_dir', 'backup/sparekeys') remote_dir = remote_dir.format(**PARAMS) run_flags = 'sOEW' if get_informer().quiet else 'soEW' for host in hosts: try: run(['ssh', host, f'mkdir -p {remote_dir}'], run_flags) run(['scp', '-r', workspace, f'{host}:{remote_dir}'], run_flags) except Error as e: e.reraise(codicil=e.cmd) display(f"Archive copied to '{host}'.")
def create(self, contents): path = self.path try: if path.exists(): # file creation (init) requested, but file already exists # don't overwrite the file, instead read it so the information # can be used to create any remaining files. display("%s: already exists." % path) return # create the file display("%s: creating." % path) # file is not encrypted with path.open("wb") as f: f.write(contents.encode("utf-8")) except OSError as err: raise Error(os_error(err))
def remove_duplicates(seqs): already_seen = set() num_duplicates = 0 unique_seqs = [] for seq in seqs: if seq.id in already_seen: num_duplicates += 1 continue already_seen.add(seq.id) unique_seqs.append(seq) if num_duplicates: display(f"Removed {plural(num_duplicates):# duplicate sequence/s}.") return unique_seqs
def list_signals(): # Read command line {{{2 cmdline = docopt(__doc__) args = cmdline['<signal>'] if not args: args = ['*'] psf_file = get_psf_filename(cmdline['--psf-file']) show_meta = cmdline['--long'] use_cache = not cmdline['--no-cache'] # List signals {{{2 try: psf = PSF(psf_file, sep=':', use_cache=use_cache) if show_meta: nw = uw = kw = 0 # name width, units width, kind width data = [] for name in expand_args(psf.signals.keys(), args): if name not in psf.signals: warn('not found.', culprit=name) signal = psf.get_signal(name) if len(signal.name) > nw: nw = len(signal.name) units = psf.units_to_unicode(signal.units) if len(units) > uw: uw = len(units) kind = signal.type.kind kind = kinds.get(kind, kind) if len(kind) > kw: kw = len(kind) points = len(signal.ordinate) data.append((signal.name, units, kind, points)) if not data: raise Error(f'{plural(args):no match/es}.', culprit=args) for name, units, kind, points in data: display( f' {name:<{nw}} {units:<{uw}} {kind:<{kw}} ({points} points)' ) else: signals = expand_args(psf.signals.keys(), args) if not signals: raise Error(f'{plural(args):no match/es}.', culprit=args) display(columns(signals)) except Error as e: e.terminate()
def run(cls, command, args, settings, options): # read command line docopt(cls.USAGE, argv=[command] + args) # display summary display(full_stop(settings.get_summary())) # initialize the network settings.initialize_network() # initializing the network must be done before reading the hosts # file as it may try to do network operations # create SSH config file components # header name = settings.network.Name() desc = settings.network.description if desc: network = f"{name} network -- {desc}" else: network = f"{name} network" now = arrow.now() time = now.format(DATE_FORMAT) header = SSH_HEADER.format(network=network, time=time, config_dir=settings.config_dir) # overrides overrides = settings.ssh_overrides if overrides: overrides = SSH_OVERRIDES.format(overrides=overrides) # hosts settings.read_hosts() hosts = SSH_HOSTS.format(hosts=settings.hosts.output()) # defaults defaults = settings.ssh_defaults if defaults: defaults = SSH_DEFAULTS.format(defaults=defaults) # combine everything and write as SSH config file contents = "\n\n\n".join( section.strip() for section in [header, overrides, hosts, defaults] if section) settings.write_ssh_config(contents)
def test_possess(): with messenger(stream_policy='header') as (msg, stdout, stderr, logfile): out = [ 'hey now!', 'hey now!', ] err = [ 'Aiko aiko all day', 'jockomo feeno na na nay', 'jockomo feena nay.', ] display(*out) warn(*err, sep=', ') assert msg.errors_accrued() == 0 assert errors_accrued(True) == 0 assert strip(stdout) == ' '.join(out) assert strip(stderr) == 'warning: ' + ', '.join(err)
def test_unbuckle(): with messenger() as (msg, stdout, stderr, logfile): msg.set_stream_policy(lambda i, so, se: se if i.severity else so) out = [ 'hey now!', 'hey now!', ] err = [ 'Aiko aiko all day', 'jockomo feeno na na nay', 'jockomo feena nay.', ] display(*out) warn(*err, sep=', ') assert msg.errors_accrued() == 0 assert errors_accrued(True) == 0 assert strip(stdout) == ' '.join(out) assert strip(stderr) == 'warning: ' + ', '.join(err)
def get_argv(): argv = sys.argv[1:] if argv: # save the command line arguments for next time try: with open(saved_arguments_filename, 'w') as f: args = [a for a in argv if a not in ['-c', '--no-cache']] f.write('\n'.join(args)) except OSError as e: warn(os_error(e)) else: # command line arguments not give, reuse previous ones try: with open(saved_arguments_filename) as f: argv = f.read().split('\n') display('Using command:', ' '.join(argv)) except OSError: done() return argv
def query_passcode(config): narrate("Getting a passcode for the archive") # The authentication system is special in that if no plugins are specified, # the 'getpass' plugin will be used by default. plugins = select_plugins(config, 'auth', ['getpass']) # Try each authentication method until one works. for plugin in plugins: subconfig = config.get('auth', {}).get(plugin.name, {}) try: return eval_plugin(plugin, config, subconfig) except SkipPlugin as e: display(f"Skipping '{plugin.name}' authentication: {e}") continue raise AllAuthFailed(plugins)
def publish_mount(config, workspace): """ Copy the archive to one or more mounted/mountable drives. """ drives = require_one_or_more(config, 'drive') remote_dir = config.get('remote_dir', 'backup/sparekeys') remote_dir = remote_dir.format(**PARAMS) for drive in drives: narrate(f"copying archive to '{drive}'.") try: with mount(drive): dest = to_path(drive, remote_dir) rm(dest); mkdir(dest) cp(workspace, dest) except Error as e: error(e, culprit=drive, codicil='Skipping.') else: display(f"Archive copied to '{drive}'.")
def test_billfold(): with messenger() as (msg, stdout, stderr, logfile): display('hey now!', culprit=('yo', 'ho')) display('yep,\nYEP!', culprit='yep yep yep yep yep yep yep yep yep yep yep') expected = dedent(''' yo, ho: hey now! yep yep yep yep yep yep yep yep yep yep yep: yep, YEP! ''').strip() assert msg.errors_accrued() == 0 assert errors_accrued() == 0 assert strip(stdout) == expected assert strip(stderr) == '' assert log_strip(logfile) == dedent(''' ack: invoked as: <exe> ack: invoked on: <date> {expected} ''').strip().format(expected=expected)
def play(self, quit): for song_filename in self.songs: if song_filename in self.skip: continue if skip_song_that_was_playing_when_last_killed: self.played.append(song_filename) self.playing = True song_path = Path(song_filename).expanduser() metadata = MetaData(song_path, self.now_playing_path) metadata.now_playing() display(metadata.summary()) self.player.set_property("uri", "file://" + str(song_path.resolve())) self.player.set_state(Gst.State.PLAYING) while self.playing: sleep(0.1) if not skip_song_that_was_playing_when_last_killed: self.played.append(song_filename) self.skip = [] self.played = [] sleep(1) quit()
def build_archive(config, interactive=True): narrate("Building the archive") plugins = select_plugins(config, 'archive') if not plugins: raise ConfigError(f"'plugins.archive' not specified, nothing to do.") # Make the archive directory: name = config.get('archive_name', '{host}').format(**PARAMS) workspace = to_path(appdirs.user_data_dir(__slug__), name) archive = to_path(workspace / 'archive') rm(workspace) mkdir(archive) # Apply any 'archive' plugins: for plugin in plugins: subconfigs = config.get('archive', {}).get(plugin.name, []) run_plugin(plugin, config, subconfigs, archive) # Show the user which files were included in the archive. display("The following files were included in the archive:") for root, _, files in os.walk(archive): root = to_path(root).relative_to(archive) for file in files: display(' ', root / file) display() if interactive: input("Is this correct? <Enter> to continue, <Ctrl-C> to cancel: ") return workspace
def main(): import docopt args = docopt.docopt(__doc__) # Create the workspace directory: work = SharedWorkspace(args['<workspace>']) if args['--force']: work.rmdir() if work.exists(): error(f"Workspace '{args['<workspace>']}' already exists.") fatal("Use the -f flag to overwrite. Aborting.") work.mkdir() # Fill in the workspace: copyfile(args['<fasta>'], work.target_fasta) copyfile(args['<pdb>'], work.get_target_pdb(args['<pdb>'])) work.loophash_db.symlink_to( os.path.relpath( Path(args['<loophash_db>']).resolve(), work.root, ), target_is_directory=True, ) work.params['target'] = { 'pdb_chain': args['<pdb_chain>'], } work.params['user'] = { 'email': args['--user-email'], } work.write_params() display("Workspace successfully initialized.")
def run(cls, command, args, settings, options): # read command line cmdline = docopt(cls.USAGE, argv=[command] + args) mount_point = cmdline["<mount_point>"] if mount_point: mount_point = settings.to_path(mount_point, resolve=False) else: mount_point = settings.as_path("default_mount_point") if not mount_point: raise Error("must specify directory to use as mount point") display("mount point is:", mount_point) archive = cmdline["--archive"] date = cmdline["--date"] mount_all = cmdline["--all"] include_external_archives = cmdline["--include-external"] # get the desired archive if not archive: if date: archive = get_name_of_nearest_archive(settings, date) elif not mount_all: archive = get_name_of_latest_archive(settings) # create mount point if it does not exist try: mkdir(mount_point) except OSError as e: raise Error(os_error(e)) # run borg borg = settings.run_borg( cmd="mount", args=[settings.destination(archive), mount_point], emborg_opts=options, strip_prefix=include_external_archives, ) out = borg.stdout if out: output(out.rstrip())
def create(self, contents, gpg_ids): path = self.path try: to_path(get_setting('settings_dir')).mkdir(parents=True, exist_ok=True) if path.exists(): # file creation (init) requested, but file already exists # don't overwrite the file, instead read it so the information # can be used to create any remaining files. display("%s: already exists." % path) return # create the file display('%s: creating.' % path) if path.suffix in ['.gpg', '.asc']: narrate('encrypting.', culprit=path) # encrypt it self.save(contents, gpg_ids) else: narrate('not encrypting.', culprit=path) # file is not encrypted with path.open('w') as f: f.write(contents) except OSError as err: raise Error(os_error(err))
def main(): try: mappingSummary = '\n'.join([ ' %-9s %s' % (m.key, m.desc) for m in mappings ]) # Read command line cmdSummary = __doc__.format(mappings = mappingSummary) arguments = docopt(cmdSummary) # Configure output to user Inform(log=False, quiet = arguments['--quiet']) # Process command line arguments file1 = arguments['<file1>'] file2 = arguments['<file2>'] file3 = arguments['<file3>'] file4 = arguments['<file4>'] useGUI = None if arguments['--vim']: useGUI = False if arguments['--gvim']: useGUI = True force = arguments['--force'] # Run vdiff vdiff = Vdiff(file1, file2, file3, file4, useGUI) if force or vdiff.differ(): vdiff.edit() else: display('%s and %s are the same.' % (file1, file2)) except KeyboardInterrupt: vdiff.cleanup() raise SystemExit('Killed by user') except Error as err: err.terminate()
def main(): try: # Read command line {{{1 cmdline = docopt(__doc__) keys = cmdline["--keys"].split(",") if cmdline["--keys"] else [] update = cmdline["--update"].split(",") if cmdline["--update"] else [] skip = cmdline["--skip"].split(",") if cmdline["--skip"] else [] Inform( narrate=cmdline["--narrate"] or cmdline["--verbose"], verbose=cmdline["--verbose"], logfile=".sshdeploy.log", prog_name=False, flush=True, version=__version__, ) if keys and not cmdline["--trial-run"]: fatal( "Using the --keys option results in incomplete authorized_keys files.", "It may only be used for testing purposes.", "As such, --trial-run must also be specified when using --keys.", sep="\n", ) # Generated detailed help {{{1 if cmdline["manual"]: from pkg_resources import resource_string try: Run(cmd=["less"], modes="soeW0", stdin=resource_string("src", "manual.rst").decode("utf8")) except OSError as err: error(os_error(err)) terminate() # Read config file {{{1 try: config_file = cmdline.get("--config-file") config_file = config_file if config_file else "sshdeploy.conf" contents = to_path(config_file).read_text() except OSError as err: fatal(os_error(err)) code = compile(contents, config_file, "exec") config = {} try: exec(code, config) except Exception as err: fatal(err) # Move into keydir {{{1 keydir = cmdline["--keydir"] keydir = to_path(keydir if keydir else "keys-" + date) if cmdline["generate"]: comment("creating key directory:", keydir) rm(keydir) mkdir(keydir) cd(keydir) elif cmdline["distribute"]: cd(keydir) # determine default values for key options defaults = {} for name, default in [ ("keygen-options", DefaultKeygenOpts), ("abraxas-account", DefaultAbraxasAccount), ("remote-include-filename", DefaultRemoteIncludeFilename), ]: defaults[name] = config.get(name, default) # Generate keys {{{1 if cmdline["generate"]: for keyname in sorted(config["keys"].keys()): data = config["keys"][keyname] if keys and keyname not in keys: # user did not request this key continue # get default values for missing key options for option in defaults: data[option] = data.get(option, defaults[option]) # generate the key key = Key(keyname, data, update, skip, cmdline["--trial-run"]) key.generate() # Publish keys {{{1 elif cmdline["distribute"]: for keyname in sorted(config["keys"].keys()): data = config["keys"][keyname] if keys and keyname not in keys: continue # user did not request this key # get default values for missing key options for option in defaults: data[option] = data.get(option, defaults[option]) # publish the key pair to clients key = Key(keyname, data, update, skip, cmdline["--trial-run"]) key.publish_private_key() key.gather_public_keys() # publish authorized_keys files to servers {{{1 if cmdline["distribute"]: for each in sorted(AuthKeys.known): authkey = AuthKeys.known[each] authkey.publish() authkey.verify() # Process hosts {{{1 elif cmdline["test"] or cmdline["clean"] or cmdline["hosts"]: hosts = set() for keyname, data in config["keys"].items(): if keys and keyname not in keys: continue # user did not request this key # add servers to list of hosts for server, options in data["servers"].items(): if update and server not in update or server in skip: continue if "bypass" not in options: hosts.add(server) # add clients to list of hosts for client in data["clients"].keys(): if update and client not in update or client in skip: continue hosts.add(client) # process the hosts if cmdline["test"]: # test host for host in sorted(hosts): test_access(host) elif cmdline["clean"]: # clean host for host in sorted(hosts): clean(host) else: # list hosts for host in sorted(hosts): display(host) except OSError as err: error(os_error(err)) except KeyboardInterrupt: display("Killed by user") done()