Remote execution via stdin/stdout messaging.
Project description
implant
**********
.. inclusion-marker-do-not-remove
.. image:: https://travis-ci.org/diefans/implant.svg?branch=master
:target: https://travis-ci.org/diefans/implant
A proof-of-concept for asynchronous adhoc remote procedure calls in Python.
This is work in progress and serves basically as an exercise.
Features
========
- Python >= 3.5 asyncio
- adhoc transferable remote procedures
- remote part of a :py:obj:`implat.core.Command` may reside in a separate module
- a :py:obj:`implant.core.Command` specific :py:obj:`implant.core.Channel`
enables arbitrary protocols between local and remote side
- events
- quite small core
- tests
Limitations
===========
- Python >= 3.5
- only pure Python modules are supported for remote import, if no venv is used
- :py:obj:`implant.core.Command` s must reside in a module other then `__main__`
- at the moment sudo must not ask for password
Example
=======
General application
-------------------
.. code:: python
import asyncio
import pathlib
from implant import core, connect, commands
async def remote_tasks():
# create a connector for a python process
connector = connect.Lxd(
container='zesty',
hostname='localhost'
)
connector_args = {
'python_bin': pathlib.Path('/usr/bin/python3')
}
# connect to a remote python process
remote = await connector.launch(**connector_args)
# start remote communication tasks
com_remote = asyncio.ensure_future(remote.communicate())
try:
# execute command
cmd = commands.SystemLoad()
result = await remote.execute(cmd)
print("Remote system load:", result)
finally:
# stop communication tasks
com_remote.cancel()
await com_remote
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(remote_tasks())
loop.close()
An example Echo Command
-----------------------
.. code:: python
import logging
import os
from implant import core
log = logging.getLogger(__name__)
class Echo(core.Command):
"""Demonstrate the basic command API."""
async def local(self, context):
"""The local side of the RPC.
:param context: :py:obj:`implant.core.DispatchLocalContext`
"""
# custom protocol
# first: send
await context.channel.send_iteration("send to remote")
# second: receive
from_remote = []
async for x in context.channel:
from_remote.append(x)
log.debug("************ receiving from remote: %s", from_remote)
# third: wait for remote to finish and return result
remote_result = await context.remote_future
result = {
'from_remote': ''.join(from_remote),
}
result.update(remote_result)
return result
async def remote(self, context):
"""The remote side of the RPC.
:param context: :py:obj:`implant.core.DispatchRemoteContext`
"""
# first: receive
from_local = []
async for x in context.channel:
from_local.append(x)
log.debug("************ receiving from local: %s", from_local)
# second: send
await context.channel.send_iteration("send to local")
# third: return result
return {
'from_local': ''.join(from_local),
'remote_self': self,
'pid': os.getpid()
}
Internals
=========
::
master <-----------------------------------------> remote
|
stdin/stdout
|
chunks
|
channels
|
--> send ---> | | --> queue -->
| module:class/fqin |
<-- queue <-- | | <--- send <--
**********
.. inclusion-marker-do-not-remove
.. image:: https://travis-ci.org/diefans/implant.svg?branch=master
:target: https://travis-ci.org/diefans/implant
A proof-of-concept for asynchronous adhoc remote procedure calls in Python.
This is work in progress and serves basically as an exercise.
Features
========
- Python >= 3.5 asyncio
- adhoc transferable remote procedures
- remote part of a :py:obj:`implat.core.Command` may reside in a separate module
- a :py:obj:`implant.core.Command` specific :py:obj:`implant.core.Channel`
enables arbitrary protocols between local and remote side
- events
- quite small core
- tests
Limitations
===========
- Python >= 3.5
- only pure Python modules are supported for remote import, if no venv is used
- :py:obj:`implant.core.Command` s must reside in a module other then `__main__`
- at the moment sudo must not ask for password
Example
=======
General application
-------------------
.. code:: python
import asyncio
import pathlib
from implant import core, connect, commands
async def remote_tasks():
# create a connector for a python process
connector = connect.Lxd(
container='zesty',
hostname='localhost'
)
connector_args = {
'python_bin': pathlib.Path('/usr/bin/python3')
}
# connect to a remote python process
remote = await connector.launch(**connector_args)
# start remote communication tasks
com_remote = asyncio.ensure_future(remote.communicate())
try:
# execute command
cmd = commands.SystemLoad()
result = await remote.execute(cmd)
print("Remote system load:", result)
finally:
# stop communication tasks
com_remote.cancel()
await com_remote
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(remote_tasks())
loop.close()
An example Echo Command
-----------------------
.. code:: python
import logging
import os
from implant import core
log = logging.getLogger(__name__)
class Echo(core.Command):
"""Demonstrate the basic command API."""
async def local(self, context):
"""The local side of the RPC.
:param context: :py:obj:`implant.core.DispatchLocalContext`
"""
# custom protocol
# first: send
await context.channel.send_iteration("send to remote")
# second: receive
from_remote = []
async for x in context.channel:
from_remote.append(x)
log.debug("************ receiving from remote: %s", from_remote)
# third: wait for remote to finish and return result
remote_result = await context.remote_future
result = {
'from_remote': ''.join(from_remote),
}
result.update(remote_result)
return result
async def remote(self, context):
"""The remote side of the RPC.
:param context: :py:obj:`implant.core.DispatchRemoteContext`
"""
# first: receive
from_local = []
async for x in context.channel:
from_local.append(x)
log.debug("************ receiving from local: %s", from_local)
# second: send
await context.channel.send_iteration("send to local")
# third: return result
return {
'from_local': ''.join(from_local),
'remote_self': self,
'pid': os.getpid()
}
Internals
=========
::
master <-----------------------------------------> remote
|
stdin/stdout
|
chunks
|
channels
|
--> send ---> | | --> queue -->
| module:class/fqin |
<-- queue <-- | | <--- send <--
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
implant-0.1.0.tar.gz
(38.2 kB
view details)
Built Distribution
implant-0.1.0-py3-none-any.whl
(146.6 kB
view details)
File details
Details for the file implant-0.1.0.tar.gz
.
File metadata
- Download URL: implant-0.1.0.tar.gz
- Upload date:
- Size: 38.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c8bc4d289e4f72d83b0ae15fafe44f3ecbb6e0d3b0b21e34ffdbdd07b8c36a7d |
|
MD5 | 205aea9e6ee1b262727c68c79c50eb23 |
|
BLAKE2b-256 | 333a0954de8ded67c932757e841c8279c9e7fd49bc5ea84afa49453bd0e3e3af |
File details
Details for the file implant-0.1.0-py3-none-any.whl
.
File metadata
- Download URL: implant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 146.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 53c4035f53922fed07f009f82152491d94d4606b1dd111f0bc17e4e7a3bac710 |
|
MD5 | 99edae1da01b97beacd7b963050ccb55 |
|
BLAKE2b-256 | c2bc5ffc4988c757f7ea60bb65aaafd41b811e280b4fce01a201da1d4a01ef16 |