Дополнительные инструменты (tools)

This commit is contained in:
parent 1deb68bd75
commit 7af8d82513

110
tools/subnet-calc.sh Normal file
View file

@ -0,0 +1,110 @@
#!/bin/bash
# ============================================================
# Калькулятор подсетей для экзамена 09.02.06 (python3 + ipaddress)
#
# ЗАПУСК:
# bash tools/subnet-calc.sh <базовая_сеть> [имя=ЧИСЛО ...]
#
# ЧИСЛО — это АДРЕСА, а не хосты: считаются адрес сети и broadcast.
# Если нужен именно объём ХОСТОВ — добавьте суффикс h, например:
# srv=30 -> 30 адресов (включая сеть и broadcast)
# srv=28h -> 28 хостов + 2 спецадреса = 30 адресов
#
# ПРИМЕР (адреса подбираются последовательно из базовой сети):
# bash tools/subnet-calc.sh 10.0.0.0/24 hq_srv=32 hq_cli=16 mgmt=8 br_srv=16 br_link=2
#
# Для туннеля и линка BR-FW<->BR-RTR достаточно 2 адресов хостов:
# link=2 -> маска /30 (4 адреса: сеть, 2 хоста, broadcast)
#
# Пример вывода готовой таблицы — просто скопировать в отчёт (таблица 2).
# ============================================================
if ! command -v python3 >/dev/null 2>&1; then
echo "Нужен python3. Установите: dnf install -y python3"
exit 1
fi
python3 - "$@" <<'PY'
import sys
import ipaddress
def usage_and_exit():
print("Использование: subnet-calc.sh <базовая_сеть> [имя=число_адресов ...]")
print("Суффикс h означает число ХОСТОВ (например srv=28h -> 28 хостов).")
sys.exit(1)
def parse_size(value):
value = value.strip().lower()
hosts = value.endswith("h")
if hosts:
value = value[:-1]
try:
count = int(value)
except ValueError:
usage_and_exit()
if count < 1:
usage_and_exit()
need = count + 2 if hosts else count
return count, need
def main():
if len(sys.argv) < 2:
usage_and_exit()
try:
base = ipaddress.ip_network(sys.argv[1], strict=False)
except ValueError:
print("Некорректная базовая сеть:", sys.argv[1])
usage_and_exit()
print("Базовая сеть:", base, "| всего адресов:", base.num_addresses)
print()
cursor = int(base.network_address)
last = int(base.broadcast_address)
rows = []
overflow = False
for item in sys.argv[2:]:
name, sep, raw = item.partition("=")
if not sep:
print("Ошибка: параметр без '=' ->", item)
usage_and_exit()
count, need = parse_size(raw)
prefix = base.max_prefixlen - (need - 1).bit_length()
# Выравниваем начало подсети по границе новой маски
step = 1 << (base.max_prefixlen - prefix)
cursor = (cursor + step - 1) // step * step
net = ipaddress.ip_network((cursor, prefix), strict=False)
if int(net.broadcast_address) > last:
overflow = True
rows.append([
name,
str(net.network_address) + "/" + str(prefix),
str(net.netmask),
str(net.network_address + 1) if need > 2 else str(net.network_address),
str(net.broadcast_address - 1) if need > 2 else str(net.broadcast_address),
str(net.broadcast_address),
str(net.num_addresses),
])
cursor = int(net.broadcast_address) + 1
if overflow:
print("ВНИМАНИЕ: подсетей не хватило внутри базовой сети!")
print()
headers = ["Назначение", "Сеть", "Маска", "Первый хост", "Последний хост",
"Broadcast", "Адресов"]
widths = [max(len(headers[i]), max(len(r[i]) for r in rows)) for i in range(len(headers))]
line = "-+-".join("-" * w for w in widths)
print(" | ".join(h.ljust(w) for h, w in zip(headers, widths)))
print(line)
for row in rows:
print(" | ".join(c.ljust(w) for c, w in zip(row, widths)))
main()
PY