koi-net 1.2.0b1__py3-none-any.whl → 1.2.0b3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/__init__.py +1 -1
- koi_net/assembler.py +95 -0
- koi_net/cli/models.py +2 -2
- koi_net/config/core.py +71 -0
- koi_net/config/full_node.py +31 -0
- koi_net/config/loader.py +46 -0
- koi_net/config/partial_node.py +18 -0
- koi_net/core.py +43 -206
- koi_net/default_actions.py +1 -2
- koi_net/effector.py +27 -15
- koi_net/entrypoints/__init__.py +2 -0
- koi_net/entrypoints/base.py +5 -0
- koi_net/{poller.py → entrypoints/poller.py} +14 -12
- koi_net/entrypoints/server.py +94 -0
- koi_net/handshaker.py +5 -5
- koi_net/identity.py +3 -4
- koi_net/lifecycle.py +42 -34
- koi_net/logger.py +176 -0
- koi_net/network/error_handler.py +7 -7
- koi_net/network/event_queue.py +9 -7
- koi_net/network/graph.py +8 -8
- koi_net/network/poll_event_buffer.py +26 -0
- koi_net/network/request_handler.py +23 -28
- koi_net/network/resolver.py +14 -14
- koi_net/network/response_handler.py +74 -9
- koi_net/{context.py → processor/context.py} +11 -19
- koi_net/processor/handler.py +4 -1
- koi_net/processor/{default_handlers.py → knowledge_handlers.py} +32 -31
- koi_net/processor/knowledge_object.py +2 -3
- koi_net/processor/kobj_queue.py +4 -4
- koi_net/processor/{knowledge_pipeline.py → pipeline.py} +25 -28
- koi_net/protocol/api_models.py +5 -2
- koi_net/protocol/envelope.py +5 -6
- koi_net/protocol/model_map.py +61 -0
- koi_net/protocol/node.py +3 -3
- koi_net/protocol/secure.py +14 -8
- koi_net/secure.py +6 -7
- koi_net/workers/__init__.py +2 -0
- koi_net/{worker.py → workers/base.py} +7 -0
- koi_net/{processor → workers}/event_worker.py +19 -23
- koi_net/{kobj_worker.py → workers/kobj_worker.py} +12 -13
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/METADATA +2 -1
- koi_net-1.2.0b3.dist-info/RECORD +56 -0
- koi_net/behaviors.py +0 -51
- koi_net/config.py +0 -161
- koi_net/models.py +0 -14
- koi_net/poll_event_buffer.py +0 -17
- koi_net/server.py +0 -145
- koi_net-1.2.0b1.dist-info/RECORD +0 -49
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/WHEEL +0 -0
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/entry_points.txt +0 -0
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
|
|
2
2
|
import time
|
|
3
|
-
import
|
|
4
|
-
from .processor.kobj_queue import KobjQueue
|
|
5
|
-
from .lifecycle import NodeLifecycle
|
|
6
|
-
from .network.resolver import NetworkResolver
|
|
7
|
-
from .config import NodeConfig
|
|
3
|
+
import structlog
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
from .base import EntryPoint
|
|
6
|
+
from ..processor.kobj_queue import KobjQueue
|
|
7
|
+
from ..lifecycle import NodeLifecycle
|
|
8
|
+
from ..network.resolver import NetworkResolver
|
|
9
|
+
from ..config.partial_node import PartialNodeConfig
|
|
10
10
|
|
|
11
|
+
log = structlog.stdlib.get_logger()
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
class NodePoller(EntryPoint):
|
|
13
15
|
"""Manages polling based event loop for partial nodes."""
|
|
14
16
|
kobj_queue: KobjQueue
|
|
15
17
|
lifecycle: NodeLifecycle
|
|
16
18
|
resolver: NetworkResolver
|
|
17
|
-
config:
|
|
19
|
+
config: PartialNodeConfig
|
|
18
20
|
|
|
19
21
|
def __init__(
|
|
20
22
|
self,
|
|
21
|
-
|
|
23
|
+
config: PartialNodeConfig,
|
|
22
24
|
lifecycle: NodeLifecycle,
|
|
25
|
+
kobj_queue: KobjQueue,
|
|
23
26
|
resolver: NetworkResolver,
|
|
24
|
-
config: NodeConfig
|
|
25
27
|
):
|
|
26
28
|
self.kobj_queue = kobj_queue
|
|
27
29
|
self.lifecycle = lifecycle
|
|
@@ -33,7 +35,7 @@ class NodePoller:
|
|
|
33
35
|
neighbors = self.resolver.poll_neighbors()
|
|
34
36
|
for node_rid in neighbors:
|
|
35
37
|
for event in neighbors[node_rid]:
|
|
36
|
-
self.kobj_queue.
|
|
38
|
+
self.kobj_queue.push(event=event, source=node_rid)
|
|
37
39
|
|
|
38
40
|
def run(self):
|
|
39
41
|
"""Runs polling event loop."""
|
|
@@ -42,6 +44,6 @@ class NodePoller:
|
|
|
42
44
|
start_time = time.time()
|
|
43
45
|
self.poll()
|
|
44
46
|
elapsed = time.time() - start_time
|
|
45
|
-
sleep_time = self.config.
|
|
47
|
+
sleep_time = self.config.poller.polling_interval - elapsed
|
|
46
48
|
if sleep_time > 0:
|
|
47
49
|
time.sleep(sleep_time)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import structlog
|
|
2
|
+
import uvicorn
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from fastapi import FastAPI, APIRouter
|
|
5
|
+
from fastapi.responses import JSONResponse
|
|
6
|
+
|
|
7
|
+
from .base import EntryPoint
|
|
8
|
+
from ..network.response_handler import ResponseHandler
|
|
9
|
+
from ..protocol.model_map import API_MODEL_MAP
|
|
10
|
+
from ..protocol.api_models import ErrorResponse
|
|
11
|
+
from ..protocol.errors import ProtocolError
|
|
12
|
+
from ..lifecycle import NodeLifecycle
|
|
13
|
+
from ..config.full_node import FullNodeConfig
|
|
14
|
+
|
|
15
|
+
log = structlog.stdlib.get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NodeServer(EntryPoint):
|
|
19
|
+
"""Manages FastAPI server and event handling for full nodes."""
|
|
20
|
+
config: FullNodeConfig
|
|
21
|
+
lifecycle: NodeLifecycle
|
|
22
|
+
response_handler: ResponseHandler
|
|
23
|
+
app: FastAPI
|
|
24
|
+
router: APIRouter
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
config: FullNodeConfig,
|
|
29
|
+
lifecycle: NodeLifecycle,
|
|
30
|
+
response_handler: ResponseHandler,
|
|
31
|
+
):
|
|
32
|
+
self.config = config
|
|
33
|
+
self.lifecycle = lifecycle
|
|
34
|
+
self.response_handler = response_handler
|
|
35
|
+
self._build_app()
|
|
36
|
+
|
|
37
|
+
def _build_app(self):
|
|
38
|
+
"""Builds FastAPI app and adds endpoints."""
|
|
39
|
+
@asynccontextmanager
|
|
40
|
+
async def lifespan(*args, **kwargs):
|
|
41
|
+
async with self.lifecycle.async_run():
|
|
42
|
+
yield
|
|
43
|
+
|
|
44
|
+
self.app = FastAPI(
|
|
45
|
+
lifespan=lifespan,
|
|
46
|
+
title="KOI-net Protocol API",
|
|
47
|
+
version="1.0.0"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
self.app.add_exception_handler(ProtocolError, self.protocol_error_handler)
|
|
51
|
+
|
|
52
|
+
self.router = APIRouter(prefix="/koi-net")
|
|
53
|
+
|
|
54
|
+
for path, models in API_MODEL_MAP.items():
|
|
55
|
+
def create_endpoint(path: str):
|
|
56
|
+
async def endpoint(req):
|
|
57
|
+
return self.response_handler.handle_response(path, req)
|
|
58
|
+
|
|
59
|
+
# programmatically setting type hint annotations for FastAPI's model validation
|
|
60
|
+
endpoint.__annotations__ = {
|
|
61
|
+
"req": models.request_envelope,
|
|
62
|
+
"return": models.response_envelope
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return endpoint
|
|
66
|
+
|
|
67
|
+
self.router.add_api_route(
|
|
68
|
+
path=path,
|
|
69
|
+
endpoint=create_endpoint(path),
|
|
70
|
+
methods=["POST"],
|
|
71
|
+
response_model_exclude_none=True
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
self.app.include_router(self.router)
|
|
75
|
+
|
|
76
|
+
def protocol_error_handler(self, request, exc: ProtocolError):
|
|
77
|
+
"""Catches `ProtocolError` and returns as `ErrorResponse`."""
|
|
78
|
+
log.info(f"caught protocol error: {exc}")
|
|
79
|
+
resp = ErrorResponse(error=exc.error_type)
|
|
80
|
+
log.info(f"returning error response: {resp}")
|
|
81
|
+
return JSONResponse(
|
|
82
|
+
status_code=400,
|
|
83
|
+
content=resp.model_dump(mode="json")
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def run(self):
|
|
87
|
+
"""Starts FastAPI server and event handler."""
|
|
88
|
+
uvicorn.run(
|
|
89
|
+
app=self.app,
|
|
90
|
+
host=self.config.server.host,
|
|
91
|
+
port=self.config.server.port,
|
|
92
|
+
log_config=None,
|
|
93
|
+
access_log=False
|
|
94
|
+
)
|
koi_net/handshaker.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import structlog
|
|
2
2
|
from rid_lib.ext import Cache
|
|
3
3
|
from rid_lib.types import KoiNetNode
|
|
4
4
|
from koi_net.identity import NodeIdentity
|
|
5
5
|
from koi_net.network.event_queue import EventQueue
|
|
6
6
|
from .protocol.event import Event, EventType
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
log = structlog.stdlib.get_logger()
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Handshaker:
|
|
@@ -24,14 +24,14 @@ class Handshaker:
|
|
|
24
24
|
Pushes successive `FORGET` and `NEW` events to target node to
|
|
25
25
|
reset the target's cache in case it already knew this node.
|
|
26
26
|
"""
|
|
27
|
-
|
|
28
|
-
self.event_queue.
|
|
27
|
+
log.debug(f"Initiating handshake with {target}")
|
|
28
|
+
self.event_queue.push(
|
|
29
29
|
Event.from_rid(
|
|
30
30
|
event_type=EventType.FORGET,
|
|
31
31
|
rid=self.identity.rid),
|
|
32
32
|
target=target
|
|
33
33
|
)
|
|
34
|
-
self.event_queue.
|
|
34
|
+
self.event_queue.push(
|
|
35
35
|
event=Event.from_bundle(
|
|
36
36
|
event_type=EventType.NEW,
|
|
37
37
|
bundle=self.cache.read(self.identity.rid)),
|
koi_net/identity.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from rid_lib.types.koi_net_node import KoiNetNode
|
|
3
|
-
from .config import NodeConfig
|
|
3
|
+
from .config.core import NodeConfig
|
|
4
4
|
from .protocol.node import NodeProfile
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
6
|
+
log = structlog.stdlib.get_logger()
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class NodeIdentity:
|
koi_net/lifecycle.py
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from contextlib import contextmanager, asynccontextmanager
|
|
3
3
|
|
|
4
4
|
from rid_lib.ext import Bundle, Cache
|
|
5
5
|
from rid_lib.types import KoiNetNode
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
from .config import NodeConfig
|
|
7
|
+
from .handshaker import Handshaker
|
|
8
|
+
from .network.request_handler import RequestHandler
|
|
9
|
+
from .workers.kobj_worker import KnowledgeProcessingWorker
|
|
10
|
+
from .network.event_queue import EventQueue
|
|
11
|
+
from .workers import EventProcessingWorker
|
|
12
|
+
from .protocol.api_models import ErrorResponse
|
|
13
|
+
from .workers.base import STOP_WORKER
|
|
14
|
+
from .config.core import NodeConfig
|
|
15
15
|
from .processor.kobj_queue import KobjQueue
|
|
16
16
|
from .network.graph import NetworkGraph
|
|
17
17
|
from .identity import NodeIdentity
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
log = structlog.stdlib.get_logger()
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class NodeLifecycle:
|
|
@@ -31,7 +31,7 @@ class NodeLifecycle:
|
|
|
31
31
|
event_worker: EventProcessingWorker
|
|
32
32
|
cache: Cache
|
|
33
33
|
handshaker: Handshaker
|
|
34
|
-
|
|
34
|
+
request_handler: RequestHandler
|
|
35
35
|
|
|
36
36
|
def __init__(
|
|
37
37
|
self,
|
|
@@ -44,7 +44,7 @@ class NodeLifecycle:
|
|
|
44
44
|
event_worker: EventProcessingWorker,
|
|
45
45
|
cache: Cache,
|
|
46
46
|
handshaker: Handshaker,
|
|
47
|
-
|
|
47
|
+
request_handler: RequestHandler
|
|
48
48
|
):
|
|
49
49
|
self.config = config
|
|
50
50
|
self.identity = identity
|
|
@@ -55,32 +55,32 @@ class NodeLifecycle:
|
|
|
55
55
|
self.event_worker = event_worker
|
|
56
56
|
self.cache = cache
|
|
57
57
|
self.handshaker = handshaker
|
|
58
|
-
self.
|
|
58
|
+
self.request_handler = request_handler
|
|
59
59
|
|
|
60
60
|
@contextmanager
|
|
61
61
|
def run(self):
|
|
62
62
|
"""Synchronous context manager for node startup and shutdown."""
|
|
63
63
|
try:
|
|
64
|
-
|
|
64
|
+
log.info("Starting node lifecycle...")
|
|
65
65
|
self.start()
|
|
66
66
|
yield
|
|
67
67
|
except KeyboardInterrupt:
|
|
68
|
-
|
|
68
|
+
log.info("Keyboard interrupt!")
|
|
69
69
|
finally:
|
|
70
|
-
|
|
70
|
+
log.info("Stopping node lifecycle...")
|
|
71
71
|
self.stop()
|
|
72
72
|
|
|
73
73
|
@asynccontextmanager
|
|
74
74
|
async def async_run(self):
|
|
75
75
|
"""Asynchronous context manager for node startup and shutdown."""
|
|
76
76
|
try:
|
|
77
|
-
|
|
77
|
+
log.info("Starting async node lifecycle...")
|
|
78
78
|
self.start()
|
|
79
79
|
yield
|
|
80
80
|
except KeyboardInterrupt:
|
|
81
|
-
|
|
81
|
+
log.info("Keyboard interrupt!")
|
|
82
82
|
finally:
|
|
83
|
-
|
|
83
|
+
log.info("Stopping async node lifecycle...")
|
|
84
84
|
self.stop()
|
|
85
85
|
|
|
86
86
|
def start(self):
|
|
@@ -91,7 +91,7 @@ class NodeLifecycle:
|
|
|
91
91
|
of node bundle. Initiates handshake with first contact if node
|
|
92
92
|
doesn't have any neighbors. Catches up with coordinator state.
|
|
93
93
|
"""
|
|
94
|
-
|
|
94
|
+
log.info("Starting processor worker thread")
|
|
95
95
|
|
|
96
96
|
self.kobj_worker.thread.start()
|
|
97
97
|
self.event_worker.thread.start()
|
|
@@ -99,35 +99,43 @@ class NodeLifecycle:
|
|
|
99
99
|
|
|
100
100
|
# refresh to reflect changes (if any) in config.yaml
|
|
101
101
|
|
|
102
|
-
self.kobj_queue.
|
|
102
|
+
self.kobj_queue.push(bundle=Bundle.generate(
|
|
103
103
|
rid=self.identity.rid,
|
|
104
104
|
contents=self.identity.profile.model_dump()
|
|
105
105
|
))
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
log.debug("Waiting for kobj queue to empty")
|
|
108
108
|
self.kobj_queue.q.join()
|
|
109
109
|
|
|
110
|
-
# TODO: FACTOR OUT BEHAVIOR
|
|
111
|
-
|
|
112
110
|
coordinators = self.graph.get_neighbors(direction="in", allowed_type=KoiNetNode)
|
|
113
111
|
|
|
114
|
-
if len(coordinators)
|
|
115
|
-
|
|
112
|
+
if len(coordinators) > 0:
|
|
113
|
+
for coordinator in coordinators:
|
|
114
|
+
payload = self.request_handler.fetch_manifests(
|
|
115
|
+
node=coordinator,
|
|
116
|
+
rid_types=[KoiNetNode]
|
|
117
|
+
)
|
|
118
|
+
if type(payload) is ErrorResponse:
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
for manifest in payload.manifests:
|
|
122
|
+
self.kobj_queue.push(
|
|
123
|
+
manifest=manifest,
|
|
124
|
+
source=coordinator
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
elif self.config.koi_net.first_contact.rid:
|
|
128
|
+
log.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
|
|
116
129
|
|
|
117
130
|
self.handshaker.handshake_with(self.config.koi_net.first_contact.rid)
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for coordinator in self.behaviors.identify_coordinators():
|
|
122
|
-
self.behaviors.catch_up_with(coordinator, rid_types=[KoiNetNode])
|
|
123
|
-
|
|
124
132
|
|
|
125
133
|
def stop(self):
|
|
126
134
|
"""Stops a node.
|
|
127
135
|
|
|
128
136
|
Finishes processing knowledge object queue.
|
|
129
137
|
"""
|
|
130
|
-
|
|
138
|
+
log.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
|
|
131
139
|
|
|
132
|
-
self.kobj_queue.q.put(
|
|
133
|
-
self.event_queue.q.put(
|
|
140
|
+
self.kobj_queue.q.put(STOP_WORKER)
|
|
141
|
+
self.event_queue.q.put(STOP_WORKER)
|
koi_net/logger.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import logging
|
|
3
|
+
from logging.handlers import RotatingFileHandler
|
|
4
|
+
import colorama
|
|
5
|
+
import structlog
|
|
6
|
+
import sys
|
|
7
|
+
# from sentry_sdk import logger as sentry_logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def my_processor(_, __, event: dict):
|
|
11
|
+
# print(_, __, event)
|
|
12
|
+
event["path"] = event["module"] + "." + event["func_name"]
|
|
13
|
+
return event
|
|
14
|
+
|
|
15
|
+
# def sentry_processor(_, method, event: dict):
|
|
16
|
+
# print(event)
|
|
17
|
+
# if method == "critical":
|
|
18
|
+
# sentry_logger.fatal(
|
|
19
|
+
# event["event"],
|
|
20
|
+
# attributes=event
|
|
21
|
+
# )
|
|
22
|
+
# elif method == "info":
|
|
23
|
+
# sentry_logger.info(
|
|
24
|
+
# event["event"],
|
|
25
|
+
# attributes=event
|
|
26
|
+
# )
|
|
27
|
+
# elif method == "debug":
|
|
28
|
+
# sentry_logger.debug(
|
|
29
|
+
# event["event"],
|
|
30
|
+
# attributes=event
|
|
31
|
+
# )
|
|
32
|
+
# return event
|
|
33
|
+
|
|
34
|
+
console_renderer = structlog.dev.ConsoleRenderer(
|
|
35
|
+
columns=[
|
|
36
|
+
# Render the timestamp without the key name in yellow.
|
|
37
|
+
structlog.dev.Column(
|
|
38
|
+
"timestamp",
|
|
39
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
40
|
+
key_style=None,
|
|
41
|
+
value_style=colorama.Style.DIM,
|
|
42
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
43
|
+
value_repr=lambda t: datetime.fromisoformat(t).strftime("%Y-%m-%d %H:%M:%S"),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
structlog.dev.Column(
|
|
47
|
+
"level",
|
|
48
|
+
structlog.dev.LogLevelColumnFormatter(
|
|
49
|
+
level_styles={
|
|
50
|
+
level: colorama.Style.BRIGHT + color
|
|
51
|
+
for level, color in {
|
|
52
|
+
"critical": colorama.Fore.RED,
|
|
53
|
+
"exception": colorama.Fore.RED,
|
|
54
|
+
"error": colorama.Fore.RED,
|
|
55
|
+
"warn": colorama.Fore.YELLOW,
|
|
56
|
+
"warning": colorama.Fore.YELLOW,
|
|
57
|
+
"info": colorama.Fore.GREEN,
|
|
58
|
+
"debug": colorama.Fore.GREEN,
|
|
59
|
+
"notset": colorama.Back.RED,
|
|
60
|
+
}.items()
|
|
61
|
+
},
|
|
62
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
63
|
+
width=9
|
|
64
|
+
)
|
|
65
|
+
),
|
|
66
|
+
# Render the event without the key name in bright magenta.
|
|
67
|
+
|
|
68
|
+
# Default formatter for all keys not explicitly mentioned. The key is
|
|
69
|
+
# cyan, the value is green.
|
|
70
|
+
structlog.dev.Column(
|
|
71
|
+
"path",
|
|
72
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
73
|
+
key_style=None,
|
|
74
|
+
value_style=colorama.Fore.MAGENTA,
|
|
75
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
76
|
+
value_repr=str,
|
|
77
|
+
width=30
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
# structlog.dev.Column(
|
|
81
|
+
# "func_name",
|
|
82
|
+
# structlog.dev.KeyValueColumnFormatter(
|
|
83
|
+
# key_style=None,
|
|
84
|
+
# value_style=colorama.Fore.MAGENTA,
|
|
85
|
+
# reset_style=colorama.Style.RESET_ALL,
|
|
86
|
+
# value_repr=str,
|
|
87
|
+
# prefix="(",
|
|
88
|
+
# postfix=")",
|
|
89
|
+
# width=15
|
|
90
|
+
# ),
|
|
91
|
+
# ),
|
|
92
|
+
structlog.dev.Column(
|
|
93
|
+
"event",
|
|
94
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
95
|
+
key_style=None,
|
|
96
|
+
value_style=colorama.Fore.WHITE,
|
|
97
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
98
|
+
value_repr=str,
|
|
99
|
+
width=30
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
structlog.dev.Column(
|
|
103
|
+
"",
|
|
104
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
105
|
+
key_style=colorama.Fore.BLUE,
|
|
106
|
+
value_style=colorama.Fore.GREEN,
|
|
107
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
108
|
+
value_repr=str,
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
structlog.configure(
|
|
115
|
+
processors=[
|
|
116
|
+
# If log level is too low, abort pipeline and throw away log entry.
|
|
117
|
+
structlog.stdlib.filter_by_level,
|
|
118
|
+
# Add the name of the logger to event dict.
|
|
119
|
+
structlog.stdlib.add_logger_name,
|
|
120
|
+
# Add log level to event dict.
|
|
121
|
+
structlog.stdlib.add_log_level,
|
|
122
|
+
# Perform %-style formatting.
|
|
123
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
124
|
+
# Add a timestamp in ISO 8601 format.
|
|
125
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
126
|
+
# If the "stack_info" key in the event dict is true, remove it and
|
|
127
|
+
# render the current stack trace in the "stack" key.
|
|
128
|
+
structlog.processors.StackInfoRenderer(),
|
|
129
|
+
# If the "exc_info" key in the event dict is either true or a
|
|
130
|
+
# sys.exc_info() tuple, remove "exc_info" and render the exception
|
|
131
|
+
# with traceback into the "exception" key.
|
|
132
|
+
# structlog.processors.format_exc_info,
|
|
133
|
+
# If some value is in bytes, decode it to a Unicode str.
|
|
134
|
+
structlog.processors.UnicodeDecoder(),
|
|
135
|
+
# Add callsite parameters.
|
|
136
|
+
structlog.processors.CallsiteParameterAdder(
|
|
137
|
+
{
|
|
138
|
+
structlog.processors.CallsiteParameter.MODULE,
|
|
139
|
+
structlog.processors.CallsiteParameter.FUNC_NAME,
|
|
140
|
+
# structlog.processors.CallsiteParameter.LINENO,
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
my_processor,
|
|
144
|
+
# Render the final event dict as JSON.
|
|
145
|
+
# sentry_processor,
|
|
146
|
+
console_renderer
|
|
147
|
+
# structlog.processors.JSONRenderer()
|
|
148
|
+
|
|
149
|
+
],
|
|
150
|
+
# `wrapper_class` is the bound logger that you get back from
|
|
151
|
+
# get_logger(). This one imitates the API of `logging.Logger`.
|
|
152
|
+
wrapper_class=structlog.stdlib.BoundLogger,
|
|
153
|
+
# `logger_factory` is used to create wrapped loggers that are used for
|
|
154
|
+
# OUTPUT. This one returns a `logging.Logger`. The final value (a JSON
|
|
155
|
+
# string) from the final processor (`JSONRenderer`) will be passed to
|
|
156
|
+
# the method of the same name as that you've called on the bound logger.
|
|
157
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
158
|
+
# Effectively freeze configuration after creating the first bound
|
|
159
|
+
# logger.
|
|
160
|
+
cache_logger_on_first_use=True,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
file_handler = RotatingFileHandler(
|
|
164
|
+
filename="app.log",
|
|
165
|
+
maxBytes=10 * 1024 * 1024,
|
|
166
|
+
backupCount=5,
|
|
167
|
+
encoding="utf-8"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
logging.basicConfig(
|
|
171
|
+
format="%(message)s",
|
|
172
|
+
stream=sys.stdout,
|
|
173
|
+
level=logging.INFO,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# log = structlog.stdlib.get_logger()
|
koi_net/network/error_handler.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import structlog
|
|
2
2
|
from koi_net.handshaker import Handshaker
|
|
3
3
|
from koi_net.protocol.errors import ErrorType
|
|
4
4
|
from koi_net.protocol.event import EventType
|
|
5
5
|
from rid_lib.types import KoiNetNode
|
|
6
6
|
from ..processor.kobj_queue import KobjQueue
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
log = structlog.stdlib.get_logger()
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ErrorHandler:
|
|
@@ -27,11 +27,11 @@ class ErrorHandler:
|
|
|
27
27
|
self.timeout_counter.setdefault(node, 0)
|
|
28
28
|
self.timeout_counter[node] += 1
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
log.debug(f"{node} has timed out {self.timeout_counter[node]} time(s)")
|
|
31
31
|
|
|
32
32
|
if self.timeout_counter[node] > 3:
|
|
33
|
-
|
|
34
|
-
self.kobj_queue.
|
|
33
|
+
log.debug(f"Exceeded time out limit, forgetting node")
|
|
34
|
+
self.kobj_queue.push(rid=node, event_type=EventType.FORGET)
|
|
35
35
|
# do something
|
|
36
36
|
|
|
37
37
|
|
|
@@ -41,10 +41,10 @@ class ErrorHandler:
|
|
|
41
41
|
node: KoiNetNode
|
|
42
42
|
):
|
|
43
43
|
"""Attempts handshake when this node is unknown to target."""
|
|
44
|
-
|
|
44
|
+
log.info(f"Handling protocol error {error_type} for node {node!r}")
|
|
45
45
|
match error_type:
|
|
46
46
|
case ErrorType.UnknownNode:
|
|
47
|
-
|
|
47
|
+
log.info("Peer doesn't know me, attempting handshake...")
|
|
48
48
|
self.handshaker.handshake_with(node)
|
|
49
49
|
|
|
50
50
|
case ErrorType.InvalidKey: ...
|
koi_net/network/event_queue.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from queue import Queue
|
|
3
3
|
|
|
4
4
|
from rid_lib.types import KoiNetNode
|
|
5
|
+
from pydantic import BaseModel
|
|
5
6
|
|
|
6
|
-
from ..models import QueuedEvent
|
|
7
7
|
from ..protocol.event import Event
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
log = structlog.stdlib.get_logger()
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class QueuedEvent(BaseModel):
|
|
13
|
+
event: Event
|
|
14
|
+
target: KoiNetNode
|
|
15
|
+
|
|
12
16
|
class EventQueue:
|
|
13
17
|
"""Handles out going network event queues."""
|
|
14
18
|
q: Queue[QueuedEvent]
|
|
@@ -16,13 +20,11 @@ class EventQueue:
|
|
|
16
20
|
def __init__(self):
|
|
17
21
|
self.q = Queue()
|
|
18
22
|
|
|
19
|
-
def
|
|
23
|
+
def push(self, event: Event, target: KoiNetNode):
|
|
20
24
|
"""Pushes event to queue of specified node.
|
|
21
25
|
|
|
22
26
|
Event will be sent to webhook or poll queue depending on the
|
|
23
|
-
node type and edge type of the specified node.
|
|
24
|
-
to `True`, the webhook queued will be flushed after pushing the
|
|
25
|
-
event.
|
|
27
|
+
node type and edge type of the specified node.
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
30
|
self.q.put(QueuedEvent(target=target, event=event))
|
koi_net/network/graph.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from typing import Literal
|
|
3
3
|
import networkx as nx
|
|
4
4
|
from rid_lib import RIDType
|
|
@@ -7,7 +7,7 @@ from rid_lib.types import KoiNetEdge, KoiNetNode
|
|
|
7
7
|
from ..identity import NodeIdentity
|
|
8
8
|
from ..protocol.edge import EdgeProfile, EdgeStatus
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
log = structlog.stdlib.get_logger()
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class NetworkGraph:
|
|
@@ -24,22 +24,22 @@ class NetworkGraph:
|
|
|
24
24
|
|
|
25
25
|
def generate(self):
|
|
26
26
|
"""Generates directed graph from cached KOI nodes and edges."""
|
|
27
|
-
|
|
27
|
+
log.debug("Generating network graph")
|
|
28
28
|
self.dg.clear()
|
|
29
29
|
for rid in self.cache.list_rids():
|
|
30
30
|
if type(rid) == KoiNetNode:
|
|
31
31
|
self.dg.add_node(rid)
|
|
32
|
-
|
|
32
|
+
log.debug(f"Added node {rid!r}")
|
|
33
33
|
|
|
34
34
|
elif type(rid) == KoiNetEdge:
|
|
35
35
|
edge_bundle = self.cache.read(rid)
|
|
36
36
|
if not edge_bundle:
|
|
37
|
-
|
|
37
|
+
log.warning(f"Failed to load {rid!r}")
|
|
38
38
|
continue
|
|
39
39
|
edge_profile = edge_bundle.validate_contents(EdgeProfile)
|
|
40
40
|
self.dg.add_edge(edge_profile.source, edge_profile.target, rid=rid)
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
log.debug(f"Added edge {rid!r} ({edge_profile.source} -> {edge_profile.target})")
|
|
42
|
+
log.debug("Done")
|
|
43
43
|
|
|
44
44
|
def get_edge(self, source: KoiNetNode, target: KoiNetNode,) -> KoiNetEdge | None:
|
|
45
45
|
"""Returns edge RID given the RIDs of a source and target node."""
|
|
@@ -97,7 +97,7 @@ class NetworkGraph:
|
|
|
97
97
|
edge_bundle = self.cache.read(edge_rid)
|
|
98
98
|
|
|
99
99
|
if not edge_bundle:
|
|
100
|
-
|
|
100
|
+
log.warning(f"Failed to find edge {edge_rid!r} in cache")
|
|
101
101
|
continue
|
|
102
102
|
|
|
103
103
|
edge_profile = edge_bundle.validate_contents(EdgeProfile)
|