Examples of crl.interactivesessions usage

Running Commands In Background

This example demonstrates simple background execution of Linux Shell commands. The implementation uses the transparent proxies which are created by crl.interactivesessions.autorunnerterminal.AutoRunnerTerminal. Please see the code from BackgroundRunner Code.

By the default Remoteproxies proxies are blocking until the timeout expires. However, in this example the proxy of subprocess.Popen.communicate() is set to asynchronous mode:

    @staticmethod
    def _communicate(pro):
        communicate = pro.communicate
        communicate.remote_proxy_use_asynchronous_response()
        return ResponseHandle(pro=pro, handle=communicate())

The example usage of the BackgroundRunner Code class BackgroundRunner is here:

>>> from crl.interactivesessions.InteractiveSession import SshShell
>>> runner = BackgroundRunner(SshShell(
... '10.7.20.22', username='ncioadmin', password='admin_pass'))
>>> handle = runner.run_in_background('echo Hello World!;sleep 100')
>>> runner.terminate_and_get_response(handle)
RunResponse(ret=-15, out='Hello World!\n', err='')

Copying Files To/From Remote Targets

This example demonstrates binary file copying to and from the remote targets using crl.interactivesessions.autorunnerterminal provided proxies.

In more detail a simple file handle like contextmanager is created which opens the remote file is created. This is done in the following fashion.

class _RemoteFile(object):
    def __init__(self, terminal, filename, options='r'):
        self.terminal = terminal
        self.filename = filename
        self.options = options
        self.handle = None

    def __enter__(self):
        self.terminal.initialize_if_needed()
        self.handle = self.terminal.get_proxy_object_from_call(
            'open', self.filename, self.options)
        self.handle.set_proxy_spec(file)
        return self.handle

    def __exit__(self, *args, **kwargs):
        if self.handle is not None:
            self.handle.close()
            self.handle = None

The call self.terminal.initialize_if_needed will open a new terminal session in case it is either never opened or for some reason broken.

A simple roundtrip example is here:

>>> with open('hello.txt', 'w') as f:
...     f.write('Hello World!')
...
>>> from crl.interactivesessions.InteractiveSession import SshShell
>>> copier = FileCopier(SshShell(
... '10.7.20.22', username='ncioadmin', password='admin_pass'))
>>> copier.copy_file_to_remote('hello.txt', 'hello_remote.txt')
>>> copier.copy_file_from_remote('hello_remote.txt', 'hello_local.txt')
>>> with open('hello_local.txt') as f:
...     f.read()
...
'Hello World!'
>>>

Please see the full source from FileCopier Code.

Lazy Initialization of Remote Proxies

There is no need to initialize the session for the crl.interactivesessions.remoteproxies._RemoteProxy proxies before the actual usage of the proxy. This can be achieved by calling either crl.interactivesessions.autorunnerterminal.RunnerTerminal.create_empty_remote_proxy() or crl.interactivesessions.autorunnerterminal.RunnerTerminal.create_empty_recursive_proxy(). The access to proxies of this type will automatically tricker the initialization process. In this case, it is recommended that the prepare method copies the new proxy content over the old proxies so that the references to the proxies can be still used. This can be achieved by using the crl.interactivesessions.remoteproxies._RemoteProxy.set_remote_proxy_from_proxy().

Please see the details from BackgroundRunner Code.

BackgroundRunner Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
import logging
import signal
import errno
from collections import namedtuple
from crl.interactivesessions.InteractiveSession import BashShell
from crl.interactivesessions.autorunnerterminal import AutoRunnerTerminal
from crl.interactivesessions.autorecoveringterminal import (
    AutoRecoveringTerminal)


__copyright__ = 'Copyright (C) 2019, Nokia'

logger = logging.getLogger(__name__)


class _OsProxies(object):
    def __init__(self, terminal):
        self.killpg = terminal.create_empty_remote_proxy()
        self.getpgid = terminal.create_empty_remote_proxy()
        self.setsid = terminal.create_empty_remote_proxy()


ResponseHandle = namedtuple('ResponseHandle', ['pro', 'handle'])


RunResponse = namedtuple('RunResponse', ['ret', 'out', 'err'])


