hacid

Návrh softwarového démona (backend server, dále jen „server hacid1)) pro automatickou konfiguraci GPON zařízení - z počátku zaměřený na Huawei SmartAX OLT.

Server na jedné straně komunikuje přímo s OLT, která konfiguruje a spravuje (především protokolem SNMP a (S)FTP), na straně druhé poskytuje programátorské rozhraní (API) pro definici požadované konfigurace OLT a ONT. Konfigurace předávaná přes API je „abstraktní“: neobsahuje konkrétní implementační detaily GPON (například indexy ONT, GEM a pod.), ale třeba jen - v případě nejjednoduššího FTTH ONT - identifikaci (číslo) GPON portu OLT, identifikaci (sériové číslo) ONT a parametry poskytované služby (číslo zákazníka, název internetového tarifu).

Server tedy vytváří mezičlánek usnadňující konfiguraci GPON OLT/ONT podle údajů v databázi zákazníků internetového poskytovatele (ISP). Server není závislý na nějaké konkrétní podobě databáze zákazníků: každý ISP, který chce využívat server hacid, si musí implementovat napojení své databáze zákazníků na API serveru hacid. Obecně je ale toto napojení pro ISP mnohem snazší, než přímá komunikace s OLT (SNMP a pod.).

Způsob, jakým je abstraktní konfigurace „překládána“ na detailní nastavení všech parametrů OLT a ONT, je závislý na zvyklostech konkrétního ISP. Někteří ISP poskytují jen internet, jiní i multicastovou IPTV atd. Implementace serveru je proto otevřená (open source), aby si každý ISP mohl detaily nastavení uzpůsobit svým zvyklostem. Kdybychom místo toho nezavedli abstraktní konfiguraci a ponechali na serverovém API všechny detaily nastavení GPON, tak bychom vlastně celý server hacid degradovali na převodní můstek mezi „Huawei API“ (tedy protokolem SNMP) a nějakým jiným (REST) API.

Běžný ISP má údaje o svých zákaznících a službách, které jim poskytuje, uloženy v nějaké databázi zákazníků. Při zprovoznění služby je pak podle těchto dat nastavován hardware (aktivní síťové prvky) a data o zákaznících jsou tak vlastně na dvou místech: v databázi zákazníků a v hardware. Cílem je, aby to první místo - databáze zákazníků - bylo autoritativním (směrodatným, primárním) zdrojem dat o zákaznících a druhé místo - hardware - data co nejvěrněji kopírovalo. Vlastně chceme při nastavování sítě dosáhnout co nejlepší replikace dat o službách z databáze zákazníků do (nastavení) hardware.

Existující systémy pro správu sítě si většinou udržují vlastní databázi s kompletní konfigurací sítě. Síť podle ní konfigurují a směrem k systémům ISP poskytují jen nějaké rozhraní, přes které musí ISP upravovat (replikovat) konfigurační databázi podle databáze zákazníků. Tento model je přirozený: systém pro správu sítě díky němu dokáže fungovat hned po dodání samostatně (nezávisle na dalších systémech ISP).

My se ale tomuto modelu chceme vyhnout, protože tím vzniká další kopie dat a vždy tak musíte řešit její správnou replikaci. Server hacid si tedy nebude vytvářet vlastní databázi s konfigurací sítě:

  • při jednoduchých změnách konfigurace (přidání jedné služby a pod.) prostě provede požadovanou změnu v hardware tak, aby při tom nepotřeboval znát konfiguraci jiných služeb,
  • při složitějších operacích (typicky: kompletní kontrola konfigurace OLT, která se provádí po restartu serveru nebo restartu OLT nebo namátkově) si server hacid vyžádá konfiguraci přímo z databáze zákazníků. Tuto žádost iniciuje server hacid, proto musí být API mezi serverem hacid a databází zákazníků obousměrné (nově zavedený opačný směr nazveme „reverzní API“ a původní „přímý“ směr volání nazveme pro odlišení „hlavní API“):

Reverzní API (tj. když se server hacid dotazuje databáze zákazníků) může být jednoduché: stačí nějaké rozhraní REST, ve kterém se server hacid jako REST klient napojí na databázi zákazníků a data si „synchronně“ stáhne (data jsou z povahy databáze dostupná „okamžitě“). Když se toto napojení nepodaří nebo při něm vznikne nějaká chyba, zkusí to server hacid znovu později.

