def save_recent_session(session_key, argv): """ Saves session arguments into recents file. :param session_key: key to save under (only one session per key is saved) :param argv: argument list to save :return: None """ # add current line to history, if not already there cmdline = " ".join( [x if x and not ' ' in x else "'{}'".format(x) for x in argv]) if not _last_input: if cmdline != readline.get_history_item( readline.get_current_history_length()): readline.add_history(cmdline) make_radiopadre_dir() try: readline.write_history_file(HISTORY_FILE) except IOError: traceback.print_exc() warning("Error writing history file (see above). Proceeding anyway.") readline.clear_history() # reform command-line without persisting options cmdline = " ".join([ x if x and not ' ' in x else "'{}'".format(x) for x in argv if x not in config.NON_PERSISTING_OPTIONS ]) recents = _load_recent_sessions(False) or OrderedDict() session_key = ":".join(map(str, session_key)) if session_key in recents: del recents[session_key] if len(recents) >= 5: del recents[list(recents.keys())[0]] recents[session_key] = cmdline make_radiopadre_dir() with open(RECENTS_FILE, 'wt') as rf: for key, cmdline in recents.items(): rf.write("{}:::{}\n".format(key, cmdline)) global _recent_sessions _recent_sessions = recents
def init_specific_options(remote_host, notebook_path, options): global _DEFAULT_KEYS global _CMDLINE_DEFAULTS parser = configparser.ConfigParser() hostname = ff("{remote_host}") if remote_host else "local sesssion" session = ff("{hostname}:{notebook_path}") config_exists = os.path.exists(CONFIG_FILE) use_config_files = not options.remote and not options.inside_container # try to read config file for host and host:path (not in --remote mode though) if use_config_files and config_exists: parser.read(CONFIG_FILE) for sect_key in "global defaults", hostname, session: if parser.has_section(sect_key): section = dict(parser.items(sect_key)) if section: message( ff(" loading settings from {CONFIG_FILE} [{sect_key}]" )) for key in _DEFAULT_KEYS: lkey = key.lower() if lkey in section: value = _get_config_value(section, lkey) if value != globals()[key]: message(ff(" {key} = {value}")) globals()[key] = value # update using command-line options command_line_updated = [] for key in globals().keys(): if re.match("^[A-Z]", key): optname = key.lower() opt_switch = "--" + optname.replace("_", "-") value = getattr(options, optname, None) # skip DEFAULT_VALUE placeholders, trust in config if value is DEFAULT_VALUE or value is None: continue if type(value) is list: value = ",".join(value) if value is not _CMDLINE_DEFAULTS.get(key, None): if use_config_files: # do not mark options such as --update for saving if value is not _CMDLINE_DEFAULTS.get(key) and DefaultConfig.get(key) is not None \ and opt_switch not in NON_PERSISTING_OPTIONS: command_line_updated.append(key) message(ff(" command line specifies {key} = {value}")) globals()[key] = value # save new config if use_config_files and command_line_updated: if options.save_config_host: message( ff(" saving command-line settings to {CONFIG_FILE} [{hostname}]" )) if not parser.has_section(hostname): parser.add_section(hostname) for key in command_line_updated: parser.set(hostname, key, _set_config_value(key)) if options.save_config_session: if not parser.has_section(session): parser.add_section(session) message( ff(" saving command-line settings to {CONFIG_FILE} [{session}]" )) for key in command_line_updated: parser.set(session, key, _set_config_value(key)) if options.save_config_host or options.save_config_session: radiopadre_dir = make_radiopadre_dir() with open(CONFIG_FILE + ".new", "w") as configfile: if not config_exists: message(ff(" creating new config file {CONFIG_FILE}")) if not parser.has_section('global defaults'): configfile.write( "[global defaults]\n# defaults that apply to all sessions go here\n\n" ) parser.write(configfile) configfile.write("\n\n## default settings follow\n") for key, value in DefaultConfig.items(): configfile.write("# {} = {}\n".format(key.lower(), value)) # if successful, rename files if config_exists: if os.path.exists(CONFIG_FILE + ".old"): os.unlink(CONFIG_FILE + ".old") os.rename(CONFIG_FILE, CONFIG_FILE + ".old") os.rename(CONFIG_FILE + ".new", CONFIG_FILE) message(ff("saved updated config to {CONFIG_FILE}"))
def _init_session_dir(): radiopadre_dir = make_radiopadre_dir() global SESSION_INFO_DIR SESSION_INFO_DIR = ff("{radiopadre_dir}/sessions") make_dir(SESSION_INFO_DIR)
def start_session(container_name, selected_ports, userside_ports, notebook_path, browser_urls): from iglesia import ABSROOTDIR, LOCAL_SESSION_DIR, SHADOW_SESSION_DIR, SNOOP_MODE radiopadre_dir = make_radiopadre_dir() docker_local = make_dir(radiopadre_dir + "/.docker-local") js9_tmp = make_dir(radiopadre_dir + "/.js9-tmp") session_info_dir = get_session_info_dir(container_name) message( ff("Container name: {container_name}")) # remote script will parse it docker_opts = [ docker, "run", "--rm", "--name", container_name, "--cap-add=SYS_ADMIN", "-w", ABSROOTDIR, "--user", "{}:{}".format(os.getuid(), os.getgid()), "-e", "USER={}".format(os.environ["USER"]), "-e", "HOME={}".format(os.environ["HOME"]), "-e", "RADIOPADRE_DIR={}".format(radiopadre_dir), "-e", ff("RADIOPADRE_CONTAINER_NAME={container_name}"), "-e", ff("RADIOPADRE_SESSION_ID={config.SESSION_ID}"), ] # enable detached mode if not debugging, and also if not doing conversion non-interactively if not config.CONTAINER_DEBUG and not config.NBCONVERT: docker_opts.append("-d") for port1, port2 in zip(selected_ports, CONTAINER_PORTS): docker_opts += ["-p", "{}:{}/tcp".format(port1, port2)] container_ports = list(CONTAINER_PORTS) # setup mounts for work dir and home dir, if needed homedir = os.path.expanduser("~") docker_opts += [ "-v", "{}:{}{}".format(ABSROOTDIR, ABSROOTDIR, ":ro" if SNOOP_MODE else ""), "-v", "{}:{}".format(homedir, homedir), "-v", "{}:{}".format(radiopadre_dir, radiopadre_dir), ## hides /home/user/.local, which can confuse jupyter and ipython ## into seeing e.g. kernelspecs that they should not see "-v", "{}:{}/.local".format(docker_local, homedir), # mount session info directory (needed to serve e.g. js9prefs.js) "-v", "{}:{}".format(session_info_dir, LOCAL_SESSION_DIR), "-v", "{}:{}".format(session_info_dir, SHADOW_SESSION_DIR), # mount a writeable tmp dir for the js9 install -- needed by js9helper "-v", "{}:/.radiopadre/venv/js9-www/tmp".format(js9_tmp), "--label", "radiopadre.user={}".format(USER), "--label", "radiopadre.dir={}".format(os.getcwd()), ] if config.CONTAINER_DEV: if os.path.isdir(SERVER_INSTALL_PATH): docker_opts += ["-v", "{}:/radiopadre".format(SERVER_INSTALL_PATH)] if os.path.isdir(CLIENT_INSTALL_PATH): docker_opts += [ "-v", "{}:/radiopadre-client".format(CLIENT_INSTALL_PATH) ] # add image docker_opts.append(docker_image) # build up command-line arguments docker_opts += _collect_runscript_arguments(container_ports + userside_ports) if notebook_path: docker_opts.append(notebook_path) _run_container(container_name, docker_opts, jupyter_port=selected_ports[0], browser_urls=browser_urls) if config.NBCONVERT: return global running_container running_container = container_name atexit.register(reap_running_container) if config.CONTAINER_PERSIST and config.CONTAINER_DETACH: message("exiting: container session will remain running.") running_container = None # to avoid reaping sys.exit(0) else: if config.CONTAINER_PERSIST: prompt = "Type 'exit' to kill the container session, or 'D' to detach: " else: prompt = "Type 'exit' to kill the container session: " try: while True: a = INPUT(prompt) if a.lower() == 'exit': sys.exit(0) if a.upper( ) == 'D' and config.CONTAINER_PERSIST and container_name: running_container = None # to avoid reaping sys.exit(0) except BaseException as exc: if type(exc) is KeyboardInterrupt: message("Caught Ctrl+C") status = 1 elif type(exc) is SystemExit: status = getattr(exc, 'code', 0) message("Exiting with status {}".format(status)) else: message("Caught exception {} ({})".format(exc, type(exc))) status = 1 # if not status: # running_container = None # to avoid reaping sys.exit(status)
def check_recent_sessions(options, argv, parser=None): """ Loads a recent session if requested :param options: Options object from ArgumentParser :param argv: Argument list (from sys.argv[1:] initially) :param parser: ArgumentParser object used to (re)parse the options :return: options, argv Where options and argv may have been loaded from the recent options file """ make_radiopadre_dir() # load history try: readline.read_history_file(HISTORY_FILE) except IOError: pass resume_session = None # a single-digit argument resumes session #N if len(options.arguments) == 1 and re.match("^\d$", options.arguments[0]): resume_session = int(options.arguments[0]) # no arguments is resume session #0 elif not options.arguments: resume_session = 0 if resume_session is not None: last = _load_recent_sessions() num_recent = len(last) if resume_session >= num_recent: bye(ff("no recent session #{resume_session}")) message("Your most recent radiopadre sessions are:") message("") for i, (_, opts) in enumerate(list(last.items())[::-1]): message(" [#{0}] {1}".format(i, opts), color="GREEN") message("") print( "\nInteractive startup mode. Edit arguments and press Enter to run, or Ctrl+C to bail out. " ) print( " (Ctrl+U + <NUM> + Enter will paste other recent session arguments from the list above)\n" ) inp = None cmdline = '' readline.set_startup_hook(lambda: readline.insert_text(cmdline)) while inp is None: # form up list of fake args to be re-parsed for the last session cmdline = list(last.items())[-(resume_session + 1)][1] # non-persisting options raised in command line shall be appended to the fake args for opt in config.NON_PERSISTING_OPTIONS: if opt.startswith("--") and getattr(options, opt[2:].replace( "-", "_"), None): cmdline += " " + opt cmdline += " " ## colors confuse Ctrl+U and such # prompt = ff("{logger.Colors.GREEN}[#{resume_session}]:{logger.Colors.ENDC} ") prompt = ff("[#{resume_session}] ") inp = INPUT(prompt) inp = inp.strip() if not inp: resume_session = 0 inp = None elif re.match("^\d+$", inp): res = int(inp) if res >= num_recent: warning(ff("no recent session #{res}")) else: resume_session = res readline.remove_history_item(1) inp = None readline.set_startup_hook(None) global _last_input _last_input = inp argv = shlex.split(inp, posix=False) options = parser.parse_args(argv) return options, argv
def start_session(container_name, selected_ports, userside_ports, notebook_path, browser_urls): from iglesia import ABSROOTDIR, LOCAL_SESSION_DIR, SHADOW_SESSION_DIR radiopadre_dir = make_radiopadre_dir() docker_local = make_dir(radiopadre_dir + "/.docker-local") js9_tmp = make_dir(radiopadre_dir + "/.js9-tmp") session_info_dir = get_session_info_dir(container_name) # message(ff("Container name: {container_name}")) # remote script will parse it os.environ["RADIOPADRE_CONTAINER_NAME"] = container_name os.environ["XDG_RUNTIME_DIR"] = "" docker_opts = ["--workdir", ABSROOTDIR] # setup mounts for work dir and home dir, if needed homedir = os.path.expanduser("~") docker_opts += [ "-B", "{}:{}{}".format(ABSROOTDIR, ABSROOTDIR, ""), # ":ro" if orig_rootdir else ""), "-B", "{}:{}".format(radiopadre_dir, radiopadre_dir), # hides /home/user/.local, which if exposed, can confuse jupyter and ipython "-B", "{}:{}".format(docker_local, os.path.realpath(os.path.join(homedir, ".local"))), # mount session info directory (needed to serve e.g. js9prefs.js) "-B", "{}:{}".format(session_info_dir, LOCAL_SESSION_DIR), "-B", "{}:{}".format(session_info_dir, SHADOW_SESSION_DIR), # mount a writeable tmp dir for the js9 install -- needed by js9helper "-B", "{}:/.radiopadre/venv/js9-www/tmp".format(js9_tmp), ] if config.CONTAINER_DEV: if os.path.isdir(config.CLIENT_INSTALL_PATH): docker_opts += [ "-B", "{}:/radiopadre-client".format(config.CLIENT_INSTALL_PATH) ] if os.path.isdir(config.SERVER_INSTALL_PATH): docker_opts += [ "-B", "{}:/radiopadre".format(config.SERVER_INSTALL_PATH) ] # if not config.CONTAINER_DEBUG: # command = [singularity, "instance.start"] + docker_opts + \ # [singularity_image, container_name] # message("running {}".format(" ".join(map(str, command)))) # subprocess.call(command) # docker_opts = [singularity, "exec", "instance://{}".format(container_name)] # else: # docker_opts = [singularity, "exec" ] + docker_opts + [singularity_image] docker_opts = [singularity, "run"] + docker_opts + [singularity_image] container_ports = selected_ports # build up command-line arguments docker_opts += _collect_runscript_arguments(container_ports + userside_ports) if notebook_path: docker_opts.append(notebook_path) _run_container(container_name, docker_opts, jupyter_port=selected_ports[0], browser_urls=browser_urls, singularity=True) if config.NBCONVERT: return try: while True: a = INPUT("Type 'exit' to kill the container session: ") if a.lower() == 'exit': sys.exit(0) except BaseException as exc: if type(exc) is KeyboardInterrupt: message("Caught Ctrl+C") status = 1 elif type(exc) is SystemExit: status = getattr(exc, 'code', 0) message("Exiting with status {}".format(status)) else: message("Caught exception {} ({})".format(exc, type(exc))) status = 1 if status: message("Killing the container") subprocess.call([ singularity, "instance.stop", singularity_image, container_name ], stdout=DEVNULL) sys.exit(status)