SkillAgentSearch skills...

Ctreepo

parsing and working with configuration of network devices

Install / Use

/learn @ozontech/Ctreepo
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

CTreePO - Configuration Tree Patch Overview

Краткое описание

Библиотека для работы с конфигурацией сетевых устройств:

  • преобразование конфигурации в дерево
  • поиск/фильтрация конфигурации
  • вычисление разницы (diff) между двумя конфигурациями

Быстрый пример (00.quick-start.py)

  • Читаем текущий и целевой конфигурации из файлов
  • Преобразуем конфигурации в деревья, попутно размечая тегами секции bgp и static routes
  • Получаем разницу конфигураций
  • Фильтруем разницу (а можно сначала фильтровать текущее/целевое деревья, а потом вычислять разницу между ними)
<details> <summary>Листинг (click me)</summary>
In [2]: from ctreepo import CTreeEnv, Vendor

In [3]: def get_configs() -> tuple[str, str]:
   ...:     with open(file="./examples/configs/cisco-router-1.txt", mode="r") as f:
   ...:         current_config = f.read()
   ...:     with open(file="./examples/configs/cisco-router-2.txt", mode="r") as f:
   ...:         target_config = f.read()
   ...:     return current_config, target_config
   ...: 

In [4]: def get_ct_environment() -> CTreeEnv:
   ...:     tagging_rules: list[dict[str, str | list[str]]] = [
   ...:         {"regex": r"^router bgp \d+$", "tags": ["bgp"]},
   ...:         {"regex": r"^ip route \S+", "tags": ["static"]},
   ...:     ]
   ...:     return CTreeEnv(
   ...:         vendor=Vendor.CISCO,
   ...:         tagging_rules=tagging_rules,
   ...:     )
   ...: 

In [5]: current_config, target_config = get_configs()

In [6]: env = get_ct_environment()

In [7]: current = env.parse(current_config)

In [8]: target = env.parse(target_config)

In [9]: diff = env.diff(current, target)

In [10]: print("\n!-- разница конфигураций --")
    ...: print(diff.config)
    ...: 

!-- разница конфигураций --
interface Tunnel2
 no ip ospf priority 0
 ip ospf priority 1
!
router bgp 64512
 no neighbor RR peer-group
 address-family ipv4
  network 10.255.255.1 mask 255.255.255.255
!
line vty 0 4
 no exec-timeout 15 0
 exec-timeout 10 0
!
line vty 5 15
 no exec-timeout 15 0
 exec-timeout 10 0
!
ip name-server 192.168.0.9
!
no ip name-server 192.168.0.3
!
no ip route 192.168.255.1 255.255.255.255 Tunnel2
!
no ip route vrf FVRF 192.66.55.44 255.255.255.255 143.31.31.2
!

In [11]: print("\n!-- разница без секций с тегами bgp и static --")
    ...: diff_no_routing = env.search(diff, exclude_tags=["bgp", "static"])
    ...: print(diff_no_routing.config)
    ...: 

!-- разница без секций с тегами bgp и static --
interface Tunnel2
 no ip ospf priority 0
 ip ospf priority 1
!
line vty 0 4
 no exec-timeout 15 0
 exec-timeout 10 0
!
line vty 5 15
 no exec-timeout 15 0
 exec-timeout 10 0
!
ip name-server 192.168.0.9
!
no ip name-server 192.168.0.3
!

In [12]: print("\n!-- разница в секции с тегом bgp --")
    ...: diff_bgp = env.search(diff, include_tags=["bgp"])
    ...: print(diff_bgp.config)
    ...: 

!-- разница в секции с тегом bgp --
router bgp 64512
 no neighbor RR peer-group
 address-family ipv4
  network 10.255.255.1 mask 255.255.255.255
!
</details> <br>

Преобразование в дерево (01.parsing.py)

  • Преобразование текстовой конфигурации в дерево на основе отступов в тексте
  • Возможность размечать секции/строки тегами для последующей фильтрации
  • pre-run и post-run обработка конфига и получившегося дерева, например нормализация входного конфига, обработка баннеров (cisco) и пр.
<details> <summary>Листинг (click me)</summary>
In [1]: from ctreepo import CTreeEnv, Vendor

In [2]: def get_configs() -> str:
   ...:     with open(file="./examples/configs/cisco-example-1.txt", mode="r") as f:
   ...:         config = f.read()
   ...:     return config
   ...: 

In [3]: def get_ct_environment() -> CTreeEnv:
   ...:     return CTreeEnv(vendor=Vendor.CISCO)
   ...: 

In [4]: config_config = get_configs()

In [5]: env = get_ct_environment()

In [6]: current = env.parse(config_config)

In [7]: print("\n---дерево в виде привычной конфигурации---")
   ...: print(current.config)

---дерево в виде привычной конфигурации---
service tcp-keepalives-in
!
service timestamps debug datetime msec localtime show-timezone
!
enable secret 5 2Fe034RYzgb7xbt2pYxcpA==
!
aaa group server tacacs+ TacacsGroup
 server 192.168.0.100
 server 192.168.0.101
!
interface Tunnel1
 ip address 10.0.0.2 255.255.255.0
 no ip redirects
!
interface Tunnel2
 ip address 10.1.0.2 255.255.255.0
 no ip redirects
!
interface FastEthernet0
 switchport access vlan 100
 no ip address
