BGBilling Inet 9.x — Java-биллинг для ISP. Управляет договорами, тарифами, балансами, IP-пулами, DHCP-сервером, RADIUS-аутентификацией. Single source of truth для всех абонентских данных.
| Хост | 10.0.0.3 (AWG 10.9.0.197) |
| OS | Debian 12 (bookworm) |
| Hostname | bill |
| MariaDB | 10.11, bind 0.0.0.0:3306 (после миграции 2026-04-30) |
| Java GUI | "Swing" клиент — local на офисных машинах |
DSN: mysql+pymysql://longfw_ro:aed8fe96448d7ac4809607b28db5df3a@10.9.0.197/bgbilling
GRANT: SELECT ON bgbilling.* TO 'longfw_ro'@'10.9.0.196'
⚠️ MariaDB слушает 0.0.0.0 — должен быть iptables на host'е, разрешающий 3306 только для AWG 10.9.0.0/21 + 127.0.0.1.
⚠️ /tmp должен быть 1777 (sticky). Если 755 — InnoDB не запускается.
inet_serv_2Жёсткая привязка IP↔MAC↔port↔device:
SELECT contractId, INET6_NTOA(addressFrom) ip, LOWER(HEX(macAddress)) mac,
deviceId, interfaceId port, typeId, ipResourceId, dateFrom, dateTo
FROM inet_serv_2
WHERE dateTo IS NULL;
addressFrom — varbinary(16), IPv4/IPv6 → через INET6_NTOAmacAddress — varbinary 6 → через LOWER(HEX(...))dateTo IS NULL = услуга активнаinet_serv_type_2| typeId | смысл |
|---|---|
| 1 | GPON DHCP |
| 2 | ETTH DHCP |
inv_ip_category_2Стабильные id:
| categoryId | смысл |
|---|---|
| 4 | GPON-серый |
| 5 | ETTH-серый |
| 11 | Белые сети |
(typeId, pool_categoryId):
SELECT c.id, c.title, c.status, c.del,
COALESCE(
NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(cu.data, '$.customer.name'))), ''),
NULLIF(TRIM(cp_short.val), '')
) customer_name
FROM contract c
LEFT JOIN customer_link cl ON cl.contract_id = c.id AND cl.date_to IS NULL
LEFT JOIN customer cu ON cu.id = cl.customer_id
LEFT JOIN contract_parameter_type_1 cp_short ON cp_short.cid = c.id AND cp_short.pid = 18
WHERE c.del = 0;
contract_parameter_type_1 — текстовые| pid | значение |
|---|---|
| 2 | Фамилия |
| 14 | phone (тип pid?) — на самом деле телефоны в contract_parameter_type_phone |
| 18 | краткое наименование (org), fallback для customer.name |
| 19 | полное наименование (org) — приоритет над 18 для org-договоров |
| 25 | ИНН |
| 39 | Имя |
| 40 | Отчество |
contract_parameter_type_2 — адресный⚠️ Колонки cid, pid, hid, flat, room, address, comment. Запрашивать address, не val.
PID: 12/22/38 — разные виды адреса.
contract_parameter_type_phoneКолонки cid, pid, value (формат +7XXX [comment]).
contract_parameter_type_3Email — cid, pid=15, email.
-- активный тариф контракта
SELECT ct.cid, tp.title
FROM contract_tariff ct
JOIN tariff_plan tp ON tp.id = ct.tariffPlanId
WHERE ct.date2 IS NULL;
⚠️ Мусорные тарифы имеют title New tariff plan* и cnt=0.
SELECT cid, yy, mm, summa1+summa2-summa3-summa4 balance
FROM contract_balance;
inet_session_log_2_<YYYYMM>Помесячные таблицы! Создаются на каждый месяц. Читать current + previous month:
-- columns: id, deviceId, agentDeviceId, callingStationId mac, ipAddress varbinary,
-- sessionStart, sessionStop, ...
SELECT servId, deviceId, callingStationId mac, INET6_NTOA(ipAddress) ip,
sessionStart, sessionStop
FROM inet_session_log_2_202604
WHERE INET6_NTOA(ipAddress) = '10.1.4.111'
ORDER BY sessionStart DESC LIMIT 5;
В Python мерджить MAX(sessionStart) из двух месяцев. Если предыдущая таблица отсутствует — _table_exists() тихо пропустить.
| таблица | что |
|---|---|
inv_device_2 |
устройства доступа: id, title, host, deviceTypeId |
inv_device_port_2 |
физические порты устройств |
inv_ip_resource_2 |
IP-пулы (addressFrom/To = varbinary(24), размер = int.from_bytes(addressTo,"big") - int.from_bytes(addressFrom,"big") + 1) |
inv_ip_resource_subscription_2 |
привязка IP-пула к услуге, dateTo IS NULL = занят |
⚠️ deviceTypeId=3 (OLT) — у него interfaceId=-1 (нет физ.портов в схеме). total_ports считать только из inv_device_port_2.
SELECT
SUM(CASE WHEN active_subs > 0 THEN total_ports - used_ports ELSE 0 END) free_active,
SUM(CASE WHEN active_subs = 0 THEN total_ports ELSE 0 END) free_reserve,
SUM(total_ports) total_ports
FROM (
SELECT d.id,
(SELECT COUNT(*) FROM inv_device_port_2 p WHERE p.deviceId=d.id) total_ports,
(SELECT COUNT(DISTINCT s.interfaceId) FROM inet_serv_2 s
WHERE s.deviceId=d.id AND s.dateTo IS NULL AND s.interfaceId>0) used_ports,
(SELECT COUNT(*) FROM inet_serv_2 s
WHERE s.deviceId=d.id AND s.dateTo IS NULL) active_subs
FROM inv_device_2 d
WHERE d.dateTo IS NULL AND d.deviceTypeId != 3
) t;
SELECT d.id, d.title, d.host,
(SELECT COUNT(*) FROM inv_device_port_2 p WHERE p.deviceId=d.id) total_ports,
(SELECT COUNT(DISTINCT s.interfaceId) FROM inet_serv_2 s
WHERE s.deviceId=d.id AND s.dateTo IS NULL AND s.interfaceId>0) used_ports
FROM inv_device_2 d
WHERE d.dateTo IS NULL AND d.deviceTypeId != 3
HAVING total_ports > 0
ORDER BY (used_ports/total_ports) DESC;
SELECT cs.cid, c.title, cs.date1, DATEDIFF(CURDATE(), cs.date1) days_old
FROM contract_status cs
JOIN contract c ON c.id = cs.cid
WHERE cs.status = 5 AND cs.date2 IS NULL AND c.del = 0
ORDER BY cs.date1 ASC;
SELECT s.deviceId, s.interfaceId AS port, s.contractId,
c.title contract_title, s.title ip, s.typeId, s.ipResourceId, s.dateFrom,
COALESCE(
NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(cu.data, '$.customer.name'))), ''),
NULLIF(TRIM(cp_short.val), '')
) customer_name
FROM inet_serv_2 s
LEFT JOIN contract c ON c.id = s.contractId
LEFT JOIN customer_link cl ON cl.contract_id = s.contractId AND cl.date_to IS NULL
LEFT JOIN customer cu ON cu.id = cl.customer_id
LEFT JOIN contract_parameter_type_1 cp_short ON cp_short.cid = s.contractId AND cp_short.pid = 18
WHERE s.dateTo IS NULL AND s.interfaceId > 0
ORDER BY s.deviceId, s.interfaceId;
cid=2 — внутренний контракт ЛотосПлюс (23 служебные услуги). Исключать из метрик активности — иначе перевешивает.deviceTypeId=3 (OLT) — порты не считать (interfaceId=-1).COUNT(DISTINCT interfaceId) при подсчёте занятых портов — на одном порту может быть пара услуг (gray+white).COALESCE(JSON.customer.name, contract_parameter_type_1.pid=18).billing-sync контейнер каждые 60с (BGBILLING_SYNC_INTERVAL=60):
from sqlalchemy import create_engine, text
e = create_engine(os.environ['BGBILLING_DSN'])
# fetch_subscribers() → upsert в longfw.subscribers через INSERT ... ON DUPLICATE KEY UPDATE
# Stale-delete только при непустом ответе (защита от обнуления при tunnel-падении)
Web-модуль: applications/LoNGFW/web/device_ops/bgbilling.py — fetch_active_services(), fetch_subscribers(), lookup_by_ip/mac/contract.
applications/LoNGFW/docs/BGBILLING-DB-SCHEMA.md — все таблицы. BGBILLING-SAMPLES.txt — примеры данных.
@10.9.0.197/bgbilling.subscribers_sorm для fallback контактов.Changelog