Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
c3a037f
unpin eventlet.
anarkiwi May 31, 2021
3052429
Merge branch 'master' of ssh://github.com/c65sdn/beka
anarkiwi Jun 21, 2021
957b1ad
Merge remote-tracking branch 'upstream/master'
anarkiwi Jul 4, 2021
76c4061
Merge remote-tracking branch 'upstream/master'
anarkiwi Jul 26, 2021
b3f06ec
Merge remote-tracking branch 'upstream/master'
anarkiwi Jul 26, 2021
65d9114
Merge remote-tracking branch 'upstream/master'
anarkiwi Aug 3, 2021
a06b7ad
Merge branch 'master' of ssh://github.com/c65sdn/beka
anarkiwi Aug 3, 2021
a11b001
Merge remote-tracking branch 'upstream/master'
anarkiwi Aug 10, 2021
d349487
Merge remote-tracking branch 'upstream/master'
anarkiwi Aug 22, 2021
560e22b
Merge remote-tracking branch 'upstream/master'
anarkiwi Sep 2, 2021
6105268
Merge branch 'master' of ssh://github.com/c65sdn/beka
anarkiwi Sep 2, 2021
52e2bce
change package name.
anarkiwi Sep 14, 2021
36dac24
disable deb.
anarkiwi Sep 14, 2021
cdff31b
URLs.
anarkiwi Sep 14, 2021
d741579
Merge remote-tracking branch 'upstream/master'
anarkiwi Sep 19, 2021
929ab4e
Merge remote-tracking branch 'upstream/master'
anarkiwi Sep 19, 2021
0b12b57
no 3.6.
anarkiwi Sep 19, 2021
2d966a3
Merge pull request #1 from c65sdn/no36
anarkiwi Sep 19, 2021
dc67d54
Merge remote-tracking branch 'upstream/master'
anarkiwi Sep 30, 2021
2627076
Merge branch 'master' of ssh://github.com/c65sdn/beka
anarkiwi Sep 30, 2021
28612f7
Merge remote-tracking branch 'upstream/master'
anarkiwi Oct 12, 2021
cd76817
Merge remote-tracking branch 'upstream/master'
anarkiwi Oct 17, 2021
8b6a1de
Merge remote-tracking branch 'upstream/master'
anarkiwi Nov 18, 2021
5ed078d
Merge remote-tracking branch 'upstream/master'
anarkiwi Dec 1, 2021
4842b1c
Merge remote-tracking branch 'upstream/master'
anarkiwi Dec 8, 2021
3bd82e0
Merge remote-tracking branch 'upstream/master'
anarkiwi Jan 12, 2022
c3254da
Merge remote-tracking branch 'upstream/master'
anarkiwi Jan 16, 2022
b282059
Merge remote-tracking branch 'upstream/master'
anarkiwi Feb 1, 2022
c2f5f45
Merge remote-tracking branch 'upstream/master'
anarkiwi Feb 9, 2022
9999b61
test 3.10
anarkiwi Feb 22, 2022
6795817
Merge remote-tracking branch 'upstream/master'
anarkiwi Mar 8, 2022
c92a605
Merge remote-tracking branch 'upstream/master'
anarkiwi Mar 13, 2022
78e7d5d
Merge remote-tracking branch 'upstream/master'
anarkiwi Mar 22, 2022
b6e309e
Merge remote-tracking branch 'upstream/master'
anarkiwi Mar 30, 2022
1ccd25c
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 3, 2022
8cb4c1c
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 3, 2022
0132826
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 6, 2022
52ff0f8
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 9, 2022
3d8eb8e
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 20, 2022
9b35cbb
Merge remote-tracking branch 'upstream/master'
anarkiwi Apr 25, 2022
10f7051
Merge remote-tracking branch 'upstream/master'
anarkiwi May 12, 2022
35fb3c0
Merge remote-tracking branch 'upstream/master'
anarkiwi May 23, 2022
56c5279
Merge remote-tracking branch 'upstream/master'
anarkiwi Jun 8, 2022
135e3e1
Merge remote-tracking branch 'upstream/master'
anarkiwi Jun 16, 2022
e99fe9b
Merge remote-tracking branch 'upstream/master'
anarkiwi Jun 22, 2022
0421f63
Merge remote-tracking branch 'upstream/master'
anarkiwi Jul 6, 2022
1888957
Merge remote-tracking branch 'upstream/master'
anarkiwi Jul 18, 2022
eb60362
Merge remote-tracking branch 'upstream/master'
anarkiwi Aug 17, 2022
c1c0f72
Merge remote-tracking branch 'upstream/master'
anarkiwi Oct 10, 2022
6f7b6a2
Merge remote-tracking branch 'upstream/master'
anarkiwi Oct 26, 2022
52bd18c
Merge remote-tracking branch 'upstream/master'
anarkiwi Nov 1, 2022
f264c3a
Merge remote-tracking branch 'upstream/master'
anarkiwi Jan 25, 2023
438f3eb
Merge remote-tracking branch 'upstream/master'
anarkiwi Feb 21, 2023
c8b6d90
Merge remote-tracking branch 'upstream/master'
anarkiwi Feb 21, 2023
921f22f
Merge remote-tracking branch 'upstream/master'
anarkiwi Mar 12, 2023
85a4099
Merge remote-tracking branch 'upstream/main'
anarkiwi Mar 14, 2023
e17f5b4
Bump pylint from 2.17.0 to 2.17.1
dependabot[bot] Mar 23, 2023
b932479
Merge remote-tracking branch 'upstream/main'
anarkiwi Mar 23, 2023
5065957
Merge pull request #15 from c65sdn/dependabot/pip/pylint-2.17.1
anarkiwi Mar 23, 2023
9f76098
Merge remote-tracking branch 'upstream/main'
anarkiwi Apr 18, 2023
70dafcd
pytype.
anarkiwi Apr 18, 2023
31083ac
need release.
anarkiwi Apr 18, 2023
d537ae8
1.8.5
anarkiwi Apr 18, 2023
c09022a
Merge remote-tracking branch 'upstream/main'
anarkiwi May 3, 2023
6824e47
Merge remote-tracking branch 'upstream/main'
anarkiwi May 9, 2023
108807c
Merge remote-tracking branch 'upstream/main'
anarkiwi May 14, 2023
921e1d5
Merge remote-tracking branch 'upstream/main'
anarkiwi May 24, 2023
ee9eb15
Bump pypa/gh-action-pypi-publish from 1.8.6 to 1.8.7
dependabot[bot] Jun 27, 2023
9ab9614
Merge pull request #20 from c65sdn/dependabot/github_actions/pypa/gh-…
anarkiwi Jul 10, 2023
18d0005
Merge remote-tracking branch 'upstream/main'
anarkiwi Jul 26, 2023
bce0b9a
Bump pylint from 2.17.4 to 2.17.5
dependabot[bot] Jul 27, 2023
dd665b1
Merge pull request #22 from c65sdn/dependabot/pip/pylint-2.17.5
anarkiwi Jul 27, 2023
d299253
Merge remote-tracking branch 'upstream/main'
anarkiwi Jul 29, 2023
73faaf4
Merge branch 'main' of ssh://github.com/c65sdn/beka
anarkiwi Jul 29, 2023
f713eb1
Merge remote-tracking branch 'upstream/main'
anarkiwi Aug 30, 2023
92f306e
Bump codecov/codecov-action from 3 to 4
dependabot[bot] Sep 15, 2023
600e2e2
Merge remote-tracking branch 'upstream/main'
anarkiwi Sep 22, 2023
2eda993
Merge remote-tracking branch 'upstream/main'
anarkiwi Oct 3, 2023
1c28b1e
Merge remote-tracking branch 'upstream/main'
anarkiwi Oct 3, 2023
2ed2fe3
Merge remote-tracking branch 'upstream/main'
anarkiwi Oct 26, 2023
25b9525
Merge pull request #25 from c65sdn/dependabot/github_actions/codecov/…
anarkiwi Oct 26, 2023
d4d0731
Merge remote-tracking branch 'upstream/main'
anarkiwi Nov 19, 2023
5a08456
Merge remote-tracking branch 'upstream/main'
anarkiwi Nov 24, 2023
c1fea60
Merge remote-tracking branch 'upstream/main'
anarkiwi Dec 10, 2023
21c4516
Merge remote-tracking branch 'upstream/main'
anarkiwi Dec 11, 2023
0ed866e
Merge remote-tracking branch 'upstream/main'
anarkiwi Dec 19, 2023
7f3b7e8
Merge remote-tracking branch 'upstream/main'
anarkiwi Jan 25, 2024
29d3a00
Bump pytype from 2023.12.8 to 2024.1.24
dependabot[bot] Jan 25, 2024
9429091
drop 3.8
anarkiwi Jan 25, 2024
5d46e34
codecov
anarkiwi Jan 25, 2024
95bb571
Merge remote-tracking branch 'upstream/main'
anarkiwi Jan 30, 2024
3febd18
Merge remote-tracking branch 'upstream/main'
anarkiwi Feb 11, 2024
b06d615
Merge remote-tracking branch 'upstream/main'
anarkiwi Feb 14, 2024
f094e4e
Merge remote-tracking branch 'upstream/main'
anarkiwi Feb 23, 2024
e652fde
Merge branch 'main' into dependabot/pip/pytype-2024.1.24
anarkiwi Feb 26, 2024
151360f
Merge pull request #46 from c65sdn/dependabot/pip/pytype-2024.1.24
anarkiwi Mar 3, 2024
d57cd69
Merge remote-tracking branch 'upstream/main'
anarkiwi Mar 3, 2024
3c2b1c5
Merge branch 'main' of ssh://github.com/c65sdn/beka
anarkiwi Mar 3, 2024
4d9c1e9
Bump pypa/gh-action-pypi-publish from 1.8.12 to 1.8.14
dependabot[bot] Mar 8, 2024
ef41e3f
Bump pytype from 2024.2.27 to 2024.3.11
dependabot[bot] Mar 12, 2024
1b05a79
Merge pull request #47 from c65sdn/dependabot/github_actions/pypa/gh-…
anarkiwi Mar 15, 2024
c98f215
Merge pull request #48 from c65sdn/dependabot/pip/pytype-2024.3.11
anarkiwi Mar 15, 2024
e7e6df9
Merge remote-tracking branch 'upstream/main'
anarkiwi Mar 21, 2024
4bee2f6
Merge branch 'main' of ssh://github.com/c65sdn/beka
anarkiwi Mar 21, 2024
49ca94f
Bump pytype from 2024.3.19 to 2024.4.11
dependabot[bot] Apr 12, 2024
13c44f2
Merge pull request #50 from c65sdn/dependabot/pip/pytype-2024.4.11
anarkiwi Apr 12, 2024
97ec0b5
Merge remote-tracking branch 'upstream/main'
anarkiwi Apr 22, 2024
7da23ee
Merge branch 'main' of ssh://github.com/c65sdn/beka
anarkiwi Apr 22, 2024
32ef397
Merge remote-tracking branch 'upstream/main'
anarkiwi May 16, 2024
3ee6c7c
Merge remote-tracking branch 'upstream/main'
anarkiwi Jun 4, 2024
351edea
Merge remote-tracking branch 'upstream/main'
anarkiwi Jul 2, 2024
67bc1bb
Merge remote-tracking branch 'upstream/main'
anarkiwi Jul 30, 2024
a28c634
Merge remote-tracking branch 'upstream/main'
anarkiwi Sep 22, 2024
9727859
Merge remote-tracking branch 'upstream/main'
anarkiwi Oct 10, 2024
3be1ace
Merge remote-tracking branch 'upstream/main'
anarkiwi Oct 20, 2024
3730372
Merge remote-tracking branch 'upstream/main'
anarkiwi Nov 15, 2024
99caa82
Merge remote-tracking branch 'upstream/main'
anarkiwi Dec 3, 2024
3746503
Merge remote-tracking branch 'upstream/main'
anarkiwi Dec 16, 2024
94b4718
Merge remote-tracking branch 'upstream/main'
anarkiwi Jan 5, 2025
98dfb86
Merge remote-tracking branch 'upstream/main'
anarkiwi Jan 29, 2025
5b3f1c4
Merge remote-tracking branch 'upstream/main'
anarkiwi Mar 31, 2025
09b1b0d
Merge remote-tracking branch 'upstream/main'
anarkiwi Jun 11, 2025
47d1919
Merge remote-tracking branch 'upstream/main'
anarkiwi Sep 2, 2025
01ea1f4
Drop eventlet, port to stdlib threading
anarkiwi May 5, 2026
4cc0d71
Merge pull request #60 from c65sdn/drop-eventlet
anarkiwi May 5, 2026
bbb11cc
Drop EOL Python 3.8/3.9, add 3.13 and 3.14 to CI matrices
anarkiwi May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests-codecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, '3.10']
python-version: ['3.10', 3.11, 3.12, 3.13, 3.14]
steps:
- name: Checkout repo
uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, '3.10', 3.11]
python-version: ['3.10', 3.11, 3.12, 3.13, 3.14]
steps:
- name: Checkout repo
uses: actions/checkout@v5
Expand Down
70 changes: 45 additions & 25 deletions beka/peering.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import threading
import time

