hacid
Obecné
Návrh softwarového démona (backend server, dále jen „server hacid“1)) 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.
Výběr formy 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.
Celkové schéma API
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:
(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). |
Hlavní API
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
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).
Změna: objektová databáze
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 atributolt
=„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ř. utv.profile
(M):service
(N), tj. vservice
je atributtv_profiles
, jeho hodnotou je pole hodnottv.profiles.name
(zde se neodkazujeme na_id
, protožeprofiles
netvoří samostatný objekt v databázi (a nemají vlastní databázový_id
), ale jsou součástí strukturovaného objektutv
.
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]