Hlavní API (tj. když se systémy ISP/databáze zákazníků napojují na server hacid) je ale možné implementovat více způsoby. Operace zde není vždy možné odbavit okamžitě, jako to šlo v opačném směru (nelze nastavit hardware, který je offline, a pod.). Nabízí se tak využití nějakého asynchronního API třeba v podobě perzistentní fronty zpráv (Apache ActiveMQ a pod.). Má-li takové API dobře fungovat, nesmí při jeho činnosti vznikat faktické chyby (přidání ONT na port, jehož kapacita už je plně obsazena atd.). Aby se uživatel API vyhnul těmto chybám, musel by znát detaily vnitřní konfigurace hardware. My ale chceme naopak API s abstraktní konfigurací, ve které žádné takové detaily nejsou. Proto jsme se nakonec rozhodli i hlavní API implementovat jako synchronní REST API:

  • jednoduché operace (například přidání jedné služby) se provedou přímo a jejich výsledek se rovnou v rámci volání REST API oznámí zpět klientovi; pokud při tom nastane chyba (hardware je offline, naplněná kapacita portu atd.), tak se operace neprovede (a klient REST API tuto chybu musí někde zobrazit uživateli, který operaci inicioval, aby tu operaci zkusil jinak/znovu),
  • vybrané složitější operace (například globální změna rychlosti tarifu, která se musí zapsat do všech OLT) se provedou asynchronně: volání REST API skončí hned po formální kontrole parametrů operace a případné pozdější chyby se ohlásí formou události nebo alarmu.

Právě popsaná volba synchronního API je kompromisem. Snažíme se co nejvíce zjednodušit použití API (hlavně tím, že uživatel API nemusí znát a kontrolovat detaily konfigurace hardware), ale je to za cenu určitého omezení funkcionality (například nelze přidávat služby na OLT ještě před tím, než je OLT uvedeno do provozu). Budeme se ale snažit tento kompromis přizpůsobit běžnému praktickému použití technologie GPON (přidávat služby pro ONT půjde ještě před tím, než bude ONT zapojeno, ale musí být při přidávání dostupné příslušné OLT). Kdyby měl server hacid konfigurovat jinou technologii (například switche Ethernet), volili bychom tyto kompromisy jinak (u switchů by uživateli už pravděpodobně vadilo, že server nepřijme konfiguraci služeb na portech switche, dokud není switch nainstalován).

Pro usnadnění vývoje serveru hacid i pro jeho snazší nasazování v prostředí ISP jsme se rozhodli vytvořit pomocnou konfigurační databázi nazvanou „(server) configdb“. Tímto serverem je možné z počátku nahradit implementaci REST serveru v databázi zákazníků (tj. server reverzního API). Server configdb si bude pamatovat aktuální konfiguraci sítě, jak to bylo popsáno na začátku této části, a bude ji poskytovat serveru hacid. Server configdb se ale musí tuto konfiguraci sítě nějak naučit. Proto rozšíříme reverzní API o operace ukládání: když server hacid zkonfiguruje nějakou jednotlivou službu, tak navíc (volitelně) tuto část konfigurace pošle (přes reverzní API) serveru configdb. Kromě toho je možné databázi configdb upravovat ručně: bude ve formátu JSON.

Následující obrázek shrnuje všechny druhy komunikace, tedy vlastně API, v systémech se serverem hacid. Kromě serveru hacid a konfigurovaného hardware (znázorněného několika OLT) je zde ještě databáze uživatelů userdb (plně v režii konkrétního ISP - názvem „userdb“ jsou zde obecně označeny informační systémy ISP komunikující se serverem hacid) a také pomocná/dočasná konfigurační databáze configdb:

Celkové schéma API

(Jednostranné) šipky na obrázku zobrazují druhy volání REST API. Šipka míří vždy od REST klienta k REST serveru. Šipky ukazující na server hacid tedy tvoří hlavní API, opačné šipky jsou součástí reverzního API. Druhy API shrnuje následující tabulka:

druh „směr“ API popis
Config hlavní Hlavní konfigurační příkazy:
- změny globálních parametrů konfigurace sítě,
- přidávání/odebírání OLT,
- přidávání/změny/odebírání služeb (a tím i ONT).
Query hlavní Pomocné dotazy:
- přehled o stavu serveru hacid a jím konfigurované sítě
- seznam neznámých ONT (výsledky funkce „autofind“).
Event reverzní Události a alarmy:
- chyby příkazů prováděných asynchronně (např. změny globální konfigurace),
- chyby vnikající v rámci údržby sítě (pravidelná/namátková kontrola konfigurace sítě),
- provozní události (připojení neznámého ONT).
Load reverzní Hromadné vyčítání konfigurační databáze.
Save reverzní Změny konfigurační databáze (volitelně, hlavně při použití configdb).

