wgctl/core/lib/peers.py
Nuno Duque Nunes 1308f9e07a refactor: split json_helper.py into lib/ modules
- core/lib/util.py: shared utilities, ip_to_name, reverse_lookup, parse_since
- core/lib/events.py: fw_events, wg_events, follow_logs, event parsers
- core/lib/peers.py: peer_data, peer_transfer, peer_transfer_delta
- core/lib/activity.py: activity_aggregate
- json_helper.py: thin dispatcher importing from lib/
- events.py: --since, --filter-event, --filter-dest-ip/port query flags
- util.py: parse_since supporting relative (2h/7d) and EU/ISO date formats
2026-05-24 22:02:50 +00:00

180 lines
No EOL
5.7 KiB
Python

"""
peers.py — peer data, transfer stats, group lookups.
"""
import os
import json
import sys
import glob
from lib.util import DATETIME_FMT, build_ip_to_name, build_pubkey_to_name, fmt_ts
def peer_data(clients_dir, meta_dir, events_log):
"""
Output: name|ip|rule|type|last_ts|last_evt|main_group
"""
meta = {}
for f in glob.glob(f"{meta_dir}/*.meta"):
name = os.path.basename(f).replace('.meta', '')
try:
with open(f) as mf:
meta[name] = json.load(mf)
except Exception:
meta[name] = {}
last_events = {}
try:
with open(events_log) as f:
for line in f:
try:
e = json.loads(line.strip())
client = e.get('client', '')
if client:
last_events[client] = e
except Exception:
pass
except Exception:
pass
for conf in sorted(glob.glob(f"{clients_dir}/*.conf")):
name = os.path.basename(conf).replace('.conf', '')
ip = ''
try:
with open(conf) as f:
for line in f:
if line.startswith('Address'):
ip = line.split('=')[1].strip().split('/')[0]
break
except Exception:
pass
m = meta.get(name, {})
rule = m.get('rule', '')
peer_type = m.get('type', '')
main_group = m.get('main_group', '')
last_event = last_events.get(name, {})
last_ts = last_event.get('timestamp', '')
last_evt = last_event.get('event', '')
print(f"{name}|{ip}|{rule}|{peer_type}|{last_ts}|{last_evt}|{main_group}")
def peer_transfer(wg_interface):
"""Get total transfer bytes per peer."""
import subprocess
low = int(os.environ.get('ACTIVITY_TOTAL_LOW_BYTES', '1000000'))
med = int(os.environ.get('ACTIVITY_TOTAL_MED_BYTES', '10000000'))
high = int(os.environ.get('ACTIVITY_TOTAL_HIGH_BYTES', '100000000'))
try:
result = subprocess.run(
['wg', 'show', wg_interface, 'transfer'],
capture_output=True, text=True
)
for line in result.stdout.strip().split('\n'):
if not line:
continue
parts = line.split('\t')
if len(parts) == 3:
pubkey, rx, tx = parts
total = int(rx) + int(tx)
if total == 0: level = 'none'
elif total < low: level = 'low'
elif total < med: level = 'medium'
elif total < high: level = 'high'
else: level = 'very high'
print(f"{pubkey}|{rx}|{tx}|{level}")
except Exception:
pass
def peer_transfer_delta(wg_interface, cache_file):
"""Calculate current transfer rate using delta from previous sample."""
import subprocess
import time
low = int(os.environ.get('ACTIVITY_CURRENT_LOW_BYTES', '10000'))
med = int(os.environ.get('ACTIVITY_CURRENT_MED_BYTES', '100000'))
high = int(os.environ.get('ACTIVITY_CURRENT_HIGH_BYTES', '1000000'))
current = {}
now = time.time()
try:
result = subprocess.run(
['wg', 'show', wg_interface, 'transfer'],
capture_output=True, text=True
)
for line in result.stdout.strip().split('\n'):
if not line:
continue
parts = line.split('\t')
if len(parts) == 3:
pubkey, rx, tx = parts
current[pubkey] = {'rx': int(rx), 'tx': int(tx), 'ts': now}
except Exception:
pass
prev = {}
if os.path.exists(cache_file):
try:
with open(cache_file) as f:
prev = json.load(f)
except Exception:
pass
try:
with open(cache_file, 'w') as f:
json.dump(current, f)
except Exception:
pass
for pubkey, data in current.items():
if pubkey in prev:
dt = data['ts'] - prev[pubkey].get('ts', data['ts'])
if dt > 0:
rx_rate = max(0, (data['rx'] - prev[pubkey]['rx']) / dt)
tx_rate = max(0, (data['tx'] - prev[pubkey]['tx']) / dt)
total = rx_rate + tx_rate
if total <= 0: level = 'idle'
elif total < low: level = 'low'
elif total < med: level = 'medium'
elif total < high: level = 'high'
else: level = 'very high'
print(f"{pubkey}|{int(rx_rate)}|{int(tx_rate)}|{level}")
else:
print(f"{pubkey}|0|0|idle")
else:
print(f"{pubkey}|0|0|unknown")
def peer_group_map(groups_dir):
"""Return peer:group pairs for all groups."""
try:
for group_file in glob.glob(f"{groups_dir}/*.group"):
try:
with open(group_file) as f:
g = json.load(f)
name = g.get('name', '')
for peer in g.get('peers', []):
if peer:
print(f"{peer}:{name}")
except Exception:
pass
except Exception:
pass
def peer_groups(groups_dir, peer_name):
"""Find all groups containing a peer."""
try:
for group_file in glob.glob(f"{groups_dir}/*.group"):
try:
with open(group_file) as f:
g = json.load(f)
if peer_name in g.get('peers', []):
print(g.get('name', ''))
except Exception:
pass
except Exception:
pass