Nəzəri əsaslar və memarlıq
Operator səviyyəli konfiqurasiya generasiyası Python, Jinja2 şablonlaşdırıcısı və çatdırma kitabxanaları (Scrapli, Netmiko, NAPALM, Nornir) üzərində qurulur. Infrastructure as Code (IaC) prinsipi məlumatı, şablonu və nəqliyyatı ayırır: 1. Xidmət məlumatları (Service Data) YAML və ya JSON-da saxlanılır (müştəri adı, RD, RT, CE prefikslər, QoS parametrləri) və render-dən əvvəl sxem (Pydantic, JSON Schema) və biznes qaydaları ilə yoxlanılır. 2. Jinja2 şablonu (.j2) konkret vendorun sintaksisini (Cisco, Huawei, Juniper, MikroTik) dəyişən əvəzləmələri ({{ vrf_name }}, {{ rd }}) və idarəetmə konstruksiyaları ({% for peer in peers %}, {% if vlan %}) ilə saxlayır; StrictUndefined boş dəyərin render olunmasına imkan vermir. 3. Python skripti konfiqurasiyanı render edir və çatdırır: Netmiko və Scrapli CLI sətirlərini tranzaksiyasız göndərir (nəticə yoxlanmalıdır), NAPALM load_merge_candidate, compare_config, commit_config və rollback-u yalnız dəstəklənən draylerlər üçün təklif edir (məsələn, ios və junos; Huawei VRP və RouterOS üçün əsas paketdə draylerlər yoxdur), Nornir tapşırıqları paralel icra edir. Bu yanaşma yüzlərlə oxşar blokda səhvləri aradan qaldırır, lakin məlumat yoxlamasını, tətbiqdən əvvəl diff-i və post-yoxlamaları əvəz etmir.
Həqiqət mənbəyi şablondan ayrıdır
PE üçün Infrastructure as Code yanaşması üç şeyin ayrılmasına əsaslanır: məlumat (nə konfiqurasiya olunmalıdır), şablon (bu konkret platformada necə yazılır) və nəqliyyat (necə çatdırılır). Məlumat niyyəti təsvir edir: A müştərisi, RT 65000:100, PE-CE interfeysi. O, Git-də və ya NetBox-da YAML/JSON şəklində saxlanılır. Şablon hər istehsalçı üçün biznes məntiqsiz Jinja2 faylıdır. Nəqliyyat Netmiko, NAPALM, Scrapli və ya Nornir-dir. Bir niyyət dörd sintaksisə render olunur, cihazlarda isə əl ilə düzəliş olmur.
# data/customers.yml (source of truth, no secrets)
asn: 65000
vrfs:
- {name: A, rt: "65000:100", rd_offset: 0, iface: ether2}
- {name: B, rt: "65000:200", rd_offset: 10, iface: ether3}
- {name: SS, rt: "65000:900", rt_import: ["65000:900", "65000:100"],
rd_offset: 20, iface: ether4}RD asn:(pe_id + rd_offset) düsturu ilə yığılır və hər PE-də unikaldır; bu, bir VRF-nin prefikslərini iki PE-dən ayrıca elan etmək üçün lazımdır (multipath, sürətli konvergensiya).
Render-dən əvvəl məlumatların validasiyası
Məlumatlar sxemlə yoxlanılır: Pydantic (tiplər, RT və RD üçün ^\d+:\d+$ requlyar ifadəsi), JSON Schema və ya yamllint. Biznes qaydaları kodla yazılır: müştəri RT-si icazəli siyahıdadır, VRF adları unikaldır, RD PE daxilində unikaldır, interfeys PE-yə aiddir. Məlumat yanlışdırsa, şablon çağırılmamalıdır: bu mərhələdəki xəta sıfıra başa gəlir, cihazda isə insidentə.
Jinja2: konfiqurasiyalar üçün həqiqətən nə lazımdır
StrictUndefined: susmaya görə naməlum dəyişən boş sətir kimi render olunur və dəyərsizroute-target exportalırsınız.StrictUndefinedilə renderUndefinedErrorilə sınır.trim_blocks=True,lstrip_blocks=True,keep_trailing_newline=True:{% %}teqləri boş sətir və çəkinti qoymur. Konfiqurasiyalar üçün çəkinti vacibdir (Cisco və Huawei-də iç-içəlik boşluqlarla müəyyən olunur).loop.indexilə dövrlər,sort(attribute="name")filtri (sabit çıxış sırası, deməli təmiz diff), məcburi olmayan sahələr üçündefault([...]),join(","),selectattr,map,unique.- İfadələrdə arifmetika (
pe_id + v.rd_offset), təkrarlanan bloklar üçünmacrovəinclude, dövr daxilində sayğaclar üçünnamespace. - Konfiqurasiyalar üçün
autoescapesöndürülüb: bu HTML deyil. Lakin etibarsız mənbədən olan şablonları da render etmək olmaz; bunun üçünSandboxedEnvironmentvar. - Fərdi filtrlər (məsələn, RD yoxlaması)
env.filters-də qeydiyyatdan keçirilir.
Render deterministik olmalıdır: eyni məlumat bayt-bayt eyni nəticə verir, əks halda CI-da diff küylü olur.
import yaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined
env = Environment(
loader=FileSystemLoader("templates"),
undefined=StrictUndefined,
trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True,
)
data = yaml.safe_load(open("data/customers.yml"))
cfg = env.get_template("pe_ios.j2").render(**data, pe_id=1)Nəqliyyat: Netmiko, NAPALM, Scrapli, Nornir
- Netmiko (
ConnectHandler,cisco_xe,juniper_junos,huawei_vrpv8,mikrotik_routerostipləri) CLI sətirlərini göndərir. O, tranzaksiyalar haqqında heç nə bilmir:error_patternverilməyibsə,send_config_set% Invalid inputzamanı dayanmır. Çıxışı yoxlayın. - NAPALM
ios,junosvə digər drayverlər üçün vahidload_merge_candidate,compare_config,commit_config,discard_config,rollbackAPI-si verir; əsas paketdə Huawei VRP və RouterOS üçün drayver yoxdur. Junos-dacommit_config(revert_in=...)commit confirmed-i həyata keçirir, IOS-XE-də isə geri qayıtmaconfigure replace-ə əsaslanır. - Scrapli böyük parklarda Netmiko-dan sürətlidir və tipli drayverlərə malikdir; istehsalçı platformaları düzgün seçilməlidir (IOS-XE drayveri Junos-a uyğun gəlmir).
- Nornir tapşırıqları paralel icra edir (
num_workers), inventory-ni Python-da saxlayır və mürəkkəb ardıcıllıqlar üçün istifadə olunur: əvvəl canary, sonra qalanlar.
İdempotentlik və diff
Hər işə salınmada bütün konfiqurasiyanı göndərmək pisdir: loqları küylü edir və riski artırır. Fərqi hesablamaq lazımdır. Junos-da compare_config candidate diff-ini göstərir. IOS-XE-də render olunmuş sətirlər show running-config | section vrf definition ilə müqayisə olunur. RouterOS üçün müqayisə /export ilə aparılır. Diff boşdursa, cihaza heç nə göndərilmir: bu, skript səviyyəsində idempotentlikdir.