Globální konfigurace GPON

PUT /olt/btv

Změny kanálů IPTV a jejich balíčků. V těle dotazu se předává vždy kompletní nová konfigurace:

{
    "programs": {
        "nova": { "ip": "224.1.2.3" },
        "prima": { "ip": "224.1.2.5" },
        "ctsport": { "ip": "239.3.4.5" }
    },
    "profiles": {
        "bramburkar": { "programs": [ "nova", "prima" ] },
        "sportovec": { "programs": [ "ctsport", "prima", "nova" ] }
    }
}

Tímto příkazem lze mj. odstranit balíček, který je používán. Příslušným uživatelům bude tento balíček odmazán.

Vybrané výsledné kódy:

  • 202 - změna byla přijata, provede se asynchronně,
  • 200 - nová konfigurace je shodná s původní.

Konfigurace OLT

PUT /olt/{olt_ip}

Přidání nebo přejmenování OLT. Tělo dotazu obsahuje jen jméno OLT (identifikátorem OLT v API je vždy jeho IP, jméno slouží jen jako pomocný údaj pro statistiky a pod.):

{ "olt_name": "olt-2.internet.cz" }

Vybrané výsledné kódy:

  • 201 - bylo přidáno nové OLT,
  • 200 - OLT již existovalo: jméno bylo (případně) změněno.
DELETE /olt/{olt_ip}

Odebrání OLT. Dotaz nemá žádné tělo. Server hacid po tomto příkazu přestane ovládat uvedené OLT. Příkaz se provede i v případě, že jsou na OLT definovány nějaké služby (tyto služby hacid neodstraní ani z configdb).

Konfigurace služeb

PUT /olt/{olt_ip}/{frame}/{slot}/{port}/ont/{ont_sn}/{service_name}

Přidání nebo změna služby. Zatím používáme ONT v režimu FTTH, tj. jedna služba odpovídá jednomu ONT. URL už je připraveno i pro FTTB, ale zatím řešíme jen přidání/odebrání služby a server hacid při tom automaticky přidává/odebírá i ONT. Tělo dotazu obsahuje parametry služby:

{
    "svlan": 123,
    "btv_profiles": [ "sportovec" ],
    "plan": "50na20"
}

Pole btv_profiles obsahuje seznam balíčků IPTV. Pokud je seznam prázdný, IPTV se vůbec nezkonfiguruje. Pole plan odkazuje na názvy rychlostních profilů. Tyto profily nejsou definovány přes API, jsou jen součástí implementace serveru hacid pro konkrétního poskytovatele (například profil „50na20“ může znamenat použití předdefinovaného DBA a zároveň nastavení předdefinovaných shaperů na řídicí kartě OLT).

Vybrané výsledné kódy:

  • 201 - služba byla přidána,
  • 200 - služba již existovala: byla (případně) modifikována,
  • 409/OntSnConflict - toto sériové číslo ONT již existuje na jiném portu,
  • 403/OntTooManyServices - pokus o přidání druhé služby (existuje již stejné ONT s jiným názvem služby).
DELETE /olt/{olt_ip}/{frame}/{slot}/{port}/ont/{ont_sn}/{service_name}

Odebrání služby.

Konfigurace ONT

PATCH /olt/{olt_ip}/{frame}/{slot}/{port}/ont/{ont_sn}

Změna sériového čísla ONT (např. při výměně vadného ONT za jiný kus). Tělo dotazu bude obsahovat nové číslo:

{ "ont_sn": "485754430011ffee" }

Reverzní API bude detailně popsáno později. Obecně ale bude vracet (na metodu GET) stejná data, jako byla předána (metodou PUT) v hlavním API. Jen zde budou navíc hromadné operace (např. GET vracející všechna OLT a ne pouze jedno konkrétní).

Server hacid si může během provádění konfiguračních změn (metody PUT a DELETE v hlavním API) vyžádat „starou“ konfiguraci přes reverzní API. To nám dovolí implementaci serveru optimalizovat. Je proto nutné zajistit, aby reverzní API při změnách konfigurace vracelo staré hodnoty až do té doby, než je ukončeno volání hlavního API, které má konfiguraci změnit.

