diff --git a/src/execo_g5k/__init__.py b/src/execo_g5k/__init__.py index 176c718e1c267d505311d83d50b00c493b97173f..95f95c4537c025802e4a9af9c24b9acd1aaf73e2 100644 --- a/src/execo_g5k/__init__.py +++ b/src/execo_g5k/__init__.py @@ -30,7 +30,7 @@ from .oargrid import oargridsub, oargriddel, \ get_oargrid_job_oar_jobs, wait_oargrid_job_start, \ get_oargrid_job_nodes, get_oargrid_job_key -from .kadeploy import Deployment, Kadeployer, deploy +from .kadeploy import Deployment, Kadeployer, deploy, KaconsoleProcess from .utils import get_kavlan_host_name, G5kAutoPortForwarder diff --git a/src/execo_g5k/kadeploy.py b/src/execo_g5k/kadeploy.py index 316dd4db82458a1174464eee4f3d30456e60061a..e14bef6a9c88665d558d0b4c906d4bd7db4bc47a 100644 --- a/src/execo_g5k/kadeploy.py +++ b/src/execo_g5k/kadeploy.py @@ -23,7 +23,8 @@ from execo.config import make_connection_params from execo.host import get_hosts_set, Host from execo.log import style, logger from execo.process import ProcessOutputHandler, get_process, \ - handle_process_output + handle_process_output, SshProcess, ProcessLifecycleHandler, \ + ExpectOutputHandler from execo.time_utils import format_seconds from execo.utils import comma_join, compact_output, singleton_to_collection from execo_g5k.config import default_frontend_connection_params @@ -32,6 +33,7 @@ from .utils import get_default_frontend import copy import re import time +import threading class Deployment(object): """A kadeploy3 deployment, POD style class.""" @@ -501,3 +503,60 @@ def deploy(deployment, logger.debug(style.emph("undeployed hosts:") + " %s", undeployed_hosts) return (deployed_hosts, undeployed_hosts) + +class KaconsoleProcessLifecycleHandler(ProcessLifecycleHandler): + + def start(self, p): + p.expect('Connection to console .* opened', + timeout=max(0, p.start_date + p.console_connection_timeout - time.time()) if p.console_connection_timeout else None, + expect_output_handler=p.console_connection_expect_output_handler) + p.write("\x04\n\x04\n") # to make sure we disconnect if a console was previously opened, and to get the login prompt + p.expect('login: ', + timeout=max(0, p.start_date + p.console_connection_timeout - time.time()) if p.console_connection_timeout else None, + expect_output_handler=p.console_connection_expect_output_handler) + p.write(p.console_connection_params['user'] + "\n") + p.expect('Password: ', + timeout=max(0, p.start_date + p.console_connection_timeout - time.time()) if p.console_connection_timeout else None, + expect_output_handler=p.console_connection_expect_output_handler) + p.write(p.console_connection_params['password'] + "\n") + p.console_connected.set() + +class KaconsoleProcess(SshProcess): + """A connection to the console of a Grid5000 through kaconsole3. + + Unlike other process classes, this class, once started, leaves the + connection opened, allowing to send commands (and expect outputs) + through the pipe. + + Note that only one console can be simultaneously opened at one time to a node + """ + + def __init__(self, host, frontend_connection_params=None, connection_timeout=20, prompt='root@[^#]*#', **kwargs): + host = Host(host) + self.console_connection_timeout = connection_timeout + self.console_connection_params = make_connection_params(kwargs.get("connection_params"), {'user': 'root', 'password': 'grid5000'}) + if 'connection_params' in kwargs: del kwargs['connection_params'] + super().__init__("kaconsole3 " + host.address, + host = get_frontend_host(_get_host_frontend(host)), + connection_params = make_connection_params(frontend_connection_params, default_frontend_connection_params), + **kwargs) + self.pty = True + self.prompt = prompt + self.console_connected = threading.Event() + self.console_connection_expect_output_handler = ExpectOutputHandler() + self.lifecycle_handlers.append(KaconsoleProcessLifecycleHandler()) + + def wait_connected(self, timeout=None): + if self.ended: + return False + if not self.console_connected.wait(timeout=timeout): + return False + return True + + def expect_prompt(self, timeout=None): + wait_start = time.time() + if not self.wait_connected(timeout=timeout): + return None, None + return self.expect(self.prompt, + timeout=max(0, wait_start + timeout - time.time()) if timeout else None, + expect_output_handler=self.console_connection_expect_output_handler)