aboutsummaryrefslogtreecommitdiff
path: root/ribreader.py
diff options
context:
space:
mode:
Diffstat (limited to 'ribreader.py')
-rw-r--r--ribreader.py152
1 files changed, 67 insertions, 85 deletions
diff --git a/ribreader.py b/ribreader.py
index 856ca1d..4bc3e92 100644
--- a/ribreader.py
+++ b/ribreader.py
@@ -1,36 +1,14 @@
+#
+# SPDX-FileCopyrightText: 2021 - 2026 Dimitri Staessens
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
"""
Ouroboros RIB filesystem reader
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following
-disclaimer in the documentation and/or other materials provided
-with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its
-contributors may be used to endorse or promote products derived
-from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
"""
+from __future__ import annotations
+
import os
import re
from typing import Optional
@@ -64,12 +42,11 @@ IPCP_STATES = [IPCP_STATE_NULL,
class OuroborosRIBReader:
"""
- Class for reading stuff from the Ouroboros system
- Resource Information Base (RIB)
+ Reader for the Ouroboros Resource Information Base (RIB)
"""
def __init__(self,
- rib_path: str):
+ rib_path: str) -> None:
self.rib_path = rib_path
@@ -100,7 +77,7 @@ class OuroborosRIBReader:
def _get_path_for_ipcp_flow_n_plus_1_info(self,
ipcp_name: str,
- fd: str):
+ fd: str) -> str:
return os.path.join(self.rib_path, ipcp_name, 'flow-allocator', fd)
@@ -109,6 +86,7 @@ class OuroborosRIBReader:
fd: str) -> str:
dt_dir = self._get_dt_dir_for_ipcp(ipcp_name)
+
return os.path.join(dt_dir, fd)
def _get_path_for_frct_flow_info(self,
@@ -116,12 +94,14 @@ class OuroborosRIBReader:
fd: str) -> str:
process_dir = self._get_dir_for_process(process)
+
return os.path.join(process_dir, str(fd), 'frct')
def _get_ipcp_type_for_ipcp(self,
ipcp_name: str) -> str:
_dir = self._get_dir_for_ipcp(ipcp_name)
+
path = f'{_dir}/info/_type'
if not os.path.exists(path):
return IPCP_TYPE_UNKNOWN
@@ -136,6 +116,7 @@ class OuroborosRIBReader:
ipcp_name: str) -> str:
_dir = self._get_dir_for_ipcp(ipcp_name)
+
path = f'{_dir}/info/_layer'
if not os.path.exists(path):
return '(error)'
@@ -150,6 +131,7 @@ class OuroborosRIBReader:
ipcp_name: str) -> str:
_dir = self._get_dir_for_ipcp(ipcp_name)
+
path = f'{_dir}/info/_state'
if not os.path.exists(path):
return IPCP_TYPE_UNKNOWN
@@ -166,7 +148,6 @@ class OuroborosRIBReader:
path = os.path.join(self._get_dir_for_ipcp(ipcp_name),
'flow-allocator')
-
if not os.path.exists(path):
return []
@@ -194,7 +175,7 @@ class OuroborosRIBReader:
return []
def _get_address_for_ipcp(self,
- ipcp_name):
+ ipcp_name: str) -> Optional[str]:
path = self._get_dir_for_ipcp(ipcp_name)
try:
@@ -243,8 +224,10 @@ class OuroborosRIBReader:
src = line.split()[-1]
if src not in nodes:
nodes += [src]
+
if src == address:
neighbors += 1
+
if 'dst' in line:
dst = line.split()[-1]
if dst not in nodes:
@@ -260,9 +243,10 @@ class OuroborosRIBReader:
return stats
@staticmethod
- def _get_trailing_number(s: str) -> int:
- """Extract trailing integer from a string."""
+ def _get_trailing_number(s: str) -> Optional[int]:
+
m = re.search(r'-?\d+$', s)
+
return int(m.group()) if m else None
def get_dht_stats_for_ipcp(self,
@@ -289,9 +273,11 @@ class OuroborosRIBReader:
with open(path, encoding='utf-8') as f:
for line in f.readlines():
split_line = line.split(':')
+
phrase = split_line[0]
if phrase not in str_to_metric:
continue
+
metric = str_to_metric[phrase]
value = self._get_trailing_number(split_line[1])
ret[metric] = value
@@ -300,9 +286,8 @@ class OuroborosRIBReader:
def _get_flows_for_process(self,
process_name: str) -> list[str]:
- """Get flow descriptors for a process."""
- path = self._get_dir_for_process(process_name)
+ path = self._get_dir_for_process(process_name)
if not os.path.exists(path):
return []
@@ -316,7 +301,6 @@ class OuroborosRIBReader:
def _get_flow_info_for_n_plus_1_flow(self,
ipcp_name: str,
fd: str) -> dict:
- """Get info for a single N+1 flow."""
str_to_metric = {
'Local endpoint ID': 'endpoint_id',
@@ -353,16 +337,17 @@ class OuroborosRIBReader:
path = self._get_path_for_ipcp_flow_n_plus_1_info(
ipcp_name, fd)
-
if not os.path.exists(path):
return {}
with open(path, encoding='utf-8') as f:
for line in f.readlines():
split_line = line.split(':')
+
phrase = split_line[0]
if phrase not in str_to_metric:
continue
+
metric = str_to_metric[phrase]
value = self._get_trailing_number(split_line[1])
ret[metric] = value
@@ -372,7 +357,6 @@ class OuroborosRIBReader:
def _get_frct_info_for_process_flow(self,
process: str,
fd: str) -> dict:
- """Get FRCT info for a single process flow."""
str_to_metric = {
'Maximum packet lifetime (ns)': 'mpl_timer_ns',
@@ -466,7 +450,6 @@ class OuroborosRIBReader:
ret = {}
path = self._get_path_for_frct_flow_info(process, fd)
-
if not os.path.exists(path):
return {}
@@ -475,9 +458,11 @@ class OuroborosRIBReader:
with open(path, encoding='utf-8') as f:
for line in f.readlines():
split_line = line.split(':')
+
phrase = split_line[0]
if phrase not in str_to_metric:
continue
+
metric = str_to_metric[phrase]
value = self._get_trailing_number(split_line[1])
ret[metric] = value
@@ -490,8 +475,9 @@ class OuroborosRIBReader:
"""
Get the flow information for all N+1 flows in an IPCP.
:param ipcp_name: name of the IPCP
- :return: dict with flow information
+ :return: list of dicts with per-flow information
"""
+
flow_info = []
flow_descriptors = self._get_n_plus_1_flows_for_ipcp(
@@ -506,7 +492,6 @@ class OuroborosRIBReader:
def _get_flow_info_for_n_minus_1_flow(self,
ipcp_name: str,
fd: str) -> dict:
- """Get info for a single N-1 (data transfer) flow."""
ret = {}
@@ -536,6 +521,7 @@ class OuroborosRIBReader:
with open(path, encoding='utf-8') as f:
_current_cube = ''
ret['fd'] = fd
+
for line in [_l for _l in f.readlines() if _l != '\n']:
if 'Endpoint address' in line:
ret['endpoint'] = (
@@ -567,7 +553,7 @@ class OuroborosRIBReader:
"""
Get flow info for all Data Transfer (N-1) flows in an IPCP.
:param ipcp_name: name of the IPCP
- :return: flow information for the data transfer flows
+ :return: list of dicts with per-flow information
"""
flow_info = []
@@ -584,16 +570,15 @@ class OuroborosRIBReader:
def get_frct_info_for_process(self,
process_name: str) -> list[dict]:
"""
- Get the frct information for all flows for a process.
+ Get the FRCT information for all flows of a process.
:param process_name: name of the process
- :return: flow information for the N-1 flows
+ :return: list of dicts with per-flow FRCT information
"""
frct_info = []
flow_descriptors = self._get_flows_for_process(
process_name)
-
for flow in flow_descriptors:
info = self._get_frct_info_for_process_flow(
process_name, flow)
@@ -601,20 +586,11 @@ class OuroborosRIBReader:
return frct_info
- # pylint: disable-msg=too-many-arguments,too-many-positional-arguments
def get_ipcp_list(self,
- names_only: bool = False,
- types: bool = True,
- states: bool = True,
- layers: bool = True,
- flows: bool = True) -> list[dict]:
+ names_only: bool = False) -> list[dict]:
"""
Get a list of all IPCPs.
:param names_only: only return IPCP names and layer names
- :param types: return IPCP type
- :param states: return IPCP state
- :param layers: return layer the IPCP is enrolled in
- :param flows: return number of allocated (N+1) flows
:return: list of dicts containing IPCP info
"""
@@ -623,50 +599,49 @@ class OuroborosRIBReader:
if not os.path.exists(self.rib_path):
return []
- for ipcp_dir in [f.path for f in os.scandir(self.rib_path)
- if f.is_dir()
- and not f.name.startswith('proc.')]:
- ipcp_name = os.path.split(ipcp_dir)[-1]
+ for entry in os.scandir(self.rib_path):
+ if not entry.is_dir() or entry.name.startswith('proc.'):
+ continue
+
+ ipcp_name = os.path.split(entry.path)[-1]
ipcp_type = None
ipcp_state = None
- ipcp_layer = (self._get_layer_name_for_ipcp(ipcp_name)
- if layers else None)
ipcp_flows = None
+ n_flows = None
+
+ ipcp_layer = self._get_layer_name_for_ipcp(ipcp_name)
+
if not names_only:
- ipcp_type = (
- self._get_ipcp_type_for_ipcp(ipcp_name)
- if types else None)
- ipcp_state = (
- self._get_ipcp_state_for_ipcp(ipcp_name)
- if states else None)
- ipcp_flows = (
- self._get_n_plus_1_flows_for_ipcp(ipcp_name)
- if flows else None)
+ ipcp_type = self._get_ipcp_type_for_ipcp(ipcp_name)
+ ipcp_state = self._get_ipcp_state_for_ipcp(ipcp_name)
+ ipcp_flows = self._get_n_plus_1_flows_for_ipcp(ipcp_name)
+
+ if ipcp_flows:
+ n_flows = len(ipcp_flows)
ipcp_list += [{
'name': ipcp_name,
'type': ipcp_type,
'state': ipcp_state,
'layer': ipcp_layer,
- 'flows': (len(ipcp_flows)
- if ipcp_flows else None)}]
+ 'flows': n_flows}]
+
return ipcp_list
- # pylint: enable-msg=too-many-arguments,too-many-positional-arguments
def get_process_list(self) -> list[str]:
"""
- Get a list of all Ouroboros applications exposing frct stats.
+ Get a list of all Ouroboros application processes in the RIB.
:return: list of process names ("proc.<pid>")
"""
+
proc_list = []
if not os.path.exists(self.rib_path):
return []
- for proc in [f.name for f in os.scandir(self.rib_path)
- if f.is_dir()
- and f.name.startswith('proc.')]:
- proc_list += [proc]
+ for entry in os.scandir(self.rib_path):
+ if entry.is_dir() and entry.name.startswith('proc.'):
+ proc_list += [entry.name]
return proc_list
@@ -675,17 +650,22 @@ class OuroborosRIBReader:
path = os.path.join(self._get_dir_for_ipcp(ipcp_name),
'eth')
-
if not os.path.exists(path):
return []
+ eth_flows = []
+
try:
- return [f.name for f in os.scandir(path)
- if f.name != 'summary']
+ for entry in os.scandir(path):
+ if entry.name != 'summary':
+ eth_flows += [entry.name]
except IOError as e:
print(e)
+
return []
+ return eth_flows
+
def get_eth_summary_for_ipcp(self,
ipcp_name: str) -> dict:
"""
@@ -724,6 +704,7 @@ class OuroborosRIBReader:
with open(path, encoding='utf-8') as f:
for line in f.readlines():
split_line = line.split(':')
+
phrase = split_line[0]
if phrase not in str_to_metric:
continue
@@ -760,6 +741,7 @@ class OuroborosRIBReader:
with open(path, encoding='utf-8') as f:
for line in f.readlines():
split_line = line.split(':')
+
phrase = split_line[0]
if phrase not in str_to_metric:
continue