Případný souběh (race condition) bude řešen takto: server hacid v hlavním API bude požadavky na změny serializovat (neprovede dva požadavky na změnu jedné služby najednou, ale postupně). Při prvním požadavku z této „série“ si může vyžádat v reverzním API staré hodnoty. Ale při zpracování dalších požadavků té série si už data z reverzního API nevyžádá (bude si je muset dočasně pamatovat - třeba do další kompletní kontroly konfigurace OLT).

Obecně k návrhu:

  • type obsahuje typ objektu,
  • _id je unikátní identifikátor objektu v rámci celé databáze (to je i vlastnost CouchDB), buď se vytvoří náhodně (algoritmem pro tvorbu UUID), ale raději se sestaví podle identifikace zdrojových dat (např. primární klíč v SQL databází zákazníků); v příkladu je ukázka toho sestavení: aby se zaručila unikátnost _id v celé databázi, tak se použije prefix obsahující typ objektu a dvojtečku :,
  • pokud se nějaký objekt odkazuje na jiný objekt (relace 1:N), tak „dítě“ (tj. objekt na té straně „N“) bude obsahovat atribut, jehož název bude shodný s typem (hodnotou type) „rodiče“ (tj. objektu na straně „1“) a hodnota toho atributu bude shodná s hodnotou atributu _id rodiče: např. když se ONT (dítě) odkazuje na OLT (rodič, má type=„olt“ a _id=„olt:abcd“, tak v objektu ONT bude atribut olt=„olt:abcd“,
  • relaci M:N řešíme obdobně: na straně „N“ (kde se předpokládá N > M) se vytvoří atribut pojmenovaný podle typu „M“, ale na konec jména se přidá písmeno s (anglické množné číslo), hodnotou atributu pak bude pole odpovídajících hodnot _id - zatím např. u tv.profile(M):service(N), tj. v service je atribut tv_profiles, jeho hodnotou je pole hodnot tv.profiles.name (zde se neodkazujeme na _id, protože profiles netvoří samostatný objekt v databázi (a nemají vlastní databázový _id), ale jsou součástí strukturovaného objektu tv.

Příklad objektů v navrhované konfigurační databázi (kvůli komentářům jsem použil zápis ve formátu YAML, přestože dokumenty v databázi jsou uloženy jako JSON):

- _id: tv # ID musí být přesně 'tv' (protože objekt TV je jediný)
  type: tv
  programs:
  - name: nova # unikátní jméno programu (v rámci tohoto objektu)
    ip: 224.1.2.3 # group IP
  - name: prima
    ip: 224.1.2.5
  - name: ctsport
    ip: 239.3.4.5
  profiles:
  - name: bramburkar # unikátní jméno profilu (v rámci tohoto objektu)
    programs: [ nova, prima ]
  - name: sportovec
    programs: [ ctsport, prima, nova ]

- _id: olt:olt-t # jen příklad tvorby ID (může být klidně i náhodný UUID atp.)
  type: olt
  name: olt-t # unikátní?
  ip: olt-t.nwk.din.cz # IP adresa nebo hostname managementu OLT
  hacid: test1 # název instance serveru hacid, který toto OLT spravuje
  vlan_mgmt: 101 # číslo VLAN pro in-band management
  vlan_tv_mcast: 222 # číslo VLAN pro (uplink) multicast
  lag_ports: [ 0/8/0, 0/9/0 ] # uplink porty (spojeny do jednoho LACP trunku)

- _id: ont:olt-t.485754430011ffee # opět jen příklad tvorby ID
  type: ont
  name: olt-t.485754430011ffee # unikátní?
  sn_gpon: 485754430011ffee # GPON SN: 16 znaků [0-9a-f]
  olt: olt:olt-t # ref:olt._id
  olt_port: 0/1/1 # jméno portu GPON na OLT, ve kterém je toto ONT
  mode: sfu # zatím jediná (ale povinná) hodnota 'sfu'

- _id: service:novak # opět jen příklad tvorby ID
  type: service
  name: novak # unikátní?
  ont: ont:olt-t.485754430011ffee # ref:ont._id
  vlan_net: 123 # číslo VLAN (tj. service VLAN, S-VLAN) pro službu internet
  plan_net: 50na20 # tarif služby internet (ovlivňuje např. rychlost)
  tv_profiles: [ sportovec ] # (nepovinný atribut) ref:[tv.profiles.name]

1)
název vznikl z počátečních písmen slov Huawei (hardware) abstract configuration interface daemon
  • huawei/hacid.txt
  • Poslední úprava: 3. 6. 2019, 08:54
  • autor: Milan Krčmář