class BackgroundRunner(object):
    def __init__(self, shells=None):
        self.terminal = None
        self.popen = None
        self.pipe = None
        self.os = None
        self._initialize(BashShell() if shells is None else shells)

    def _initialize(self, shells):
        self.terminal = AutoRunnerTerminal()
        self.terminal.initialize_with_shells(shells=shells,
                                             prepare=self._prepare)
        self._setup_with_empty_proxies()

    def _setup_with_empty_proxies(self):
        self.os = _OsProxies(self.terminal)
        self.popen = self.terminal.create_empty_recursive_proxy()
        self.pipe = self.terminal.create_empty_remote_proxy()

    def _prepare(self):
        self._import_libraries()
        self._setup_proxies()

    def _import_libraries(self):
        self.terminal.import_libraries('os', 'subprocess')

    def _setup_proxies(self):
        self._setup_subprocess_proxies()
        self._setup_os_proxies()

    def _setup_subprocess_proxies(self):
        self.popen.set_from_remote_proxy(
            self.terminal.get_recursive_proxy('subprocess.Popen'))
        self.pipe.set_from_remote_proxy(
            self.terminal.get_proxy_object('subprocess.PIPE', None))

    def _setup_os_proxies(self):
        self.os.killpg.set_from_remote_proxy(
            self.terminal.get_proxy_object('os.killpg', None))
        self.os.getpgid.set_from_remote_proxy(
            self.terminal.get_proxy_object('os.getpgid', None))
        self.os.setsid.set_from_remote_proxy(
            self.terminal.get_proxy_object('os.setsid', None))

    def run_in_background(self, cmd):
        pro = self.popen(cmd,
                         stdout=self.pipe,
                         stderr=self.pipe,
                         shell=True,
                         preexec_fn=self.os.setsid)
        return self._communicate(pro)

    @staticmethod
    def _communicate(pro):
        communicate = pro.communicate
        communicate.remote_proxy_use_asynchronous_response()
        return ResponseHandle(pro=pro, handle=communicate())

    def terminate_and_get_response(self, handle):
        self._terminate_if_needed(handle.pro)
        out, err = handle.pro.get_remote_proxy_response(handle.handle,
                                                        timeout=10)
        return RunResponse(ret=handle.pro.returncode, out=out, err=err)

    def _terminate_if_needed(self, pro):
        try:
            self.os.killpg(self.os.getpgid(pro.pid), signal.SIGTERM)
        except OSError as e:
            if e.errno == errno.ESRCH:
                logger.debug('Not terminating: process already terminated')
            else:
                raise

FileCopier Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from crl.interactivesessions.InteractiveSession import BashShell
from crl.interactivesessions.autorunnerterminal import AutoRunnerTerminal
from crl.interactivesessions.autorecoveringterminal import (
    AutoRecoveringTerminal)


__copyright__ = 'Copyright (C) 2019, Nokia'


class _RemoteFile(object):
    def __init__(self, terminal, filename, options='r'):
        self.terminal = terminal
        self.filename = filename
        self.options = options
        self.handle = None

    def __enter__(self):
        self.terminal.initialize_if_needed()
        self.handle = self.terminal.get_proxy_object_from_call(
            'open', self.filename, self.options)
        self.handle.set_proxy_spec(file)
        return self.handle

    def __exit__(self, *args, **kwargs):
        if self.handle is not None:
            self.handle.close()
            self.handle = None


class FileCopier(object):
    def __init__(self, shells=None):
        self.terminal = None
        self.buffersize = 32768
        self._initialize(BashShell() if shells is None else shells)
        self._buf = None

    def set_buffersize(self, buffersize):
        self.buffersize = buffersize

    def _initialize(self, shells):
        self.terminal = AutoRunnerTerminal()
        self.terminal.initialize_with_shells(shells=shells)

    def copy_file_to_remote(self, source, dest):
        self._copy_file_from_ropen_to_wopen(
            lambda: open(source, 'rb'),
            lambda: _RemoteFile(self.terminal, dest, 'wb'))

    def copy_file_from_remote(self, source, dest):
        self._copy_file_from_ropen_to_wopen(
            lambda: _RemoteFile(self.terminal, source, 'rb'),
            lambda: open(dest, 'wb'))

    def _copy_file_from_ropen_to_wopen(self, ropen, wopen):
        with ropen() as readf:
            with wopen() as writef:
                self._copy_file_from_readf_to_writef(readf, writef)

    def _copy_file_from_readf_to_writef(self, readf, writef):
        while self._read(readf):
            writef.write(self._buf)

    def _read(self, readf):
        self._buf = readf.read(self.buffersize)
        return self._buf