!
router bgp 64512
 neighbor 192.168.255.1 remote-as 64512
 neighbor 192.168.255.1 update-source Loopback0
 address-family ipv4
  network 192.168.100.0 mask 255.255.255.0
  neighbor 192.168.255.1 activate
!

In [8]: print("\n---конфигурация с маскированными секретами---")
   ...: print(current.masked_config)

---конфигурация с маскированными секретами---
service tcp-keepalives-in
!
service timestamps debug datetime msec localtime show-timezone
!
enable secret 5 ******
!
aaa group server tacacs+ TacacsGroup
 server 192.168.0.100
 server 192.168.0.101
!
interface Tunnel1
 ip address 10.0.0.2 255.255.255.0
 no ip redirects
!
interface Tunnel2
 ip address 10.1.0.2 255.255.255.0
 no ip redirects
!
interface FastEthernet0
 switchport access vlan 100
 no ip address
!
router bgp 64512
 neighbor 192.168.255.1 remote-as 64512
 neighbor 192.168.255.1 update-source Loopback0
 address-family ipv4
  network 192.168.100.0 mask 255.255.255.0
  neighbor 192.168.255.1 activate
!

In [9]: print("\n---дерево в виде патча для устройства---")
   ...: print(current.patch)

---дерево в виде патча для устройства---
service tcp-keepalives-in
service timestamps debug datetime msec localtime show-timezone
enable secret 5 2Fe034RYzgb7xbt2pYxcpA==
aaa group server tacacs+ TacacsGroup
server 192.168.0.100
server 192.168.0.101
exit
interface Tunnel1
ip address 10.0.0.2 255.255.255.0
no ip redirects
exit
interface Tunnel2
ip address 10.1.0.2 255.255.255.0
no ip redirects
exit
interface FastEthernet0
switchport access vlan 100
no ip address
exit
router bgp 64512
neighbor 192.168.255.1 remote-as 64512
neighbor 192.168.255.1 update-source Loopback0
address-family ipv4
network 192.168.100.0 mask 255.255.255.0
neighbor 192.168.255.1 activate
exit
exit

In [10]: print("\n---патч с маскированными секретами---")
    ...: print(current.masked_patch)

---патч с маскированными секретами---
service tcp-keepalives-in
service timestamps debug datetime msec localtime show-timezone
enable secret 5 ******
aaa group server tacacs+ TacacsGroup
server 192.168.0.100
server 192.168.0.101
exit
interface Tunnel1
ip address 10.0.0.2 255.255.255.0
no ip redirects
exit
interface Tunnel2
ip address 10.1.0.2 255.255.255.0
no ip redirects
exit
interface FastEthernet0
switchport access vlan 100
no ip address
exit
router bgp 64512
neighbor 192.168.255.1 remote-as 64512
neighbor 192.168.255.1 update-source Loopback0
address-family ipv4
network 192.168.100.0 mask 255.255.255.0
neighbor 192.168.255.1 activate
exit
exit

In [11]: print("\n---дерево в виде формальной конфигурации (аналогично formal в ios-xr)---")
    ...: print(current.formal_config)

---дерево в виде формальной конфигурации (аналогично formal в ios-xr)---
service tcp-keepalives-in
service timestamps debug datetime msec localtime show-timezone
enable secret 5 2Fe034RYzgb7xbt2pYxcpA==
aaa group server tacacs+ TacacsGroup / server 192.168.0.100
aaa group server tacacs+ TacacsGroup / server 192.168.0.101
interface Tunnel1 / ip address 10.0.0.2 255.255.255.0
interface Tunnel1 / no ip redirects
interface Tunnel2 / ip address 10.1.0.2 255.255.255.0
interface Tunnel2 / no ip redirects
interface FastEthernet0 / switchport access vlan 100
interface FastEthernet0 / no ip address
router bgp 64512 / neighbor 192.168.255.1 remote-as 64512
router bgp 64512 / neighbor 192.168.255.1 update-source Loopback0
router bgp 64512 / address-family ipv4 / network 192.168.100.0 mask 255.255.255.0
router bgp 64512 / address-family ipv4 / neighbor 192.168.255.1 activate
</details> <br>

Поиск/фильтрация (02.searching.py)

  • может быть на основе тегов, проставленных во время преобразования в дерево
  • может быть по строке (regex)
  • в результате получается копия дерева с которой можно работать так же, как и с оригиналом
<details> <summary>Листинг (click me)</summary>
In [1]: from ctreepo import CTreeEnv, Vendor
   ...: 
   ...: 
   ...: def get_configs() -> str:
   ...:     with open(file="./examples/configs/cisco-example-1.txt", mode="r") as f:
   ...:         config = f.read()
   ...:     return config
   ...: 
   ...: 
   ...: def get_ct_environment() -> CTreeEnv:
   ...:     tagging_rules: list[dict[str, str | list[str]]] = [
   ...:         {"regex": r"^router bgp \d+$", "tags": ["bgp"]},
   ...:         {"regex": r"^interface (Tunnel1) / ip address .*", "tags": ["interface", "tunnel-1-ip"]},
   ...:         {"regex": r"^interface (Tunnel2) / ip address .*", 
View on GitHub
GitHub Stars8
CategoryDevelopment
Updated3mo ago
Forks1

Languages

Python

Security Score

82/100

Audited on Dec 24, 2025

No findings