from eventlet import sleep, GreenPool
from eventlet.queue import Queue
import eventlet.greenthread as greenthread

from .chopper import Chopper
from .event import EventTimerExpired, EventMessageReceived
from .bgp_message import BgpMessageParser, BgpMessagePacker
from .error import SocketClosedError, IdleError

# Sentinel placed on output queues during shutdown to wake any thread
# blocked in ``Queue.get()`` so it can observe ``_stop_event`` and exit.
_QUEUE_POISON = object()


class Peering(object):
def __init__(
self, state_machine, peer_address, socket, route_handler, error_handler=None
):
self.input_stream = None
self.chopper = None
self.pool = None
self.eventlets = None
self.threads = None
self.parser = None
self.packer = None
self.state_machine = state_machine
Expand All @@ -27,37 +27,42 @@ def __init__(
self.route_handler = route_handler
self.error_handler = error_handler
self.start_time = int(time.time())
self._stop_event = threading.Event()

def uptime(self):
return int(time.time()) - self.start_time

def run(self):
self.input_stream = self.socket.makefile(mode="rb")
self.chopper = Chopper(self.input_stream)
self.pool = GreenPool()
self.parser = BgpMessageParser()
self.packer = BgpMessagePacker()
self.state_machine.open_handler = self.open_handler
self.eventlets = []

self.eventlets.append(self.pool.spawn(self.send_messages))
self.eventlets.append(self.pool.spawn(self.print_route_updates))
self.eventlets.append(self.pool.spawn(self.kick_timers))
self.eventlets.append(self.pool.spawn(self.receive_messages))

self.pool.waitall()
targets = (
self.send_messages,
self.print_route_updates,
self.kick_timers,
self.receive_messages,
)
self.threads = [
threading.Thread(target=t, name=t.__name__, daemon=True) for t in targets
]
for thread in self.threads:
thread.start()
for thread in self.threads:
thread.join()

def open_handler(self, capabilities):
self.parser.capabilities = capabilities
self.packer.capabilities = capabilities

def receive_messages(self):
while True:
sleep(0)
while not self._stop_event.is_set():
try:
message_type, serialised_message = self.chopper.next()
except SocketClosedError as e:
if self.error_handler:
if self.error_handler and not self._stop_event.is_set():
self.error_handler("Peering %s: %s" % (self.peer_address, e))
self.shutdown()
break
Expand All @@ -73,25 +78,30 @@ def receive_messages(self):
break

def send_messages(self):
while True:
sleep(0)
while not self._stop_event.is_set():
message = self.state_machine.output_messages.get()
if message is _QUEUE_POISON:
break
self.socket.send(self.packer.pack(message))

def empty_message_queue(self):
while self.state_machine.output_messages.qsize():
message = self.state_machine.output_messages.get()
if message is _QUEUE_POISON:
continue
self.socket.send(self.packer.pack(message))

def print_route_updates(self):
while True:
sleep(0)
while not self._stop_event.is_set():
route_update = self.state_machine.route_updates.get()
if route_update is _QUEUE_POISON:
break
self.route_handler(route_update)

def kick_timers(self):
while True:
sleep(1)
# ``Event.wait(timeout)`` returns True as soon as the event is set,
# giving prompt shutdown without burning CPU between ticks.
while not self._stop_event.wait(timeout=1):
tick = int(time.time())
try:
self.state_machine.event(EventTimerExpired(), tick)
Expand All @@ -102,6 +112,16 @@ def kick_timers(self):
break

def shutdown(self):
if self._stop_event.is_set():
return
self._stop_event.set()
self.empty_message_queue()
for eventlet in self.eventlets:
eventlet.kill()
# Unblock any thread parked in ``Queue.get()``. Each consumer
# checks ``_stop_event`` after waking and exits.
self.state_machine.output_messages.put(_QUEUE_POISON)
self.state_machine.route_updates.put(_QUEUE_POISON)
# Force ``chopper.next()``'s underlying recv to return.
try:
self.socket.shutdown(2) # socket.SHUT_RDWR
except OSError:
pass
2 changes: 1 addition & 1 deletion beka/state_machine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from eventlet.queue import Queue
from queue import Queue
from collections import OrderedDict

from .event import Event
Expand Down
55 changes: 35 additions & 20 deletions beka/stream_server.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import socket

from eventlet import GreenPool, listen
import eventlet.greenthread as greenthread
import threading
from concurrent.futures import ThreadPoolExecutor


class StreamServer:
"""Listen on ``address`` and dispatch each accepted socket to ``handler`` in its own thread."""

DEFAULT_MAX_HANDLERS = 64

def __init__(self, address, handler):
self.address = address
self.handler = handler
self.greenlets = set()
self.running = False
self.server = None
self._executor = None
self._accept_thread = None

def _family(self):
if ":" in self.address[0]:
Expand All @@ -19,24 +23,35 @@ def _family(self):

def serve_forever(self):
self.running = True
self.server = listen(self.address, self._family())
pool = GreenPool()

self.server = socket.socket(self._family(), socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(self.address)
self.server.listen()
self._executor = ThreadPoolExecutor(
max_workers=self.DEFAULT_MAX_HANDLERS,
thread_name_prefix="beka-handler",
)
self._accept_thread = threading.current_thread()
try:
while self.running:
sock, address = self.server.accept()
pool.spawn(self.call_handler, sock, address)
self.greenlets
except OSError:
pass

def call_handler(self, sock, address):
self.greenlets.add(greenthread.getcurrent())
self.handler(sock, address)
self.greenlets.remove(greenthread.getcurrent())
try:
sock, address = self.server.accept()
except OSError:
# accept() raises after stop() shuts the listening socket
break
self._executor.submit(self.handler, sock, address)
finally:
self._executor.shutdown(wait=False)
self._executor = None

def stop(self):
self.running = False
for greenlet in self.greenlets:
greenlet.kill()
self.server.shutdown(socket.SHUT_RDWR)
if self.server is not None:
try:
self.server.shutdown(socket.SHUT_RDWR)
except OSError:
pass
try:
self.server.close()
except OSError:
pass
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
eventlet>=0.33.3
pbr>=1.9
16 changes: 10 additions & 6 deletions run.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import signal
import sys
import threading
import yaml

from eventlet import GreenPool

from beka.beka import Beka


Expand All @@ -20,7 +19,7 @@ def __init__(self):

def run(self):
signal.signal(signal.SIGINT, self.signal_handler)
pool = GreenPool()
threads = []

with open("beka.yaml", encoding="utf-8") as file:
config = yaml.safe_load(file.read())
Expand All @@ -46,9 +45,14 @@ def run(self):
for route in router["routes"]:
beka.add_route(route["prefix"], route["next_hop"])
self.bekas.append(beka)
pool.spawn_n(beka.run)
pool.waitall()
printmsg("All greenlets gone, exiting")
thread = threading.Thread(
target=beka.run, name="beka-%s" % router["local_address"]
)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
printmsg("All threads gone, exiting")

def signal_handler(self, _signal, _frame):
printmsg("[SIGINT] Shutting down")
Expand Down
9 changes: 3 additions & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
[metadata]
name = beka
name = c65beka
summary = A bare-bones BGP speaker
long_description =
Beka is a fairly basic BGP speaker. It can send
and receive unicast route updates in IPv4 and IPv6,
but not too much else. It is designed to be simple to use
and to extend, without too much overhead.

It uses eventlet for concurrency, but is easy enough to port to
gevent if that takes your fancy.

More information at https://github.com/faucetsdn/beka
More information at https://github.com/c65sdn/beka
license = Apache-2
author = Sam Russell
author-email = sam.h.russell@gmail.com
home-page = https://github.com/faucetsdn/beka
home-page = https://github.com/c65sdn/beka
classifiers =
Development Status :: 2 - Pre-Alpha
Intended Audience :: System Administrators
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

from setuptools import setup

if sys.version_info < (3,):
if sys.version_info < (3, 10):
print(
"""You are trying to install beka on python {py}

beka is not compatible with python 2, please upgrade to python 3.8 or newer.""".format(
beka is not compatible with python earlier than 3.10, please upgrade.""".format(
py=".".join([str(v) for v in sys.version_info[:3]])
),
file=sys.stderr,
Expand All @@ -20,6 +20,6 @@
setup(
name="beka",
setup_requires=["pbr>=1.9", "setuptools>=17.1"],
python_requires=">=3.8",
python_requires=">=3.10",
pbr=True,
)
Loading