147 профілів на 67 вендорів у ncm.profiles: Cisco, Huawei, Juniper, MikroTik, Eltex, D-Link, HP, Brocade, Extreme, Alcatel, Allied Telesis, Qtech, ZTE, BDCOM та інші. Розбір через ast, без виконання: NOC-скрипт тягне половину свого фреймворку, і імпортувати його означало б або принести весь NOC, або підсунути заглушки, які мовчки змінюють поведінку. З кожного get_config.py береться перша команда збору, гілка startup, strip_first_lines і регекси платформ. Альтернативи лишаються в коментарі. Шум (exit, sh, changeto system, terminal width 200) відсіюється явним списком: Cisco.ASA після фільтра дає рівно more system:running-config, Juniper.JUNOSe — рівно show running-configuration замість чотирьох команд із сусідніх гілок. Пейджер додається лише мережевому CLI: Eltex SMG і TAU знімають конфіг через cat, і terminal datadump у bash просто впав би. Важливо: команди — з NOC, а промпти й вимкнення пейджера — ні. У дампі всі __init__.py порожні (359 із 364), а NOC тримає pattern_prompt саме там. Вони проставлені за родиною вендора з типових значень і потребують перевірки на живому залізі. Не витягнулись 23 з 170, і не через помилку: 21 знімає конфіг по HTTP (камери, відеокодери, MikroTik SwOS, HP iLO2), 2 — порожні заглушки в самому NOC. Виконувати профілі поки нікому: агентського модуля ncm (SSH/Telnet) немає, це Етап 7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.2 KiB
Python
69 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
||
"""Чому конкретний get_config.py не дав команди — з цитатою з джерела."""
|
||
import ast
|
||
import pathlib
|
||
import re
|
||
import sys
|
||
|
||
ROOT = pathlib.Path(sys.argv[1])
|
||
sys.path.insert(0, str(pathlib.Path(__file__).parent))
|
||
from extract_noc import parse_file # noqa: E402
|
||
|
||
|
||
def classify(path, res):
|
||
src = path.read_text(encoding="utf-8", errors="replace")
|
||
|
||
if "error" in res:
|
||
return "не розібрався", res["error"]
|
||
|
||
variants = res["variants"]
|
||
if not variants:
|
||
# Метод execute* є, але без cli/snmp — або його немає взагалі.
|
||
if "def execute" not in src:
|
||
return "немає методу execute", ""
|
||
return "execute без cli()", first_body(src)
|
||
|
||
if not any(v["running"] or v["startup"] for v in variants):
|
||
if any(v["uses_snmp"] for v in variants):
|
||
return "лише SNMP", ""
|
||
if any(v["dynamic_cli"] for v in variants):
|
||
return "команда збирається в рантаймі", dynamic_line(src)
|
||
return "невідомо", ""
|
||
return None, ""
|
||
|
||
|
||
def first_body(src):
|
||
m = re.search(r"def execute\w*\([^)]*\):\s*\n((?:\s{8}.*\n){1,6})", src)
|
||
return (m.group(1).strip().splitlines()[0] if m else "")[:100]
|
||
|
||
|
||
def dynamic_line(src):
|
||
for line in src.splitlines():
|
||
if ".cli(" in line and not re.search(r'\.cli\(\s*["\']', line):
|
||
return line.strip()[:100]
|
||
return ""
|
||
|
||
|
||
def main():
|
||
groups = {}
|
||
for f in sorted(ROOT.rglob("get_config.py")):
|
||
res = parse_file(f)
|
||
reason, detail = classify(f, res)
|
||
if reason is None:
|
||
continue
|
||
rel = str(f.relative_to(ROOT)).replace("\\", "/")
|
||
groups.setdefault(reason, []).append((rel, detail))
|
||
|
||
total = sum(len(v) for v in groups.values())
|
||
print(f"не витягнулось: {total}\n")
|
||
for reason, items in sorted(groups.items(), key=lambda x: -len(x[1])):
|
||
print(f"=== {reason}: {len(items)} ===")
|
||
for rel, detail in items:
|
||
print(f" {rel}")
|
||
if detail:
|
||
print(f" {detail}")
|
||
print()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|