-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit dc066da
Showing
6 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions | ||
|
||
name: Build | ||
|
||
on: | ||
push: | ||
paths-ignore: ['README.md'] | ||
branches: [ main ] | ||
tags: | ||
- 'v*.*' | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Setup Python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: 3.11 | ||
- name: Build | ||
run: | | ||
python -m pip install build | ||
python -m build | ||
- name: Archive build | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: dist | ||
path: dist | ||
- name: Release | ||
uses: softprops/action-gh-release@v2 | ||
if: startsWith(github.ref, 'refs/tags/') | ||
with: | ||
files: dist/* | ||
prerelease: true | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea | ||
.twine.env | ||
venv | ||
dist | ||
*.egg-info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
Normally, this utility will be installed and upgraded by the TARPN installer scripts. | ||
The following are manual installation instructions. | ||
|
||
Determine your desired installation directory | ||
```commandline | ||
export INSTALL_DIR=/opt/tarpn | ||
``` | ||
|
||
Create a virtualenv | ||
|
||
```commandline | ||
sudo mkdir -p $INSTALL_DIR | ||
sudo chown pi:pi $INSTALL_DIR | ||
python3 -m venv $INSTALL_DIR | ||
``` | ||
|
||
Install the stress test package | ||
```commandline | ||
$INSTALL_DIR/bin/pip install --upgrade https://github.com/tarpn/tarpn-stress-test/releases/download/v0.1/tarpn_stress_test-0.1-py3-none-any.whl | ||
``` | ||
|
||
The script can be run directly from its installation directory | ||
|
||
```commandline | ||
$INSTALL_DIR/bin/tarpn-stress-test | ||
``` | ||
|
||
If desired, create a symlink to add to your PATH. | ||
```commandline | ||
sudo ln -s $INSTALL_DIR/bin/tarpn-stress-test /usr/tarpn/sbin/tarpn-stress-test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[build-system] | ||
requires = ["setuptools>=64", "setuptools_scm>=8"] | ||
build-backend = "setuptools.build_meta" | ||
|
||
[tool.setuptools] | ||
packages = ["tarpn"] | ||
|
||
[tool.setuptools_scm] | ||
|
||
[project] | ||
dynamic = ["version"] | ||
name = "tarpn-stress-test" | ||
description = "Command line utility for stress testing a TARPN packet link" | ||
authors = [ | ||
{name = "David Arthur", email = "[email protected]"} | ||
] | ||
classifiers = [ | ||
"Development Status :: 3 - Alpha", | ||
"License :: OSI Approved :: MIT License", | ||
"Programming Language :: Python :: 3", | ||
"Topic :: Communications :: Ham Radio" | ||
] | ||
requires-python = ">=3.7" | ||
dependencies = [ | ||
"telnetlib3~=2.0" | ||
] | ||
|
||
[project.scripts] | ||
tarpn-stress-test = "tarpn.tarpn_stress_test:main" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import argparse | ||
import asyncio | ||
from functools import partial | ||
import logging | ||
import time | ||
|
||
import telnetlib3 | ||
from telnetlib3.stream_reader import TelnetReader | ||
from telnetlib3.stream_writer import TelnetWriter | ||
|
||
|
||
async def read_until(reader, s): | ||
buf = "" | ||
while True: | ||
try: | ||
buf += await asyncio.wait_for(reader.read(1), timeout=30.0) | ||
if buf.endswith(s): | ||
return buf | ||
except asyncio.TimeoutError: | ||
raise RuntimeError(f"Never found {s} in the output \"{buf}\". Aborting") | ||
|
||
|
||
async def empty_reader(reader, timeout=10): | ||
await asyncio.shield(asyncio.wait_for(reader.read(1024), timeout=timeout)) | ||
|
||
|
||
async def neighbor_shell( | ||
username: str, | ||
our_node: str, | ||
neighbor: str, | ||
reader: TelnetReader, | ||
writer: TelnetWriter | ||
): | ||
logger = logging.getLogger(neighbor) | ||
|
||
user_sent = False | ||
pwd_sent = False | ||
connected = False | ||
finished_reading = False | ||
|
||
logger.info(f"Testing via {neighbor}") | ||
file_size_times_2 = 20000 | ||
line_buffer = "" | ||
while True: | ||
line = await reader.read(32) | ||
#logger.info(line) | ||
line_buffer += line | ||
line_buffer = line_buffer[-100:] | ||
if not user_sent: | ||
if line.endswith(":"): | ||
writer.write(f"{username}\r\n") | ||
user_sent = True | ||
elif not pwd_sent: | ||
if line.endswith(":"): | ||
writer.write("p\r\n") | ||
pwd_sent = True | ||
elif not connected: | ||
await empty_reader(reader) | ||
logger.info(f"Connecting to {neighbor}") | ||
writer.write(f"C {neighbor}\r\n") | ||
await read_until(reader, "Connected") | ||
await empty_reader(reader) | ||
logger.info("Connected! Now connecting back to your node...") | ||
writer.write(f"C {our_node}\r\n") | ||
await read_until(reader, "Connected") | ||
logger.info("Loop established. Starting stress test.") | ||
await empty_reader(reader) | ||
#logger.info("Listing files..") | ||
writer.write("BBS\r\n") | ||
await read_until(reader, "Boss") | ||
#try: | ||
# File size is 2x since we are reading it from ourselves through our neighbor | ||
# logger.info(f"Found g8bpqloop.txt! Now reading it.") | ||
#except: | ||
# logger.info("Could not find g8bpqloop.txt, aborting.") | ||
# writer.write("BYE\r\n") | ||
# break | ||
writer.write("read g8bpqloop.txt\r\n") | ||
start = time.time() | ||
connected = True | ||
elif not finished_reading: | ||
if "End of File" in line_buffer: | ||
finished_reading = True | ||
end = time.time() | ||
elif "Invalid command" in line_buffer: | ||
logger.info("Aborting") | ||
end = time.time() | ||
break | ||
else: | ||
# logger.info("Leaving") | ||
await asyncio.sleep(5) | ||
writer.write("BYE\r\n") | ||
await asyncio.sleep(5) | ||
writer.write("BYE\r\n") | ||
break | ||
|
||
try: | ||
dt = end - start | ||
rate = file_size_times_2 / dt | ||
logger.info(f"10K bytes in {dt:0.2f} seconds at a rate of {rate:0.2f} bytes/sec.") | ||
except: | ||
logger.error("Something went wrong.") | ||
|
||
|
||
async def connect_to_neighbor(username, hostname, node, neighbor): | ||
factory = partial(neighbor_shell, username, node, neighbor) | ||
reader, writer = await telnetlib3.open_connection(hostname, 8010, shell=factory) | ||
await writer.protocol.waiter_closed | ||
|
||
|
||
def main(): | ||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s -- %(message)s') | ||
parser = argparse.ArgumentParser(description='Connect to neighbors and read their g8bpqloop.txt file') | ||
parser.add_argument("callsign", help="Your call sign, without any \"-2\" part. E.g., \"k4dbz\"") | ||
parser.add_argument("tarpn_hostname", help="IP address of your TARPN node") | ||
parser.add_argument("node", help="Your node callsign with the \"-2\" part. E.g., \"K4DBZ-2\"") | ||
parser.add_argument("neighbors", help="Comma-separated list of neighbor callsigns. E.g., \"KA2DEW-2, KN4ORB-2\"") | ||
args = parser.parse_args() | ||
|
||
loop = asyncio.get_event_loop() | ||
tasks = [] | ||
for neighbor in args.neighbors.split(","): | ||
neighbor = neighbor.strip() | ||
logging.info(f"Scheduling task for {neighbor}") | ||
tasks.append(connect_to_neighbor(args.callsign, args.tarpn_hostname, args.node, neighbor)) | ||
all_tasks = asyncio.gather(*tasks) | ||
loop.run_until_complete(all_tasks) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |