Commit c874eebb authored by Matej Feder's avatar Matej Feder

Merge branch 'cleanup-main' into 'asyncio-and-code-improvement'

#61 #62 #63 #64 #65 #66 #67 Multiple issues

See merge request !18
parents 93794d05 714f0219
[flake8]
max-line-length = 120
.scannerwork
*~
.idea
__pycache__
*.swp
*.egg-info
__pycache__
.idea
.scannerwork
# Sphinx
_build
_autosummary
# Log files
*.log
# Install docker
sudo apt install docker.io docker-compose
# Add user to docker group
usermod -a -G docker $USER
# Logout and login for group change above to take effect
groups | grep docker
pydtn adm cdrom sudo dip plugdev lpadmin sambashare docker
# Start topology
docker-compose up --build
# See pyDTN-1 logs
docker logs -f docker_pyDTN-1_1
# See pyDTN-2 logs
docker logs -f docker_pyDTN-2_1
# See uPCN-1 logs
docker logs -f docker_uPCN-1_1
# Send bundle
# Alternatively, you can use Postman and import docker/REST/pyDTN.postman_collection.json
cd tools/send_bundle/
./send.sh
# List running containers including commands running inside
docker-compose top
# Restart a container
docker-compose restart pyDTN-1
# Cleanup
docker-compose down
docker container prune -f
docker image prune -a -f
# docker-compose up console
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: Connection on TCPCL level established 'dtn://pyDTN-2.dtn' [components/cla/src/posix/TCPCL/cla.c:187]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: Couldn't find matching contact in existing gs list! (-1) [components/cla/src/posix/TCPCL/cla.c:74]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: Will create opportunistic contact rx task! (-1) [components/cla/src/posix/TCPCL/cla.c:75]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: contact_rx_task: Started up opportunistic (-1) [components/cla/src/posix/TCPCL/cla_contact_rx_task.c:417]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: The peer has disconnected gracefully! (-1) [components/cla/src/posix/TCPCL/cla_io.c:308]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: Payload size is 69 [components/upcn/src/bundleProcessor.c:362]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: Received local bundle! (-1) [components/upcn/src/bundleProcessor.c:377]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: configAgentTask: got routing command! (-1) [components/agents/src/config_agent.c:70]
uPCN-1_1 | [Wed Mar 21 14:41:43 2018]: RouterTask: Command processed successfully (49) [components/upcn/src/routerTask.c:135]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: ContactManager: Scheduled contact added (1184879392) [components/upcn/src/contactManager.c:226]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: contact_rx_task: Started up successfully (-1) [components/cla/src/posix/TCPCL/cla_contact_rx_task.c:419]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: contact_tx_task: Started successfully (1184879936) [components/cla/src/posix/TCPCL/cla_contact_tx_task.c:51]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Connection on TCPCL level established 'dtn://pyDTN-1.dtn' [components/cla/src/posix/TCPCL/cla.c:187]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Couldn't find matching contact in existing gs list! (-1) [components/cla/src/posix/TCPCL/cla.c:74]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Will create opportunistic contact rx task! (-1) [components/cla/src/posix/TCPCL/cla.c:75]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: contact_rx_task: Started up opportunistic (-1) [components/cla/src/posix/TCPCL/cla_contact_rx_task.c:417]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: RouterTask: Processed bundle successfully (2) [components/upcn/src/routerTask.c:183]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: The peer has disconnected gracefully! (-1) [components/cla/src/posix/TCPCL/cla_io.c:308]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Got dtn! magic! (-1) [components/cla/src/posix/TCPCL/cla_management.c:196]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Received EID is dtn://node2.dtn [components/cla/src/posix/TCPCL/cla_management.c:220]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Scheduled contact established on TCPCL level! (-1) [components/cla/src/posix/TCPCL/cla_management.c:222]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: Sending bundle with SN (0) [components/cla/src/posix/TCPCL/cla_contact_tx_task.c:88]
uPCN-1_1 | [Wed Mar 21 14:41:48 2018]: RouterTask: Sent bundle successfully (2) [components/upcn/src/routerTask.c:220]
uPCN-1_1 | [Wed Mar 21 14:42:03 2018]: ContactManager: Contact removed (1184879392) [components/upcn/src/contactManager.c:238]
uPCN-1_1 | [Wed Mar 21 14:42:03 2018]: Set contact pair to opportunistic! (-1) [components/cla/src/posix/TCPCL/cla_management.c:63]
uPCN-1_1 | [Wed Mar 21 14:42:03 2018]: RouterTask: Contact removed from table (1184879392) [components/upcn/src/routerTask.c:201]
# pyDTN-1 console
172.25.1.1 - - [21/Mar/2018 13:41:46] "POST /bundle?destination=dtn://pyDTN-2.dtn HTTP/1.1" 200 -
0000 64 74 6E 21 03 00 00 00 11 64 74 6E 3A 2F 2F 70 dtn!.....dtn://p
0016 79 44 54 4E 2D 31 2E 64 74 6E yDTN-1.dtn
Going to send contact header of length 26
bytearray(b'dtn!\x03\x00\x00\x00\x11dtn://pyDTN-1.dtn')
Message arrived, length 28
0000 64 74 6E 21 03 00 00 00 13 64 74 6E 3A 2F 2F 6F dtn!.....dtn://o
0016 70 73 2D 73 61 74 2E 64 74 6E 00 00 ps-sat.dtn..
# pyDTN-2 console
Received payload block: @
@
@
@
`@` @: `@ @ '@@@@@, @@ @@# @ @@ :@@#
+@ @@ @; @' #@ @@, '@# @@@+, @ @@ ,@,.@@
@@ #@ ;@ @@ @`#@ @: @@: @ @@ @;
@;`@` @` #@@, ;@ @` .@@# ;@ @@ @ `@# @#
,@@+ #@ @.#@ @::@ :@ #@ @ @@ @:@' `@+
@@ #@@@@@ @ ;@ @ `@ +@'+ ::` @ @.@@ @@; `@@,
;@@@ @# @; @@ @# '@@ @':. .;@`@@ @,@' ;@+
@, @, `@.@ @ @ `@#.#,` +,@ @@ @ `@# `@
@@ ;@ @@# @@@ @'@@@@@@`@@ @@ @ @@ @
#@ @@ ;@ .@ `@: ,'@.@ @@ @ @@ @` #@
.@ @+ @ @ .@`:.`:. @@ @ @@ #@@@@
@@+:` `
@@+,
@@
# Overview
This is the PoC (Proof of Concept) implementation of:
* [Bundle Protocol Version 7](https://tools.ietf.org/html/draft-ietf-dtn-bpbis-12)
* [Bundle Protocol Version 7](https://tools.ietf.org/html/draft-ietf-dtn-bpbis-13)
* Send/receive a bundle
* Register EIDs
* Schedule contacts
* Bundle routing
* Send bundle functionality is available via `REST` API, useful for testing
* Schedule contact functionality is available via `REST` API, useful for integration with [µPCN](https://upcn.eu/)
* Scheduling contacts for uPCN nodes is available via `REST` API, useful for integration with [uPCN](https://upcn.eu/)
* [Delay-Tolerant Networking TCP Convergence-Layer Protocol](https://tools.ietf.org/html/rfc7242)
* Send/receive contact headers
* [DTN IP Neighbor Discovery (IPND)](https://tools.ietf.org/html/draft-irtf-dtnrg-ipnd-03)
* Send/receive a beacon, code exists, integration in progress, see %2 milestone
* Send/receive a beacon, code exists, integration halted, since uPCN team decided not to support it
# Repository structure
```
/pydtn - Python library implementing DTN protocols
/pydtn_rest - Python library implementing REST API wrapping pydtn library API
/doc - Project documentation
/demo_app - Python demo application (imports pydtn_rest library)
/tools - Currently contains scripts and data files used for the demo
/docker - Containers for the demo
```
# Installation
Required Python version: 3.7.3+
#### pydtn library
```
pip3 install pydtn/
```
#### pydtn_rest library
```
pip3 install pydtn_rest/
```
# Documentation
# Use Case
![current_state](doc/readme/current_state.png)
#### Install requirements
```
pip3 install sphinx sphinx_rtd_theme
```
#### pydtn library
```
make -C pydtn/docs html
```
output:
pydtn/docs/_build/html/index.html
1. `pyDTN-2` receives `REST` request to schedule contact: `//pyDTN-2.dtn` is available on `172.25.2.11:2002`
1. `pyDTN-2` schedules contact with `uPCN`, note following issues:
* Proprietary (not part of any draft) `uPCN` config bundle is used
* `pyDTN-2` receives `uPCN`'s IP address/port on a [command line](docker/docker-compose.yml#L39)
1. `pyDTN-1` receives `REST` request to send a bundle to `pyDTN-2`
1. `pyDTN-1` sends bundle to `uPCN`, note following issues:
* `pyDTN-1` receives `uPCN`'s IP address/port on a [command line](docker/docker-compose.yml#L20)
* IP address/port is then set (kind of hard-coded) in [ipnd/httpd.py](src/httpd/httpd.py#L42)
1. `uPCN` receives a bundle from `pyDTN-1` and immediately forwards it to `pyDTN-2`, note following issues:
* `uPCN` currently does not store any bundles, if destination is not available, bundle is simply dropped
#### pydtn_rest library
```
make -C pydtn_rest/docs html
```
output:
pydtn_rest/docs/_build/html/index.html
Issues listed above will be solved as a part of %2 milestone.
# Try it yourself
See [DEMO_HOWTO.txt](DEMO_HOWTO.txt) (eventually will be integrated into this file)
# Demonstration
* [Communication with uPCN](UPCN_DEMO.md)
This diff is collapsed.
import argparse
import asyncio
import json
import logging.config
import yaml
import pydtn_rest
import app
logging.basicConfig(format="%(asctime)s - [%(name)s] - [%(levelname)-5s] - %(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)
parser = argparse.ArgumentParser()
parser.add_argument("--yaml", type=str, default="")
parser.add_argument("--json", type=str, default="")
def load_config():
args = parser.parse_args()
# forbid using both formats at once
assert not (args.yaml and args.json)
config = {}
if args.yaml:
config = yaml.safe_load(args.yaml)
logger.debug(f"command line --yaml has value:\n{args.yaml}")
if args.json:
config = json.loads(args.json)
logger.debug(f"command line --json has value:\n{args.json}")
return config
def start():
config = load_config()
app.start(config)
asyncio.run(pydtn_rest.start(config))
start()
import logging
from pydtn.bundle_protocol import bp_agent
logger = logging.getLogger(__name__)
async def recv_callback(adu, adu_len, app_eid, src_eid):
logger.info(f'BP client "{app_eid}" received ADU from of length {adu_len} from "{src_eid}"')
print(adu.decode("ascii"))
def start(config):
"""Start example application that prints received bundle payloads"""
bp_agent.register(config["app-eid"], recv_callback)
pydtn~=1.0.0
pydtn_rest~=1.0.0
<mxfile modified="2019-03-24T15:41:00.922Z" host="www.draw.io" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/73.0.3683.75 Chrome/73.0.3683.75 Safari/537.36" etag="974iRFzm_IsnAKCbOAYh" version="10.5.4" type="device"><diagram name="Page-1" id="43d2112b-42d4-6ef0-ef21-dd1f29dee117">7Zpbc+I2FMc/DTPtA4wuli+PAZK03XQnM9mZ7j7tGCyMGmO5tkggn76SLWEbYy4pSbxdMpNEOrrY6Pz8PwdZPTxarG5TP5n/yQMa9RAIVj087iFkA9eV/5RlXVggggAUpjBlgTaWhgf2QrXRdFuygGa1joLzSLCkbpzyOKZTUbP5acqf691mPKpfNfFD2jA8TP2oaf2LBWJeWC1gPoZq+I2ycK4vbRHTMvGnj2HKl7G+YA/hWf5TNC98M5nun839gD9XTPi6h0cp56IoLVYjGqnlNetWjLtpad3ceEpjccwAR/vqyY+W1NyyHcmxw4A9yWKoisY0SbctcuZav/wzibVZSEFXyj4Xi0gaoCymNGMv/iTvAGTdXwqeFQCoZj9iYSzLEZ2pgU80FUz65UqbBU+kNUv8KYvDL6oy7lvSknAWi9zpZNgjY2mZ8VhosDDZ3Fp1YfRaqUvQVcWkF+qW8gUV6Vp2Ma3As4sxGuy+S1xv4LiOBzzXsYjlFc3PJTPGz/MqLdrma0rDzZVKR8mC9tVuv93+Edq/24/edDa6fkmePq3vw099aJGGB2ggydZVnoo5D3nsR9eldZjTSgPtj7LPHc/XV3nlbyrEWq+mcljdpXTFxFc9XJW/qfLAIbo6XlXaxmtTieUn/lrpqerfqo3luLxmBmYi5Y+bxxKCuq+hBfY5O+PLdEr3LSHUD4Tw05CKvYut3ahWeC8+KY18wZ7q2nJ+58P2R1ktUA0L+58lNw394vG7kh2kTierstU81sl6/OVzHw0CEbcrwcQYoIMGiAzgAMKKSkyO0pLiRlvEpARVUfc8Z4I+SCVQrc8yEtWhnLEoGvGIp/lYHPjUnU13sVLwVOlqT106mZ1HMqS+egOyJRrYcge2Eg3sOBBg3BAN19uhGrb3ZrKB7P1BYAc4Stj7WqwVOVO5RjRtomPmkZIdH0OguxNABACqUFJMdlIgem92tgPXhAvBF5UApxfsPJS5VpMyCxygDIMdlMG3C07I6ThlLnAvlLVThjH8ASgj9kemQKZ8agqEupQCYftHTYFweyA7SwoET0uBwE+dAlXyHbkQXcp3cNcjkcx34CUStUYiGzSi0AHC3jsK4QPbKh9OmMx1LoS1E0Zw9xHz3irW7dgA5EnWz3xRhD/wS2+EekNyP/r8axss7VuIjRiJ9sbIV08MXzXx/ykAm1bioTrMjg3tbu1A2NZHZu1gYBL1TfZ9TNLepZzd6MzhnN1s9pwvZ8+HXqWpv6500Dvy5cz3ylCh0vGa3yYJcpoie9MyA/JOnEEWirssQd183P+y6Q46Hustor4UX2J9mz7apKGPzgF9fO9gb1mtjCU7wYhYTPvm5vJYr95EkXbEjs0ZdiMGW+Pnxpy00iUXgCUZPUyWnyXF294ZWykam0g1+Ij8CY3uecYE47UGg9jdVocFC4I8umwzuGk4A3LY8nBDuhyI90MH8S7o8NtB176b0gno0AW6k94SII9sQ+e44IDSvTd0pD2adgI6coHutOBqQtdG5xB293+TfnfkSLeRwxfkTtq7gQ2Zcx3gdUzm2regO8GcdWHuJJmDLt6KrI5nd0vmbNh01EcdDjv6xWiX9ljI0UfDzPZ/V96L2ugjXQ9/JtebUy5dcT3Z9SZqiwW2yM9DV32Xy+xwc5i5skmjjzPjoR41ZotQ3lrEJvLvVAr/dz8VqsgXyVJKcybLd34iePIdInclfwdJHPYa+0Tq5+bmqBhwDsEmHtjOEjxw6B2PhXBTsvErJFtWyzPexU5keZYeX/8L</diagram></mxfile>
\ No newline at end of file
......@@ -4,33 +4,78 @@ version: "2"
services:
pyDTN-1:
image: pydtn
build:
context: pyDTN
image: python:3
hostname: pyDTN-1
ports:
- "8081:8081"
tty: true
restart: always
volumes:
- ../src:/pyDTN
command: --node dtn://pyDTN-1.dtn --port 2001 --http-port 8081 --upcn-host 172.25.1.21 --upcn-port 4556
- ..:/src:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
command: >
/src/docker/pydtn_demo_run.sh --yaml "
app-eid: dtn://pyDTN-1.dtn/demo_app
node:
id: dtn://pyDTN-1.dtn
routes:
# Send all traffic through uPCN node
null: dtn://ops-sat.dtn
convergence-layer-adapters:
TCPCL:
host: 0.0.0.0
port: 2001
neighbors:
'dtn://ops-sat.dtn':
host: 172.25.1.21
port: 4556
contacts:
'dtn://ops-sat.dtn':
- start-time: '2019-08-01 00:00:00'
end-time: '2020-08-01 00:00:00'
date-time-format: '%Y-%m-%d %H:%M:%S'
api:
host: 0.0.0.0
port: 8081
"
networks:
dtnnet1:
ipv4_address: 172.25.1.11
pyDTN-2:
image: pydtn
build:
context: pyDTN
image: python:3
hostname: pyDTN-2
ports:
- "8082:8082"
tty: true
restart: always
volumes:
- ../src:/pyDTN
command: --node dtn://pyDTN-2.dtn --port 2002 --http-port 8082 --upcn-host 172.25.2.21 --upcn-port 4556
- ..:/src:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
command: >
/src/docker/pydtn_demo_run.sh --yaml "
app-eid: dtn://pyDTN-2.dtn/demo_app
node:
id: dtn://pyDTN-2.dtn
routes:
'dtn://ops-sat.dtn/config': dtn://ops-sat.dtn
convergence-layer-adapters:
TCPCL:
host: 0.0.0.0
port: 2002
neighbors:
'dtn://ops-sat.dtn':
host: 172.25.2.21
port: 4556
contacts:
'dtn://ops-sat.dtn':
- start-time: '2019-08-01 00:00:00'
end-time: '2020-08-01 00:00:00'
date-time-format: '%Y-%m-%d %H:%M:%S'
api:
host: 0.0.0.0
port: 8082
"
networks:
dtnnet2:
ipv4_address: 172.25.2.11
......
# Basic Python 3 image
FROM python:3
# Update aptitude with new repo
RUN apt-get update
RUN apt-get install -y python3-pip joe tcpdump
COPY requirements.txt /
RUN pip install -r /requirements.txt
WORKDIR /pyDTN
ENTRYPOINT ["python3", "-m", "main"]
#!/usr/bin/env sh
pip install /src/pydtn
pip install /src/pydtn_rest
python /src/demo_app "$@"
# Don't stop the container automatically so the user can attach to it for debugging
sleep infinity
<mxfile modified="2019-09-08T15:22:06.814Z" host="www.draw.io" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0" etag="64BKceM-uzHjfMBL6WNH" version="11.2.8" type="device" pages="1"><diagram name="Page-1" id="43d2112b-42d4-6ef0-ef21-dd1f29dee117">7Zpbc+I2FMc/DTPtA4wuli+PAZK03XQnM9mZ7j7tGCyMGmO5tkggn76SLWEbYy4pSbxdMpNEOrrY6Pz8PwdZPTxarG5TP5n/yQMa9RAIVj087iEECbblP2VZFxbPcbQlTFmge5WGB/ZCtRFo65IFNKt1FJxHgiV145THMZ2Kms1PU/5c7zbjUf2qiR/ShuFh6kdN618sEPPCagEAyobfKAvn+tIWMS0Tf/oYpnwZ6wv2EJ7lP0XzwjeT6f7Z3A/4c8WEr3t4lHIuitJiNaKRWl2zbsW4m5bWzY2nNBbHDHDcYsSTHy2puWU7kmOHAXuSxVAVjWmSblvkzLV++WcSa7OQgq6UfS4WkTRAWUxpxl78Sd4ByLq/FDwrAFDNfsTCWJYjOlMDn2gqmPTLlTYLnkhrlvhTFodfVGXct6Ql4SwWudPJsEfG0jLjsdBgYbK5terC6LVSl6Crikkv1C3lCyrStexiWoGnMdZc913iegPHdTzguY5FLK9ofi6ZMX6eV2nRNl9TGm6uVDpKFrSvdvvt9o/Q/t1+9Kaz0fVL8vRpfR9+6kOLNDxAA0m2rvJUzHnIYz+6Lq3DnFYaaH+Ufe54vr7KK39TIdZ6NZXD6i6lKya+6uGq/E2VBw7R1fGq0jZem0osP/HXSk9V/1ZtLMflNTMwEyl/3DyWENR9DS2wz9kZX6ZTum8JoX4ghJ+GVOxdbO1GtcJ78Ulp5Av2VNeW8zsftj/KaoFqWNj/LLlp6BeP35XsAAFIVmWreayT9fjL5z4aBCJuV4KJMUAHDRAZoAGEFZWYHKUlxY22iEkJqqLuec4EfZBKoFqfZSCqQzljUTTiEU/zsTjwqTub7mKl4KnS1Z66dDI7j2RIffUGZEs0sOUObCUa2HEgwLghGq63QzVs781kA9n7g8AOcJSw97VYK3Kmco1o2kTHzCMlOz6GQHcngAgAVKGkmOykQPTe7GwHrgkXgi8qAU4v2Hkoc60mZRY4QBkGOyiDbxeckNNxylzgXihrpwxj+ANQRuyPTIFM+dQUCHUpBTJf3368FAi3B7KzpEDwtBQI/tQpUCXfAQPYpXwHdz0SyXwHXiJRaySyQSMKHSDsvaMQPrCt8uGEyVznQlg7YQR3HzHvrWLdjg1AnmT9zBdF+AO/9EaoNyT3o8+/tsHSvoXYiJFob4x89cToVRP/nwLwZj/eQ3WYHRva3dqBsK2PzNrBwCTqm+z7mKS9Szm70ZnDObvZ7Dlfzp4PvUpTf13poHfky5nvlaFCpeM1v00S5DRF9qZlBuSdOIMsFHdZgrr5uP9l0x10PNZbRH0pvsT6Nn20SUMfnQP6+N7B3rJaGUt2ghGxmPbNzeWxXr2JIu2IHZsz7EYMtsbPjTlppUsuAEsyepgsP0uKt70ztlI0NpFq8BH5Exrd84wJxmsNBrG7rQ4LFgR5dNlmcNNwBuSw5eGGdDkQ74cO4l3Q4beDrn03pRPQoQt0J70lQB7Zhs5xwQGle2/oSHs07QR05ALdacHVhK6NziHs7v8m/e7IkW4jhy/InbR3Axsy5zrA65jMtW9Bd4I568LcSTIHXbwVWR3P7pbM2bDpqI86HHb0i9Eu7bGQo4+Gme3/rrwXtdFHuh7+TK43p1y64nqy603UFgtskZ+Hrvoul9nh5jBzZZNGH2fGQz1qzBahvLWITeTfqRT+734qVJEvkqWU5kyW7/xE8OQ7RO5K/g6SOOw19onUz83NUTHgHIJNPLCdJXjg0DseC+GmZONXSLaslme8i53I8ig9vv4X</diagram></mxfile>
\ No newline at end of file
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
{{ fullname }}
{{ underline }}
.. contents::
:local:
.. automodule:: {{fullname}}
Members
=======
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import os
import sys
sys.path.insert(0, os.path.abspath(".."))
# -- Project information -----------------------------------------------------
project = "pyDTN"
copyright = "2019, X-works"
author = "X-works"
# The full version, including alpha/beta/rc tags
release = "1.0.0"
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.napoleon"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
autoclass_content = "both"
autodoc_default_options = {
"members": True,
"inherited-members": True,
"private-members": True,
"show-inheritance": True,
}
autosummary_generate = True
napoleon_numpy_docstring = False
napoleon_use_rtype = False
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
Welcome to pyDTN's documentation!
=================================
.. autosummary::
:toctree: _autosummary
pydtn
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
"""pyDTN
Submodules
==========
.. autosummary::
:toctree: _autosummary
pydtn
bundle_protocol
encoding
tests
util
"""
from .pydtn import start
"""Bundle Protocol Version 7
Submodules
==========
.. autosummary::
:toctree: _autosummary
bp
conn_manager
misc
convergence_layers
Conforms to::
https://tools.ietf.org/html/draft-ietf-dtn-bpbis-13
Note:
Services of the Bundle Protocol Agent should be accessed using
bp_agent singleton defined here (or objects returned by it).
"""
from .bp import BPAgent
bp_agent = BPAgent()
This diff is collapsed.
"""Connection Manager
This is a layer between BP Agent and CLAs that manages connections
using available CLAs.
Bundle Protocol layer doesn't know about CLAs
(except for registration interface).
It it responsibility of this layer to decide which underlying CLA
layers to use in specific situations.
"""
import asyncio
import logging
from .convergence_layers.api import ConvergenceLayerAdapter, ConnectFailedError
from ..util.exceptions import AlreadyRegisteredError
logger = logging.getLogger(__name__)
class NoNeighborForEIDError(Exception):
"""Raised when there is no neighbor with specified EID"""
class ConnectionManager:
"""Connection Manager"""
def __init__(self, request_handler):
self._cl_adapter_instances = []
self._cl_adapter_classes = []
self._connections = []
self._request_handler = request_handler
def register_cl_adapter(self, adapter):
"""Register Convergence Layer Adapter for utilization by Connection Manager
Adapter should be and instance initialized with suitable config
Args:
adapter (ConvergenceLayerAdapter): Convergence Layer Adapter to register
Raises:
ValueError: If adapter is not instance of ConvergenceLayerAdapter
AlreadyRegisteredError: If adapter is already registered
"""
if not isinstance(adapter, ConvergenceLayerAdapter):
raise ValueError
if adapter in self._cl_adapter_instances:
raise AlreadyRegisteredError
self._cl_adapter_instances.append(adapter)
def register_neighbor(self, neighbor):
"""Register connection option for particular neighbor
Args:
neighbor (ConvergenceLayerNeighbor): CLA-specific information about how to
connect to particular neighbor
"""
for adapter in self._cl_adapter_instances:
if isinstance(adapter, neighbor.cla_class):
adapter.register_neighbor(neighbor)
def _add_connection(self, conn):
if conn in self._connections:
return
logger.debug("ConnectionManager: Adding connection to the list of existing connections")
self._connections.append(conn)
def _remove_connection(self, conn):
if conn not in self._connections:
logger.warning("ConnectionManager: Trying to remove connection that isn't registered")
return
logger.debug("ConnectionManager: Removing connection from the list of existing connections")
self._connections.remove(conn)
async def _request_handler(self, conn):
self._add_connection(conn)
try:
await self._request_handler(conn)
finally:
self._remove_connection(
conn
) # TODO: Don't automatically close connection after one bundle has been received
async def run_forever(self):
"""Block and process incoming data"""
await asyncio.wait(
[
asyncio.create_task(cl_adapter.run_forever(self._request_handler))
for cl_adapter in self._cl_adapter_instances
]
)
async def _connect(self, remote_eid):
"""Try to establish connection to node with specified EID
If some nodes have this information but all attempts to connect to the node fail
then ConnectFailedError should be raised.
Otherwise new connection object should be returned.
Args:
remote_eid: EID of node to connect to
Returns:
ConvergenceLayerConnection: Established connection
Raises:
NoNeighborForEIDError: If no CLA adapter has any information how to connect to node with specified EID
ConnectFailedError: If every attempt to connect to node with specified EID (using all available means) fails
"""
logger.info(f'ConnectionManager: Connecting to "{remote_eid}" node')
some_conn_failed = False
for adapter in self._cl_adapter_instances:
for neighbor in adapter.get_neighbors_for_eid(remote_eid):
try:
conn = await adapter.connect(neighbor)
except ConnectFailedError:
some_conn_failed = True
logger.info(f'ConnectionManager: Failed to connect to "{remote_eid}" node')
continue
self._add_connection(conn)